transcend-0.3+dfsg2.orig/0000750000175000017500000000000010305077130013720 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/0000750000175000017500000000000010305077605015667 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/0000750000175000017500000000000010305077064017357 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/HostAddress.h0000640000175000017500000002154007554101513021755 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-December-12 Jason Rohrer * Made serializable. * Changed the constructor signature. * * 2001-December-14 Jason Rohrer * Added a method for getting the local address. * Fixed several typos. * Added functions for testing equality and making copies. * Fixed a comment. * * 2002-February-25 Jason Rohrer * Made destructor virtual. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * Changed equality function to resolve both addresses to * their numerical form first. * Changed equality function to be localhost savvy. * * 2002-March-26 Jason Rohrer * Added some print statements for testing. * Removed testing print statements. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-April-8 Jason Rohrer * Added an isNumerical function because hostname lookups on * numerical addresses are slow on Win32. * * 2002-April-11 Jason Rohrer * Fixed a bug in equals when both lookups fail. * * 2002-July-23 Jason Rohrer * Fixed a bug in equals when our local lookup fails. */ #include "minorGems/common.h" #ifndef HOST_ADDRESS_CLASS_INCLUDED #define HOST_ADDRESS_CLASS_INCLUDED #include #include #include "minorGems/io/Serializable.h" #include "minorGems/util/stringUtils.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Address of a network host. * * @author Jason Rohrer */ class HostAddress : public Serializable { public: /** * Constructs an uninitialized address. * * Note that all other functions (besides deserialize and the * destructor) assume that the HostAddress is initialized. * * Useful prior to deserialization. */ HostAddress(); /** * Constructs an address. * * @param inAddressString a \0-terminated string containing the * host address. This string will be destroyed when this class * is destroyed, so it cannot be const. * @param inPort the port of the host. */ HostAddress( char *inAddressString, int inPort ); virtual ~HostAddress(); /** * Gets the address of the local host. * * @return the address of the local host. * Note that the mPort field in the returned HostAddress is * set to 0. * Must be destroyed by caller. */ static HostAddress *getLocalAddress(); /** * Gets the address of the local host in numerical format. * * @return the address of the local host. * Note that the mPort field in the returned HostAddress is * set to 0. * Must be destroyed by caller. */ static HostAddress *getNumericalLocalAddress(); /** * Gets a numerical version of this host address. * * For example, if we are using IPv4, this will generate a * HostAddress containing an aaa.bbb.ccc.ddd style address. * * @return a numerical version of this address, or NULL * if address resolution fails. * Must be destroyed by caller if non-NULL. */ HostAddress *getNumericalAddress(); /** * Gets whether this address is in numerical format. * For IpV4, this is xxx.xxx.xxx.xxx * * @return true if this address is numerical. */ char isNumerical(); /** * Gets whether another address is equivalent to this address. * * @param inAddress the address to compare to this address. * * @return true iff inAddress is equivalent to this address. */ char equals( HostAddress *inAddress ); /** * Makes a copy of this host address. * * @return the copy of this address. */ HostAddress *copy(); /** * Prints this address to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); char *mAddressString; int mAddressLength; int mPort; }; inline HostAddress::HostAddress( char *inAddressString, int inPort ) : mAddressString( inAddressString ), mPort( inPort ) { mAddressLength = strlen( mAddressString ); } inline HostAddress::HostAddress() : mAddressString( NULL ) { mAddressLength = 0; mPort = 0; } inline HostAddress::~HostAddress() { if( mAddressString != NULL ) { delete [] mAddressString; } } inline HostAddress *HostAddress::getNumericalLocalAddress() { HostAddress *address = getLocalAddress(); HostAddress *numAddress = address->getNumericalAddress(); delete address; return numAddress; } inline char HostAddress::equals( HostAddress *inAddress ) { // if the port numbers are not equal, we can return // right away if( mPort != inAddress->mPort ) { return false; } // otherwise, the port numbers are equal, // so we need to compare the addresses // first, try to compare numercally looked-up addresses HostAddress *numericalThis = this->getNumericalAddress(); if( numericalThis != NULL ) { HostAddress *numericalOther = inAddress->getNumericalAddress(); if( numericalOther != NULL ) { char returnVal = false; // watch out for localhost loopbacks if( !strcmp( numericalThis->mAddressString, "127.0.0.1" ) ) { // this address is localhost // make sure other address is not our external local address HostAddress *localAddress = getNumericalLocalAddress(); if( localAddress != NULL ) { if( !strcmp( localAddress->mAddressString, numericalOther->mAddressString ) ) { returnVal = true; } delete localAddress; } else { // numerical lookup failed for one but not the // other, so assume addresses are different returnVal = false; } } else if( !strcmp( numericalOther->mAddressString, "127.0.0.1" ) ) { // other address is localhost // make sure this address is not our external local address HostAddress *localAddress = getNumericalLocalAddress(); if( localAddress != NULL ) { if( !strcmp( localAddress->mAddressString, numericalThis->mAddressString ) ) { returnVal = true; } delete localAddress; } else { // numerical lookup failed for one but not the // other, so assume addresses are different returnVal = false; } } // if numerical addresses are identical, // then hosts are equal if( !strcmp( numericalThis->mAddressString, numericalOther->mAddressString ) ) { returnVal = true; } delete numericalOther; delete numericalThis; return returnVal; } delete numericalThis; } // otherwise, if lookup fails, compare raw adddresses if( !strcmp( inAddress->mAddressString, mAddressString ) ) { return true; } else { return false; } } inline HostAddress *HostAddress::copy() { char *stringCopy = new char[ strlen( mAddressString ) + 1 ]; strcpy( stringCopy, mAddressString ); return new HostAddress( stringCopy, mPort ); } inline void HostAddress::print() { printf( "%s:%d", mAddressString, mPort ); } inline int HostAddress::serialize( OutputStream *inOutputStream ) { int numTransmitted = 0; numTransmitted += inOutputStream->writeLong( (long)mAddressLength ); numTransmitted +=inOutputStream->write( (unsigned char *)mAddressString, mAddressLength ); numTransmitted += inOutputStream->writeLong( (long)mPort ); return numTransmitted; } inline int HostAddress::deserialize( InputStream *inInputStream ) { int numTransmitted = 0; long readLong; numTransmitted += inInputStream->readLong( &readLong ); mAddressLength = (int)readLong; if( mAddressString != NULL ) { delete [] mAddressString; } mAddressString = new char[ mAddressLength ]; numTransmitted +=inInputStream->read( (unsigned char *)mAddressString, mAddressLength ); numTransmitted += inInputStream->readLong( &readLong ); mPort = readLong; return numTransmitted; } #endif transcend-0.3+dfsg2.orig/minorGems/network/unix/0000750000175000017500000000000010305077064020342 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/unix/SocketUDPUnix.cpp0000640000175000017500000001453710155050216023517 0ustar pabspabs/* * Modification History * * 2004-November-3 Jason Rohrer * Created. * * 2004-November-4 Jason Rohrer * Added code for win32 compatibility. * Fixed a memcpy bug. * * 2004-December-7 Jason Rohrer * Fixed a bug in the evaluation of wait return codes. */ /* * The following changes were needed for win32 compatibility: * -- Winsock.h included * -- socklen_t defined * -- closesocket instead of close * -- inet_aton does not exist on win32 (switched to the linux-obsolete * inet_addr) */ #include "minorGems/network/SocketUDP.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #ifndef WIN_32 // standard unix includes #include #include #include #include #include #include #include #else // special includes for win32 #include // windows does not define socklen_t typedef int socklen_t; // windows does not have a close function for sockets #define close( inSocketID ) closesocket( inSocketID ) #endif #ifdef BSD // BSD does not define socklen_t typedef int socklen_t; #endif // prototypes /** * Waits on a socket for incoming data. * * @param inSocketID the socket to watch. * @param inMilliseconds the timeout. * * @return 1 if data is ready to be read with recvfrom, -2 if we * timeout, and -1 on an error. */ int waitForIncomingData( int inSocketID, long inMilliseconds ); SocketUDP::SocketUDP( unsigned short inReceivePort ) { int socketID; socketID = socket( AF_INET, SOCK_DGRAM, 0 ); // bind to receivePort struct sockaddr_in bindAddress; bindAddress.sin_family = AF_INET; bindAddress.sin_port = htons( inReceivePort ); bindAddress.sin_addr.s_addr = INADDR_ANY; bind( socketID, (struct sockaddr *)&bindAddress, sizeof(bindAddress) ); int *socketIDArray = new int[1]; socketIDArray[0] = socketID; mNativeObjectPointer = (void *)socketIDArray; } SocketUDP::~SocketUDP() { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; close( socketID ); delete [] socketIDArray; } struct UDPAddress *SocketUDP::makeAddress( const char *inAddress, unsigned short inPort ) { // try converting it from aaa.bbb.ccc.ddd int convertedAddress = inet_addr( inAddress ); if( convertedAddress != -1 ) { struct UDPAddress *address = new struct UDPAddress; address->mIPAddress = convertedAddress; address->mPort = htons( inPort ); return address; } else { return NULL; } } char *SocketUDP::extractAddress( struct UDPAddress *inAddress, unsigned short *outPort ) { struct in_addr addressStructure; addressStructure.s_addr = inAddress->mIPAddress; NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *addressString = stringDuplicate( inet_ntoa( addressStructure) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); *outPort = ntohs( inAddress->mPort ); return addressString; } int SocketUDP::send( struct UDPAddress *inAddress, unsigned char *inData, unsigned long inNumBytes ) { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; // pack our address into the required structure struct sockaddr_in toAddress; toAddress.sin_family = AF_INET; toAddress.sin_port = inAddress->mPort; toAddress.sin_addr.s_addr = inAddress->mIPAddress; return sendto( socketID, (char *)inData, inNumBytes, 0, (struct sockaddr *)( &toAddress ), sizeof( toAddress ) ); } int SocketUDP::receive( struct UDPAddress **outAddress, unsigned char **outData, long inTimeout ) { // unwrap our native object int *socketIDArray = (int *)( mNativeObjectPointer ); int socketID = socketIDArray[0]; if( inTimeout != -1 ) { int waitValue = waitForIncomingData( socketID, inTimeout ); // timed out or saw an error while waiting if( waitValue == -1 || waitValue == -2 ) { *outAddress = NULL; *outData = NULL; return waitValue; } // else we have data waiting } struct sockaddr_in fromAddress; int bufferSize = 10000; unsigned char *receiveBuffer = new unsigned char[ bufferSize ]; socklen_t fromAddressLength = sizeof( fromAddress ); int numReceived; numReceived = recvfrom( socketID, (char *)receiveBuffer, bufferSize, 0, (struct sockaddr *)( &fromAddress ), &fromAddressLength ); // if no error and no overflow if( numReceived >=0 && numReceived <= bufferSize ) { *outAddress = new struct UDPAddress; (*outAddress)->mPort = fromAddress.sin_port; (*outAddress)->mIPAddress = fromAddress.sin_addr.s_addr; *outData = new unsigned char[ numReceived ]; memcpy( (void *)( *outData ), (void *)receiveBuffer, numReceived ); } else { *outAddress = NULL; *outData = NULL; } delete [] receiveBuffer; return numReceived; } /* socket timing code adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ int waitForIncomingData( int inSocketID, long inMilliseconds ) { fd_set fsr; struct timeval tv; int returnValue; FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; returnValue = select( inSocketID + 1, &fsr, NULL, NULL, &tv ); if( returnValue == 0 ) { return -2; } else if( returnValue == -1 ) { printf( "Selecting socket during receive failed.\n" ); return returnValue; } else { return returnValue; } } transcend-0.3+dfsg2.orig/minorGems/network/web/0000750000175000017500000000000010305077064020134 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/web/WebClient.h0000640000175000017500000001147307554101513022167 0ustar pabspabs/* * Modification History * * 2002-May-5 Jason Rohrer * Created. * Added a utility function for receiving data. * * 2002-May-12 Jason Rohrer * Added support for fetching the final (after redirects) URL. * * 2002-May-26 Jason Rohrer * Added support for fetching mime types and content length. * Added a function for fetching MIME types alone. */ #include "minorGems/common.h" #ifndef WEB_CLIENT_INCLUDED #define WEB_CLIENT_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketClient.h" #include "minorGems/network/SocketStream.h" #include #include /** * A class that implements a basic web client. * * @author Jason Rohrer. */ class WebClient { public: /** * Gets a web page. * * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * @param outFinalURL pointer to where the actual * URL of the content (after following redirects) should be returned, * or NULL to ignore the final URL. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param outMimeType pointer to where the MIME type * of the content should be returned, * or NULL to ignore the MIME type. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * * @return the fetched web page as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *getWebPage( char *inURL, int *outContentLength, char **outFinalURL = NULL, char **outMimeType = NULL ); /** * Gets the MIME type for a web page without fetching the content. * * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return the fetched MIME type as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *getMimeType( char *inURL ); protected: /** * Receives data on a connection until the connection is closed. * * @param inSocketStream the stream to read from. * Must be destroyed by caller. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * * @return the received data as a \0-terminated string. * Must be destroyed by caller. */ static char *receiveData( SocketStream *inSocketStream, int *outContentLength ); /** * Executes a web method. * * @param inMethod the method to execute (for example, GET or HEAD). * Must be destroyed by caller if non-const. * @param inURL the URL to get as a \0-terminated string. * Must be destroyed by caller if non-const. * @param outContentLength pointer to where the length of the content, * in bytes, should be returned. * Useful for binary content which cannot be reliably terminated * by \0. * @param outFinalURL pointer to where the actual * URL of the content (after following redirects) should be returned, * or NULL to ignore the final URL. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * @param outMimeType pointer to where the MIME type * of the content should be returned, * or NULL to ignore the MIME type. Defaults to NULL. * The string returned in this location must be destroyed * by caller. * * @return the fetched web page as a \0-terminated string, * or NULL if fetching the page fails. * Must be destroyed by caller if non-NULL. */ static char *executeWebMethod( char *inMethod, char *inURL, int *outContentLength, char **outFinalURL = NULL, char **outMimeType = NULL ); }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/URLUtils.cpp0000640000175000017500000002200107550627305022326 0ustar pabspabs/* * Modification History * * 2002-May-10 Jason Rohrer * Created. * * 2002-May-11 Jason Rohrer * Added functions for hex encoding and decoding. * * 2002-May-12 Jason Rohrer * Added conversion for #, &, and ?. * Added conversion for CR and LF. * * 2002-August-1 Jason Rohrer * Added conversion for /, \, ., and :. * * 2002-September-12 Jason Rohrer * Added missing breaks. * * 2002-September-25 Jason Rohrer * Fixed a bug with the way + characters are handled. * Changed to trim the returned buffer. * * 2002-October-8 Jason Rohrer * Added functions for extracting query arguments. */ #include "URLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *URLUtils::getRootServer( char *inURL ) { char *urlCopy = stringDuplicate( inURL ); char *endOfURLType = strstr( urlCopy, "://" ); char *returnString = NULL; if( endOfURLType != NULL ) { char *startOfRootServer = &( endOfURLType[ 3 ] ); char *endOfRootServer = strstr( startOfRootServer, "/" ); if( endOfRootServer != NULL ) { endOfRootServer[0] = '\0'; } returnString = stringDuplicate( startOfRootServer ); } delete [] urlCopy; return returnString; } char *URLUtils::getRootRelativePath( char *inURL ) { char *urlCopy = stringDuplicate( inURL ); char *endOfURLType = strstr( urlCopy, "://" ); char *returnString = NULL; if( endOfURLType != NULL ) { char *startOfRootServer = &( endOfURLType[ 3 ] ); char *endOfRootServer = strstr( startOfRootServer, "/" ); if( endOfRootServer == NULL ) { returnString = stringDuplicate( "/" ); } else { char *lastSlash = endOfRootServer; char *currentSlash = strstr( &( lastSlash[1] ), "/" ); while( currentSlash != NULL ) { lastSlash = currentSlash; currentSlash = strstr( &( lastSlash[1] ), "/" ); } // terminate string right after last slash lastSlash[1] = '\0'; returnString = stringDuplicate( endOfRootServer ); } } delete [] urlCopy; return returnString; } char *URLUtils::hexDecode( char *inString ) { // first run through string and replace any + characters with spaces char *workingString = stringDuplicate( inString ); char *plusLocation = strstr( workingString, "+" ); while( plusLocation != NULL ) { plusLocation[0] = ' '; plusLocation = strstr( plusLocation, "+" ); } int stringLength = strlen( workingString ); char *returnString = new char[ stringLength + 1 ]; int stringIndex = 0; int returnStringIndex = 0; while( stringIndex < stringLength + 1 ) { if( workingString[ stringIndex ] != '%' ) { // not a hex representation returnString[ returnStringIndex ] = workingString[ stringIndex ]; stringIndex++; returnStringIndex++; } else { // the start of hex char twoChars[2]; twoChars[0] = workingString[ stringIndex + 1 ]; twoChars[1]= workingString[ stringIndex + 2 ]; char summedChar = 0; for( int i=0; i<2; i++ ) { int shiftAmount = 4 * ( 1 - i ); switch( twoChars[i] ) { case '0': summedChar += 0x0 << shiftAmount; break; case '1': summedChar += 0x1 << shiftAmount; break; case '2': summedChar += 0x2 << shiftAmount; break; case '3': summedChar += 0x3 << shiftAmount; break; case '4': summedChar += 0x4 << shiftAmount; break; case '5': summedChar += 0x5 << shiftAmount; break; case '6': summedChar += 0x6 << shiftAmount; break; case '7': summedChar += 0x7 << shiftAmount; break; case '8': summedChar += 0x8 << shiftAmount; break; case '9': summedChar += 0x9 << shiftAmount; break; case 'A': summedChar += 0xA << shiftAmount; break; case 'B': summedChar += 0xB << shiftAmount; break; case 'C': summedChar += 0xC << shiftAmount; break; case 'D': summedChar += 0xD << shiftAmount; break; case 'E': summedChar += 0xE << shiftAmount; break; case 'F': summedChar += 0xF << shiftAmount; break; default: break; } } returnString[ returnStringIndex ] = summedChar; stringIndex += 3; returnStringIndex++; } } delete [] workingString; // trim the return string workingString = returnString; returnString = stringDuplicate( workingString ); delete [] workingString; return returnString; } char *URLUtils::hexEncode( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i; for( i=0; ipush_back( '%' ); returnStringVector->push_back( '0' ); returnStringVector->push_back( 'A' ); break; case '\r': returnStringVector->push_back( '%' ); returnStringVector->push_back( '0' ); returnStringVector->push_back( 'D' ); break; case '#': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( '3' ); break; case '&': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( '6' ); break; case '?': returnStringVector->push_back( '%' ); returnStringVector->push_back( '3' ); returnStringVector->push_back( 'F' ); break; case '\\': returnStringVector->push_back( '%' ); returnStringVector->push_back( '5' ); returnStringVector->push_back( 'C' ); break; case '/': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'F' ); break; case ':': returnStringVector->push_back( '%' ); returnStringVector->push_back( '3' ); returnStringVector->push_back( 'A' ); break; case '.': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'E' ); break; case '+': returnStringVector->push_back( '%' ); returnStringVector->push_back( '2' ); returnStringVector->push_back( 'B' ); break; case ' ': returnStringVector->push_back( '+' ); break; default: returnStringVector->push_back( inString[i] ); break; } } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } char *URLUtils::extractArgument( char *inHaystack, char *inArgName ) { char *argNameWithEquals = new char[ strlen( inArgName ) + 2 ]; sprintf( argNameWithEquals, "%s%s", inArgName, "=" ); char *haystackCopy = stringDuplicate( inHaystack ); char *pointerToArgStart = strstr( haystackCopy, argNameWithEquals ); if( pointerToArgStart == NULL ) { delete [] haystackCopy; delete [] argNameWithEquals; return NULL; } else { char *pointerToArgEnd = strstr( pointerToArgStart, "&" ); if( pointerToArgEnd != NULL ) { // terminate string at arg end pointerToArgEnd[0] = '\0'; } // else entire remainder of string is argument char *pointerToArgValue = &( pointerToArgStart[ strlen( argNameWithEquals ) ] ); // trim string char *returnString = stringDuplicate( pointerToArgValue ); delete [] argNameWithEquals; delete [] haystackCopy; return returnString; } } char *URLUtils::extractArgumentRemoveHex( char *inHaystack, char *inArgName ) { char *extractedArg = extractArgument( inHaystack, inArgName ); if( extractedArg != NULL ) { char *convertedArg = URLUtils::hexDecode( extractedArg ); delete [] extractedArg; return convertedArg; } else { return NULL; } } transcend-0.3+dfsg2.orig/minorGems/network/web/URLUtils.h0000640000175000017500000000717307554101513022000 0ustar pabspabs/* * Modification History * * 2002-May-10 Jason Rohrer * Created. * * 2002-May-11 Jason Rohrer * Added functions for hex encoding and decoding. * * 2002-October-8 Jason Rohrer * Added functions for extracting query arguments. */ #include "minorGems/common.h" #ifndef URL_UTILS_INCLUDED #define URL_UTILS_INCLUDED /** * Utilities for handling URLS. * * @author Jason Rohrer */ class URLUtils { public: /** * Gets the root server from a URL. * * For example, if the URL is http://www.yahoo.com/test.html * the root server is www.yahoo.com * * @param inURL the URL as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return the root server as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *getRootServer( char *inURL ); /** * Gets the root-relative path from a URL. * * For example, if the URL is http://www.yahoo.com/temp/test/test.html * the root-relative path is /temp/test/ * * @param inURL the URL as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return the root-relative path as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *getRootRelativePath( char *inURL ); /** * Removes explicit hex encoding from a string. * * For example * http%3A%2F%2Fservera.com%2Ftesta.html * would be converted to * http://servera.com/testa.html * * @param inString the string to convert in \0-terminated form. * Must be destroyed by caller if non-const. * * @return a newly allocated converted string in \0-terminated form. * Must be destroyed by caller. */ static char *hexDecode( char *inString ); /** * Encodes a string in a browser-safe hex encoding * (including adding '+' for each space). * * @param inString the string to encode. * Must be destroyed by caller. * * @return an encoded string. Must be destroyed by caller. */ static char *hexEncode( char *inString ); /** * Extracts the value from an argument of the form: * name=value& or * name=value[string_end] * * Note that if name is the suffix of an argument with a longer name * the longer-named argument's value may be returned. Thus, * argument names should not be suffixes of eachother. * * All parameters must be destroyed by caller. * * @param inHaystack the string to extract the argument from. * @param inArgName the argument name (without ?, &, or =) to search * for in inHaystack. * * @return the value of the argument, or NULL if the argument is * not found. Must be destroyed by caller if non-NULL. */ static char *extractArgument( char *inHaystack, char *inArgName ); /** * The same as extractArgument, except that explicit * hex representations are translated to plain ascii * before the argument value is returned. */ static char *extractArgumentRemoveHex( char *inHaystack, char *inArgName ); }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/MimeTyper.h0000640000175000017500000000417107724666437022247 0ustar pabspabs/* * Modification History * * 2002-April-20 Jason Rohrer * Created. * * 2002-September-17 Jason Rohrer * Moved mime.ini into settings directory. * * 2002-October-7 Jason Rohrer * Added a function for getting mime types from file names. * * 2003-September-1 Jason Rohrer * Copied into minorGems from the konspire2b project. */ #ifndef MIME_TYPER_INCLUDED #define MIME_TYPER_INCLUDED #include /** * A class that can resolve file extensions to mime types. * * @author Jason Rohrer */ class MimeTyper { public: /** * Constructs a mime typer. * * @param inFileName the configuration file from, or * NULL to specify the default file name, "mime.ini". * File name is relative to the settings directory. * Defaults to NULL. * Must be destroyed by caller if non-NULL and non-const. */ MimeTyper( char *inConfigFileName = NULL ); ~MimeTyper(); /** * Gets a mime type string from a file extension string. * * @param inFileExtension a \0-terminated string containing * a file extension, including the '.' * Must be destroyed by caller if non-const. * * @return the mime type as a \0-terminated string, * or NULL if there is no match. * Must be destroyed by caller if non-NULL. */ char *getMimeType( char *inFileExtension ); /** * Gets a mime type string from a file name. * * @param inFileName a \0-terminated string containing * a file name with extension. * Must be destroyed by caller if non-const. * * @return the mime type as a \0-terminated string, * or NULL if there is no match. * Must be destroyed by caller if non-NULL. */ char *getFileNameMimeType( char *inFileName ); protected: // a string containing all types read from the configuration file char *mMimeTypesString; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/WebClient.cpp0000640000175000017500000002377007563604550022536 0ustar pabspabs/* * Modification History * * 2002-May-5 Jason Rohrer * Created. * Changed to default to http:// if no URL type specified. * Added support for the Host: header. * * 2002-May-7 Jason Rohrer * Added support for pages that have been moved permanently. * * 2002-May-12 Jason Rohrer * Added support for the Object Moved message type. * Added support for fetching the final (after redirects) URL. * Fixed a memory leak. * * 2002-May-26 Jason Rohrer * Added support for fetching mime types and content length. * Added a function for fetching MIME types alone. * Changed to use case-ignoring string comparison where appropriate. * * 2002-July-19 Jason Rohrer * Changed to deal with not found headers properly. * * 2002-August-5 Jason Rohrer * Fixed a typo. * * 2002-November-10 Jason Rohrer * Added a fix to deal with a slashdot bug. */ #include "WebClient.h" #include "minorGems/util/log/AppLog.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" char *WebClient::getWebPage( char *inURL, int *outContentLength, char **outFinalURL, char **outMimeType ) { return executeWebMethod( "GET", inURL, outContentLength, outFinalURL, outMimeType ); } char *WebClient::getMimeType( char *inURL ) { int contentLength; char *mimeType; char *content = executeWebMethod( "HEAD", inURL, &contentLength, NULL, &mimeType ); if( content != NULL ) { delete [] content; } return mimeType; } char *WebClient::executeWebMethod( char *inMethod, char *inURL, int *outContentLength, char **outFinalURL, char **outMimeType ) { char *returnString = NULL; char *startString = "http://"; char *urlCopy = stringDuplicate( inURL ); char *urlStart = stringLocateIgnoreCase( urlCopy, startString ); char *serverStart; if( urlStart == NULL ) { // no http:// at start of URL serverStart = urlCopy; } else { serverStart = &( urlStart[ strlen( startString ) ] ); } // find the first occurrence of "/", which is the end of the // server name char *serverNameCopy = stringDuplicate( serverStart ); char *serverEnd = strstr( serverNameCopy, "/" ); char *getPath = strstr( serverStart, "/" ); if( serverEnd == NULL ) { serverEnd = &( serverStart[ strlen( serverStart ) ] ); getPath = "/"; } // terminate the url here to extract the server name serverEnd[0] = '\0'; int portNumber = 80; // look for a port number char *colon = strstr( serverNameCopy, ":" ); if( colon != NULL ) { char *portNumberString = &( colon[1] ); int numRead = sscanf( portNumberString, "%d", & portNumber ); if( numRead != 1 ) { portNumber = 80; } // terminate the name here so port isn't taken as part // of the address colon[0] = '\0'; } HostAddress *host = new HostAddress( stringDuplicate( serverNameCopy ), portNumber ); Socket *sock = SocketClient::connectToServer( host ); char *finalURL = stringDuplicate( inURL ); char *mimeType = NULL; int receivedLength = 0; if( sock != NULL ) { SocketStream *stream = new SocketStream( sock ); // method and trailing space need to be sent in the same // buffer to work around a bug in certain web servers char *methodWithSpace = new char[ strlen( inMethod ) + 2 ]; sprintf( methodWithSpace, "%s ", inMethod ); // send the request stream->writeString( methodWithSpace ); stream->writeString( getPath ); stream->writeString( " HTTP/1.0\r\n" ); stream->writeString( "Host: " ); stream->writeString( serverNameCopy ); stream->writeString( "\r\n\r\n" ); delete [] methodWithSpace; // the simplest thing to do is to read upto the // socket close first, then extract the content char *received = receiveData( stream, &receivedLength ); char *content = NULL; char notFound = false; if( stringLocateIgnoreCase( received, "404 Not Found" ) != NULL ) { notFound = true; } // watch for redirection headers if( stringLocateIgnoreCase( received, "302 Found" ) != NULL || stringLocateIgnoreCase( received, "301 Moved Permanently" ) != NULL || stringLocateIgnoreCase( received, "302 Object Moved" ) != NULL ) { // call ourself recursively to fetch the redirection char *locationTag = "Location: "; char *locationTagStart = stringLocateIgnoreCase( received, locationTag ); if( locationTagStart != NULL ) { char *locationStart = &( locationTagStart[ strlen( locationTag ) ] ); // replace next \r with \0 char *nextChar = locationStart; while( nextChar[0] != '\r' && nextChar[0] != '\0' ) { nextChar = &( nextChar[1] ); } nextChar[0] = '\0'; char *newFinalURL; content = getWebPage( locationStart, &receivedLength, &newFinalURL, &mimeType ); delete [] finalURL; finalURL = newFinalURL; if( content == NULL ) { // not found recursively notFound = true; } } } char *contentStartString = "\r\n\r\n"; char *contentTypeStartString = "Content-type:"; if( notFound ) { returnString = NULL; } else { if( content == NULL ) { // scan for content type char *contentTypeStartMarker = stringLocateIgnoreCase( received, contentTypeStartString ); if( contentTypeStartMarker != NULL ) { // skip marker char *contentTypeStart = &( contentTypeStartMarker[ strlen( contentTypeStartString ) ] ); // extract content type // make sure the buffer is big enough char *contentType = new char[ strlen( contentTypeStartMarker ) ]; int numRead = sscanf( contentTypeStart, "%s", contentType ); if( numRead == 1 ) { // trim mimeType = stringDuplicate( contentType ); } delete [] contentType; } // extract the content from what we've received char *contentStart = strstr( received, contentStartString ); if( contentStart != NULL ) { content = &( contentStart[ strlen( contentStartString ) ] ); receivedLength = receivedLength - strlen( contentStartString ) - ( (int)contentStart - (int)received ); returnString = new char[ receivedLength + 1 ]; returnString = (char*)memcpy( returnString, content, receivedLength ); returnString[ receivedLength ] = '\0'; } } else { // we already obtained our content recursively returnString = new char[ receivedLength + 1 ]; returnString = (char*)memcpy( returnString, content, receivedLength ); returnString[ receivedLength ] = '\0'; delete [] content; } } delete [] received; delete stream; delete sock; } delete host; delete [] serverNameCopy; delete [] urlCopy; if( outFinalURL != NULL ) { *outFinalURL = finalURL; } else { delete [] finalURL; } if( outMimeType != NULL ) { *outMimeType = mimeType; } else { if( mimeType != NULL ) { delete [] mimeType; } } *outContentLength = receivedLength; return returnString; } char *WebClient::receiveData( SocketStream *inSocketStream, int *outContentLength ) { SimpleVector *receivedVector = new SimpleVector(); char connectionBroken = false; long bufferLength = 5000; unsigned char *buffer = new unsigned char[ bufferLength ]; while( !connectionBroken ) { int numRead = inSocketStream->read( buffer, bufferLength ); if( numRead != bufferLength ) { connectionBroken = true; } if( numRead > 0 ) { for( int i=0; ipush_back( buffer[i] ); } } } delete [] buffer; // copy our vector into an array int receivedSize = receivedVector->size(); char *received = new char[ receivedSize + 1 ]; for( int i=0; igetElement( i ) ); } received[ receivedSize ] = '\0'; delete receivedVector; *outContentLength = receivedSize; return received; } transcend-0.3+dfsg2.orig/minorGems/network/web/MimeTyper.cpp0000640000175000017500000000757607724666437022616 0ustar pabspabs/* * Modification History * * 2002-April-20 Jason Rohrer * Created. * * 2002-April-22 Jason Rohrer * Fixed a bug with the mime type string. * * 2002-April-30 Jason Rohrer * Removed an unused variable. * * 2002-September-17 Jason Rohrer * Moved mime.ini into settings directory. * * 2002-October-7 Jason Rohrer * Added a function for getting mime types from file names. * * 2003-September-1 Jason Rohrer * Copied into minorGems from the konspire2b project. */ #include "MimeTyper.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include #include MimeTyper::MimeTyper( char *inConfigFileName ) { File *configFile; char **pathSteps = new char*[1]; pathSteps[0] = "settings"; if( inConfigFileName == NULL ) { configFile = new File( new Path( pathSteps, 1, false ), "mime.ini" ); } else { configFile = new File( new Path( pathSteps, 1, false ), inConfigFileName ); } delete [] pathSteps; if( !configFile->exists() ) { char *name = configFile->getFileName(); printf( "Error: MIME config file %s does not exist.\n", name ); delete [] name; mMimeTypesString = new char[ 1 ]; mMimeTypesString[0] = '\0'; } else { int length = configFile->getLength(); mMimeTypesString = new char[ length + 1 ]; char *name = configFile->getFileName(); FileInputStream *inStream = new FileInputStream( configFile ); int numRead = inStream->read( (unsigned char *)mMimeTypesString, length ); if( numRead != length ) { printf( "Error reading from MIME config file %s.\n", name ); delete [] mMimeTypesString; mMimeTypesString = new char[ 1 ]; mMimeTypesString[0] = '\0'; } else { mMimeTypesString[ length ] = '\0'; } delete [] name; delete inStream; } delete configFile; } MimeTyper::~MimeTyper() { if( mMimeTypesString != NULL ) { delete [] mMimeTypesString; } } char *MimeTyper::getMimeType( char *inFileExtension ) { char *extensionMatch = strstr( mMimeTypesString, inFileExtension ); if( extensionMatch ) { // we should be able to scan two strings from // the position of the match // the first string should be the extension // the second string should be the mime type // make sure buffer is big enough char *buffer = new char[ strlen( mMimeTypesString ) + 1 ]; int numRead = sscanf( extensionMatch, "%s", buffer ); char *returnString = NULL; if( numRead == 1 ) { int numToSkip = strlen( buffer ); // skip first string token (extension) // and read next string token (mime type) numRead = sscanf( &( extensionMatch[ numToSkip ] ), "%s", buffer ); if( numRead == 1 ) { returnString = stringDuplicate( buffer ); } } delete [] buffer; return returnString; } else { return NULL; } } char *MimeTyper::getFileNameMimeType( char *inFileName ) { int fileNameLength = strlen( inFileName ); int lastPeriodIndex = -1; for( int i=0; i #include /** * A class that handles permissions for received connections. * * @author Jason Rohrer. */ class ConnectionPermissionHandler { public: /** * Constructs a handler. */ ConnectionPermissionHandler(); ~ConnectionPermissionHandler(); /** * Gets whether a connection is permitted. * * @param inAddress the address of the host connecting. * Must be destroyed by caller. * * @return true iff a connection is allowed. */ char isPermitted( HostAddress *inAddress ); private: SimpleVector *mPermittedAddresses; SimpleVector *mPermittedPatterns; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/server/RequestHandlingThread.h0000640000175000017500000000600307726131706026047 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Fixed include order. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef REQUEST_HANDLING_THREAD_INCLUDED #define REQUEST_HANDLING_THREAD_INCLUDED #include "PageGenerator.h" #include "ConnectionPermissionHandler.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketStream.h" #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include #include #include #define REQUEST_HANDLING_THREAD_BUFFER_SIZE 4096 /** * Request handler for WebServer. * * @author Jason Rohrer. */ class RequestHandlingThread : public Thread { public: /** * Construct a handler. * * @param inSocket the socket to send the requested * file trough. Is destroyed before this thread terminates. * @param inGenerator the class that will generate the * page content. * Is not destroyed by this class. * @param inConnectionPermissionHandler the class that will * grant connection permissions * Is not destroyed by this class. */ RequestHandlingThread( Socket *inSocket, PageGenerator *inGenerator, ConnectionPermissionHandler *inConnectionPermissionHandler); ~RequestHandlingThread(); /** * Returns true if this handler is done and ready to be destroyed. * * Synchronized, so may block. * * @return true if this handler is done and ready to be destroyed. */ char isDone(); /** * Gets a string representation of the current time. * * @return a timestamp string. Must be destroyed by caller. */ static char* getTimestamp(); // implements the Thread interface virtual void run(); private: Socket *mSocket; PageGenerator *mGenerator; ConnectionPermissionHandler *mConnectionPermissionHandler; MutexLock *mDoneLock; char mDone; /** * Sends an HTTP "not found" message with a "not found" web page. * * @param inStream the stream to send the not found page to. * @param inFileName the name of the requested file, * or NULL. */ void sendNotFoundPage( SocketStream *inStream, char *inFileName ); /** * Sends a "bad request" web page. * * @param inStream the stream to send the page to. */ void sendBadRequest( SocketStream *inStream ); }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/server/ConnectionPermissionHandler.cpp0000640000175000017500000000641007726131706027625 0ustar pabspabs/* * Modification History * * 2002-August-2 Jason Rohrer * Created. * Fixed a buffer size error. * * 2002-September-17 Jason Rohrer * Changed to use the SettingsManager. * * 2002-November-9 Jason Rohrer * Added support for matching address patterns. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "ConnectionPermissionHandler.h" #include "minorGems/io/file/File.h" #include "minorGems/util/SettingsManager.h" ConnectionPermissionHandler::ConnectionPermissionHandler() { SimpleVector *addressVector = SettingsManager::getSetting( "allowedWebHosts" ); int numAddresses = addressVector->size(); mPermittedAddresses = new SimpleVector(); mPermittedPatterns = new SimpleVector(); for( int i=0; igetElement( i ) ); char *starLocation = strstr( addressString, "*" ); if( starLocation == NULL ) { // an address mPermittedAddresses->push_back( new HostAddress( addressString, 0 ) ); } else { // an address pattern mPermittedPatterns->push_back( addressString ); } } delete addressVector; } ConnectionPermissionHandler::~ConnectionPermissionHandler() { int numAddresses = mPermittedAddresses->size(); int i; for( i=0; igetElement( i ) ); delete address; } int numPatterns = mPermittedPatterns->size(); for( i=0; igetElement( i ) ); delete [] pattern; } delete mPermittedAddresses; delete mPermittedPatterns; } char ConnectionPermissionHandler::isPermitted( HostAddress *inAddress ) { int numAddresses = mPermittedAddresses->size(); int i; for( i=0; igetElement( i ) ); if( address->equals( inAddress ) ) { return true; } } // didn't match any address exactly // check if it matches one of our patterns HostAddress* numericalAddress = inAddress->getNumericalAddress(); if( numericalAddress == NULL ) { return false; } char *addressString = numericalAddress->mAddressString; int numPatterns = mPermittedPatterns->size(); char foundMatch = false; for( i=0; igetElement( i ) ); int patternLength = strlen( pattern ); int addressLength = strlen( addressString ); char done = false; for( int j=0; jlock(); tempDone = mDone; mDoneLock->unlock(); return mDone; } // example HTTP request and response /* GET /images/title_homepage4.gif HTTP/1.0 HTTP/1.0 200 OK Date: Fri, 11 May 2001 18:05:08 GMT Server: GWS/1.10 Connection: close Expires: Sun, 17 Jan 2038 19:14:07 GMT Content-Length: 7963 Content-Type: image/gif Last-Modified: Tue, 21 Nov 2000 16:20:07 GMT GIF89a1s */ void RequestHandlingThread::run() { HostAddress *receivedAddress = mSocket->getRemoteHostAddress(); if( receivedAddress == NULL ) { printf( "Failed to obtain host address, so " "refusing web connection.\n" ); // refuse delete mSocket; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); return; } else if( ! mConnectionPermissionHandler->isPermitted( receivedAddress ) ) { printf( "Refusing web connection from: " ); receivedAddress->print(); printf( "\n" ); // not permitted delete mSocket; delete receivedAddress; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); return; } // else permitted delete receivedAddress; int maxLength = 5000; // first, receive the request and parse it SocketStream *sockStream = new SocketStream( mSocket ); int requestBufferLength = maxLength; char *requestBuffer = new char[requestBufferLength]; int requestBufferIndex = 0; // read until we see two \r\n 's in a row unsigned char *charRead = new unsigned char[1]; charRead[0] = 0; char requestDone = false; char error = false; // _we_ actually only care about the first line of // the request, but we need to read the entire request // to make the other host happy char firstLineDone = false; int numRead = 0; while( !requestDone && !error ) { while( charRead[0] != 13 && !error ) { numRead = sockStream->read( charRead, 1 ); if( !firstLineDone ) { if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } // read data into our buffer else if( requestBufferIndex < requestBufferLength ) { requestBuffer[ requestBufferIndex ] = charRead[0]; requestBufferIndex++; if( charRead[0] == 13 ) { firstLineDone = true; } } else { error = true; sendBadRequest( sockStream ); } } } if( !error ) { // look for rest of double \r\n // this will effectively skip other lines in the request, // since we don't care about them numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 10 && numRead == 1 ) { numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 13 && numRead == 1 ) { numRead = sockStream->read( charRead, 1 ); if( charRead[0] == 10 && numRead == 1 ) { requestDone = true; } } } if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } } } // \0 terminate the request buffer if( requestBufferIndex < requestBufferLength ) { requestBuffer[ requestBufferIndex ] = '\0'; } else { requestBuffer[ requestBufferLength - 1 ] = '\0'; } if( !error ) { // at this point, we have received the entire // request, and stored the most important part in // requestBuffer // if maxLength = 500, // formatString = "%499s" // used to limit length of scanned string char *formatString = new char[ 20 ]; sprintf( formatString, "%%%ds", maxLength - 1 ); // the second string scanned from the buffer should // be the file path requested char *filePathBuffer = new char[ maxLength ]; int numRead = sscanf( requestBuffer, formatString, filePathBuffer ); if( numRead != 1 || strcmp( filePathBuffer, "GET" ) != 0 ) { // an invalid request error = true; sendBadRequest( sockStream ); } else { // a proper GET request // skip the GET and read the file name numRead = sscanf( &( requestBuffer[3] ), formatString, filePathBuffer ); if( numRead != 1 ) { error = true; sendBadRequest( sockStream ); } } delete [] requestBuffer; delete [] charRead; delete [] formatString; if( !error ) { // now we have the requested file string sockStream->writeString( "HTTP/1.0 200 OK\r\n" ); char *mimeType = mGenerator->getMimeType( filePathBuffer ); sockStream->writeString( "Content-Type: " ); sockStream->writeString( mimeType ); sockStream->writeString( "\r\n" ); delete [] mimeType; // even if the client requests a keep-alive, we force a close sockStream->writeString( "Connection: close" ); // finish header sockStream->writeString( "\r\n\r\n" ); // pass it to our page generator, which will send the content mGenerator->generatePage( filePathBuffer, sockStream ); } delete [] filePathBuffer; } else { delete [] requestBuffer; delete [] charRead; } delete sockStream; delete mSocket; // flag that we're done mDoneLock->lock(); mDone = true; mDoneLock->unlock(); } void RequestHandlingThread::sendNotFoundPage( SocketStream *inStream, char *inFileName ) { // example "not found" response /* HTTP/1.0 404 Not Found Date: Fri, 11 May 2001 18:26:16 GMT Server: GWS/1.10 Connection: close Set-Cookie: PREF=ID=3300d4623bf73a57:TM=989605576:LM=989605576; domain=.google.com; path=/; expires=Sun, 17-Jan-2038 19:14:07 GMT Content-Length: 142 Content-Type: text/html Not Found

404 Not Found

The requested URL /fjfj was not found on this server. */ char *buffer = new char[500]; if( inFileName != NULL ) { sprintf( buffer, "HTTP/1.0 404 Not Found\r\n\r\n" "

404 Not Found

The requested file " "%s was not found\r\n", inFileName ); } else { sprintf( buffer, "HTTP/1.0 404 Not Found\r\n\r\n" "

404 Not Found

The requested " "file was not found\r\n" ); } inStream->write( (unsigned char *)buffer, strlen( buffer ) ); delete [] buffer; } void RequestHandlingThread::sendBadRequest( SocketStream *inStream ) { // exampl "bad request" response /* Bad Request

400 Bad Request

Your client has issued a malformed or illegal request. */ char *buffer = new char[500]; sprintf( buffer, "

400 Bad Request

" "Your client has issued a malformed or illegal request." "\r\n" ); inStream->write( (unsigned char *)buffer, strlen( buffer ) ); delete [] buffer; } char* RequestHandlingThread::getTimestamp() { char *stampBuffer = new char[99]; time_t t = time( NULL ); char *asciiTime = ctime( &t ); // this time string ends with a newline... // get rid of it asciiTime[ strlen(asciiTime) - 1 ] = '\0'; sprintf( stampBuffer, "[%s]", asciiTime ); // delete [] asciiTime; return stampBuffer; } transcend-0.3+dfsg2.orig/minorGems/network/web/server/WebServer.cpp0000640000175000017500000000476107726131706024072 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * Added timeouts to socket accept to support checking for stop signal. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2002-August-6 Jason Rohrer * Changed member init order. * * 2002-September-17 Jason Rohrer * Removed argument to ConnectionPermissionHandler constructor. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "WebServer.h" #include "minorGems/util/log/AppLog.h" WebServer::WebServer( int inPort, PageGenerator *inGenerator ) : mPortNumber( inPort ), mMaxQueuedConnections( 100 ), mThreadHandler( new ThreadHandlingThread() ), mPageGenerator( inGenerator ), mConnectionPermissionHandler( new ConnectionPermissionHandler() ) { mServer = new SocketServer( mPortNumber, mMaxQueuedConnections ); this->start(); } WebServer::~WebServer() { stop(); join(); delete mServer; delete mThreadHandler; delete mPageGenerator; delete mConnectionPermissionHandler; } void WebServer::run() { char *logMessage = new char[100]; sprintf( logMessage, "Listening for connections on port %d\n", mPortNumber ); AppLog::info( "WebServer", logMessage ); delete [] logMessage; char acceptFailed = false; // main server loop while( !isStopped() && !acceptFailed ) { char timedOut = true; // 5 seconds long timeout = 5000; Socket *sock; AppLog::info( "WebServer", "Waiting for connection." ); while( timedOut && !isStopped() ) { sock = mServer->acceptConnection( timeout, &timedOut ); } if( sock != NULL ) { AppLog::info( "WebServer", "Connection received." ); RequestHandlingThread *thread = new RequestHandlingThread( sock, mPageGenerator, mConnectionPermissionHandler ); thread->start(); mThreadHandler->addThread( thread ); } else if( isStopped() ) { AppLog::info( "WebServer", "Received stop signal." ); } else { AppLog::error( "WebServer", "Accepting a connection failed." ); acceptFailed = true; } } } transcend-0.3+dfsg2.orig/minorGems/network/web/server/ThreadHandlingThread.h0000640000175000017500000000256007726131706025632 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef THREAD_HANDLING_THREAD_INCLUDED #define THREAD_HANDLING_THREAD_INCLUDED #include "RequestHandlingThread.h" #include "minorGems/system/StopSignalThread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include #include /** * Thread handler for the microWeb server. Runs periodically * and checks for finished threads that can be deleted. * * @author Jason Rohrer. */ class ThreadHandlingThread : public StopSignalThread { public: /** * Constructs and starts a handler. */ ThreadHandlingThread(); /** * Stops and destroys this handler. */ ~ThreadHandlingThread(); /** * Adds a thread to the set managed by this handler. * * @param inThread the thread to add. */ void addThread( RequestHandlingThread *inThread ); // implements the Thread interface virtual void run(); private: SimpleVector *mThreadVector; MutexLock *mLock; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/server/WebServer.h0000640000175000017500000000326407726131706023534 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Fixed include order. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2002-August-2 Jason Rohrer * Added use of ConnectionPermissionHandler. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #ifndef WEB_SERVER_INCLUDED #define WEB_SERVER_INCLUDED #include "PageGenerator.h" #include "ConnectionPermissionHandler.h" #include "minorGems/network/Socket.h" #include "minorGems/network/SocketServer.h" #include "minorGems/network/SocketStream.h" #include "RequestHandlingThread.h" #include "ThreadHandlingThread.h" #include "minorGems/system/StopSignalThread.h" #include #include /** * A class that implements a basic web server. * * @author Jason Rohrer. */ class WebServer : public StopSignalThread { public: /** * Constructs an starts this server. * * @param inPort the port to listen on. * @param inGenerator the class to use for generating pages. * Will be destroyed when this class is destroyed. */ WebServer( int inPort, PageGenerator *inGenerator ); /** * Stops and destroys this server. */ ~WebServer(); // implements the Thread::run() interface void run(); private: int mPortNumber; int mMaxQueuedConnections; SocketServer *mServer; ThreadHandlingThread *mThreadHandler; PageGenerator *mPageGenerator; ConnectionPermissionHandler *mConnectionPermissionHandler; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/web/server/ThreadHandlingThread.cpp0000640000175000017500000000471507726131706026171 0ustar pabspabs/* * Modification History * * 2002-March-12 Jason Rohrer * Created. * * 2002-April-5 Jason Rohrer * Changed to extend StopSignalThread. * * 2002-August-6 Jason Rohrer * Changed member init order. * * 2003-September-5 Jason Rohrer * Moved into minorGems. */ #include "ThreadHandlingThread.h" ThreadHandlingThread::ThreadHandlingThread() : mThreadVector( new SimpleVector() ), mLock( new MutexLock() ) { this->start(); } ThreadHandlingThread::~ThreadHandlingThread() { stop(); join(); mLock->lock(); int numThreads = mThreadVector->size(); // join each thread and delete it for( int i=0; igetElement( i ) ); thread->join(); delete thread; } delete mThreadVector; mLock->unlock(); delete mLock; } void ThreadHandlingThread::addThread( RequestHandlingThread *inThread ) { mLock->lock(); mThreadVector->push_back( inThread ); mLock->unlock(); } void ThreadHandlingThread::run() { while( !isStopped() ) { // sleep for 5 seconds sleep( 5000 ); // printf( "Thread handler looking for finished threads\n" ); // look for threads that need to be deleted mLock->lock(); char threadFound = true; // examine each thread while( threadFound ) { threadFound = false; int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); if( thread->isDone() ) { // join the thread before destroying it // to prevent memory leaks thread->join(); // remove the thread from the vector and delete it // printf( "deleting a thread\n" ); mThreadVector->deleteElement( i ); delete thread; threadFound = true; // jump out of the for loop, since our // vector size has changed i = numThreads; } } } mLock->unlock(); } } transcend-0.3+dfsg2.orig/minorGems/network/SocketClient.h0000640000175000017500000000265107554101513022123 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. */ #include "minorGems/common.h" #ifndef SOCKET_CLIENT_CLASS_INCLUDED #define SOCKET_CLIENT_CLASS_INCLUDED #include "Socket.h" #include "HostAddress.h" #include /** * Class that can make connections to socket servers. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SocketClient { public: /** * Connects to a server and returns a socket. * * @param inAddress the host to connect to. Must be destroyed * by caller. * @param inTimeoutInMilliseconds the timeout value for the connect * in milliseconds, or -1 for no timeout. Defaults to -1. * @param outTimedOut pointer to where the timeout flag should * be returned, or NULL for no timeout. If timeout used, will * be set to true if timeout happened, or false if it did not. * Defaults to NULL. * * @return a socket for the connection, or NULL if an error occurs. */ static Socket *connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds = -1, char *outTimedOut = NULL ); }; #endif transcend-0.3+dfsg2.orig/minorGems/network/SocketManager.h0000640000175000017500000000324010157336507022260 0ustar pabspabs/* * Modification History * * 2004-December-13 Jason Rohrer * Created. */ #ifndef SOCKET_MANAGER_INCLUDED #define SOCKET_MANAGER_INCLUDED #include "minorGems/network/Socket.h" #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" /** * Ensures proper destruction of static data items at program termination. */ class SocketManagerDataWrapper { public: SocketManagerDataWrapper(); ~SocketManagerDataWrapper(); MutexLock *mLock; SimpleVector *mSocketVector; }; /** * A class that ensures thread-safe socket shutdown and destruction. * * Useful if a thread needs to break a socket that another thread is using. */ class SocketManager { public: /** * Adds a socket to this manager. * * @param inSocket the socket to add. * Will be destroyed by this manager. */ static void addSocket( Socket *inSocket ); /** * Breaks the connection (both directions) associated with a socket. * * This call is safe even if inSocket has already been destroyed. * * @param inSocket the socket to break. */ static void breakConnection( Socket *inSocket ); /** * Destroys a socket and removes it from this manager. * * @param inSocket the socket to destroy. */ static void destroySocket( Socket *inSocket ); private: // allocated statically to ensure destruction on program termination static SocketManagerDataWrapper mDataWrapper; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/SocketManager.cpp0000640000175000017500000000375010157336507022621 0ustar pabspabs/* * Modification History * * 2004-December-13 Jason Rohrer * Created. */ #include "SocketManager.h" // static initialization SocketManagerDataWrapper SocketManager::mDataWrapper; SocketManagerDataWrapper::SocketManagerDataWrapper() : mLock( new MutexLock() ), mSocketVector( new SimpleVector() ) { } SocketManagerDataWrapper::~SocketManagerDataWrapper() { int numSockets = mSocketVector->size(); for( int i=0; igetElement( i ) ); } delete mSocketVector; delete mLock; } void SocketManager::addSocket( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); socketVector->push_back( inSocket ); lock->unlock(); } void SocketManager::breakConnection( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); int numSockets = socketVector->size(); char found = false; for( int i=0; igetElement( i ) ); if( currentSocket == inSocket ) { currentSocket->breakConnection(); found = true; } } lock->unlock(); } void SocketManager::destroySocket( Socket *inSocket ) { MutexLock *lock = mDataWrapper.mLock; SimpleVector *socketVector = mDataWrapper.mSocketVector; lock->lock(); int numSockets = socketVector->size(); char found = false; for( int i=0; igetElement( i ) ); if( currentSocket == inSocket ) { delete currentSocket; socketVector->deleteElement( i ); found = true; } } lock->unlock(); } transcend-0.3+dfsg2.orig/minorGems/network/SocketUDP.h0000640000175000017500000001325410144135464021337 0ustar pabspabs/* * Modification History * * 2004-November-3 Jason Rohrer * Created. * * 2004-November-4 Jason Rohrer * Added default receive timeout (infinite). * * 2004-November-9 Jason Rohrer * Added functions for comparing and copying UDPAddresses. */ #ifndef SOCKET_UDP_INCLUDED #define SOCKET_UDP_INCLUDED /** * Structure representing a UDP endpoint. */ struct UDPAddress { // binary internet address in network byte order unsigned long mIPAddress; // port number in network byte order unsigned short mPort; }; /** * Network socket that can be used as an endpoint for sending and receiving * UDP packets (unreliable datagrams). * * Note: Implementation for the functions defined here is provided * separately for each platform (in the unix/ and win32/ subdirectories). * * Socket::initSocketFramework() must be called once before this class * is used. * * @author Jason Rohrer */ class SocketUDP { public: /** * Constructs a UDP socket and starts listening for incoming datagrams. * * @param inReceivePort the port to listen on, in platform-dependent * byte order. */ SocketUDP( unsigned short inReceivePort ); ~SocketUDP(); /** * Makes a UDPAddress structure. * * @param inAddress the IP address in ascii numbers-and-dots notation. * Must be destroyed by caller if non-const. * @param inPort the port number in platform-specific byte order. * * @return an address structure, or NULL if converting the address * fails. * Must be destroyed by caller if non-NULL. */ static struct UDPAddress *makeAddress( const char *inAddress, unsigned short inPort ); /** * Extracts address elements from a UDPAddress structure. * * @param inAddress the address structure. Must be destroyed by * caller. * @param outPort pointer to where the port number, in * platform-specific byte order, should be returned. * * @return the IP address in ascci numbers-and-dots notation. * Must be destroyed by caller. */ static char *extractAddress( struct UDPAddress *inAddress, unsigned short *outPort ); /** * Compares two UDP addresses. * * @param inFirst the first address. * @param inSecond the second address. * * @return true if the addresses are equal, or false if they are * different. */ static char compare( struct UDPAddress *inFirst, struct UDPAddress *inSecond ); /** * Makes a copy of a UDP address. * * @param inAddress the address to copy. * * @return a copy of the address. Must be destroyed by caller. */ static struct UDPAddress *copy( struct UDPAddress *inAddress ); /** * Sends a datagram through this socket. * * Note: the recommended maximum data length is 512 bytes * to ensure that the datagram can be routed without * fragmentation through all spec-compliant routers. * Most routers support larger datagrams, however. * * @param inAddress the address to send data through. Must be * destroyed by caller. * @param inData the data bytes to send. * @param inNumBytes the number of bytes to send. * * @return the number of bytes sent successfully, * or -1 for a socket error. */ int send( struct UDPAddress *inAddress, unsigned char *inData, unsigned long inNumBytes ); /** * Receives a datagram from this socket. * * @param outAddress pointer to where the address of the remote * host (the datagram sender) should be returned. * Will be set to NULL on socket error or timeout. * Must be destroyed by caller if non-NULL. * @param outData pointer to where the received data should be * returned. Will be set to NULL on socket error or timeout. * Must be destroyed by caller if non-NULL. * @param inTimeout the timeout for this receive operation in * milliseconds. Set to -1 for an infinite timeout. * -2 is returned from this call in the event of a timeout. * Defaults to -1. * * @return the number of bytes received successfully, * -1 for a socket error, or -2 for a timeout. */ int receive( struct UDPAddress **outAddress, unsigned char **outData, long inTimeout = -1 ); /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; inline char SocketUDP::compare( struct UDPAddress *inFirst, struct UDPAddress *inSecond ) { if( inFirst->mIPAddress == inSecond->mIPAddress && inFirst->mPort == inSecond->mPort ) { return true; } else { return false; } } inline struct UDPAddress *SocketUDP::copy( struct UDPAddress *inAddress ) { struct UDPAddress *returnAddress = new struct UDPAddress; returnAddress->mIPAddress = inAddress->mIPAddress; returnAddress->mPort = inAddress->mPort; return returnAddress; } #endif transcend-0.3+dfsg2.orig/minorGems/network/socketTest.cpp0000640000175000017500000000426210157336507022225 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added more output. * Added better error checking. * * 2001-December-14 Jason Rohrer * Added a test of HostAddress::getLocalAddress(). * * 2002-July-30 Jason Rohrer * Added a test of streams. * * 2002-July-31 Jason Rohrer * Changed to test multiple blocks. * * 2002-February-4 Jason Rohrer * Added test of getting local address from socket. */ #include "Socket.h" #include "SocketStream.h" #include "SocketServer.h" #include "SocketClient.h" #include "HostAddress.h" #include int main() { HostAddress *address = HostAddress::getLocalAddress(); printf( "Local address: " ); address->print(); printf( "\n" ); int port = 5158; SocketServer *server = new SocketServer( port, 100 ); printf( "Waiting for a connection on port %d\n", port ); Socket *receiveSocket = server->acceptConnection(); if( receiveSocket == NULL ) { return 1; } printf( "Connection received\n" ); HostAddress *localAddress = receiveSocket->getLocalHostAddress(); if( localAddress != NULL ) { printf( "Our local address (fetched from socket) is " ); localAddress->print(); printf( "\n" ); delete localAddress; } SocketStream *receiveStream = new SocketStream( receiveSocket ); receiveStream->setReadTimeout( 10000 ); int numBytes = 4000; int checksum = 0; unsigned char *buffer = new unsigned char[numBytes]; /* for( int i=0; iread( buffer, 1 ); checksum += buffer[ 0 ]; if( numRec != 1) { printf( "Faiedl to read.\n" ); return 1; } //sleep( 1 ); } */ int count = 0; while( true ) { int numRec = receiveStream->read( buffer, numBytes ); printf( "Received %d successfully,\tcount = %d\n", numBytes, count ); count++; } /* for( int i=0; i #include DuplicateMessageDetector::DuplicateMessageDetector( int inMessageHistorySize ) : mMaxHistorySize( inMessageHistorySize ), mLock( new MutexLock() ), mSeenIDs( new SimpleVector() ), mTotalMessageCount( 0 ), mHistoryOutputFile( NULL ) { mHistoryOutputFile = fopen( "messageHistory.log", "w" ); } DuplicateMessageDetector::~DuplicateMessageDetector() { int numIDs = mSeenIDs->size(); for( int i=0; igetElement( i ) ); } delete mSeenIDs; delete mLock; fclose( mHistoryOutputFile ); } char DuplicateMessageDetector::checkIfMessageSeen( char *inMessageUniqueID ) { mLock->lock(); mTotalMessageCount++; int numIDs = mSeenIDs->size(); char matchSeen = false; for( int i=0; igetElement( i ) ); if( strcmp( otherID, inMessageUniqueID ) == 0 ) { // match // push the ID back to the end of the queue mSeenIDs->deleteElement( i ); mSeenIDs->push_back( otherID ); matchSeen = true; } } if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, "%d %d %s", mTotalMessageCount, (int)( time( NULL ) ), inMessageUniqueID ); } if( !matchSeen ) { // add the message mSeenIDs->push_back( stringDuplicate( inMessageUniqueID ) ); // make sure history not too long if( mSeenIDs->size() > mMaxHistorySize ) { delete [] *( mSeenIDs->getElement( 0 ) ); mSeenIDs->deleteElement( 0 ); } if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, "\n" ); } } else { // add duplicate tag if( mHistoryOutputFile != NULL ) { fprintf( mHistoryOutputFile, " D\n" ); } } if( mHistoryOutputFile != NULL ) { fflush( mHistoryOutputFile ); } mLock->unlock(); return matchSeen; } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/protocolUtils.h0000640000175000017500000000370107721440564024136 0ustar pabspabs/* * Modification History * * 2003-August-12 Jason Rohrer * Created. * * 2003-August-22 Jason Rohrer * Added function for getting a token after reading. */ #ifndef P2P_PROTOCOL_UTILS_INCLUDED #define P2P_PROTOCOL_UTILS_INCLUDED #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * A collection of common protocol processing functions. * * @author Jason Rohrer */ /** * Reads from a stream up to (and including) the * first occurence of a tag. * * @param inInputStream the stream to read from. * Must be destroyed by caller. * @param inTag the tag to look for. * Must be destroyed by caller if non-const. * @param inMaxCharsToRead the maximum number of characters to read. * * @return the read string, or NULL if reading fails. * NULL is also returned if the character count limit is * reached. * Must be destroyed by caller if non-NULL. */ char *readStreamUpToTag( InputStream *inInputStream, char *inTag, int inMaxCharsToRead ); /** * Reads from a stream up to (and including) the * first occurence of a tag and gets a specific token from the read data. * * Tokens are split by whitespace. * * @param inInputStream the stream to read from. * Must be destroyed by caller. * @param inTag the tag to look for. * Must be destroyed by caller if non-const. * @param inMaxCharsToRead the maximum number of characters to read. * @param inTokenNumber the token to get after reading up to inTag and * tokenizing the read data. 0 specifies the first token. * * @return the read token string, or NULL if reading or token extraction fails. * NULL is also returned if the character count limit is * reached. * Must be destroyed by caller if non-NULL. */ char *readStreamUpToTagAndGetToken( InputStream *inInputStream, char *inTag, int inMaxCharsToRead, int inTokenNumber ); #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/MultipleConnectionPreventer.cpp0000640000175000017500000000333307674671761027332 0ustar pabspabs/* * Modification History * * 2003-June-20 Jason Rohrer * Created. */ #include "MultipleConnectionPreventer.h" MultipleConnectionPreventer::MultipleConnectionPreventer() : mLock( new MutexLock() ), mConnections( new SimpleVector() ) { } MultipleConnectionPreventer::~MultipleConnectionPreventer() { mLock->lock(); int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); } delete mConnections; mLock->unlock(); delete mLock; } char MultipleConnectionPreventer::addConnection( HostAddress *inAddress ) { mLock->lock(); char connectionExists = false; int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); if( inAddress->equals( otherConnection ) ) { connectionExists = true; } } if( !connectionExists ) { mConnections->push_back( inAddress->copy() ); } mLock->unlock(); return !connectionExists; } void MultipleConnectionPreventer::connectionBroken( HostAddress *inAddress ) { mLock->lock(); char connectionFound = false; int numConnections = mConnections->size(); for( int i=0; igetElement( i ) ); if( inAddress->equals( otherConnection ) ) { connectionFound = true; delete otherConnection; mConnections->deleteElement( i ); } } mLock->unlock(); } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/MessagePerSecondLimiter.h0000640000175000017500000000402307775323015025767 0ustar pabspabs/* * Modification History * * 2003-October-9 Jason Rohrer * Created. * * 2003-October-12 Jason Rohrer * Switched to a floating point limit. * * 2004-January-2 Jason Rohrer * Added seprate mutex for transmission function to prevent UI freeze. */ #ifndef MESSAGE_PER_SECOND_LIMITER_INCLUDED_H #define MESSAGE_PER_SECOND_LIMITER_INCLUDED_H #include "minorGems/system/MutexLock.h" /** * Class that limits the number of messages transmitted per second. * * @author Jason Rohrer */ class MessagePerSecondLimiter { public: /** * Constructs a limiter. * * @param inLimitPerSecond the maximum number of messages * transmitted per second, or -1 for no limit. * Defaults to -1. */ MessagePerSecondLimiter( double inLimitPerSecond = -1 ); ~MessagePerSecondLimiter(); /** * Sets the limit. * * Thread safe. * * @param inLimitPerSecond the maximum number of messages * transmitted per second, or -1 for no limit. */ void setLimit( double inLimitPerSecond ); /** * Gets the limit. * * Thread safe. * * @return the maximum number of messages * transmitted per second, or -1 if no limit set. */ double getLimit(); /** * Called by a message transmitter to indicate that a message * is about to be transmitted. Will block if messages * are being transmitted too frequently. * * Thread safe. */ void messageTransmitted(); protected: MutexLock *mLock; MutexLock *mTransmitLock; double mLimitPerSecond; unsigned long mMillisecondsBetweenMessages; unsigned long mSecondTimeOfLastMessage; unsigned long mMillisecondTimeOfLastMessage; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/DuplicateMessageDetector.h0000640000175000017500000000272507716716063026176 0ustar pabspabs/* * Modification History * * 2003-August-7 Jason Rohrer * Created. * * 2003-August-14 Jason Rohrer * Changed to output message history to file. */ #ifndef DUPLICATE_MESSAGE_DETECTOR_INCLUDED_H #define DUPLICATE_MESSAGE_DETECTOR_INCLUDED_H #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include /** * Class that detects duplicates of past messages so that they can be * discarded. * * @author Jason Rohrer */ class DuplicateMessageDetector { public: /** * Constructs a detector. * * @param inMessageHistorySize the number of message IDs to * maintain in our history. * Defaults to 1000. */ DuplicateMessageDetector( int inMessageHistorySize = 1000 ); ~DuplicateMessageDetector(); /** * Checks if a message has been seen in the past. * * @param inMessageUniqueID the unique ID for the message. * Must be destroyed by caller. * * @return true if the message has already been seen, or * false if the message is new. */ char checkIfMessageSeen( char *inMessageUniqueID ); protected: int mMaxHistorySize; MutexLock *mLock; SimpleVector *mSeenIDs; int mTotalMessageCount; FILE *mHistoryOutputFile; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/MessagePerSecondLimiter.cpp0000640000175000017500000000523110227735471026323 0ustar pabspabs/* * Modification History * * 2003-October-9 Jason Rohrer * Created. * * 2003-October-12 Jason Rohrer * Switched to a floating point limit. * * 2004-January-2 Jason Rohrer * Added seprate mutex for transmission function to prevent UI freeze. * * 2005-April-15 Jason Rohrer * Changed to use updated Thread interface. */ #include "MessagePerSecondLimiter.h" #include "minorGems/system/Time.h" #include "minorGems/system/Thread.h" MessagePerSecondLimiter::MessagePerSecondLimiter( double inLimitPerSecond ) : mLock( new MutexLock() ), mTransmitLock( new MutexLock() ), mLimitPerSecond( inLimitPerSecond ) { if( mLimitPerSecond != -1 ) { mMillisecondsBetweenMessages = (int)( 1000 / mLimitPerSecond ); } else { mMillisecondsBetweenMessages = 0; } Time::getCurrentTime( &mSecondTimeOfLastMessage, &mMillisecondTimeOfLastMessage ); } MessagePerSecondLimiter::~MessagePerSecondLimiter() { delete mLock; delete mTransmitLock; } void MessagePerSecondLimiter::setLimit( double inLimitPerSecond ) { mLock->lock(); mLimitPerSecond = inLimitPerSecond; if( mLimitPerSecond != -1 ) { mMillisecondsBetweenMessages = (int)( 1000 / mLimitPerSecond ); } else { mMillisecondsBetweenMessages = 0; } mLock->unlock(); } double MessagePerSecondLimiter::getLimit() { mLock->lock(); double limit = mLimitPerSecond; mLock->unlock(); return limit; } void MessagePerSecondLimiter::messageTransmitted() { // allow only one transmitter to report at a time mTransmitLock->lock(); // protect our variables (make sure settings functions are not // called while we touch the variables) mLock->lock(); unsigned long millisecondsSinceLastMessage = Time::getMillisecondsSince( mSecondTimeOfLastMessage, mMillisecondTimeOfLastMessage ); if( millisecondsSinceLastMessage < mMillisecondsBetweenMessages ) { // this message is coming too soon after last message // sleep unsigned long sleepTime = mMillisecondsBetweenMessages - millisecondsSinceLastMessage; // unlock main lock befor sleeping so that settings can be changed mLock->unlock(); Thread::staticSleep( sleepTime ); // relock mLock->lock(); } Time::getCurrentTime( &mSecondTimeOfLastMessage, &mMillisecondTimeOfLastMessage ); mLock->unlock(); mTransmitLock->unlock(); } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/OutboundChannel.h0000640000175000017500000001150610157121106024327 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Added a setHost function. * * 2003-September-23 Jason Rohrer * Replaced sleep-waiting with semaphores. This results in a major speedup. * * 2003-October-9 Jason Rohrer * Added support for message limiters. * * 2004-December-12 Jason Rohrer * Added a queue size parameter. */ #ifndef OUTBOUND_CHANNEL_INCLUDED #define OUTBOUND_CHANNEL_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/io/OutputStream.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/Semaphore.h" #include "minorGems/system/Thread.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/network/p2pParts/MessagePerSecondLimiter.h" /** * A channel that can send messages to a receiving host. * * NOTE: * None of the member functions are safe to call if this class has been * destroyed. Since the application-specific channel manager class can * destroy a given instance of this class at any time, these member functions * should NEVER be called directly. * Instead, the appropriate channel manager functions should be called. * * @author Jason Rohrer */ class OutboundChannel : public Thread { public: /** * Constructs an OutboundChannel and starts the sending thread. * * @param inOutputStream the stream wrapped by this channel. * Must be destroyed by caller. * @param inHost the host on the other end of this channel. * Will be destroyed when this class is destroyed. * @param inLimiter the limiter for outbound messages. * Must be destroyed by caller after this class is destroyed. * @param inQueueSize the size of the send queue. Defaults to 50. */ OutboundChannel( OutputStream *inOutputStream, HostAddress *inHost, MessagePerSecondLimiter *inLimiter, unsigned long inQueueSize = 50 ); /** * Stops the sending thread and destroys this channel. */ ~OutboundChannel(); /** * Sends a message to this channel's receiver. * * Thread safe. * * This call queues the message to be sent, so it returns before * the send is complete. * * @param inMessage the message to send. * Must be destroyed by caller if non-const. * @param inPriority the priority of this message. * Values less than or equal to 0 indicate default priority, * while positive values suggest higher priority. * Granularity of prioritization is implementation dependent. * Defaults to 0. * * @return true if the channel is still functioning properly, * or false if the channel has been broken. */ char sendMessage( char *inMessage, int inPriority = 0 ); /** * Gets the host receiving from this channel. * * @return this channel's receiving host. * Must be destroyed by caller. */ HostAddress * getHost( ); /** * Sets the address of the remotely connected host. * * @param inHost the host on the other end of this channel. * Will be destroyed when this class is destroyed. */ void setHost( HostAddress *inHost ); /** * Gets the number of outbound messages that have been sent on * this channel. * * Thread safe. * * @return the number of sent messages. */ int getSentMessageCount(); /** * Gets the number of outbound messages that are currently * queued for this channel. * * Thread safe. * * @return the number of queued messages. */ int getQueuedMessageCount(); /** * Gets the number of outbound messages that have been dropped * by this channel. * * Thread safe. * * @return the number of dropped messages. */ int getDroppedMessageCount(); // implements the Thread interface void run(); protected: MutexLock *mLock; Semaphore *mMessageReadySemaphore; OutputStream *mStream; HostAddress *mHost; MessagePerSecondLimiter *mLimiter; char mConnectionBroken; char mThreadStopped; SimpleVector *mMessageQueue; SimpleVector *mHighPriorityMessageQueue; int mMaxQueueSize; int mDroppedMessageCount; int mSentMessageCount; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/MultipleConnectionPreventer.h0000640000175000017500000000305007674671762026774 0ustar pabspabs/* * Modification History * * 2003-June-20 Jason Rohrer * Created. */ #ifndef MULTIPLE_CONNECTION_PREVENTER_INCLUDED_H #define MULTIPLE_CONNECTION_PREVENTER_INCLUDED_H #include "minorGems/system/MutexLock.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/network/HostAddress.h" /** * Class that tracks current connections of a p2p node and prevents * multiple connections to the same address. * * @author Jason Rohrer */ class MultipleConnectionPreventer { public: MultipleConnectionPreventer(); ~MultipleConnectionPreventer(); /** * Adds a connection to a host, and checks if the connection * is permitted. * * The connection will not be permitted if another connection * to the address already exists. * * Thread safe. * * @param inAddress the address of the connection. * Must be destroyed by caller. * * @return true if the connection is permitted, or false otherwise. */ char addConnection( HostAddress *inAddress ); /** * Reports that a connection has been broken. * * Thread safe. * * @param inAddress the address of the broken connection. * Must be destroyed by caller. */ void connectionBroken( HostAddress *inAddress ); protected: MutexLock *mLock; SimpleVector *mConnections; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/OutboundChannel.cpp0000640000175000017500000001363410157121106024666 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Added a setHost function. * * 2003-September-23 Jason Rohrer * Replaced sleep-waiting with semaphores. This results in a major speedup. * * 2003-October-9 Jason Rohrer * Added support for message limiters. * * 2004-January-11 Jason Rohrer * Made include paths explicit to help certain compilers. * * 2004-December-12 Jason Rohrer * Added a queue size parameter. */ #include "minorGems/network/p2pParts/OutboundChannel.h" OutboundChannel::OutboundChannel( OutputStream *inOutputStream, HostAddress *inHost, MessagePerSecondLimiter *inLimiter, unsigned long inQueueSize ) : mLock( new MutexLock() ), mMessageReadySemaphore( new Semaphore() ), mStream( inOutputStream ), mHost( inHost ), mLimiter( inLimiter ), mConnectionBroken( false ), mThreadStopped( false ), mMessageQueue( new SimpleVector() ), mHighPriorityMessageQueue( new SimpleVector() ), mMaxQueueSize( inQueueSize ), mDroppedMessageCount( 0 ), mSentMessageCount( 0 ) { // start our thread start(); } OutboundChannel::~OutboundChannel() { mLock->lock(); mThreadStopped = true; mLock->unlock(); // wake the thread up if it is waiting mMessageReadySemaphore->signal(); // wait for our thread to get the stop signal and finish join(); mLock->lock(); delete mMessageReadySemaphore; // clear the queues int numMessages = mMessageQueue->size(); int i; for( i=0; igetElement( i ) ); delete [] message; } delete mMessageQueue; numMessages = mHighPriorityMessageQueue->size(); for( i=0; igetElement( i ) ); delete [] message; } delete mHighPriorityMessageQueue; delete mHost; mLock->unlock(); delete mLock; } char OutboundChannel::sendMessage( char * inMessage, int inPriority ) { mLock->lock(); char sent; if( !mConnectionBroken ) { // add it to the queue SimpleVector *queueToUse; if( inPriority <=0 ) { queueToUse = mMessageQueue; } else { queueToUse = mHighPriorityMessageQueue; } queueToUse->push_back( stringDuplicate( inMessage ) ); sent = true; if( queueToUse->size() > mMaxQueueSize ) { // the queue is over-full // drop the oldest message char *message = *( queueToUse->getElement( 0 ) ); queueToUse->deleteElement( 0 ); delete [] message; mDroppedMessageCount++; } } else { // channel no longer working sent = false; } mLock->unlock(); if( sent ) { mMessageReadySemaphore->signal(); } return sent; } HostAddress * OutboundChannel::getHost() { return mHost->copy(); } void OutboundChannel::setHost( HostAddress *inHost ) { delete mHost; mHost = inHost->copy(); } int OutboundChannel::getSentMessageCount() { mLock->lock(); int count = mSentMessageCount; mLock->unlock(); return count; } int OutboundChannel::getQueuedMessageCount() { mLock->lock(); int count = mMessageQueue->size() + mHighPriorityMessageQueue->size(); mLock->unlock(); return count; } int OutboundChannel::getDroppedMessageCount() { mLock->lock(); int count = mDroppedMessageCount; mLock->unlock(); return count; } void OutboundChannel::run() { mLock->lock(); char stopped = mThreadStopped; mLock->unlock(); while( !stopped ) { // get a message from the queue, checking high priority queue first char *message = NULL; mLock->lock(); if( mHighPriorityMessageQueue->size() >= 1 ) { message = *( mHighPriorityMessageQueue->getElement( 0 ) ); mHighPriorityMessageQueue->deleteElement( 0 ); } else if( mMessageQueue->size() >= 1 ) { message = *( mMessageQueue->getElement( 0 ) ); mMessageQueue->deleteElement( 0 ); } mLock->unlock(); // note that we're unlocked during the send, so messages // can be freely added to the queue without blocking while we send // this message if( message != NULL ) { // obey the limit // we will block here if message rate is too high mLimiter->messageTransmitted(); int bytesSent = mStream->writeString( message ); int messageLength = strlen( message ); delete [] message; char sent; if( bytesSent == messageLength ) { sent = true; mLock->lock(); mSentMessageCount++; mLock->unlock(); } else { sent = false; } if( !sent ) { // connection is broken // stop this thread mLock->lock(); mConnectionBroken = true; mThreadStopped = true; mLock->unlock(); } } else { // no messages in the queue. // wait for more messages to be ready mMessageReadySemaphore->wait(); } // check if we've been stopped mLock->lock(); stopped = mThreadStopped; mLock->unlock(); } } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/HostCatcher.cpp0000640000175000017500000001516010161606173024010 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2003-July-27 Jason Rohrer * Fixed a bug when catcher is empty. * * 2003-December-21 Jason Rohrer * Fixed a memory leak when catcher is full. * * 2004-January-11 Jason Rohrer * Made include paths explicit to help certain compilers. * * 2004-December-20 Jason Rohrer * Changed to convert to numerical form before comparing against host list. * Changed getHost to return hosts in random order. * Added a getOrderedHost function that returns hosts in linear order. */ #include "minorGems/network/p2pParts/HostCatcher.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/random/StdRandomSource.h" HostCatcher::HostCatcher( int inMaxListSize ) : mMaxListSize( inMaxListSize ), mHostVector( new SimpleVector() ), mLock( new MutexLock() ), mRandSource( new StdRandomSource() ) { } HostCatcher::~HostCatcher() { mLock->lock(); int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); } delete mHostVector; mLock->unlock(); delete mLock; delete mRandSource; } void HostCatcher::addHost( HostAddress * inHost ) { // convert to numerical form once and for all here // (to avoid converting over and over in equals checks below) HostAddress *numericalAddress = inHost->getNumericalAddress(); if( numericalAddress != NULL ) { mLock->lock(); // make sure this host doesn't already exist in our list char exists = false; int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); if( otherHost->equals( numericalAddress ) ) { exists = true; // jump out of loop i = numHosts; } } if( !exists ) { mHostVector->push_back( numericalAddress->copy() ); } while( mHostVector->size() > mMaxListSize ) { // remove first host from queue HostAddress *host = *( mHostVector->getElement( 0 ) ); mHostVector->deleteElement( 0 ); delete host; } mLock->unlock(); delete numericalAddress; } } HostAddress * HostCatcher::getHostOrdered( ) { mLock->lock(); int numHosts = mHostVector->size(); if( numHosts == 0 ) { mLock->unlock(); return NULL; } // remove first host from queue HostAddress *host = *( mHostVector->getElement( 0 ) ); mHostVector->deleteElement( 0 ); // add host to end of queue mHostVector->push_back( host ); HostAddress *hostCopy = host->copy(); mLock->unlock(); return hostCopy; } HostAddress * HostCatcher::getHost( ) { mLock->lock(); int numHosts = mHostVector->size(); if( numHosts == 0 ) { mLock->unlock(); return NULL; } // remove random host from queue int index = mRandSource->getRandomBoundedInt( 0, numHosts - 1 ); HostAddress *host = *( mHostVector->getElement( index ) ); mHostVector->deleteElement( index ); // add host to end of queue mHostVector->push_back( host ); HostAddress *hostCopy = host->copy(); mLock->unlock(); return hostCopy; } SimpleVector *HostCatcher::getHostList( int inMaxHostCount, HostAddress *inSkipHost ) { HostAddress *hostToSkip; if( inSkipHost != NULL ) { hostToSkip = inSkipHost->copy(); } else { // don't skip any host // create a dummy host that won't match any other valid hosts // make sure dummy is in numerical form to avoid DNS lookups hostToSkip = new HostAddress( stringDuplicate( "1.1.1.1" ), 1 ); } SimpleVector *collectedHosts = new SimpleVector(); char repeat = false; int numCollected = 0; // This function assumes that getHostOrdered() draws // hosts in order with no repetition except when we have // exhausted the host supply. // Note that this will not be true when other threads // have getHostOrdered() (or getHost) calls interleaved with ours, but this // should be a rare case. It will simply result // in a smaller host list being returned. HostAddress *firstHost = getHostOrdered(); if( firstHost == NULL ) { // the catcher is empty delete hostToSkip; // an empty host list return collectedHosts; } if( ! hostToSkip->equals( firstHost ) ) { collectedHosts->push_back( firstHost ); numCollected++; } while( numCollected < inMaxHostCount && !repeat ) { HostAddress *nextHost = getHostOrdered(); if( nextHost->equals( firstHost ) ) { delete nextHost; repeat = true; } else { if( ! hostToSkip->equals( nextHost ) ) { collectedHosts->push_back( nextHost ); numCollected++; } else { delete nextHost; } } } if( hostToSkip->equals( firstHost ) ) { // we didn't include firstHost in our collectedHosts, so // we must delete it. delete firstHost; } delete hostToSkip; return collectedHosts; } void HostCatcher::addHostList( SimpleVector * inHostList ) { int numToAdd = inHostList->size(); for( int i=0; igetElement( i ) ) ); } } void HostCatcher::noteHostBad( HostAddress * inHost ) { mLock->lock(); // make sure this host already exists in our list char exists = false; HostAddress *foundHost = NULL; int numHosts = mHostVector->size(); for( int i=0; igetElement( i ) ); if( otherHost->equals( inHost ) ) { exists = true; // delete the host that we've found mHostVector->deleteElement( i ); foundHost = otherHost; // jump out of loop i = numHosts; } } if( exists ) { delete foundHost; //mHostVector->push_back( foundHost ); } mLock->unlock(); } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/HostCatcher.h0000640000175000017500000000730210161606173023454 0ustar pabspabs/* * Modification History * * 2003-June-22 Jason Rohrer * Copied from the konspire2b project and modified. * * 2004-December-20 Jason Rohrer * Changed getHost to return hosts in random order. * Added a getOrderedHost function that returns hosts in linear order. */ #ifndef HOST_CATCHER_INCLUDED #define HOST_CATCHER_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/system/MutexLock.h" /** * Manages a collection of hosts. * * @author Jason Rohrer */ class HostCatcher { public: /** * Constructs a host catcher. * * @param inMaxListSize the maximum number of hosts to hold. */ HostCatcher( int inMaxListSize ); ~HostCatcher(); /** * Adds a host to this catcher. * * Thread safe. * * @param inHost the host to add. * Must be destroyed by caller. */ void addHost( HostAddress * inHost ); /** * Gets a "fresh" host from this catcher. * * The returned host is "fresh" in that it has not * been returned by this call in a while. * * Thread safe. * * @return a host, or NULL if there are no hosts. * Must be destroyed by caller if non-NULL. */ HostAddress * getHost( ); /** * Gets a "fresh" host list from this catcher. * The result of this function call is similar to that obtained * by calling getHost inMaxHostCount times and constructing * a HostList from the result, except that this function * will produce a list with no repeated hosts. * * Thread safe. * * @para inMaxHostCount the maximum number of hosts to retrieve. * @param inSkipHost a host to skip when building the list, * or NULL to not skip any host. (Useful * fo constructing a host list at the request of another host.) * Defaults to NULL. Must be destroyed by caller. * * @return a host list containing <= inMaxHostCount hosts. * Vector and addresses must be destroyed by caller. */ SimpleVector *getHostList( int inMaxHostCount, HostAddress *inSkipHost = NULL ); /** * Adds an entire list of hosts to this catcher. * * Thread safe. * * @param inHostList the list to add. * Vector and addresses must be destroyed by caller. */ void addHostList( SimpleVector *inHostList ); /** * Tells this catcher that a host is "bad" * (in other words, dead, dropping connections, etc.). * * Thread safe. * * @param inHost the bad host. * Must be destroyed by caller. */ void noteHostBad( HostAddress * inHost ); protected: int mMaxListSize; SimpleVector *mHostVector; MutexLock *mLock; RandomSource *mRandSource; /** * Gets a "fresh" host from this catcher, walking through the host * list in order. * * The returned host is "fresh" in that it has not * been returned by this call in a while. * * Thread safe. * * @return a host, or NULL if there are no hosts. * Must be destroyed by caller if non-NULL. */ HostAddress *getHostOrdered(); }; #endif transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/MultiSourceDownloader.cpp0000640000175000017500000000764710154110372026077 0ustar pabspabs/* * Modification History * * 2004-November-21 Jason Rohrer * Created. * * 2004-November-23 Jason Rohrer * Fixed compile errors caused by multiple definitions. * * 2004-December-4 Jason Rohrer * Fixed bug in source indexing. */ #include "MultiSourceDownloader.h" #include int MULTISOURCE_DOWNLOAD_IN_PROGRESS = 0; int MULTISOURCE_DOWNLOAD_FAILED = 1; int MULTISOURCE_DOWNLOAD_CANCELED = 2; void multiSourceGetFile( void *inFileDescriptor, unsigned long inFileSize, unsigned long inChunkSize, int inNumSources, void **inFileSources, unsigned char * (*inChunkGetter)( void *, void *, unsigned long, unsigned long ), char (*inDownloadProgressHandler)( int, unsigned long, void * ), void *inProgressHandlerExtraArgument, char *inDestinationPath ) { FILE *outputFile = fopen( inDestinationPath, "wb" ); if( outputFile == NULL ) { inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, 0, inProgressHandlerExtraArgument ); return; } // for now, just fetch chunks in linear order, using each file source // until it fails char failed = false; char canceled = false; unsigned long chunksSoFar = 0; unsigned long numChunks = inFileSize / inChunkSize; if( inFileSize % inChunkSize != 0 ) { // extra partial chunk numChunks++; } unsigned long bytesSoFar = 0; int sourceIndex = 0; while( sourceIndex < inNumSources && !failed && !canceled && chunksSoFar < numChunks ) { void *source = inFileSources[ sourceIndex ]; unsigned long chunkSize = inChunkSize; if( chunksSoFar * inChunkSize + chunkSize > inFileSize ) { // partial chunk chunkSize = inFileSize - chunksSoFar * inChunkSize; } unsigned char *chunkData = inChunkGetter( source, inFileDescriptor, chunksSoFar, chunkSize ); if( chunkData != NULL ) { chunksSoFar += 1; bytesSoFar += chunkSize; unsigned long numWritten = fwrite( chunkData, 1, chunkSize, outputFile ); if( numWritten == chunkSize ) { char shouldContinue = inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_IN_PROGRESS, bytesSoFar, inProgressHandlerExtraArgument ); if( !shouldContinue ) { canceled = true; // call handler last time inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_CANCELED, bytesSoFar, inProgressHandlerExtraArgument ); } } else { // failed to write to file, so download cannot continue inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, bytesSoFar, inProgressHandlerExtraArgument ); failed = true; } delete [] chunkData; } else { // else go on to the next host sourceIndex ++; } } // check if we got here because we ran out of hosts (another type of // failure if( !failed && !canceled && chunksSoFar < numChunks ) { inDownloadProgressHandler( MULTISOURCE_DOWNLOAD_FAILED, bytesSoFar, inProgressHandlerExtraArgument ); } fclose( outputFile ); } transcend-0.3+dfsg2.orig/minorGems/network/p2pParts/protocolUtils.cpp0000640000175000017500000000644507721440564024501 0ustar pabspabs/* * Modification History * * 2003-August-12 Jason Rohrer * Created. * * 2003-August-22 Jason Rohrer * Added function for getting a token after reading. */ #include "minorGems/network/p2pParts/protocolUtils.h" #include "minorGems/util/log/AppLog.h" #include "minorGems/util/stringUtils.h" char *readStreamUpToTag( InputStream *inInputStream, char *inTag, int inMaxCharsToRead ) { char *readCharBuffer = new char[ inMaxCharsToRead + 1 ]; int numCharsRead = 0; char tagSeen = false; char readError = false; int tagLength = strlen( inTag ); // start with empty string readCharBuffer[ numCharsRead ] = '\0'; while( numCharsRead < inMaxCharsToRead && !tagSeen && !readError ) { long numRead = inInputStream->readByte( (unsigned char *) ( &( readCharBuffer[ numCharsRead ] ) ) ); if( numRead != 1 ) { readError = true; readCharBuffer[ numCharsRead ] = '\0'; } else { numCharsRead++; // terminate and check if buffer ends with our tag readCharBuffer[ numCharsRead ] = '\0'; if( numCharsRead > tagLength ) { char *possibleBodyStart = &( readCharBuffer[ numCharsRead - tagLength ] ); if( strcmp( possibleBodyStart, inTag ) == 0 ) { tagSeen = true; } } } } if( !readError && tagSeen ) { char *returnString = stringDuplicate( readCharBuffer ); delete [] readCharBuffer; return returnString; } else { char *message = autoSprintf( "Failed to find end tag \"%s\", read %d characters:\n%s\n", inTag, numCharsRead, readCharBuffer ); AppLog::info( "readStreamUpToTag", message ); delete [] message; delete [] readCharBuffer; return NULL; } } char *readStreamUpToTagAndGetToken( InputStream *inInputStream, char *inTag, int inMaxCharsToRead, int inTokenNumber ) { // read the string char *readString = readStreamUpToTag( inInputStream, inTag, inMaxCharsToRead ); if( readString == NULL ) { return NULL; } SimpleVector *readTokens = tokenizeString( readString ); delete [] readString; // second token should be their key char *selectedToken = NULL; int numTokens = readTokens->size(); if( numTokens > inTokenNumber ) { selectedToken = stringDuplicate( *( readTokens->getElement( inTokenNumber ) ) ); } else { char *message = autoSprintf( "Looking for token %d, but only %d tokens available\n", inTokenNumber, numTokens ); AppLog::error( "readStreamUpToTagAndGetToken", message ); } for( int i=0; igetElement( i ) ); } delete readTokens; // will be NULL if not enough tokens read return selectedToken; } transcend-0.3+dfsg2.orig/minorGems/network/SocketServer.h0000640000175000017500000000347107554101513022154 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2002-March-27 Jason Rohrer * Added a timeout to accept. */ #include "minorGems/common.h" #ifndef SOCKET_SERVER_CLASS_INCLUDED #define SOCKET_SERVER_CLASS_INCLUDED #include #include "Socket.h" /** * Network socket server that listens for connections on a certain port. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SocketServer { public: /** * Constructs a SocketServer and begins listening for connections. * * @param inPort the port to listen on. * @param inMaxQueuedConnections the number of connection requests * that will be queued before further requests are refused. */ SocketServer( int inPort, int inMaxQueuedConnections ); ~SocketServer(); /** * Accepts a an incoming connection on our port. * * @param inTimeoutInMilliseconds the timeout in milliseconds, * or -1 for no timeout. Defaults to -1. * @param outTimedOut pre-allocated char where timeout * flag will be returned. If non-NULL, true will * be inserted upon timeout, and false will be inserted * upon other error or no error/timeout. * Must be destroyed by caller if non-NULL. * Defaults to NULL. * * @return a socket for the accepted connection, * or NULL if a socket error/timeout occurred. */ Socket *acceptConnection( long inTimeoutInMilliseconds = -1, char *outTimedOut = NULL ); private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; #endif transcend-0.3+dfsg2.orig/minorGems/network/socketTestCompile0000750000175000017500000000065710157336507022763 0ustar pabspabsg++ -g -o socketTest -I../.. socketTest.cpp linux/SocketLinux.cpp linux/SocketClientLinux.cpp linux/SocketServerLinux.cpp linux/HostAddressLinux.cpp ../system/linux/MutexLockLinux.cpp NetworkFunctionLocks.cpp g++ -g -o socketClientTest -I../.. socketClientTest.cpp linux/SocketLinux.cpp linux/SocketClientLinux.cpp linux/SocketServerLinux.cpp linux/HostAddressLinux.cpp ../system/linux/MutexLockLinux.cpp NetworkFunctionLocks.cpp transcend-0.3+dfsg2.orig/minorGems/network/linux/0000750000175000017500000000000010305077064020516 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/linux/gnut_net.c0000640000175000017500000002560107552330061022511 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #include #include #include #ifndef WIN32 #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include "gnut_lib.h" #include "gnut_threads.h" #include "gnut_if.h" #include "protocol.h" uchar local_ip[4]; // uint32 net_local_ip() // // returns the local ip address // uchar *net_local_ip() { return local_ip; } int gnut_net_host() { char host[256]; struct hostent *he; int ret; g_debug(2,"entering\n"); ret=gethostname(host,sizeof(host)); if (ret<0) { perror("gnut_net_host, gethostname"); return ret; } he=gethostbyname(host); if (he==NULL) { perror("gnut_net_host, gethostbyname"); return ret; } memcpy(local_ip,he->h_addr_list[0],4); return 0; } // int net_init(char *iface) // // Does misc. net init stuff. Mostly just finds our local IP address // int net_init(char *iface) { struct sockaddr_in *sinptr=NULL; g_debug(2,"entering\n"); if (iface!=NULL) { if (inet_aton(iface,(struct in_addr *) local_ip)) { g_debug(1,"local was set by command to: %i.%i.%i.%i\n",local_ip[0],local_ip[1], local_ip[2],local_ip[3]); return 0; } } if ((sinptr = get_if_addr(iface))==NULL) { g_debug(1,"Can't get local IP address through interface, trying host name...\n"); gnut_net_host(); } else { memcpy(local_ip,&(sinptr->sin_addr),sizeof(local_ip)); } g_debug(1,"local ip is: %i.%i.%i.%i\n",local_ip[0],local_ip[1], local_ip[2],local_ip[3]); return 0; } // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read(int sock, char *buf, int len, int secs) { fd_set fsr; struct timeval tv; int ret; ret=fcntl(sock,F_SETFL,O_NONBLOCK); FD_ZERO(&fsr); FD_SET(sock,&fsr); tv.tv_sec=secs; tv.tv_usec=0; ret=select(sock+1,&fsr,NULL,NULL,&tv); if (ret==0) { return -2; } if (ret<0) return ret; ret=read(sock,buf,len); fcntl(sock,F_SETFL,0); return ret; } // int timed_connect(int sock, struct sockaddr *sa,int addrlen,int secs) // // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect // int timed_connect(int sockfd, struct sockaddr *sa, int addrlen,int secs) { int ret; fd_set fsr; struct timeval tv; int val,len; g_debug(1,"entering sock=%i secs=%i\n",sockfd,secs); ret=fcntl(sockfd,F_SETFL,O_NONBLOCK); g_debug(5,"fcntl returned %i\n",ret); if (ret<0) return ret; ret=connect(sockfd,sa,addrlen); g_debug(5,"connect returned %i\n",ret); if (ret==0) { g_debug(0,"immediate connection!\n"); // wooah! immediate connection return -2; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO(&fsr); FD_SET(sockfd,&fsr); tv.tv_sec=secs; tv.tv_usec=0; ret=select(sockfd+1,NULL,&fsr,NULL,&tv); g_debug(5,"select returned %i\n",ret); if (ret==0) { // timeout g_debug(1,"timeout\n"); fcntl(sockfd,F_SETFL,0); return -2; } len=4; ret=getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&val,&len); g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if (ret<0) { g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { g_debug(3,"returning failure!\n"); return -1; } ret=fcntl(sockfd,F_SETFL,0); g_debug(1,"fcntl: %i\n",ret); g_debug(3,"returning success val=%i\n",val); return 0; } // int create_listen_socket(int * port) // // attempts to create a socket to listen on port // returns the actual port bound to in port or <0 if not // successfull // int create_listen_socket(int *port) { struct sockaddr_in sin; int sock; int ret,i; int val; g_debug(3,"entering\n"); sock=socket(AF_INET,SOCK_STREAM,6); if (sock<0) { perror("create_listen_socket, socket"); return sock; } val=1; ret=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)); i=0; ret=-1; while (ret<0) { sin.sin_addr.s_addr=INADDR_ANY; sin.sin_port=htons(*port); sin.sin_family=AF_INET; ret=bind(sock,(struct sockaddr *) &sin,sizeof(struct sockaddr_in)); if (++i>50) { (*port)++; i=0; } } if (ret<0) { perror("create_listen_socket, bind"); return ret; } ret=listen(sock,5); if (ret<0) { perror("create_listen_socket, listen"); return ret; } g_debug(1,"bound to port %i\n",*port); printf("Bound to port: %i\n",*port); ret=fcntl(sock,F_SETFL,O_NONBLOCK); if (ret<0) { perror("create_listen_socket, fcntl"); return ret; } return sock; } // int read_line(int sock, char *buf, int len) // // reads a line from sock into buf, not exceeding len chars // returns 0 on EOF, -1 on error, or num of characters read // and -2 if buffer length exceeded // int read_line(int sock, char *buf, int len) { int i,ret; char c; char *ptr; ptr=buf; g_debug(3,"entering\n"); fcntl(sock,F_SETFL,0); buf[0]=0; for (i=1;i=1) perror("read_line, read"); return -1; } } buf[i]=0; g_debug(3,"returning success\n"); return i; } #ifndef PTHREADS_DRAFT4 pthread_mutex_t make_connection_mutex=PTHREAD_MUTEX_INITIALIZER; #else pthread_mutex_t make_connection_mutex; #endif struct hostent *gnut_hostent_copy(struct hostent *he) { struct hostent *n; int i,len; if (!he) return NULL; n=(struct hostent *) calloc(sizeof(struct hostent),1); memcpy(n,he,sizeof(struct hostent)); if (he->h_name) n->h_name=strdup(he->h_name); for (len=0;he->h_addr_list[len];len++); n->h_addr_list=(char **) calloc(sizeof(char *) * (len+1),1); for (i=0;ih_addr_list[i]=xmalloc(4); memcpy(n->h_addr_list[i],he->h_addr_list[i],4); } return n; } void gnut_hostent_delete(struct hostent *he) { int i,len; g_debug(1,"entering\n"); for (len=0;he->h_addr_list[len];len++); g_debug(1,"len=%i\n",len); for (i=0;ih_addr_list[i]) free(he->h_addr_list[i]); } g_debug(1,"freeing h_name\n"); if (he->h_name) free(he->h_name); g_debug(1,"freeing h_addr_list\n"); free(he->h_addr_list); g_debug(1,"freeing he\n"); free(he); g_debug(1,"returning\n"); } // int make_connection(char *host, uint port, uchar ip[4]) // // creates a socket, and attempts to make a connection to the host // named. it tries multiple resolved ip addresses if necessary, // returning the socket on success and storing the good ip number in ip // otherwise it returns <0 // // Notes: due to the fact that gethostbyname is not thread safe // we need to protect this function with a mutex // int make_connection(uchar *host, uint port, uchar ip[4]) { struct hostent *he; struct sockaddr_in sin; int i,sock=-1,ret; g_debug(1,"entering\n"); pthread_mutex_lock(&make_connection_mutex); he=gnut_hostent_copy(gethostbyname(host)); if (he==NULL) { pthread_mutex_unlock(&make_connection_mutex); return -1; } pthread_mutex_unlock(&make_connection_mutex); for (i=0,ret=-1;he->h_addr_list[i]!=NULL && ret<0;i++) { g_debug(1,"trying host %i.%i.%i.%i:%i\n", (uchar) he->h_addr_list[i][0],(uchar) he->h_addr_list[i][1], (uchar) he->h_addr_list[i][2],(uchar) he->h_addr_list[i][3],port); sock=socket(AF_INET,SOCK_STREAM,6); if (sock<0) { perror("make_connection, socket"); gnut_hostent_delete(he); return sock; } sin.sin_family=AF_INET; sin.sin_port=htons(port); memcpy(&sin.sin_addr.s_addr,he->h_addr_list[i],4); ret=timed_connect(sock,(struct sockaddr *) &sin, sizeof(struct sockaddr_in),10); g_debug(5,"timed_connect returned: %i\n",ret); if (ret<0) { g_debug(1,"host bad, closing\n"); close(sock); } else { break; } } if (ret<0 || he->h_addr_list[i]==NULL) { g_debug(1,"returning failure\n"); gnut_hostent_delete(he); return -2; } g_debug(5,"about to copy ip from slot %i\n",i); memcpy(ip,he->h_addr_list[i],4); g_debug(4,"trying to unlock mutex\n"); gnut_hostent_delete(he); g_debug(1,"returning success\n"); return sock; } // int send_packet (int sock, gnutella_packet *gpa) // // sends the packet described by gpa over socket sock // return 0 on success or <0 for error // int send_packet(int sock, gnutella_packet *gpa) { int ret; int t; g_debug(3,"entering\n"); ret=write(sock,(char *) &gpa->gh,sizeof(gnutella_hdr)); if (ret<0) { if (gnut_lib_debug>3) perror("send_packet, write header"); return ret; } memcpy(&t,gpa->gh.dlen,4); t=GUINT32_FROM_LE(t); if (t>0) { ret=write(sock,gpa->data,t); if (ret<0) { if (gnut_lib_debug>3) perror("send_packet, write data"); return ret; } } g_debug(3,"returning success\n"); return 23+t; } // int read_packet(int sock, gnutella_packet *gpa) // // reads a packet from the socket sock into the packet // structure of gpa. // returns 0 on success or <0 on error // int read_packet(int sock, gnutella_packet *gpa) { int ret; char *ptr; int left; uint dlen; int i; g_debug(3,"entering\n"); ptr=(char *) &gpa->gh; for (left=sizeof(gnutella_hdr);left>0;) { ret=timed_read(sock,ptr,left,10); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2) return 0; if (ret==0) { return -2; } else if (ret<0) { if (errno!=EINTR) { if (gnut_lib_debug>3) perror("read_packet, header"); return ret; } else ret=0; } ptr+=ret; left-=ret; } assert(left==0); memcpy(&dlen,gpa->gh.dlen,4); dlen=GUINT32_FROM_LE(dlen); while (dlen>32767 || (gpa->gh.func==129 && dlen>65536)) { // We're out of sync! I'm going to do large reads until // one returns 23, which is probably a ping packet.... g_debug(2,"out of sync! func=%i dlen=%i\n",gpa->gh.func,dlen); ptr=(char *) xmalloc(100); ret=1; i=0; while (ret>0 && ret!=23 && ret!=49) { ret=timed_read(sock,ptr,60,2); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2 || (++i>5)) { free(ptr); return 0; } } if (ret<=0) { free(ptr); return ret; } free(ptr); memcpy(&gpa->gh,ptr,23); memcpy(&dlen,gpa->gh.dlen,4); dlen=GUINT32_FROM_LE(dlen); } if (dlen>0) { gpa->data=(char *) calloc(dlen,1); ptr=gpa->data; for (left=dlen;left>0;) { ret=timed_read(sock,ptr,left,10); g_debug(6,"timed_read returned: %i\n",ret); if (ret==-2) return 0; if (ret==0) return -2; if (ret<0) { if (gnut_lib_debug>3) perror("read_packet, data"); return ret; } ptr+=ret; left-=ret; } } if (dlen>3000) g_debug(2,"OVERSIZE packet! size: %i\n",dlen); g_debug(3,"returning success\n"); return 23+dlen; } transcend-0.3+dfsg2.orig/minorGems/network/linux/socketServerTimeoutTest.compile0000750000175000017500000000016007455745246026776 0ustar pabspabsg++ -g -o socketServerTimeoutTest socketServerTimeoutTest.cpp -I../../.. SocketServerLinux.cpp SocketLinux.cpp transcend-0.3+dfsg2.orig/minorGems/network/linux/SocketClientLinux.cpp0000640000175000017500000001633207776034461024653 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Commented out a redundant print statement (it's better if these kinds * of messages are handled at a higher level). * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * * 2001-January-29 Jason Rohrer * Fixed an endian bug with the port number. * * 2001-November-13 Jason Rohrer * Added a missing include. * * 2001-December-12 Jason Rohrer * Changed the include order to make BSD compatible. * * 2002-August-5 Jason Rohrer * Removed use of obsolete call. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. * * 2002-October-17 Jason Rohrer * Added missing include. * Added support for BSD's version of getsockopt. * * 2002-Novenber-27 Jason Rohrer * Changed to print error number. There's a bug that needs to be fixed. * * 2002-December-2 Jason Rohrer * Fixed resource leak when connection fails. * * 2003-March-18 Jason Rohrer * Fixed a thread safety issue with gethostbyname. * * 2004-January-2 Jason Rohrer * Fixed a memory leak when host name lookup fails. * * 2004-January-4 Jason Rohrer * Added use of network function locks. */ #include "minorGems/network/SocketClient.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/system/MutexLock.h" #include #include #include #include #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD typedef int socklen_t; #endif // prototypes struct in_addr *nameToAddress( char *inAddress ); int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ); Socket *SocketClient::connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds, char *outTimedOut ) { if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } int socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == -1 ) { printf( "Creating socket failed, error %d.\n", errno ); return NULL; } union sock { struct sockaddr s; struct sockaddr_in i; } sock; struct in_addr *internet_address = nameToAddress( inAddress->mAddressString ); if( internet_address == NULL ) { printf( "Host name lookup failed: " ); inAddress->print(); printf( "\n" ); close( socketID ); return NULL; } sock.i.sin_family = AF_INET; sock.i.sin_port = htons( inAddress->mPort ); sock.i.sin_addr = *internet_address; int error; if( inTimeoutInMilliseconds != -1 ) { // use timeout error = timed_connect( socketID, &sock.s, sizeof( struct sockaddr ), inTimeoutInMilliseconds ); if( error == -2 ) { *outTimedOut = true; error = -1; } else { *outTimedOut = false; } } else { // don't use timeout error = connect( socketID, &sock.s, sizeof( struct sockaddr ) ); } delete internet_address; if( error == -1 ) { //printf( "Connecting to host failed: " ); //inAddress->print(); //printf( "\n" ); close( socketID ); return NULL; } // package into a Socket and return it Socket *returnSocket = new Socket(); int *idSpace = new int[1]; idSpace[0] = socketID; returnSocket->mNativeObjectPointer = (void *)idSpace; return returnSocket; } /* Converts ascii text to in_addr struct. NULL is returned if the address can not be found. Result must be destroyed by caller. Adapted from the Unix Socket FAQ */ struct in_addr *nameToAddress( char *inAddress ) { struct hostent *host; static struct in_addr saddr; struct in_addr *copiedSaddr = new struct in_addr; /* First try it as aaa.bbb.ccc.ddd. */ // this is obsolete on linux // saddr.s_addr = inet_addr( inAddress ); int error = inet_aton( inAddress, &saddr ); if( error != 0 ) { // copy to avoid returning pointer to stack memcpy( copiedSaddr, &saddr, sizeof( struct in_addr ) ); return copiedSaddr; } // must keep this locked until we are done copying the in_addr out // of the returned hostent NetworkFunctionLocks::mGetHostByNameLock.lock(); char hostFound = false; host = gethostbyname( inAddress ); if( host != NULL ) { memcpy( copiedSaddr, *host->h_addr_list, sizeof( struct in_addr ) ); hostFound = true; } NetworkFunctionLocks::mGetHostByNameLock.unlock(); if( hostFound ) { return copiedSaddr; } else { delete copiedSaddr; } return NULL; } /* timed_connect adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ) { int ret; fd_set fsr; struct timeval tv; int val; socklen_t len; //g_debug(1,"entering sock=%i secs=%i\n",inSocketID,inTimeoutInSeconds); ret = fcntl( inSocketID, F_SETFL, O_NONBLOCK ); //g_debug(5,"fcntl returned %i\n",ret); if( ret < 0 ) { return ret; } ret = connect( inSocketID, inSocketAddress, inAddressLength ); //g_debug(5,"connect returned %i\n",ret); if( ret == 0 ) { //g_debug(0,"immediate connection!\n"); // wooah! immediate connection // return -2; // changed from what Josh originally returned (-2) // immediate connection *can* happen sometimes, right? // for example, when connecting to localhost... return 0; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inTimeoutInMilliseconds / 1000; int remainder = inTimeoutInMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSocketID+1, NULL, &fsr, NULL, &tv ); //g_debug(5,"select returned %i\n",ret); if( ret==0 ) { // timeout //g_debug(1,"timeout\n"); fcntl( inSocketID, F_SETFL, 0 ); return -2; } len = 4; ret = getsockopt( inSocketID, SOL_SOCKET, SO_ERROR, &val, &len ); //g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if( ret < 0 ) { //g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { //g_debug(3,"returning failure!\n"); return -1; } ret = fcntl( inSocketID, F_SETFL, 0 ); //g_debug(1,"fcntl: %i\n",ret); //g_debug(3,"returning success val=%i\n",val); return 0; } transcend-0.3+dfsg2.orig/minorGems/network/linux/socketServerTimeoutTest.cpp0000640000175000017500000000226007455745246026131 0ustar pabspabs/* * Modification History * * 2002-April-12 Jason Rohrer * Created. */ #include "minorGems/network/Socket.h" #include "minorGems/network/SocketServer.h" #include void usage( char *inAppName ); int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } int port; int numRead = sscanf( inArgs[1], "%d", &port ); if( numRead != 1 ) { usage( inArgs[0] ); } SocketServer *server = new SocketServer( port, 100 ); char timedOut; Socket *sock; printf( "waiting for connection on port %d\n", port ); sock = server->acceptConnection( 5000, &timedOut ); if( timedOut ) { printf( "timed out after 5 seconds, waiting again\n" ); sock = server->acceptConnection( 5000, &timedOut ); } if( !timedOut && sock != NULL ) { printf( "connection received\n" ); delete sock; } else { printf( "timed out again\n" ); } delete server; return 1; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( " %s port_number\n", inAppName ); exit( 1 ); } transcend-0.3+dfsg2.orig/minorGems/network/linux/gnut_lib.c0000640000175000017500000000401707552330060022466 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #include #include #include #include #include #ifndef WIN32 #include #endif #ifdef MCHECK #include #endif #include "gnut_lib.h" #include "gnut_threads.h" int gnut_mprobe(void *d) { #ifdef MCHECK return mprobe(d); #else return 0; #endif } #ifndef HAVE_XMALLOC #ifndef xmalloc char *xmalloc(int size) { char *ptr; ptr=malloc(size); if (!ptr) { g_debug(0,"malloc(%d) failed!\n", size); exit(1); } return ptr; } #endif #endif char *expand_path(char *a) { char *b; if (strncmp(a,"~/", 2)==0) { #ifndef WIN32 char *c; c=getenv("HOME"); #else char c[MAX_PATH]; GetWindowsDirectory(c, MAX_PATH); #endif b=(char *)xmalloc(strlen(a)+strlen(c)+1); strcpy(b, c); strcat(b, &a[1]); return b; } b=(char *) xmalloc(strlen(a)+1); strcpy(b,a); return b; } int gnut_lib_debug=0; #ifndef PTHREADS_DRAFT4 pthread_mutex_t _g_debug_mutex=PTHREAD_MUTEX_INITIALIZER; #else pthread_mutex_t _g_debug_mutex; #endif #ifndef WIN32 void _g_debug(char *file, int line, int num, char *format, ...) { va_list argp; if (gnut_lib_debug>=num) { pthread_mutex_lock(&_g_debug_mutex); fprintf(stderr,"%i %s:%i > ", getpid(),file,line); fflush(stdout); va_start(argp,format); vfprintf(stderr,format,argp); va_end(argp); pthread_mutex_unlock(&_g_debug_mutex); } } #else void g_debug(int a, char *format, ...) { va_list argp; if (gnut_lib_debug>=a) { pthread_mutex_lock(&_g_debug_mutex); fprintf(stderr,"%li > ", (long) pthread_self()); va_start(argp,format); vfprintf(stderr,format,argp); va_end(argp); pthread_mutex_unlock(&_g_debug_mutex); } } #endif char *gnut_strdelimit(char *string, char *delim, char new_delim) { char *c; for (c=string;*c;c++) { if (strchr(delim,*c)) *c=new_delim; } return string; } transcend-0.3+dfsg2.orig/minorGems/network/linux/SocketServerLinux.cpp0000640000175000017500000001322110016416560024656 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Commented out redundant status messages that should be handled at a * higher level. * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * * 2001-January-29 Jason Rohrer * Fixed an endian bug with the port number. * * 2001-May-12 Jason Rohrer * Added a port number to the "bad socket bind" error message. * * 2001-November-13 Jason Rohrer * Added a missing include. * * 2001-December-12 Jason Rohrer * Changed some type usage and the include order to make BSD compatible. * Added a BSD definition test to make socklen_t type compatible * on both BSD and Linux. * * 2002-March-27 Jason Rohrer * Added a timeout to accept. * * 2002-March-29 Jason Rohrer * Fixed a bug in return value when accept fails. * * 2002-April-11 Jason Rohrer * Changed to use select instead of poll to make it more portable. * Fixed a bug in the timeout implementation. * * 2002-April-12 Jason Rohrer * Changed to use poll on Linux and select on BSD, * since select does not seem to work on LinuxX86. * Fixed X86 accept/select bug by getting rid of union * and some unused parameters (it seems that stack space was a problem). * * 2004-February-23 Jason Rohrer * Added socket option to clear up lingering bound ports after shutdown. * Changed to close server socket instead of using shutdown. */ #include "minorGems/network/SocketServer.h" #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD typedef int socklen_t; #else // poll not available on BSD #include #endif /** * BSD Compilation note: * * Use g++ option -DBSD to define the BSD preprocessor variable. */ SocketServer::SocketServer( int inPort, int inMaxQueuedConnections ) { int error = 0; if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); exit( 1 ); } } // create the socket int sockID = socket( AF_INET, SOCK_STREAM, 0 ); if( sockID == -1 ) { printf( "Failed to construct a socket\n" ); exit( 1 ); } // store socket id in native object pointer int *idStorage = new int[1]; idStorage[0] = sockID; mNativeObjectPointer = (void *)idStorage; // this setsockopt code partially copied from gnut // set socket option to enable reusing addresses so that the socket is // unbound immediately when the server is shut down // (otherwise, rebinding to the same port will fail for a while // after the server is shut down) int reuseAddressValue = 1; error = setsockopt( sockID, SOL_SOCKET, // socket-level option SO_REUSEADDR, // reuse address option &reuseAddressValue, // value to set for this option sizeof( reuseAddressValue) ); // size of the value if( error == -1 ) { printf( "Failed to set socket options\n" ); exit( 1 ); } // bind socket to the port struct sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons( inPort ); address.sin_addr.s_addr = INADDR_ANY; error = bind( sockID, (struct sockaddr *) &address, sizeof( address ) ); if( error == -1 ) { printf( "Bad socket bind, port %d\n", inPort ); exit( 1 ); } // start listening for connections error = listen( sockID, inMaxQueuedConnections ); if( error == -1 ) { printf( "Bad socket listen\n" ); exit(1); } } SocketServer::~SocketServer() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; close( socketID ); delete [] socketIDptr; } Socket *SocketServer::acceptConnection( long inTimeoutInMilliseconds, char *outTimedOut ) { // printf( "Waiting for a connection.\n" ); // extract socket id from native object pointer int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( outTimedOut != NULL ) { *outTimedOut = false; } if( inTimeoutInMilliseconds != -1 ) { // if we have a timeout specified, select before accepting // this found in the Linux man page for select, // but idea (which originally used poll) was found // in the Unix Socket FAQ fd_set rfds; struct timeval tv; int retval; // insert our socket descriptor into this set FD_ZERO( &rfds ); FD_SET( socketID, &rfds ); // convert our timeout into the structure's format tv.tv_sec = inTimeoutInMilliseconds / 1000; tv.tv_usec = ( inTimeoutInMilliseconds % 1000 ) * 1000 ; retval = select( socketID + 1, &rfds, NULL, NULL, &tv ); if( retval == 0 ) { // timeout if( outTimedOut != NULL ) { *outTimedOut = true; } return NULL; } } int acceptedID = accept( socketID, NULL, NULL ); if( acceptedID == -1 ) { printf( "Failed to accept a network connection.\n" ); return NULL; } else { Socket *acceptedSocket = new Socket(); int *idStorage = new int[1]; idStorage[0] = acceptedID; acceptedSocket->mNativeObjectPointer = (void *)idStorage; //printf( "Connection received.\n" ); return acceptedSocket; } } transcend-0.3+dfsg2.orig/minorGems/network/linux/HostAddressLinux.cpp0000640000175000017500000000631707776034461024511 0ustar pabspabs/* * Modification History * * 2001-December-14 Jason Rohrer * Created. * Fixed a typo. * Added a missing include. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * * 2002-March-26 Jason Rohrer * Changed to use strdup. * Fixed problem on some platforms where local address does * not include domain name. * Fixed so that numerical address is actually retrieved by * getNumericalAddress(). * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-8 Jason Rohrer * Changed to return numerical address from getLocalAddress, * since it is simpler, and we probably want the numerical * address anyway. * This new implementation also fixes a BSD domain-name lookup bug. * Changed to check if address is numerical before * performing a lookup on it. * * 2002-July-10 Jason Rohrer * Fixed a bug that occurs when fetching numerical address fails. * * 2004-January-4 Jason Rohrer * Added use of network function locks. */ #include "minorGems/network/HostAddress.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #include #include #include #include #include HostAddress *HostAddress::getLocalAddress() { int bufferLength = 200; char *buffer = new char[ bufferLength ]; gethostname( buffer, bufferLength ); char *name = stringDuplicate( buffer ); delete [] buffer; // this class will destroy name for us HostAddress *nameAddress = new HostAddress( name, 0 ); HostAddress *fullAddress = nameAddress->getNumericalAddress(); if( fullAddress != NULL ) { delete nameAddress; return fullAddress; } else { return nameAddress; } } HostAddress *HostAddress::getNumericalAddress() { // make sure we're not already numerical if( this->isNumerical() ) { return this->copy(); } // need to use two function locks here // first, lock for gethostbyname NetworkFunctionLocks::mGetHostByNameLock.lock(); struct hostent *host = gethostbyname( mAddressString ); if (host != NULL) { // this line adapted from the // Unix Socket FAQ struct in_addr *inetAddress = (struct in_addr *) *host->h_addr_list; // now lock for inet_ntoa NetworkFunctionLocks::mInet_ntoaLock.lock(); // the returned string is statically allocated, so copy it char *inetAddressString = stringDuplicate( inet_ntoa( *inetAddress ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); // done with returned hostent now, so unlock first lock NetworkFunctionLocks::mGetHostByNameLock.unlock(); return new HostAddress( inetAddressString, mPort ); } else { // unlock first lock before returning NetworkFunctionLocks::mGetHostByNameLock.unlock(); return NULL; } } char HostAddress::isNumerical() { struct in_addr addressStruct; int isValid = inet_aton( mAddressString, &addressStruct ); if( isValid == 0 ) { return false; } else { return true; } } transcend-0.3+dfsg2.orig/minorGems/network/linux/socketSelectAcceptBug.cpp0000640000175000017500000000600707455757734025461 0ustar pabspabs/* * Demonstration of "accept after select" socket bug. * * This program works fine on: * LinuxPPC, FreeBSD, and MacOSX * * This program does not work on: * LinuxX86 Kernel 2.4 * (accept fails after select returns) * * This bug has something to do with the size of the * variables on the stack... * Getting rid of the union (which is unnecessary) seems * to fix the problem. * * * Jason Rohrer * 2002-April-12 * rohrer@cse.ucsc.edu */ #include #include #include #include #include #include #include #include union sock { struct sockaddr s; struct sockaddr_in i; } sock; void usage( char *inAppName ); int acceptConnection( int inSocketID, long inTimeoutMS ); int port; int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } int numRead = sscanf( inArgs[1], "%d", &port ); // create a socket and start listening for connections printf( "Creating socket\n" ); int socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == -1 ) { printf( "Socket creation failed\n" ); exit( 1 ); } // bind socket to the port union sock sockAddress; sockAddress.i.sin_family = AF_INET; sockAddress.i.sin_port = htons( port ); sockAddress.i.sin_addr.s_addr = INADDR_ANY; printf( "Binding to socket at port %d\n", port ); int error = bind( socketID, &(sockAddress.s), sizeof( struct sockaddr ) ); if( error == -1 ) { printf( "Bad socket bind, port %d\n", port ); exit( 1 ); } // start listening for connections, max queued = 100 connections printf( "Starting to listen to socket\n" ); error = listen( socketID, 100 ); if( error == -1 ) { printf( "Bad socket listen\n" ); exit(1); } // no we're listening to this socket int acceptedID = acceptConnection( socketID, 50000 ); if( acceptedID != -1 ) { // close the connection shutdown( acceptedID, SHUT_RDWR ); close( acceptedID ); } shutdown( socketID, SHUT_RD ); return 1; } int acceptConnection( int inSocketID, long inTimeoutMS ) { // use select to timeout if no connection received in 10 seconds struct timeval tv; socklen_t addressLength; union sock acceptedAddress; int acceptedID = accept( inSocketID, &( acceptedAddress.s ), &addressLength ); if( acceptedID == -1 ) { printf( "Failed to accept the connection\n" ); printf( "Error = %d\n", errno ); return -1; } else { printf( "Successfully accepted the connection\n" ); return acceptedID; } } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( " %s port_number\n", inAppName ); exit( 1 ); } transcend-0.3+dfsg2.orig/minorGems/network/linux/socketSelectAcceptBug.compile0000750000175000017500000000007107455750432026310 0ustar pabspabsg++ -g -o socketSelectAcceptBug socketSelectAcceptBug.cpptranscend-0.3+dfsg2.orig/minorGems/network/linux/SocketLinux.cpp0000640000175000017500000001406210262514000023462 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-10 Jason Rohrer * Fixed a bug in the receive code (in use of timed_read). * * 2001-January-15 Jason Rohrer * Added a safeguard against receives that exceed the * network buffer size. * * 2001-January-28 Jason Rohrer * Changed to comply with new initSocketFramework Socket interface. * Added a close() call to the destructor. * * 2001-January-29 Jason Rohrer * Fixed compile bug by including unistd.h for close call. * * 2001-November-13 Jason Rohrer * Changed to use ::send function instead of __send function. * Changed timeout parameter to signed, since -1 is a possible argument. * * 2001-December-12 Jason Rohrer * Changed the include order to make BSD compatible. * * 2002-August-2 Jason Rohrer * Changed to ignore SIGPIPE. * Added functon for getting remote host address. * Added fix for linux-BSD differences. * * 2002-November-15 Jason Rohrer * Fixed a security hole when getting the remote host address. * * 2003-February-3 Jason Rohrer * Added a function for getting the local host address from a socket. * * 2003-February-21 Jason Rohrer * Fixed a BSD compatibility bug. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2004-March-23 Jason Rohrer * Removed timeout error message. * * 2004-December13 Jason Rohrer * Added a breakConnection function. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. */ #include "minorGems/network/Socket.h" #include "minorGems/network/NetworkFunctionLocks.h" #include #include #include #include #include #include #include #include #include #include #include #include // BSD does not define socklen_t #ifdef BSD typedef int socklen_t; #endif // prototypes int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ); /** * Linux-specific implementation of the Socket class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * no special options needed */ char Socket::sInitialized = false; int Socket::initSocketFramework() { // ignore SIGPIPE, which occurs on send whenever the receiver // has closed the socket signal(SIGPIPE, SIG_IGN); sInitialized = true; return 0; } Socket::~Socket() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, SHUT_RDWR ); mIsConnectionBroken = true; } close( socketID ); delete [] socketIDptr; } int Socket::send( unsigned char *inBuffer, int inNumBytes ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; return ::send( socketID, inBuffer, inNumBytes, 0 ); } int Socket::receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( inTimeout == -1 ) { return recv( socketID, inBuffer, inNumBytes, MSG_WAITALL ); } else { return timed_read( socketID, inBuffer, inNumBytes, inTimeout ); } } void Socket::breakConnection() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, SHUT_RDWR ); mIsConnectionBroken = true; } close( socketID ); } HostAddress *Socket::getRemoteHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from Unix Socket FAQ socklen_t len; struct sockaddr_in sin; len = sizeof sin; int error = getpeername( socketID, (struct sockaddr *) &sin, &len ); if( error ) { return NULL; } // this is potentially insecure, since a fake DNS name might be returned // we should use the IP address only // // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr, // sizeof sin.sin_addr, // AF_INET ); NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); int port = ntohs( sin.sin_port ); return new HostAddress( ipAddress, port ); } HostAddress *Socket::getLocalHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from GTK-gnutalla code, and elsewhere struct sockaddr_in addr; socklen_t len = sizeof( struct sockaddr_in ); int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len ); if( result == -1 ) { return NULL; } else { char *stringAddress = inet_ntoa( addr.sin_addr ); return new HostAddress( stringDuplicate( stringAddress ), 0 ); } } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ) { fd_set fsr; struct timeval tv; int ret; //ret = fcntl( inSock, F_SETFL, O_NONBLOCK ); FD_ZERO( &fsr ); FD_SET( inSock, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSock + 1, &fsr, NULL, NULL, &tv ); if( ret==0 ) { // printf( "Timed out waiting for data on socket receive.\n" ); return -2; } if( ret<0 ) { printf( "Selecting socket during receive failed.\n" ); return ret; } ret = recv( inSock, inBuf, inLen, MSG_WAITALL ); //fcntl( inSock, F_SETFL, 0 ); return ret; } transcend-0.3+dfsg2.orig/minorGems/network/linux/gnut_lib.h0000640000175000017500000000435207552330061022476 0ustar pabspabs/* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ #ifndef GNUT_LIB_H #define GNUT_LIB_H 0 #include #ifndef WIN32 #include #include #endif #ifdef DMALLOC #include #endif #ifndef MIN #define MIN(a,b) (((a < b)) ? (a) : (b)) #define MAX(a,b) (((a > b)) ? (a) : (b)) #endif #ifndef __bswap_16 #define __bswap_16(x) \ ((((x) >> 8) & 0xff) | (((x) &0xff) <<8)) #endif #ifndef __bswap_32 #define __bswap_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #endif #ifndef GUINT16_TO_LE #ifdef WORDS_BIGENDIAN #define GUINT16_TO_LE(x) __bswap_16(x) #define GUINT32_TO_LE(x) __bswap_32(x) #define GUINT16_FROM_LE(x) __bswap_16(x) #define GUINT32_FROM_LE(x) __bswap_32(x) #else #define GUINT16_TO_LE(x) (x) #define GUINT32_TO_LE(x) (x) #define GUINT16_FROM_LE(x) (x) #define GUINT32_FROM_LE(x) (x) #endif #endif typedef unsigned int uint32; typedef unsigned short uint16; typedef unsigned char uchar; #ifndef WIN32 typedef unsigned long long uint64; //#define g_debug(num, format, args... ) #define g_debug(num, format, args... ) _g_debug(__FUNCTION__,__LINE__, num, format, ##args) void _g_debug(char *,int,int num, char *,...); #endif extern int gnut_lib_debug; int gnut_mprobe(void *); char *gnut_strdelimit(char *string, char *delimeters, char new_delim); char *expand_path(char *); #ifndef xmalloc char *xmalloc(int size); #endif #ifdef WIN32 #include #define VERSION "0.3.22/win32" void g_debug(int, char *, ...); typedef unsigned __int64 uint64; typedef unsigned int uint; #define sleep Sleep char *strtok_r(char *, const char *, char **); int getopt(int argc, char *argv[], char *); extern char *optarg; extern int optind; #define strncasecmp strnicmp #define inet_aton(string, ip) (ip)->s_addr = inet_addr(string) #define write(sock, buf, len) send(sock, buf, len, 0) #define read(sock, buf, len) recv(sock, buf, len, 0) #define close(sock) closesocket(sock) #define F_SETFL 1 #define O_NONBLOCK 1 int fcntl(int sock, int, uint); #define flock(a,b) 0 #endif #endif transcend-0.3+dfsg2.orig/minorGems/network/Socket.h0000640000175000017500000001703610262513724020770 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added a static framework init function. * * 2001-November-13 Jason Rohrer * Changed timeout parameter to signed, since -1 is a possible argument. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-August-2 Jason Rohrer * Added functon for getting remote host address. * * 2003-February-3 Jason Rohrer * Added a function for getting the local host address from a socket. * * 2003-August-12 Jason Rohrer * Added more verbose comment about receive timeout parameter. * Added a function for flushing socket sends. * * 2003-November-20 Jason Rohrer * Made flush function robust against bogus receive return values. * * 2004-December13 Jason Rohrer * Added a breakConnection function. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. */ #include "minorGems/common.h" #ifndef SOCKET_CLASS_INCLUDED #define SOCKET_CLASS_INCLUDED #include "minorGems/network/HostAddress.h" #include "minorGems/system/Time.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Network socket. Does not contain an interface for listening to connections * (see SocketServer for these interfaces) or for establishing connections * (see SocketClient for these interfaces). * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class Socket { public: /** * Constructor for a socket. * * Should not be called directly. Use SocketClient or SocketServer * to obtain an outbound or inbound socket connection. */ Socket(); // destroying a Socket closes the connection ~Socket(); /** * Initializes the socket framework. Must be called * once by program before sockets are used. Note * that SocketClient and SocketServer both should call * init automatically when they are first used. * * @return 0 on success, or -1 on failure. */ static int initSocketFramework(); /** * Gets whether the socket framework has been initialized. * * @return true if the framework has been initialized. */ static char isFrameworkInitialized(); /** * Sends bytes through this socket. * * @param inBuffer the buffer of bytes to send. * @param inNumBytes the number of bytes to send. * * @return the number of bytes sent successfully, * or -1 for a socket error. */ int send( unsigned char *inBuffer, int inNumBytes ); /** * Receives bytes from this socket. * * @param inBuffer the buffer where received bytes will be put. * Must be pre-allocated memory space. * @param inNumBytes the number of bytes to read from the socket. * @param inTimeout the timeout for this receive operation in * milliseconds. Set to -1 for an infinite timeout. * -2 is returned from this call in the event of a timeout. * * @return the number of bytes read successfully, * or -1, -2 for a socket error or timeout, respectively. */ int receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ); /** * Flushes sent data through the socket. * * Intended to be called before deleting the socket to ensure * that data goes through. The mechanism used by this function * does not guarantee that data goes through, but it works * better than simply closing the socket on many platforms. * * The method used by this call works best if the remote * host closes the connection. For example, if we send the * remote host a "connection closing" indicator, then * call writeFlushBeforeClose, and the remote host closes * the connection upon receiving the indicator, all of our * sent data will be received by the remote host after * writeFlushBeforeClose returns (assuming that the maximum * time is long enough for the remote host to actually close * the connection). * * Good maximum times should be in the several-second range, * though the Apache system uses 30 seconds. For slow connections, * this might be necessary. * * This call will cause any received data to be discarded. * * This call DOES NOT close the socket. The socket must be deleted * as usual after this call returns. * * @param inMaxTimeInMilliseconds the maximum time to wait * in milliseconds. This call may block slightly longer than * this if the remote host is still sending data. */ void sendFlushBeforeClose( int inMaxTimeInMilliseconds ); /** * Forces this socket's connection to break, causing any blocked * send or receive operations to fail. * * Note that this operation is inherently not thread-safe, so * some external mechanism must be used to ensure that the socket * is not destroyed by another thread before breakConnection is called. * The SocketManager class is one such external mechanism. */ void breakConnection(); /** * Gets the host connected to the other end of this socket. * * @return the address of the remote host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to the port that the host is connecting from. * Must be destroyed by caller if non-NULL. */ HostAddress *getRemoteHostAddress(); /** * Gets the local address attached to this socket. * * Getting the local address from a socket is more * accurate than non-connected methods (for example, the methods * used in HostAddress.h implementations. * * @return the address of the local host, or NULL if obtaining * the address fails. The port of the returned address * will always be set to 0. * Must be destroyed by caller if non-NULL. */ HostAddress *getLocalHostAddress(); /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; private: static char sInitialized; char mIsConnectionBroken; }; inline Socket::Socket() : mIsConnectionBroken( false ) { } inline char Socket::isFrameworkInitialized() { return sInitialized; } inline void Socket::sendFlushBeforeClose( int inMaxTimeInMilliseconds ) { unsigned char *tempBuffer = new unsigned char[1]; int numRead = -2; int timeout = 1000; if( timeout > inMaxTimeInMilliseconds ) { timeout = inMaxTimeInMilliseconds; } long totalTimeout = 0; unsigned long startSec; unsigned long startMsec; Time::getCurrentTime( &startSec, &startMsec ); // keep reading data from socket until we get an error // or wait too long (pass our max timeout) while( numRead != -1 && totalTimeout < inMaxTimeInMilliseconds ) { numRead = this->receive( tempBuffer, 1, timeout ); // track total time whether the receive timed out or not totalTimeout = (long)( Time::getMillisecondsSince( startSec, startMsec ) ); } delete [] tempBuffer; } #endif transcend-0.3+dfsg2.orig/minorGems/network/socketClientTest.cpp0000640000175000017500000000560307620021426023354 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-28 Jason Rohrer * Added more output. * Changed so that it doesn't sleep in the middle of a block * since the requirements of the socket interface have changed * and implementations no longer have to support this behavior. * * 2001-December-12 Jason Rohrer * Changed to use new HostAddress constructor. * * 2002-July-30 Jason Rohrer * Fixed a bug in localhost address. Added a test of streams. * * 2002-July-31 Jason Rohrer * Changed to test multiple blocks. * * 2002-December-2 Jason Rohrer * Changed to test socket creation failure. * Added counter to output. * * 2002-February-4 Jason Rohrer * Added test of getting local address from socket. */ #include "Socket.h" #include "SocketStream.h" #include "SocketServer.h" #include "SocketClient.h" #include "HostAddress.h" #include int main() { char *name = new char[99]; sprintf( name, "63.249.65.249" ); //char *name = new char[99]; //sprintf( name, "192.168.1.2" ); //char *name = "192.168.1.1"; //int addressLength = 11; //char *name = "monolith.2y.net"; //int addressLength = 15; int port = 5158; HostAddress *address = new HostAddress( name, port ); printf( "Trying to connect to server: " ); address->print(); printf( "\n" ); Socket *sock; int numConnections = 0; while( true ) { sock = SocketClient::connectToServer( address ); if( sock == NULL ) { printf( "%d Connecting to server failed\n", numConnections ); //return 1; } else { printf( "%d Connection established\n", numConnections ); HostAddress *localAddress = sock->getLocalHostAddress(); if( localAddress != NULL ) { printf( "Our local address (fetched from socket) is " ); localAddress->print(); printf( "\n" ); delete localAddress; } delete sock; } //usleep( 1000 ); numConnections++; } int numBytes = 4000; unsigned char *buffer = new unsigned char[numBytes]; for( int i=0; iwrite( buffer, numBytes ); printf( "Sent %d successfully,\tcount = %d\n", numSent, count ); count++; } int checksum = 0; for( int i=0; i /** * A SocketStream that logs inbound and outbound data. * * @author Jason Rohrer */ class LoggingSocketStream : public SocketStream { public: /** * Constructs a SocketStream. * * @param inSocket the newtork socket wrapped by this stream. * inSocket is NOT destroyed when the stream is destroyed. * @param inEnableInboundLog set to true to enable inbound logging. * @param inEnableOutboundLog set to true to enable outbound logging. * @param inLogSizeLimit the size limit of each log (both inbound * and outbound), in bytes. * Logging will stop after inLogSizeLimit bytes have been * written to each log. * @param inLogNamePrefix a string that will be used to name * the log files (appending .in and .out for the two logs). * Should be unique. * Must be destroyed by caller. */ LoggingSocketStream( Socket *inSocket, char inEnableInboundLog, char inEnableOutboundLog, unsigned long inLogSizeLimit, char *inLogNamePrefix ); virtual ~LoggingSocketStream(); // overrides the SocketStream implementations long read( unsigned char *inBuffer, long inNumBytes ); long write( unsigned char *inBuffer, long inNumBytes ); protected: char mEnableInboundLog; char mEnableOutboundLog; unsigned long mLogSizeLimit; FILE *mInboundLogFile; FILE *mOutboundLogFile; unsigned long mInboundSizeSoFar; unsigned long mOutboundSizeSoFar; }; inline LoggingSocketStream::LoggingSocketStream( Socket *inSocket, char inEnableInboundLog, char inEnableOutboundLog, unsigned long inLogSizeLimit, char *inLogNamePrefix ) : SocketStream( inSocket ), mEnableInboundLog( inEnableInboundLog ), mEnableOutboundLog( inEnableOutboundLog ), mLogSizeLimit( inLogSizeLimit ), mInboundLogFile( NULL ), mOutboundLogFile( NULL ), mInboundSizeSoFar( 0 ), mOutboundSizeSoFar( 0 ) { if( mEnableInboundLog ) { char *inboundFileName = autoSprintf( "%s.in", inLogNamePrefix ); mInboundLogFile = fopen( inboundFileName, "wb" ); delete [] inboundFileName; } if( mEnableOutboundLog ) { char *outboundFileName = autoSprintf( "%s.out", inLogNamePrefix ); mOutboundLogFile = fopen( outboundFileName, "wb" ); delete [] outboundFileName; } } inline LoggingSocketStream::~LoggingSocketStream() { if( mInboundLogFile != NULL ) { fclose( mInboundLogFile ); mInboundLogFile = NULL; } if( mOutboundLogFile != NULL ) { fclose( mOutboundLogFile ); mOutboundLogFile = NULL; } } inline long LoggingSocketStream::read( unsigned char *inBuffer, long inNumBytes ) { long returnVal = SocketStream::read( inBuffer, inNumBytes ); if( mEnableInboundLog && mInboundSizeSoFar < mLogSizeLimit && returnVal == inNumBytes ) { int numToLog = inNumBytes; if( mInboundSizeSoFar + numToLog > mLogSizeLimit ) { numToLog = mLogSizeLimit - mInboundSizeSoFar; } if( mInboundLogFile != NULL ) { fwrite( inBuffer, 1, numToLog, mInboundLogFile ); fflush( mInboundLogFile ); } mInboundSizeSoFar += numToLog; } return returnVal; } inline long LoggingSocketStream::write( unsigned char *inBuffer, long inNumBytes ) { long returnVal = SocketStream::write( inBuffer, inNumBytes ); if( mEnableOutboundLog && mOutboundSizeSoFar < mLogSizeLimit && returnVal == inNumBytes ) { int numToLog = inNumBytes; if( mOutboundSizeSoFar + numToLog > mLogSizeLimit ) { numToLog = mLogSizeLimit - mOutboundSizeSoFar; } if( mOutboundLogFile != NULL ) { fwrite( inBuffer, 1, numToLog, mOutboundLogFile ); fflush( mOutboundLogFile ); } mOutboundSizeSoFar += numToLog; } return returnVal; } #endif transcend-0.3+dfsg2.orig/minorGems/network/win32/0000750000175000017500000000000010305077065020322 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/network/win32/SocketWin32.cpp0000640000175000017500000001736110262514000023075 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Fixed receive so that it waits for all requested bytes to arrive. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2001-May-12 Jason Rohrer * Fixed a bug in socket receive error checking. * * 2001-November-13 Jason Rohrer * Changed timeout parameter to signed, since -1 is a possible argument. * * 2002-April-15 Jason Rohrer * Removed call to WSAGetLastError, since it seems to pick up errors from * non-socket calls. For example, if sys/stat.h stat() is called on a file * that does not exist, WSAGetLastError returns 2, which is not even a * winsock error code. We should probably report this bug, huh? * * 2002-August-2 Jason Rohrer * Added functon for getting remote host address, but no implementation. * * 2002-August-5 Jason Rohrer * Added implementation of getRemoteHostAddress(). * * 2002-September-8 Jason Rohrer * Fixed a major looping bug with broken sockets. * * 2002-November-15 Jason Rohrer * Fixed a security hole when getting the remote host address. * * 2003-February-4 Jason Rohrer * Added a function for getting the local host address from a socket. * Still need to test the win32 version of this. * * 2003-February-5 Jason Rohrer * Fixed a bug in call to gethostname. Removed unused variable. * * 2004-January-4 Jason Rohrer * Added use of network function locks. * * 2004-January-11 Jason Rohrer * Fixed a bug in handling of timeout return value. * * 2004-March-23 Jason Rohrer * Removed timeout error message. * * 2004-December-13 Jason Rohrer * Added a breakConnection function. * * 2004-December-24 Jason Rohrer * Fixed bug in close call. * * 2005-July-5 Jason Rohrer * Added port number when getting address of remote host. */ #include "minorGems/network/Socket.h" #include "minorGems/network/NetworkFunctionLocks.h" #include #include #include #include #include // prototypes int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ); /** * Windows-specific implementation of the Socket class member functions. * */ // Win32 does not define socklen_t typedef int socklen_t; char Socket::sInitialized = false; int Socket::initSocketFramework() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 0 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { // no usable DLL found printf( "WinSock DLL version 1.0 or higher not found.\n" ); return -1; } sInitialized = true; return 0; } Socket::~Socket() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { // 2 specifies shutting down both sends and receives shutdown( socketID, 2 ); mIsConnectionBroken = true; } closesocket( socketID ); delete [] socketIDptr; } int Socket::send( unsigned char *inBuffer, int inNumBytes ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; return ::send( socketID, (char*)inBuffer, inNumBytes, 0 ); } int Socket::receive( unsigned char *inBuffer, int inNumBytes, long inTimeout ) { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; int numReceived = 0; char error = false; char errorReturnValue = -1; // for win32, we can't specify MSG_WAITALL // so we have too loop until the entire message is received, // as long as there is no error. while( numReceived < inNumBytes && !error ) { // the number of bytes left to receive int numRemaining = inNumBytes - numReceived; // pointer to the spot in the buffer where the // remaining bytes should be stored unsigned char *remainingBuffer = &( inBuffer[ numReceived ] ); int numReceivedIn; if( inTimeout == -1 ) { numReceivedIn = recv( socketID, (char*)remainingBuffer, numRemaining, 0 ); } else { numReceivedIn = timed_read( socketID, remainingBuffer, numRemaining, inTimeout ); } if( numReceivedIn > 0 ) { numReceived += numReceivedIn; } else { error = true; if( numReceivedIn == 0 ) { // the socket was gracefully closed errorReturnValue = -1; } else if( numReceivedIn == SOCKET_ERROR ) { // socket error errorReturnValue = -1; } else if( numReceivedIn == -2 ) { // timeout errorReturnValue = -2; } else { printf( "Unexpected return value from socket receive: %d.\n", numReceivedIn ); errorReturnValue = -1; } } } return numReceived; } void Socket::breakConnection() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; if( !mIsConnectionBroken ) { shutdown( socketID, 2 ); mIsConnectionBroken = true; } closesocket( socketID ); } HostAddress *Socket::getRemoteHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from Unix Socket FAQ socklen_t len; struct sockaddr_in sin; len = sizeof sin; int error = getpeername( socketID, (struct sockaddr *) &sin, &len ); if( error ) { return NULL; } // this is potentially insecure, since a fake DNS name might be returned // we should use the IP address only // // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr, // sizeof sin.sin_addr, // AF_INET ); NetworkFunctionLocks::mInet_ntoaLock.lock(); // returned string is statically allocated, copy it char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); int port = ntohs( sin.sin_port ); return new HostAddress( ipAddress, port ); } HostAddress *Socket::getLocalHostAddress() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // adapted from GTK-gnutalla code, and elsewhere struct sockaddr_in addr; int len = sizeof( struct sockaddr_in ); int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len ); if( result == -1 ) { return NULL; } else { char *stringAddress = inet_ntoa( addr.sin_addr ); return new HostAddress( stringDuplicate( stringAddress ), 0 ); } } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // exactly like the real read, except that it returns -2 // if no data was read before the timeout occurred... int timed_read( int inSock, unsigned char *inBuf, int inLen, long inMilliseconds ) { fd_set fsr; struct timeval tv; int ret; FD_ZERO( &fsr ); FD_SET( inSock, &fsr ); tv.tv_sec = inMilliseconds / 1000; int remainder = inMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSock + 1, &fsr, NULL, NULL, &tv ); if( ret==0 ) { // printf( "Timed out waiting for data on socket receive.\n" ); return -2; } if( ret<0 ) { printf( "Selecting socket during receive failed.\n" ); return ret; } ret = recv( inSock, (char*)inBuf, inLen, 0 ); return ret; } transcend-0.3+dfsg2.orig/minorGems/network/win32/HostAddressWin32.cpp0000640000175000017500000000642507776034461024117 0ustar pabspabs/* * Modification History * * 2001-December-14 Jason Rohrer * Created. * Has not been compiled or tested under windows. * * 2002-March-25 Jason Rohrer * Added function for conversion to numerical addresses. * * 2002-March-26 Jason Rohrer * Changed to use strdup. * Fixed problem on some platforms where local address does * not include domain name. * Fixed so that numerical address is actually retrieved by * getNumericalAddress(). * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-7 Jason Rohrer * Changed to return numerical address from getLocalAddress, * since getdomainname() call not available on Win32. * * 2002-April-8 Jason Rohrer * Changed to init socket framework before getting local address. * Changed to check if address is numerical before * performing a lookup on it. * * 2004-January-4 Jason Rohrer * Added use of network function locks. */ #include "minorGems/network/HostAddress.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/util/stringUtils.h" #include #include #include #include "minorGems/network/Socket.h" HostAddress *HostAddress::getLocalAddress() { // first make soure sockets are initialized if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } int bufferLength = 200; char *buffer = new char[ bufferLength ]; gethostname( buffer, bufferLength ); char *name = stringDuplicate( buffer ); delete [] buffer; // this class will destroy name for us HostAddress *nameAddress = new HostAddress( name, 0 ); HostAddress *fullAddress = nameAddress->getNumericalAddress(); delete nameAddress; return fullAddress; } HostAddress *HostAddress::getNumericalAddress() { // make sure we're not already numerical if( this->isNumerical() ) { return this->copy(); } // need to use two function locks here // first, lock for gethostbyname NetworkFunctionLocks::mGetHostByNameLock.lock(); struct hostent *host = gethostbyname( mAddressString ); if (host != NULL) { // this line adapted from the // Unix Socket FAQ struct in_addr *inetAddress = (struct in_addr *) *host->h_addr_list; // now lock for inet_ntoa NetworkFunctionLocks::mInet_ntoaLock.lock(); // the returned string is statically allocated, so copy it char *inetAddressString = stringDuplicate( inet_ntoa( *inetAddress ) ); NetworkFunctionLocks::mInet_ntoaLock.unlock(); // done with returned hostent now, so unlock first lock NetworkFunctionLocks::mGetHostByNameLock.unlock(); return new HostAddress( inetAddressString, mPort ); } else { // unlock first lock before returning NetworkFunctionLocks::mGetHostByNameLock.unlock(); return NULL; } } char HostAddress::isNumerical() { int returnedValue = inet_addr( mAddressString ); if( returnedValue == INADDR_NONE ) { return false; } else { return true; } } transcend-0.3+dfsg2.orig/minorGems/network/win32/SocketClientWin32.cpp0000640000175000017500000001565307776034461024266 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-January-29 Jason Rohrer * Fixed an endian bug that was messing up the port numbers. * * 2002-October-13 Jason Rohrer * Added support for timeout on connect. * Fixed several windows compile bugs. * * 2002-December-2 Jason Rohrer * Fixed resource leak when connection fails. * Changed close calls to win32 closesocket calls. * * 2004-January-2 Jason Rohrer * Fixed a thread safety issue with gethostbyname. * * 2004-January-4 Jason Rohrer * Added use of network function locks. */ #include "minorGems/network/SocketClient.h" #include "minorGems/network/NetworkFunctionLocks.h" #include "minorGems/system/MutexLock.h" #include #include #include #include #include // prototypes struct in_addr *nameToAddress( char *inAddress ); int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ); Socket *SocketClient::connectToServer( HostAddress *inAddress, long inTimeoutInMilliseconds, char *outTimedOut ) { if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); return NULL; } } int socketID = socket( AF_INET, SOCK_STREAM, 0 ); if( socketID == INVALID_SOCKET ) { printf( "Creating socket failed.\n" ); return NULL; } union sock { struct sockaddr s; struct sockaddr_in i; } sock; //struct in_addr internet_address; //struct hostent *hp; struct in_addr *internet_address = nameToAddress( inAddress->mAddressString ); if( internet_address == NULL ) { printf( "Host name lookup failed: " ); inAddress->print(); printf( "\n" ); closesocket( socketID ); return NULL; } //hp = gethostbyname( inAddress->mAddressString ); //memcpy( &internet_address, *( hp->h_addr_list ), sizeof( struct in_addr ) ); sock.i.sin_family = AF_INET; sock.i.sin_port = htons( inAddress->mPort ); sock.i.sin_addr = *internet_address; int error; if( inTimeoutInMilliseconds != -1 ) { // use timeout error = timed_connect( socketID, &sock.s, sizeof( struct sockaddr ), inTimeoutInMilliseconds ); if( error == -2 ) { *outTimedOut = true; error = -1; } else { *outTimedOut = false; } } else { // don't use timeout error = connect( socketID, &sock.s, sizeof( struct sockaddr ) ); } delete internet_address; if( error == -1 ) { //printf( "Connecting to host failed: " ); //inAddress->print(); //printf( "\n" ); closesocket( socketID ); return NULL; } // packate into a Socket and return it Socket *returnSocket = new Socket(); int *idSpace = new int[1]; idSpace[0] = socketID; returnSocket->mNativeObjectPointer = (void *)idSpace; return returnSocket; } /* Converts ascii text to in_addr struct. NULL is returned if the address can not be found. Result must be destroyed by caller. Adapted from the Unix Socket FAQ */ struct in_addr *nameToAddress( char *inAddress ) { struct hostent *host; struct hostent *copiedHost; static struct in_addr saddr; struct in_addr *copiedSaddr = new struct in_addr; /* First try it as aaa.bbb.ccc.ddd. */ saddr.s_addr = inet_addr( inAddress ); if( saddr.s_addr != -1 ) { // copy to avoid returning pointer to stack memcpy( copiedSaddr, &saddr, sizeof( struct in_addr ) ); return copiedSaddr; } // must keep this locked until we are done copying the in_addr out // of the returned hostent NetworkFunctionLocks::mGetHostByNameLock.lock(); char hostFound = false; host = gethostbyname( inAddress ); if( host != NULL ) { memcpy( copiedSaddr, *host->h_addr_list, sizeof( struct in_addr ) ); hostFound = true; } NetworkFunctionLocks::mGetHostByNameLock.unlock(); if( hostFound ) { return copiedSaddr; } else { delete copiedSaddr; } return NULL; } /* timed_read adapted from gnut, by Josh Pieper */ /* Josh Pieper, (c) 2000 */ /* This file is distributed under the GPL, see file COPYING for details */ // just like connect except that it times out after time secs // returns -2 on timeout, otherwise same as connect int timed_connect( int inSocketID, struct sockaddr *inSocketAddress, int inAddressLength, int inTimeoutInMilliseconds ) { int ret; fd_set fsr; struct timeval tv; int val; int len; //g_debug(1,"entering sock=%i secs=%i\n",inSocketID,inTimeoutInSeconds); //ret = fcntl( inSocketID, F_SETFL, O_NONBLOCK ); // use ioctlsocket on win32 instead of fcntl // set to non-blocking mode unsigned long onFlag = 1; ret = ioctlsocket( inSocketID, FIONBIO, &onFlag ); //g_debug(5,"fcntl returned %i\n",ret); if( ret < 0 ) { return ret; } ret = connect( inSocketID, inSocketAddress, inAddressLength ); //g_debug(5,"connect returned %i\n",ret); if( ret == 0 ) { //g_debug(0,"immediate connection!\n"); // wooah! immediate connection // return -2; // changed from what Josh originally returned (-2) // immediate connection *can* happen sometimes, right? // for example, when connecting to localhost... return 0; } // if (errno != EINPROGRESS) { // perror("timed_connect, connect"); // return ret; // } FD_ZERO( &fsr ); FD_SET( inSocketID, &fsr ); tv.tv_sec = inTimeoutInMilliseconds / 1000; int remainder = inTimeoutInMilliseconds % 1000; tv.tv_usec = remainder * 1000; ret = select( inSocketID+1, NULL, &fsr, NULL, &tv ); //g_debug(5,"select returned %i\n",ret); if( ret==0 ) { // timeout //g_debug(1,"timeout\n"); //fcntl( inSocketID, F_SETFL, 0 ); unsigned long offFlag = 0; ret = ioctlsocket( inSocketID, FIONBIO, &offFlag ); return -2; } len = 4; ret = getsockopt( inSocketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len ); //g_debug(5,"getsockopt returned %i val=%i\n",ret,val); if( ret < 0 ) { //g_debug(1,"getsockopt returned: %i\n",ret); return ret; } if (val!=0) { //g_debug(3,"returning failure!\n"); return -1; } //ret = fcntl( inSocketID, F_SETFL, 0 ); // use ioctlsocket on win32 instead of fcntl // set back to blocking mode unsigned long offFlag = 0; ret = ioctlsocket( inSocketID, FIONBIO, &offFlag ); //g_debug(1,"fcntl: %i\n",ret); //g_debug(3,"returning success val=%i\n",val); return 0; } transcend-0.3+dfsg2.orig/minorGems/network/win32/WSABugDemo.cpp0000640000175000017500000000140207457040432022723 0ustar pabspabs#include #include #include #include int main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 0 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { // no usable DLL found printf( "WinSock DLL version 1.0 or higher not found.\n" ); return -1; } int error = WSAGetLastError(); printf( "WSA Error before stat call = %d\n", error ); struct stat fileInfo; int statError = stat( "DoesNotExist", &fileInfo ); printf( "stat error = %d\n", statError ); error = WSAGetLastError(); printf( "WSA Error after stat call = %d\n", error ); return 0; } transcend-0.3+dfsg2.orig/minorGems/network/win32/SocketServerWin32.cpp0000640000175000017500000000762710016424475024305 0ustar pabspabs/* * Modification History * * 2001-January-28 Jason Rohrer * Created. * * 2001-January-29 Jason Rohrer * Fixed an endian bug that was messing up the port numbers. * * 2002-April-6 Jason Rohrer * Changed to implement the new timeout-on-accept interface, * though did no actually implement timeouts. * * 2002-September-8 Jason Rohrer * Added a check for a potentially NULL argument. * * 2004-February-23 Jason Rohrer * Added timeout code for accepting connections. * Removed use of shutdown on listening socket. */ #include "minorGems/network/SocketServer.h" #include #include union sock { struct sockaddr s; struct sockaddr_in i; } sock; SocketServer::SocketServer( int inPort, int inMaxQueuedConnections ) { int error = 0; if( !Socket::isFrameworkInitialized() ) { // try to init the framework int error = Socket::initSocketFramework(); if( error == -1 ) { printf( "initializing network socket framework failed\n" ); exit( 1 ); } } // create the socket int sockID = socket( AF_INET, SOCK_STREAM, 0 ); if( sockID == INVALID_SOCKET ) { printf( "Creating socket failed.\n" ); exit( 1 ); } // store socket id in native object pointer int *idStorage = new int[1]; idStorage[0] = sockID; mNativeObjectPointer = (void *)idStorage; // bind socket to the port union sock sockAddress; sockAddress.i.sin_family = AF_INET; sockAddress.i.sin_port = htons( inPort ); sockAddress.i.sin_addr.s_addr = INADDR_ANY; error = bind( sockID, &(sockAddress.s), sizeof( struct sockaddr ) ); if( error == -1 ) { printf( "Network socket bind to port %d failed\n", inPort ); exit( 1 ); } // start listening for connections error = listen( sockID, inMaxQueuedConnections ); if( error == -1 ) { printf( "Listening for network socket connections failed.\n" ); exit(1); } } SocketServer::~SocketServer() { int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; closesocket( socketID ); delete [] socketIDptr; } Socket *SocketServer::acceptConnection( long inTimeoutInMilliseconds, char *outTimedOut ) { if( outTimedOut != NULL ) { *outTimedOut = false; } // printf( "Waiting for a connection.\n" ); // extract socket id from native object pointer int *socketIDptr = (int *)( mNativeObjectPointer ); int socketID = socketIDptr[0]; // same timeout code as in linux version if( inTimeoutInMilliseconds != -1 ) { // if we have a timeout specified, select before accepting // this found in the Linux man page for select, // but idea (which originally used poll) was found // in the Unix Socket FAQ fd_set rfds; struct timeval tv; int retval; // insert our socket descriptor into this set FD_ZERO( &rfds ); FD_SET( socketID, &rfds ); // convert our timeout into the structure's format tv.tv_sec = inTimeoutInMilliseconds / 1000; tv.tv_usec = ( inTimeoutInMilliseconds % 1000 ) * 1000 ; retval = select( socketID + 1, &rfds, NULL, NULL, &tv ); if( retval == 0 ) { // timeout if( outTimedOut != NULL ) { *outTimedOut = true; } return NULL; } } //int addressLength; //union sock acceptedAddress; struct sockaddr acceptedAddress; //int addressLength = sizeof( struct sockaddr ); int acceptedID = accept( socketID, &( acceptedAddress ), NULL ); if( acceptedID == -1 ) { printf( "Failed to accept a network connection.\n" ); return NULL; } Socket *acceptedSocket = new Socket(); int *idStorage = new int[1]; idStorage[0] = acceptedID; acceptedSocket->mNativeObjectPointer = (void *)idStorage; //printf( "Connection received.\n" ); return acceptedSocket; } transcend-0.3+dfsg2.orig/minorGems/network/SocketStream.h0000640000175000017500000000712510005505371022134 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Changed so that underlying socket is not destroyed when the stream * is destroyed. Updated to match new Stream interface, which uses longs * instead of ints. * * 2001-February-21 Jason Rohrer * Changed to set stream error messages on socket errors. * * 2002-July-31 Jason Rohrer * Added fix for partial sends. * * 2003-August-12 Jason Rohrer * Added support for read timeouts. * * 2004-January-27 Jason Rohrer * Made functions virtual to support subclassing. */ #include "minorGems/common.h" #ifndef SOCKET_STREAM_CLASS_INCLUDED #define SOCKET_STREAM_CLASS_INCLUDED #include "Socket.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * A input and output stream interface for a network socket. * * @author Jason Rohrer */ class SocketStream : public InputStream, public OutputStream { public: /** * Constructs a SocketStream. * * @param inSocket the newtork socket wrapped by this stream. * inSocket is NOT destroyed when the stream is destroyed. */ SocketStream( Socket *inSocket ); // a virtual destructor to ensure that subclass destructors are called virtual ~SocketStream(); /** * Sets the timeout for reads on this socket. * * The timeout defaults to -1 (no timeout). * * @param inMilliseconds the timeout in milliseconds, * or -1 to specify no timeout. */ void setReadTimeout( long inMilliseconds ); /** * Gets the timeout for reads on this socket. * * @return the timeout in milliseconds, * or -1 to indicate no timeout. */ long getReadTimeout(); // implements the InputStream interface. // virtual to allow subclasses to override // in addition, -2 is returned if the read times out. virtual long read( unsigned char *inBuffer, long inNumBytes ); // implements the OutputStream interface virtual long write( unsigned char *inBuffer, long inNumBytes ); protected: Socket *mSocket; long mReadTimeout; }; inline SocketStream::SocketStream( Socket *inSocket ) : mSocket( inSocket ), mReadTimeout( -1 ) { } inline SocketStream::~SocketStream() { // does nothing // exists only to allow for subclass destructors } inline void SocketStream::setReadTimeout( long inMilliseconds ) { mReadTimeout = inMilliseconds; } inline long SocketStream::getReadTimeout() { return mReadTimeout; } inline long SocketStream::read( unsigned char *inBuffer, long inNumBytes ) { int numReceived = mSocket->receive( inBuffer, inNumBytes, mReadTimeout ); if( numReceived == -1 ) { // socket error InputStream::setNewLastErrorConst( "Network socket error on receive." ); } return numReceived; } inline long SocketStream::write( unsigned char *inBuffer, long inNumBytes ) { long numTotalSent = 0; while( numTotalSent < inNumBytes ) { unsigned char *partialBuffer = &( inBuffer[numTotalSent] ); int numRemaining = inNumBytes - numTotalSent; int numSent = mSocket->send( partialBuffer, numRemaining ); if( numSent == -1 || numSent == 0 ) { // socket error OutputStream::setNewLastErrorConst( "Network socket error on send." ); return -1; } numTotalSent += numSent; } return numTotalSent; } #endif transcend-0.3+dfsg2.orig/minorGems/network/Message.h0000640000175000017500000000162507554101513021120 0ustar pabspabs/* * Modification History * * 2001-January-15 Jason Rohrer * Created. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. */ #include "minorGems/common.h" #ifndef MESSAGE_CLASS_INCLUDED #define MESSAGE_CLASS_INCLUDED #include "minorGems/io/Serializable.h" /** * A simple message that carries a 4 byte opcode. Useful for sending a * simple service request. * * @author Jason Rohrer */ class Message : public Serializable { public: long mOpcode; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline int Message::serialize( OutputStream *inOutputStream ) { return inOutputStream->writeLong( mOpcode ); } inline int Message::deserialize( InputStream *inInputStream ) { return inInputStream->readLong( &mOpcode ); } #endif transcend-0.3+dfsg2.orig/minorGems/math/0000750000175000017500000000000010305077063016616 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/math/BigInt.h0000640000175000017500000001045707474074412020163 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * Made getZero static. */ #ifndef BIG_INT_INCLUDED #define BIG_INT_INCLUDED /** * A multi-byte integer representation. * * Some of the ideas used in this class were gleaned * from studying Sun's Java 1.3 BigInteger implementation. * * @author Jason Rohrer. */ class BigInt { public: /** * Constructs an integer. * * @param inSign the sign of this integer: * -1 if negative, +1 if positive, and 0 if zero. * @param inNumBytes the number of bytes in this integer. * @param inBytes the bytes for this integer. * Copied internally, so must be destroyed by caller. */ BigInt( int inSign, int inNumBytes, unsigned char *inBytes ); /** * Constructs an integer from a 32-bit int. * * @param inInt the int to use. */ BigInt( int inInt ); ~BigInt(); /** * Adds an integer to this integer. * * @praram inOtherInt the int to add. * Must be destroyed by caller. * * @return a newly allocated integer containing the sum. * Must be destroyed by caller. */ BigInt *add( BigInt *inOtherInt ); /** * Subtracts an integer from this integer. * * @praram inOtherInt the int to subtract. * Must be destroyed by caller. * * @return a newly allocated integer containing the difference. * Must be destroyed by caller. */ BigInt *subtract( BigInt *inOtherInt ); /** * Gets whether this integer is less than another integer. * * @praram inOtherInt the integer test. * Must be destroyed by caller. * * @return true if this integer is less than the other. */ char isLessThan( BigInt *inOtherInt ); /** * Gets whether this integer is equal to another integer. * * @praram inOtherInt the integer test. * Must be destroyed by caller. * * @return true if this integer is equal to the other. */ char isEqualTo( BigInt *inOtherInt ); /** * Gets a copy of this integer. * * @return a newly allocated integer containing the copy. * Must be destroyed by caller. */ BigInt *copy(); /** * Gets an integer equal to zero. * * @return a newly allocated integer containing zero. * Must be destroyed by caller. */ static BigInt *getZero(); /** * Converts this integer to a decimal string. * * @return a \0-terminated ascii decimal string. * Must be destroyed by caller. */ //char *convertToDecimalString(); /** * Converts this integer to a hex string. * * @return a \0-terminated ascii hexx string. * Must be destroyed by caller. */ char *convertToHexString(); /** * Converts this integer to a 32-bit int. * * If this integer contains more than 32-bits, the high-order * bits will be discarded, though the sign will be preserved. */ int convertToInt(); /** * -1 if negative, +1 if positive, and 0 if zero. */ int mSign; int mNumBytes; /** * Integer is stored in big endian byte order. */ unsigned char *mBytes; protected: /** * Flips the byte order of this integer. * * @return a newly allocated integer containing the flipped version. * Must be destroyed by caller. */ BigInt *flipByteOrder(); /** * Computes the hex representation of a four-bit int. * * @param inInt the four-bit int to convert. * * @return the int as a hex ascii character, * in {0, 1, ..., A, B, ..., F}. */ char fourBitIntToHex( int inInt ); }; #endif transcend-0.3+dfsg2.orig/minorGems/math/stats/0000750000175000017500000000000010305077063017754 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/math/stats/PairwiseOrderingErrorEvaluator.h0000640000175000017500000000237007215720625026307 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef PAIRWISE_ORDERING_ERROR_EVALUATOR_INCLUDED #define PAIRWISE_ORDERING_ERROR_EVALUATOR_INCLUDED #include "ErrorEvaluator.h" /** * Implementation of ErrorEvaluator that generates an error * value based on the number of pairs of elements in the * the test vector that are not given the same order by their values * as are the corresponding pair of values in the correct vector. * * I.e., if A[i] > A[j], then B[i] < B[j] adds 1 to the error term, * while B[i] > B[j] adds 0 to the error term. * * @author Jason Rohrer */ class PairwiseOrderingErrorEvaluator : public ErrorEvaluator { // implements ErrorEvaluator interface double evaluate( double *inVectorA, double *inVectorB, int inLength ); }; inline double PairwiseOrderingErrorEvaluator::evaluate( double *inVectorA, double *inVectorB, int inLength ) { double sum = 0; // examine all pairs of components in the vectors for( int i=0; i inVectorA[j] && inVectorB[i] <= inVectorB[j] ) || ( inVectorA[i] < inVectorA[j] && inVectorB[i] >= inVectorB[j] ) ) { sum++; } } } return sum; } #endif transcend-0.3+dfsg2.orig/minorGems/math/stats/ErrorEvaluator.h0000640000175000017500000000131107215720625023103 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef ERROR_EVALUATOR_INCLUDED #define ERROR_EVALUATOR_INCLUDED /** * Interface for a class that evaluates a vector against another * to produce a scalar error value. * * @author Jason Rohrer */ class ErrorEvaluator { public: /** * Evaluates two input vectors to produce an output error value. * * @param inVectorA the first vector (the actual, or "correct" value) * @param inVectorB the second vector ( the predicted, or "test" value) * * @return the error value between the two vectors */ virtual double evaluate( double *inVectorA, double *inVectorB, int inLength ) = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/math/stats/L1ErrorEvaluator.h0000640000175000017500000000134407215720625023306 0ustar pabspabs/* * Modification History * * 2000-September-28 Jason Rohrer * Created. */ #ifndef L1_ERROR_EVALUATOR_INCLUDED #define L1_ERROR_EVALUATOR_INCLUDED #include "ErrorEvaluator.h" /** * L1 distance implementation of ErrorEvaluator. * * @author Jason Rohrer */ class L1ErrorEvaluator : public ErrorEvaluator { public: // implements ErrorEvaluator interface double evaluate( double *inVectorA, double *inVectorB, int inLength ); }; inline double L1ErrorEvaluator::evaluate( double *inVectorA, double *inVectorB, int inLength ) { double sum = 0; for( int i=0; ievaluate() ); } long InvertExpression::getID() { return mID; } inline long InvertExpression::staticGetID() { return mID; } inline void InvertExpression::print() { printf( "( 1/" ); mArgument->print(); printf( " )" ); } inline Expression *InvertExpression::copy() { InvertExpression *copy = new InvertExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/BinaryLogicExpression.h0000640000175000017500000001064107655132767025474 0ustar pabspabs/* * Modification History * * 2003-May-2 Jason Rohrer * Created. * * 2003-May-3 Jason Rohrer * Fixed a few compile-time errors. */ #ifndef BINARY_LOGIC_EXPRESSION_INCLUDED #define BINARY_LOGIC_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary logic operation. * * Evaluating this expression returns 1 if the logic operation is true * and 0 if it is false. * * During evaluation, positive values are treated as logical true, * while all other values (zero and negative) are treated as false. * * @author Jason Rohrer */ class BinaryLogicExpression : public BinaryOperationExpression { public: static const int LOGIC_AND = 0; static const int LOGIC_OR = 1; static const int LOGIC_XOR = 2; /** * Constructs a binary logic operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inLogicOperation the logical operation, one of * LOGIC_AND, LOGIC_OR, LOGIC_XOR. * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ BinaryLogicExpression( int inLogicOperation, Expression *inArgumentA, Expression *inArgumentB ); /** * Gets the logical operation. * * @return one of LOGIC_AND, LOGIC_OR, LOGIC_XOR. */ int getLogicOperation(); /** * Sets the logical operation * * @param inLogicOperation LOGIC_AND, LOGIC_OR, LOGIC_XOR. */ void setLogicOperation( int inLogicOperation ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; int mLogicOperation; }; // static init long BinaryLogicExpression::mID = 16; inline BinaryLogicExpression::BinaryLogicExpression( int inLogicOperation, Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ), mLogicOperation( inLogicOperation ) { } inline int BinaryLogicExpression::getLogicOperation() { return mLogicOperation; } inline void BinaryLogicExpression::setLogicOperation( int inLogicOperation ) { mLogicOperation = inLogicOperation; } inline double BinaryLogicExpression::evaluate() { if( mLogicOperation == LOGIC_AND ) { if( mArgumentA->evaluate() > 0 && mArgumentB->evaluate() > 0 ) { return 1; } else { return 0; } } else if( mLogicOperation == LOGIC_OR ) { if( mArgumentA->evaluate() > 0 || mArgumentB->evaluate() > 0 ) { return 1; } else { return 0; } } else if( mLogicOperation == LOGIC_XOR ) { double valA = mArgumentA->evaluate(); double valB = mArgumentB->evaluate(); if( ( valA > 0 && valB <= 0 ) || ( valA <= 0 && valB > 0 ) ) { return 1; } else { return 0; } } else { // unknown operation type // default to false return 0; } } inline long BinaryLogicExpression::getID() { return mID; } inline long BinaryLogicExpression::staticGetID() { return mID; } inline void BinaryLogicExpression::print() { printf( "( " ); mArgumentA->print(); if( mLogicOperation == LOGIC_AND ) { printf( " and " ); } else if( mLogicOperation == LOGIC_OR ) { printf( " or " ); } else if( mLogicOperation == LOGIC_XOR ) { printf( " xor " ); } else { printf( " UNKNOWN_LOGIC_OPERATION " ); } mArgumentB->print(); printf( " )" ); } inline Expression *BinaryLogicExpression::copy() { BinaryLogicExpression *copy = new BinaryLogicExpression( mLogicOperation, mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/ConstantExpression.h0000640000175000017500000000473607347050154025056 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. * * 2001-September-9 Jason Rohrer * Added an extractArgument() implemenation. */ #ifndef CONSTANT_EXPRESSION_INCLUDED #define CONSTANT_EXPRESSION_INCLUDED #include "Expression.h" /** * Expression implementation of a constant. * * @author Jason Rohrer */ class ConstantExpression : public Expression { public: /** * Constructs a constant expression. * * @param inValue the constant value. */ ConstantExpression( double inValue ); /** * Sets the constant value. * * @param inValue the constant value. */ void setValue( double inValue ); /** * Gets the constant value. * * @return the constant value. */ double getValue(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getNumArguments(); virtual long getID(); virtual Expression *copy(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); protected: double mValue; static long mID; }; // static init long ConstantExpression::mID = 1; inline ConstantExpression::ConstantExpression( double inValue ) : mValue( inValue ) { } inline void ConstantExpression::setValue( double inValue ) { mValue = inValue; } inline double ConstantExpression::getValue() { return mValue; } inline double ConstantExpression::evaluate() { return mValue; } long ConstantExpression::getNumArguments() { return 0; } inline long ConstantExpression::getID() { return mID; } inline Expression *ConstantExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void ConstantExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *ConstantExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline Expression *ConstantExpression::copy() { ConstantExpression *copy = new ConstantExpression( mValue ); return copy; } inline long ConstantExpression::staticGetID() { return mID; } inline void ConstantExpression::print() { printf( "( %f )", mValue ); } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/SumExpression.h0000640000175000017500000000337207252524064024025 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef SUM_EXPRESSION_INCLUDED #define SUM_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary sum operation. * * @author Jason Rohrer */ class SumExpression : public BinaryOperationExpression { public: /** * Constructs a binary sum operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ SumExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SumExpression::mID = 7; inline SumExpression::SumExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double SumExpression::evaluate() { return mArgumentA->evaluate() + mArgumentB->evaluate(); } inline long SumExpression::getID() { return mID; } inline long SumExpression::staticGetID() { return mID; } inline void SumExpression::print() { printf( "( " ); mArgumentA->print(); printf( " + " ); mArgumentB->print(); printf( " )" ); } inline Expression *SumExpression::copy() { SumExpression *copy = new SumExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/Variable.h0000640000175000017500000000332607655253371022734 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. * * 2003-April-29 Jason Rohrer * Added missing destructor. */ #ifndef VARIABLE_INCLUDED #define VARIABLE_INCLUDED #include "minorGems/util/stringUtils.h" /** * Wrapper for a variable value. * * @author Jason Rohrer */ class Variable { public: /** * Constructs a variable, setting its value. * * @param inName the name of the variable. * Must be destroyed by caller if non-const. * @param inValue the initial value of the variable. */ Variable( char *inName, double inValue ); virtual ~Variable(); /** * Gets the value of this variable. * * @return the variable's value. */ virtual double getValue(); /** * Sets the value for this variable. * * @param inValue the value. */ virtual void setValue( double inValue ); /** * Gets the name of this variable. * * @return the name. * Must be destroyed by caller. */ virtual char *getName(); protected: char *mName; double mValue; }; inline Variable::Variable( char *inName, double inValue ) : mName( stringDuplicate( inName ) ), mValue( inValue ) { } inline Variable::~Variable() { delete [] mName; } inline double Variable::getValue() { return mValue; } inline void Variable::setValue( double inValue ) { mValue = inValue; } inline char *Variable::getName() { return stringDuplicate( mName ); } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/ComparisonExpression.h0000640000175000017500000001246407654613702025401 0ustar pabspabs/* * Modification History * * 2003-May-2 Jason Rohrer * Created. */ #ifndef COMPARISON_EXPRESSION_INCLUDED #define COMPARISON_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary comparison operation. * * Evaluating this expression returns 1 if the comparisson is true * and 0 if it is false. * * @author Jason Rohrer */ class ComparisonExpression : public BinaryOperationExpression { public: static const int GREATER_THAN = 0; static const int LESS_THAN = 1; static const int GREATER_THAN_OR_EQUAL_TO = 2; static const int LESS_THAN_OR_EQUAL_TO = 3; static const int EQUAL_TO = 4; static const int NOT_EQUAL_TO = 5; /** * Constructs a binary comparison operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inComparison the comparison to make, one of * GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ ComparisonExpression( int inComparison, Expression *inArgumentA, Expression *inArgumentB ); /** * Gets the comparison being made. * * @return one of GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. */ int getComparison(); /** * Sets the comparison to make. * * @param inComparison the comparison to make, one of * GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL_TO, * LESS_THAN_OR_EQUAL_TO, EQUAL_TO, NOT_EQUAL_TO. */ void setComparison( int inComparison ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; int mComparison; }; // static init long ComparisonExpression::mID = 15; inline ComparisonExpression::ComparisonExpression( int inComparison, Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ), mComparison( inComparison ) { } inline int ComparisonExpression::getComparison() { return mComparison; } inline void ComparisonExpression::setComparison( int inComparison ) { mComparison = inComparison; } inline double ComparisonExpression::evaluate() { if( mComparison == GREATER_THAN ) { if( mArgumentA->evaluate() > mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == LESS_THAN ) { if( mArgumentA->evaluate() < mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == GREATER_THAN_OR_EQUAL_TO ) { if( mArgumentA->evaluate() >= mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == LESS_THAN_OR_EQUAL_TO ) { if( mArgumentA->evaluate() <= mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == EQUAL_TO ) { if( mArgumentA->evaluate() == mArgumentB->evaluate() ) { return 1; } else { return 0; } } else if( mComparison == NOT_EQUAL_TO ) { if( mArgumentA->evaluate() != mArgumentB->evaluate() ) { return 1; } else { return 0; } } else { // unknown comparison type // default to false return 0; } } inline long ComparisonExpression::getID() { return mID; } inline long ComparisonExpression::staticGetID() { return mID; } inline void ComparisonExpression::print() { printf( "( " ); mArgumentA->print(); if( mComparison == GREATER_THAN ) { printf( " > " ); } else if( mComparison == LESS_THAN ) { printf( " < " ); } else if( mComparison == GREATER_THAN_OR_EQUAL_TO ) { printf( " >= " ); } else if( mComparison == LESS_THAN_OR_EQUAL_TO ) { printf( " <= " ); } else if( mComparison == EQUAL_TO ) { printf( " == " ); } else if( mComparison == NOT_EQUAL_TO ) { printf( " != " ); } else { printf( " UNKNOWN_COMPARISON " ); } mArgumentB->print(); printf( " )" ); } inline Expression *ComparisonExpression::copy() { ComparisonExpression *copy = new ComparisonExpression( mComparison, mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/UnaryOperationExpression.h0000640000175000017500000000551207347047600026237 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-September-4 Jason Rohrer * Changed the destructor to virtual to fix a memory leak. * * 2001-September-9 Jason Rohrer * Changed setArgument to be more intelligent about duplicated calls. * Added an extractArgument() implemenation. */ #ifndef UNARY_OPERATION_EXPRESSION_INCLUDED #define UNARY_OPERATION_EXPRESSION_INCLUDED #include "Expression.h" /** * Abstract base for an unary operation expression object. * * @author Jason Rohrer */ class UnaryOperationExpression : public Expression { public: /** * Constructs a unary operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. Defaults to NULL. */ UnaryOperationExpression( Expression *inArgument = NULL ); virtual ~UnaryOperationExpression(); /** * Sets the argument for the operation. * * @param inArgument the argument. Is destroyed * when the class is destroyed, or by another * call to setArgument. */ void setArgument( Expression *inArgument ); /** * Gets the argument for the operation. * * @return the argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgument(); // implements the Expression interface virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); protected: Expression *mArgument; }; inline UnaryOperationExpression::UnaryOperationExpression( Expression *inArgument ) : mArgument( inArgument ) { } inline UnaryOperationExpression::~UnaryOperationExpression() { if( mArgument != NULL ) { delete mArgument; } } inline void UnaryOperationExpression::setArgument( Expression *inArgument ) { if( mArgument != NULL && inArgument != mArgument ) { delete mArgument; } mArgument = inArgument; } inline Expression *UnaryOperationExpression::getArgument() { return mArgument; } long UnaryOperationExpression::getNumArguments() { return 1; } inline Expression *UnaryOperationExpression::getArgument( long inArgumentNumber ) { if( inArgumentNumber == 0 ) { return getArgument(); } else { return NULL; } } inline void UnaryOperationExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { if( inArgumentNumber == 0 ) { setArgument( inArgument ); return; } else { return; } } inline Expression *UnaryOperationExpression::extractArgument( long inArgumentNumber ) { Expression *returnArg = mArgument; mArgument = NULL; return returnArg; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/ProductExpression.h0000640000175000017500000000346307252524064024702 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef PRODUCT_EXPRESSION_INCLUDED #define PRODUCT_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" /** * Expression implementation of a binary product operation. * * @author Jason Rohrer */ class ProductExpression : public BinaryOperationExpression { public: /** * Constructs a binary product operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ ProductExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long ProductExpression::mID = 5; inline ProductExpression::ProductExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double ProductExpression::evaluate() { return mArgumentA->evaluate() * mArgumentB->evaluate(); } inline long ProductExpression::getID() { return mID; } inline long ProductExpression::staticGetID() { return mID; } inline void ProductExpression::print() { printf( "( " ); mArgumentA->print(); printf( " * " ); mArgumentB->print(); printf( " )" ); } inline Expression *ProductExpression::copy() { ProductExpression *copy = new ProductExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/SqrtExpression.h0000640000175000017500000000334307653343375024221 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef SQRT_EXPRESSION_INCLUDED #define SQRT_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary sqrt operation. * * @author Jason Rohrer */ class SqrtExpression : public UnaryOperationExpression { public: /** * Constructs a unary sqrt operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ SqrtExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SqrtExpression::mID = 13; inline SqrtExpression::SqrtExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double SqrtExpression::evaluate() { return sqrt( mArgument->evaluate() ); } inline long SqrtExpression::getID() { return mID; } inline long SqrtExpression::staticGetID() { return mID; } inline void SqrtExpression::print() { printf( "( sqrt" ); mArgument->print(); printf( " )" ); } inline Expression *SqrtExpression::copy() { SqrtExpression *copy = new SqrtExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/expressionTester.cpp0000640000175000017500000000552607653343375025136 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-February-12 Jason Rohrer * Added code to test serialization. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2001-April-12 Jason Rohrer * Changed to comply with new FileInput/OutputStream interface * (underlying File must be destroyed explicitly). */ #include "Expression.h" #include "ConstantExpression.h" #include "PowerExpression.h" #include "ProductExpression.h" #include "NegateExpression.h" #include "InvertExpression.h" #include "SinExpression.h" #include "LnExpression.h" #include "SumExpression.h" #include "ExpressionSerializer.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" // test function for expressions int main() { ProductExpression *expression = new ProductExpression( new PowerExpression( new ConstantExpression( 5 ), new ConstantExpression( 6 ) ), new NegateExpression( new SinExpression( new ConstantExpression( 19 ) ) ) ); InvertExpression *invExpression = new InvertExpression( expression ); SumExpression *sumExpression = new SumExpression( invExpression, new ConstantExpression( 2 ) ); sumExpression->print(); printf( "\n" ); printf( "%f\n", sumExpression->evaluate() ); printf( "Writing to file.\n" ); char **pathSteps = new char*[2]; pathSteps[0] = new char[10]; pathSteps[1] = new char[10]; sprintf( pathSteps[0], "test" ); sprintf( pathSteps[1], "file" ); int *stepLength = new int[2]; stepLength[0] = 4; stepLength[1] = 4; Path *path = new Path( pathSteps, 2, stepLength, false ); File *file = new File( path, "test.out", 8 ); FileOutputStream *outStream = new FileOutputStream( file, false ); char *error = outStream->getLastError(); if( error != NULL ) { printf( "Error: %s\n", error ); delete error; } ExpressionSerializer::serializeExpression( sumExpression, outStream ); delete outStream; delete file; printf( "Reading back in from file.\n" ); pathSteps = new char*[2]; pathSteps[0] = new char[10]; pathSteps[1] = new char[10]; sprintf( pathSteps[0], "test" ); sprintf( pathSteps[1], "file" ); stepLength = new int[2]; stepLength[0] = 4; stepLength[1] = 4; path = new Path( pathSteps, 2, stepLength, false ); file = new File( path, "test.out", 8 ); FileInputStream *inStream = new FileInputStream( file ); error = inStream->getLastError(); if( error != NULL ) { printf( "Error: %s\n", error ); delete error; } Expression *readExpression; ExpressionSerializer::deserializeExpression( &readExpression, inStream ); delete inStream; delete file; readExpression->print(); printf( "\n" ); printf( "%f\n", readExpression->evaluate() ); delete sumExpression; delete readExpression; return 0; } transcend-0.3+dfsg2.orig/minorGems/math/expression/ExpressionMutator.h0000640000175000017500000001604107350157715024715 0ustar pabspabs/* * Modification History * * 2001-September-9 Jason Rohrer * Created. * * 2001-September-13 Jason Rohrer * Made an error message more verbose. */ #ifndef EXPRESSION_MUTATOR_INCLUDED #define EXPRESSION_MUTATOR_INCLUDED #include "minorGems/util/random/RandomSource.h" #include "Expression.h" #include "ConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "FixedConstantExpression.h" #include "MultiConstantArgumentExpression.h" /** * Utility class for mutating expressions. * * Note that the number and position of constant expressions * within the expression will not change during mutation. Thus, * the structure of the expression remains constant while the operations * being performed at each node are mutated. * * @author Jason Rohrer */ class ExpressionMutator { public: /** * Mutates an expression. * * Note that this function's implementation is recursive. * * @param inExpression the expression to mutate. Will be destroyed * by this function (or if not destroyed, then passed back through * the return value). Only the return value, not inExpression, * can be accessed safely after this function returns. * @param inMutationProb the probability of mutations at each * node in inExpression. * @param inMaxMutation the fraction of a fixed constant's value * by which it can be mutated. For example, if a fixed constant * has the value 100, and inMaxMutation is 0.5, then the mutated * constant will be a random value in the range [50, 150]. * @param inRandSource the source of random numbers to use. * Must be destroyed by caller. * * @return a new expression that is a mutated version of inExpression. * (Note that the returned expression may be inExpression). */ static Expression *mutateExpression( Expression *inExpression, double inMutationProb, double inMaxMutation, RandomSource *inRandSource ); }; inline Expression *ExpressionMutator::mutateExpression( Expression *inExpression, double inMutationProb, double inMaxMutation, RandomSource *inRandSource ) { long expressionID = inExpression->getID(); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { // mutate the constant's value ConstantExpression *c = (ConstantExpression *)inExpression; double value = c->getValue(); double maxMutationAmount = inMaxMutation * value; // a value in [-1, 1] double mutator = ( inRandSource->getRandomDouble() * 2.0 ) - 1.0; value = value + mutator * maxMutationAmount; c->setValue( value ); return c; } else if( expressionID == FixedConstantExpression::staticGetID() ) { // mutate the fixed constant's value FixedConstantExpression *c = (FixedConstantExpression *)inExpression; double value = c->getValue(); double maxMutationAmount = inMaxMutation * value; // a value in [-1, 1] double mutator = ( inRandSource->getRandomDouble() * 2.0 ) - 1.0; value = value + mutator * maxMutationAmount; delete c; return new FixedConstantExpression( value ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { MultiConstantArgumentExpression *m = (MultiConstantArgumentExpression*)inExpression; Expression *wrappedExpression = m->extractWrappedExpression(); wrappedExpression = mutateExpression( wrappedExpression, inMutationProb, inMaxMutation, inRandSource ); m->setWrappedExpression( wrappedExpression ); return m; } else { // the general expression case Expression *newExpression = inExpression; if( inRandSource->getRandomDouble() <= inMutationProb ) { // we need to mutate this node long newID; if( expressionID == InvertExpression::staticGetID() || expressionID == NegateExpression::staticGetID() || expressionID == SinExpression::staticGetID() ) { // a unary expression // pick another expression type at random newID = expressionID; while( newID == expressionID ) { int randValue = inRandSource->getRandomBoundedInt( 0, 2 ); switch( randValue ) { case 0: newID = InvertExpression::staticGetID(); break; case 1: newID = NegateExpression::staticGetID(); break; case 2: newID = SinExpression::staticGetID(); break; default: // we should never hit this // note that this newID will cause a printed // error below newID = -1; break; } } } else { // a binary expression // pick another expression type at random newID = expressionID; while( newID == expressionID ) { int randValue = inRandSource->getRandomBoundedInt( 0, 2 ); switch( randValue ) { case 0: newID = PowerExpression::staticGetID(); break; case 1: newID = ProductExpression::staticGetID(); break; case 2: newID = SumExpression::staticGetID(); break; default: // we should never hit this // note that this newID will cause a printed // error below newID = -1; break; } } } // now build an expression based on newID if( newID == InvertExpression::staticGetID() ) { newExpression = new InvertExpression( NULL ); } else if( newID == NegateExpression::staticGetID() ) { newExpression = new NegateExpression( NULL ); } else if( newID == PowerExpression::staticGetID() ) { newExpression = new PowerExpression( NULL, NULL ); } else if( newID == ProductExpression::staticGetID() ) { newExpression = new ProductExpression( NULL, NULL ); } else if( newID == SinExpression::staticGetID() ) { newExpression = new SinExpression( NULL ); } else if( newID == SumExpression::staticGetID() ) { newExpression = new SumExpression( NULL, NULL ); } else { printf( "Error in ExpressionMutator: ID does not " ); printf( "match a known expression type: %d\n", newID ); } // at this point, we have constructed a new expression node // with the same number of arguments as the old node // we need to fill in the arguments from inExpression for( int i=0; igetNumArguments(); i++ ) { // use extractArgument so that the argument // won't be destroyed when inExpression is destroyed Expression *argument = inExpression->extractArgument( i ); newExpression->setArgument( i, argument ); } // destroy the old node delete inExpression; } // mutate the sub nodes for( int i=0; igetNumArguments(); i++ ) { Expression *argument = newExpression->extractArgument( i ); // mutate the argument argument = mutateExpression( argument, inMutationProb, inMaxMutation, inRandSource ); newExpression->setArgument( i, argument ); } return newExpression; } } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/PowerExpression.h0000640000175000017500000000356707252524064024363 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef POWER_EXPRESSION_INCLUDED #define POWER_EXPRESSION_INCLUDED #include "Expression.h" #include "BinaryOperationExpression.h" #include /** * Expression implementation of a binary power operation. * Raises the first argument to the power of the second, * as in argA^argB. * * @author Jason Rohrer */ class PowerExpression : public BinaryOperationExpression { public: /** * Constructs a binary power operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. * @param inArgumentB the second argument. */ PowerExpression( Expression *inArgumentA, Expression *inArgumentB ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long PowerExpression::mID = 4; inline PowerExpression::PowerExpression( Expression *inArgumentA, Expression *inArgumentB ) : BinaryOperationExpression( inArgumentA, inArgumentB ) { } inline double PowerExpression::evaluate() { return pow( mArgumentA->evaluate(), mArgumentB->evaluate() ); } inline long PowerExpression::getID() { return mID; } inline long PowerExpression::staticGetID() { return mID; } inline void PowerExpression::print() { printf( "( " ); mArgumentA->print(); printf( " ^ " ); mArgumentB->print(); printf( " )" ); } inline Expression *PowerExpression::copy() { PowerExpression *copy = new PowerExpression( mArgumentA->copy(), mArgumentB->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/TanExpression.h0000640000175000017500000000332107653343375024006 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef TAN_EXPRESSION_INCLUDED #define TAN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary tan operation. * * @author Jason Rohrer */ class TanExpression : public UnaryOperationExpression { public: /** * Constructs a unary tan operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ TanExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long TanExpression::mID = 12; inline TanExpression::TanExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double TanExpression::evaluate() { return tan( mArgument->evaluate() ); } inline long TanExpression::getID() { return mID; } inline long TanExpression::staticGetID() { return mID; } inline void TanExpression::print() { printf( "( tan" ); mArgument->print(); printf( " )" ); } inline Expression *TanExpression::copy() { TanExpression *copy = new TanExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/BinaryOperationExpression.h0000640000175000017500000001045107347047600026363 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-September-4 Jason Rohrer * Changed the destructor to virtual to fix a memory leak. * * 2001-September-9 Jason Rohrer * Changed setArgument to be more intelligent about duplicated calls. * Added an extractArgument() implemenation. */ #ifndef BINARY_OPERATION_EXPRESSION_INCLUDED #define BINARY_OPERATION_EXPRESSION_INCLUDED #include "Expression.h" /** * Abstract base for an binary operation expression object. * * @author Jason Rohrer */ class BinaryOperationExpression : public Expression { public: /** * Constructs a binary operation expression. * * Both arguments are destroyed when the class is destroyed. * * @param inArgumentA the first argument. Defaults to NULL. * @param inArgumentA the first argument. Defaults to NULL. */ BinaryOperationExpression( Expression *inArgumentA = NULL, Expression *inArgumentB = NULL ); virtual ~BinaryOperationExpression(); /** * Sets the first argument for the operation. * * @param inArgument the first argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentA. */ void setArgumentA( Expression *inArgument ); /** * Sets the second argument for the operation. * * @param inArgument the second argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. */ void setArgumentB( Expression *inArgument ); /** * Gets the first argument for the operation. * * @return the first argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgumentA(); /** * Gets the second argument for the operation. * * @return the second argument. Is destroyed * when the class is destroyed, or by another * call to setArgumentB. Returns NULL if the * argument was never set. */ Expression *getArgumentB(); // implements the Expression interface virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); protected: Expression *mArgumentA; Expression *mArgumentB; }; inline BinaryOperationExpression::BinaryOperationExpression( Expression *inArgumentA, Expression *inArgumentB ) : mArgumentA( inArgumentA ), mArgumentB( inArgumentB ) { } inline BinaryOperationExpression::~BinaryOperationExpression() { if( mArgumentA != NULL ) { delete mArgumentA; } if( mArgumentB != NULL ) { delete mArgumentB; } } inline void BinaryOperationExpression::setArgumentA( Expression *inArgument ) { if( mArgumentA != NULL && inArgument != mArgumentA ) { delete mArgumentA; } mArgumentA = inArgument; } inline void BinaryOperationExpression::setArgumentB( Expression *inArgument ) { if( mArgumentB != NULL && inArgument != mArgumentB ) { delete mArgumentB; } mArgumentB = inArgument; } inline Expression *BinaryOperationExpression::getArgumentA() { return mArgumentA; } inline Expression *BinaryOperationExpression::getArgumentB() { return mArgumentB; } inline long BinaryOperationExpression::getNumArguments() { return 2; } inline Expression *BinaryOperationExpression::getArgument( long inArgumentNumber ) { switch( inArgumentNumber ) { case 0: return getArgumentA(); break; case 1: return getArgumentB(); break; default: return NULL; break; } } inline void BinaryOperationExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { switch( inArgumentNumber ) { case 0: setArgumentA( inArgument ); return; break; case 1: setArgumentB( inArgument ); return; break; default: return; break; } } inline Expression *BinaryOperationExpression::extractArgument( long inArgumentNumber ) { Expression *returnArg; switch( inArgumentNumber ) { case 0: returnArg = mArgumentA; mArgumentA = NULL; return returnArg; break; case 1: returnArg = mArgumentB; mArgumentB = NULL; return returnArg; break; default: return NULL; break; } } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/MultiConstantArgumentExpression.h0000640000175000017500000002001207347050250027552 0ustar pabspabs/* * Modification History * * 2001-August-30 Jason Rohrer * Created. * * 2001-August-31 Jason Rohrer * Added public access to the wrapped expression. * Fixed a comment and some bugs. * Fixed a memory leak. * * 2001-September-9 Jason Rohrer * Added public function for setting the wrapped expression after * construction. * Added an extractArgument() implemenation. * Added an extractWrappedExpression() function to work * around some destruction issues. */ #ifndef MULTI_CONSTANT_ARGUMENT_EXPRESSION_INCLUDED #define MULTI_CONSTANT_ARGUMENT_EXPRESSION_INCLUDED #include "Expression.h" #include "ConstantExpression.h" #include "minorGems/util/SimpleVector.h" /** * An expression implementation that treats the constants * in another expression as assignable arguments. * * Most common usage pattern: * Set up an Expression containing one constant for each * assignable argument and pass this expression into * the constructor. * * Notes: * * getArugment() returns the actual constant expression * contained in the wrapped expression. It therefore * should not be destroyed by the caller. * * setArgument() evaluates the passed-in expression * and then sets the value of the constant expression to the * value produced by the evaluation. * The passed-in expression must be destroyed by the caller, * and it can be destroyed as soon as setArgument() returns. * * @author Jason Rohrer */ class MultiConstantArgumentExpression : public Expression { public: /** * Constructs a new MultiArgumentExpression. * * @param inExpression the expression to use as the * body of this new expression, where each constant in * inExpression will be used as an argument to the * new expression. Will be destroyed when this class * is destroyed. */ MultiConstantArgumentExpression( Expression *inExpression ); ~MultiConstantArgumentExpression(); /** * Sets the expression wrapped by this mult-argument expression. * * Note that calling this function destroys the currently wrapped * expression. * * @param inExpression the expression to use as the * body of this new expression, where each constant in * inExpression will be used as an argument to the * new expression. Will be destroyed when this class * is destroyed. */ void setWrappedExpression( Expression *inExpression ); /** * Gets the expression wrapped by this mult-argument expression. * * @return the expression wrapped by this expression. * Will be destroyed when this class is destroyed. */ Expression *getWrappedExpression(); /** * Gets the expression wrapped by this mult-argument expression, * and sets the internal expression to NULL (thus, the returned * argument will not be destroyed when this class is destroyed. * * @return the expression wrapped by this expression. Must * be destroyed by caller. */ Expression *extractWrappedExpression(); // these implement the Expression interface virtual double evaluate(); virtual long getID(); virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); // note that extractArgument() always returns NULL for this class virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); virtual Expression *copy(); /** * A static version of getID(). */ static long staticGetID(); protected: Expression *mExpression; static long mID; long mNumConstants; ConstantExpression **mConstants; /** * Finds the constants in an expression in a consistent way * (using depth-first, left-first search). * * @param inExpression the expression to search for constants in. * @param outExpressions a pointer where the array of found * expressions will be returned. * * @return the number of constants found. */ long findConstants( Expression *inExpression, ConstantExpression ***outExpressions ); /** * Recursive proceedure used by findConstants() * * @param inExpression the (sub)expression to find constants in. * @param inConstantVector the vector to add found constants to. */ void findConstantsRecursive( Expression *inExpression, SimpleVector *inConstantVector ); }; // static init long MultiConstantArgumentExpression::mID = 8; inline MultiConstantArgumentExpression:: MultiConstantArgumentExpression( Expression *inExpression ) : mExpression( NULL ), mConstants( NULL ) { setWrappedExpression( inExpression ); } inline MultiConstantArgumentExpression:: ~MultiConstantArgumentExpression() { delete [] mConstants; delete mExpression; } inline void MultiConstantArgumentExpression:: setWrappedExpression( Expression *inExpression ) { if( mExpression != NULL && mExpression != inExpression ) { delete mExpression; } mExpression = inExpression; if( mConstants != NULL ) { delete [] mConstants; } mNumConstants = findConstants( inExpression, &mConstants ); } inline Expression *MultiConstantArgumentExpression:: getWrappedExpression() { return mExpression; } inline Expression *MultiConstantArgumentExpression:: extractWrappedExpression() { Expression *wrappedExpression = mExpression; mExpression = NULL; return wrappedExpression; } inline double MultiConstantArgumentExpression::evaluate() { return mExpression->evaluate(); } inline long MultiConstantArgumentExpression::getID() { return mID; } inline long MultiConstantArgumentExpression::staticGetID() { return mID; } inline long MultiConstantArgumentExpression::getNumArguments() { return mNumConstants; } inline Expression *MultiConstantArgumentExpression::getArgument( long inArgumentNumber ) { if( inArgumentNumber >= 0 && inArgumentNumber < mNumConstants ) { return mConstants[inArgumentNumber]; } else { return NULL; } } inline void MultiConstantArgumentExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { Expression *expressionToSet = getArgument( inArgumentNumber ); if( expressionToSet != NULL ) { ConstantExpression *constantExpression = (ConstantExpression*)expressionToSet; // set the constant to the evaluation of inArgument constantExpression->setValue( inArgument->evaluate() ); } else { printf( "MultiConstantArgumentExpression: setting an out of range " ); printf( "argument number, %ld\n", inArgumentNumber ); } } inline Expression *MultiConstantArgumentExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline void MultiConstantArgumentExpression::print() { mExpression->print(); } inline Expression *MultiConstantArgumentExpression::copy() { return new MultiConstantArgumentExpression( mExpression->copy() ); } inline long MultiConstantArgumentExpression::findConstants( Expression *inExpression, ConstantExpression ***outExpressions ) { SimpleVector *constantVector = new SimpleVector(); findConstantsRecursive( inExpression, constantVector ); long numConstants = constantVector->size(); ConstantExpression **returnArray = new ConstantExpression*[ numConstants ]; for( int i=0; igetElement(i) ); } delete constantVector; // set passed-in pointer to our array *outExpressions = returnArray; return numConstants; } inline void MultiConstantArgumentExpression::findConstantsRecursive( Expression *inExpression, SimpleVector *inConstantVector ) { if( inExpression->getID() == ConstantExpression::staticGetID() ) { // the passed-in expression is a constant inConstantVector->push_back( (ConstantExpression *)inExpression ); return; } else { // call recursively on each argument for( int i=0; igetNumArguments(); i++ ) { Expression *argument = inExpression->getArgument( i ); findConstantsRecursive( argument, inConstantVector ); } return; } } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/LnExpression.h0000640000175000017500000000331607653343375023641 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef LN_EXPRESSION_INCLUDED #define LN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary ln (natural log) operation. * * @author Jason Rohrer */ class LnExpression : public UnaryOperationExpression { public: /** * Constructs a unary ln operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ LnExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long LnExpression::mID = 10; inline LnExpression::LnExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double LnExpression::evaluate() { return log( mArgument->evaluate() ); } inline long LnExpression::getID() { return mID; } inline long LnExpression::staticGetID() { return mID; } inline void LnExpression::print() { printf( "( ln" ); mArgument->print(); printf( " )" ); } inline Expression *LnExpression::copy() { LnExpression *copy = new LnExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/SinExpression.h0000640000175000017500000000305307252524064024006 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef SIN_EXPRESSION_INCLUDED #define SIN_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary sin operation. * * @author Jason Rohrer */ class SinExpression : public UnaryOperationExpression { public: /** * Constructs a unary sin operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ SinExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long SinExpression::mID = 6; inline SinExpression::SinExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double SinExpression::evaluate() { return sin( mArgument->evaluate() ); } inline long SinExpression::getID() { return mID; } inline long SinExpression::staticGetID() { return mID; } inline void SinExpression::print() { printf( "( sin" ); mArgument->print(); printf( " )" ); } inline Expression *SinExpression::copy() { SinExpression *copy = new SinExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/Expression.h0000640000175000017500000000717407657013075023350 0ustar pabspabs/* * Modification History * * 2001-February-10 Jason Rohrer * Created. * * 2001-March-7 Jason Rohrer * Added a copy() function interface. * * 2001-September-4 Jason Rohrer * Added a virtual destructor to fix a major memory leak. * * 2001-September-9 Jason Rohrer * Added an extractArgument() function to work around some * object destruction issues. * * 2003-May-9 Jason Rohrer * Added support for replacing a variable. */ #ifndef EXPRESSION_INCLUDED #define EXPRESSION_INCLUDED #include #include "Variable.h" /** * Interface for an expression object. * * @author Jason Rohrer */ class Expression { public: virtual ~Expression(); /** * Evaluates this expression. * * @return the value of this expression. */ virtual double evaluate() = 0; /** * Gets the unique ID of this expression subtype. * * @return the ID of this subtype. */ virtual long getID() = 0; /** * Gets the number of arguments taken by this expression subtype. * * @return the number of arguments for this subtype. */ virtual long getNumArguments() = 0; /** * Gets a specified argument for this expression. * * @param inArgumentNumber the index of this argument. * * @return the specified argument. Will be destroyed * when this class is destroyed. Returns NULL if * the argument has not been set. */ virtual Expression *getArgument( long inArgumentNumber ) = 0; /** * Sets a specified argument for this expression. * * @param inArgumentNumber the index of this argument. * @param inArgument the specified argument. Will be destroyed * when this class is destroyed, or by another call to * setArgument. */ virtual void setArgument( long inArgumentNumber, Expression *inArgument ) = 0; /** * Extracts an argument from this expression. * * This is similar to getArgument(), except that internally * this expression's argument is set to NULL. In other words, * this allows you to setArgument( NULL ) without destroying * the argument. * * @param inArgumentNumber the argument to get. Must be destroyed * by caller. Returns NULL if the argument has not been set. */ virtual Expression *extractArgument( long inArgumentNumber ) = 0; /** * Prints this expression to standard out. */ virtual void print() = 0; /** * Makes a copy of this expression recursively. * * @return a copy of this expression. */ virtual Expression *copy() = 0; /** * Recursively replace a variable in this expression. * * Default implementation calls replacement recursively on each * argument. Expressions that actually deal with variables * should override this implementation. * * @param inTarget the target to replace. * Must be destroyed by caller. * @param inReplacement the variable to replace the target with. * Must be destroyed by caller after this expression is destroyed. */ virtual void replaceVariable( Variable *inTarget, Variable *inReplacement ); }; inline Expression::~Expression() { // do nothing } inline void Expression::replaceVariable( Variable *inTarget, Variable *inReplacement ) { // call recursively on each of our arguments int numArgs = getNumArguments(); for( int i=0; ireplaceVariable( inTarget, inReplacement ); } } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/RandomVariable.h0000640000175000017500000000265307655253371024077 0ustar pabspabs/* * Modification History * * 2003-May-4 Jason Rohrer * Created. */ #ifndef RANDOM_VARIABLE_INCLUDED #define RANDOM_VARIABLE_INCLUDED #include "minorGems/util/stringUtils.h" #include "minorGems/util/random/RandomSource.h" /** * Wrapper for a random-valued variable. * * @author Jason Rohrer */ class RandomVariable : public Variable{ public: /** * Constructs a variable * * @param inName the name of the variable. * Must be destroyed by caller if non-const. * @param inRandSource the source for random numbers. * Must be destroyed by caller after this class is destroyed. */ RandomVariable( char *inName, RandomSource *inRandSource ); virtual ~RandomVariable(); // overrides Variable functions virtual double getValue(); virtual void setValue( double inValue ); protected: RandomSource *mRandSource; }; inline RandomVariable::RandomVariable( char *inName, RandomSource *inRandSource ) : Variable( inName, 0 ), mRandSource( inRandSource ) { } inline RandomVariable::~RandomVariable() { } inline double RandomVariable::getValue() { return mRandSource->getRandomDouble(); } inline void RandomVariable::setValue( double inValue ) { // do nothing } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/RandomExpressionFactory.h0000640000175000017500000001005707345177105026032 0ustar pabspabs/* * Modification History * * 2001-August-30 Jason Rohrer * Created. * * 2001-August-31 Jason Rohrer * Finished implementation. * Fixed a compile bug. * * 2001-September-4 Jason Rohrer * Added support for FixedConstantExpressions. */ #ifndef RANDOM_EXPRESSION_FACTORY_INCLUDED #define RANDOM_EXPRESSION_FACTORY_INCLUDED #include "Expression.h" #include "ConstantExpression.h" #include "FixedConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "minorGems/util/random/RandomSource.h" /** * Utility class for constructing random expressions. * * @author Jason Rohrer */ class RandomExpressionFactory { public: /** * Constructs a random expression factory. * * @param inRandSource the source for random numbers * to use while constructing expressions. * Must be destroyed by caller after this class is destroyed. */ RandomExpressionFactory( RandomSource *inRandSource ); /** * Recursively constructs a random expression with all parameters * filled. * * @param inProbOfStopping the probability of stopping at each * branch of the expression. Upon stopping on a certain branch * of the recursion, it simply returns a constant expression. * @param inProbOfFixedConstant the probability of a fixed * constant expression being inserted upon stopping (as opposed * to a (mutable) constant expression). * @param inMaxDepth if this is reached, the recursion * stops regardless of inProbOfStopping. * @param inConstantMax the maximum value for a constant expression. * * @return the constructed expression. */ Expression *constructRandomExpression( double inProbOfStopping, double inProbOfFixedConstant, int inMaxDepth, double inConstantMax ); protected: RandomSource *mRandSource; }; inline RandomExpressionFactory::RandomExpressionFactory( RandomSource *inRandSource ) : mRandSource( inRandSource ) { } inline Expression *RandomExpressionFactory::constructRandomExpression( double inProbOfStopping, double inProbOfFixedConstant, int inMaxDepth, double inConstantMax ) { // fill in constant expressions only at the leaves if( inMaxDepth == 0 || mRandSource->getRandomDouble() <= inProbOfStopping ) { // stop if( mRandSource->getRandomDouble() <= inProbOfFixedConstant ) { return new FixedConstantExpression( inConstantMax * ( mRandSource->getRandomDouble() ) ); } else { return new ConstantExpression( inConstantMax * ( mRandSource->getRandomDouble() ) ); } } else { // keep filling in non constant expressions randomly // FILL IN HERE // we have 6 expression types int randVal = mRandSource->getRandomBoundedInt( 0, 3 ); Expression *outExpression; // pick an expression type switch( randVal ) { case 0: outExpression = new NegateExpression( NULL ); break; case 1: outExpression = new ProductExpression( NULL, NULL ); break; case 2: outExpression = new SinExpression( NULL ); break; case 3: outExpression = new SumExpression( NULL, NULL ); break; default: // should never happen, but... printf( "RandomExpressionFactory: " ); printf( "Error while generating random expression\n" ); // default to a constant expression of 0 outExpression = new ConstantExpression( 0 ); break; } // now recursively fill in the arguments in succession for( int i=0; igetNumArguments(); i++ ) { // create a random expression as the argument // note that we decrement inMaxDepth here Expression *argument = constructRandomExpression( inProbOfStopping, inProbOfFixedConstant, inMaxDepth - 1, inConstantMax ); // set the argument into our expression outExpression->setArgument( i, argument ); } // now expression is complete. return outExpression; } // end of non-constant else case } // end of constructRandomExpression() #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/NegateExpression.h0000640000175000017500000000306607252524064024464 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-10 Jason Rohrer * Added implementation of copy function. */ #ifndef NEGATE_EXPRESSION_INCLUDED #define NEGATE_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" /** * Expression implementation of a unary negate operation. * * @author Jason Rohrer */ class NegateExpression : public UnaryOperationExpression { public: /** * Constructs a unary negate operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ NegateExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long NegateExpression::mID = 3; inline NegateExpression::NegateExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double NegateExpression::evaluate() { return -( mArgument->evaluate() ); } inline long NegateExpression::getID() { return mID; } inline long NegateExpression::staticGetID() { return mID; } inline void NegateExpression::print() { printf( "( -" ); mArgument->print(); printf( " )" ); } inline Expression *NegateExpression::copy() { NegateExpression *copy = new NegateExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/FixedConstantExpression.h0000640000175000017500000000444607347050154026034 0ustar pabspabs/* * Modification History * * 2001-September-4 Jason Rohrer * Created. * * 2001-September-9 Jason Rohrer * Added an extractArgument() implemenation. */ #ifndef FIXED_CONSTANT_EXPRESSION_INCLUDED #define FIXED_CONSTANT_EXPRESSION_INCLUDED #include "Expression.h" /** * Expression implementation of a fixed constant. * * @author Jason Rohrer */ class FixedConstantExpression : public Expression { public: /** * Constructs a FixedConstant expression. * * @param inValue the constant value. */ FixedConstantExpression( double inValue ); /** * Gets the constant value. * * @return the constant value. */ double getValue(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getNumArguments(); virtual long getID(); virtual Expression *copy(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); virtual void print(); protected: double mValue; static long mID; }; // static init long FixedConstantExpression::mID = 9; inline FixedConstantExpression::FixedConstantExpression( double inValue ) : mValue( inValue ) { } inline double FixedConstantExpression::getValue() { return mValue; } inline double FixedConstantExpression::evaluate() { return mValue; } long FixedConstantExpression::getNumArguments() { return 0; } inline long FixedConstantExpression::getID() { return mID; } inline Expression *FixedConstantExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void FixedConstantExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *FixedConstantExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline Expression *FixedConstantExpression::copy() { FixedConstantExpression *copy = new FixedConstantExpression( mValue ); return copy; } inline long FixedConstantExpression::staticGetID() { return mID; } inline void FixedConstantExpression::print() { printf( "( %f )", mValue ); } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/CosExpression.h0000640000175000017500000000332107653343375024010 0ustar pabspabs/* * Modification History * * 2003-April-28 Jason Rohrer * Created. */ #ifndef COS_EXPRESSION_INCLUDED #define COS_EXPRESSION_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include /** * Expression implementation of a unary cos operation. * * @author Jason Rohrer */ class CosExpression : public UnaryOperationExpression { public: /** * Constructs a unary cos operation expression. * * Argument is destroyed when the class is destroyed. * * @param inArgument the argument. */ CosExpression( Expression *inArgument ); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); protected: static long mID; }; // static init long CosExpression::mID = 11; inline CosExpression::CosExpression( Expression *inArgument ) : UnaryOperationExpression( inArgument ) { } inline double CosExpression::evaluate() { return cos( mArgument->evaluate() ); } inline long CosExpression::getID() { return mID; } inline long CosExpression::staticGetID() { return mID; } inline void CosExpression::print() { printf( "( cos" ); mArgument->print(); printf( " )" ); } inline Expression *CosExpression::copy() { CosExpression *copy = new CosExpression( mArgument->copy() ); return copy; } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/VariableExpression.h0000640000175000017500000000633307657013075025012 0ustar pabspabs/* * Modification History * * 2003-April-29 Jason Rohrer * Created. * * 2003-May-9 Jason Rohrer * Added support for replacing a variable. */ #ifndef VARIABLE_EXPRESSION_INCLUDED #define VARIABLE_EXPRESSION_INCLUDED #include "Expression.h" #include "Variable.h" /** * Expression that contains a single variable. * * @author Jason Rohrer */ class VariableExpression : public Expression { public: /** * Constructs a variable expression. * * @param inVariable the variable. * Must be destroyed by caller after this class is destroyed. */ VariableExpression( Variable *inVariable ); /** * Gets this expression's variable. * * @return the variable. * Must not be destroyed by caller. */ Variable *getVariable(); /** * A static version of getID(). */ static long staticGetID(); // implements the Expression interface virtual double evaluate(); virtual long getID(); virtual void print(); virtual Expression *copy(); virtual long getNumArguments(); virtual Expression *getArgument( long inArgumentNumber ); virtual void setArgument( long inArgumentNumber, Expression *inArgument ); virtual Expression *extractArgument( long inArgumentNumber ); // overrides the default implementation virtual void replaceVariable( Variable *inTarget, Variable *inReplacement ); protected: static long mID; Variable *mVariable; }; // static init long VariableExpression::mID = 14; inline VariableExpression::VariableExpression( Variable *inVariable ) : mVariable( inVariable ) { } inline Variable *VariableExpression::getVariable() { return mVariable; } inline double VariableExpression::evaluate() { return mVariable->getValue(); } inline long VariableExpression::getID() { return mID; } inline long VariableExpression::staticGetID() { return mID; } inline void VariableExpression::print() { char *varName = mVariable->getName(); printf( " %s ", varName ); delete [] varName; } inline Expression *VariableExpression::copy() { // don't copy our variable VariableExpression *copy = new VariableExpression( mVariable ); return copy; } long VariableExpression::getNumArguments() { return 0; } inline Expression *VariableExpression::getArgument( long inArgumentNumber ) { return NULL; } inline void VariableExpression::setArgument( long inArgumentNumber, Expression *inArgument ) { // do nothing return; } inline Expression *VariableExpression::extractArgument( long inArgumentNumber ) { return NULL; } inline void VariableExpression::replaceVariable( Variable *inTarget, Variable *inReplacement ) { if( mVariable == inTarget ) { mVariable = inReplacement; } } #endif transcend-0.3+dfsg2.orig/minorGems/math/expression/ExpressionSerializer.h0000640000175000017500000001374707360711032025372 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-March-7 Jason Rohrer * Removed and extra if statement from the deserialization function. * * 2001-August-31 Jason Rohrer * Added support for MultiConstantArgumentExpressions. * * 2001-September-4 Jason Rohrer * Added support for FixedConstantExpressions. * * 2001-October-9 Jason Rohrer * Fixed a bug in deserializing MultiConstantArgumentExpressions. */ #ifndef EXPRESSION_SERIALIZER_INCLUDED #define EXPRESSION_SERIALIZER_INCLUDED #include "Expression.h" #include "UnaryOperationExpression.h" #include "BinaryOperationExpression.h" #include "ConstantExpression.h" #include "InvertExpression.h" #include "ProductExpression.h" #include "PowerExpression.h" #include "SumExpression.h" #include "NegateExpression.h" #include "SinExpression.h" #include "FixedConstantExpression.h" #include "MultiConstantArgumentExpression.h" #include "minorGems/io/OutputStream.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/TypeIO.h" /** * Utility class for serializing expressions * * @author Jason Rohrer */ class ExpressionSerializer { public: /** * Serializes an expression onto a stream. * * @param inExpression the expression to serialize. * @param inOutputStream the stream to write to. * * @return the number of bytes written. */ static long serializeExpression( Expression *inExpression, OutputStream *inOutputStream ); /** * Deserializes an expression from a stream. * * @param outExpression a pointer to where the expression * pointer will be returned. A new expression is created, * and it must be destroyed by the caller. * @param inInputStream the stream to read from. * * @return the number of bytes read. */ static long deserializeExpression( Expression **outExpression, InputStream *inInputStream ); }; // these can both be implemented recursively... exciting! inline long ExpressionSerializer::serializeExpression( Expression *inExpression, OutputStream *inOutputStream ) { int numBytes = 0; long expressionID = inExpression->getID(); // write the expression ID numBytes += inOutputStream->writeLong( expressionID ); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { // write the constant ConstantExpression *c = (ConstantExpression *)inExpression; numBytes += inOutputStream->writeDouble( c->getValue() ); } else if( expressionID == FixedConstantExpression::staticGetID() ) { // write the constant FixedConstantExpression *c = (FixedConstantExpression *)inExpression; numBytes += inOutputStream->writeDouble( c->getValue() ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { MultiConstantArgumentExpression *m = (MultiConstantArgumentExpression*)inExpression; Expression *wrappedExpression = m->getWrappedExpression(); numBytes += serializeExpression( wrappedExpression, inOutputStream ); } // finally, deal with the general case else { // write each of the expression's arguments in succession for( int i=0; igetNumArguments(); i++ ) { // call serialize recursively numBytes += serializeExpression( inExpression->getArgument( i ), inOutputStream ); } } return numBytes; } inline long ExpressionSerializer::deserializeExpression( Expression **outExpression, InputStream *inInputStream ) { int numBytes = 0; long expressionID; // read the expression ID numBytes += inInputStream->readLong( &expressionID ); // first, deal with constant case if( expressionID == ConstantExpression::staticGetID() ) { double constantValue; numBytes += inInputStream->readDouble( &constantValue ); *outExpression = new ConstantExpression( constantValue ); } else if( expressionID == FixedConstantExpression::staticGetID() ) { double constantValue; numBytes += inInputStream->readDouble( &constantValue ); *outExpression = new FixedConstantExpression( constantValue ); } // next deal with the multi-argument case else if( expressionID == MultiConstantArgumentExpression::staticGetID() ) { // call deserialize recursively Expression *wrappedExpression; numBytes += deserializeExpression( &wrappedExpression, inInputStream ); MultiConstantArgumentExpression *m = new MultiConstantArgumentExpression( wrappedExpression ); *outExpression = (Expression *)m; } // finally, deal with the general case else { // switch based on expression type // note that we can't use switch/case here because // staticGetID doesn't return a constant if( expressionID == InvertExpression::staticGetID() ) { *outExpression = new InvertExpression( NULL ); } else if( expressionID == NegateExpression::staticGetID() ) { *outExpression = new NegateExpression( NULL ); } else if( expressionID == PowerExpression::staticGetID() ) { *outExpression = new PowerExpression( NULL, NULL ); } else if( expressionID == ProductExpression::staticGetID() ) { *outExpression = new ProductExpression( NULL, NULL ); } else if( expressionID == SinExpression::staticGetID() ) { *outExpression = new SinExpression( NULL ); } else if( expressionID == SumExpression::staticGetID() ) { *outExpression = new SumExpression( NULL, NULL ); } else { printf( "Unknown expression type %d read from stream\n", expressionID ); // default to a constant expression of 0 *outExpression = new ConstantExpression( 0 ); } // now deserialize the arguments // read each of the expression's arguments in succession for( int i=0; i<(*outExpression)->getNumArguments(); i++ ) { // call deserialize recursively Expression *argument; numBytes += deserializeExpression( &argument, inInputStream ); // set the argument into our expression (*outExpression)->setArgument( i, argument ); } } // end non-constant case return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/0000750000175000017500000000000010305077063020451 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/math/geometry/Angle3D.h0000640000175000017500000001051710203707764022051 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-14 Jason Rohrer * Added data members and constructors. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-15 Jason Rohrer * Added a print() function. * * 2001-February-10 Jason Rohrer * Added a linear sum function. * * 2001-March-11 Jason Rohrer * Added a scale function. * * 2004-February-13 Jason Rohrer * Added setComponents functions. */ #ifndef ANGLE_3D_INCLUDED #define ANGLE_3D_INCLUDED #include #include "minorGems/io/Serializable.h" /** * Angle in 3-space. * * @author Jason Rohrer */ class Angle3D : public Serializable { public: // rotations in radians around x, y, and z axis double mX, mY, mZ; /** * Constructs an Angle3D. */ Angle3D( double inX, double inY, double inZ ); /** * Constructs an Angle3D by copying the parameters from * another Angle3D. * * @param inOther angle to copy parameters from. */ Angle3D( Angle3D *inOther ); /** * Sets the components of this angle. */ void setComponents( double inX, double inY, double inZ ); /** * Sets components by copying the parameters from * another Angle3D. * * @param inOther vector to copy parameters from. * Must be destroyed by caller. */ void setComponents( Angle3D *inOther ); /** * Sums another angle with this angle. * * @param inOther angle to add to this angle. */ void add( Angle3D *inOther ); /** * Subtracts a angle from this angle. * * @param inOther angle to subtract from this angle. */ void subtract( Angle3D *inOther ); /** * Multiplies this angle by a scalar. * * @param inScalar scalar to multiply this angle by. */ void scale( double inScalar ); /** * Computes the linear weighted sum of two angles. * * @param inFirst the first angle. * @param inSecond the second angle. * @param inFirstWeight the weight given to the first angle in the * sum. The second angle is weighted (1-inFirstWeight). * * @return the sum angle. Must be destroyed by caller. */ static Angle3D *linearSum( Angle3D *inFirst, Angle3D *inSecond, double inFirstWeight ); /** * Prints this angle to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Angle3D::Angle3D( double inX, double inY, double inZ ) : mX( inX ), mY( inY ), mZ( inZ ) { } inline Angle3D::Angle3D( Angle3D *inOther ) : mX( inOther->mX ), mY( inOther->mY ), mZ( inOther->mZ ) { } inline void Angle3D::setComponents( double inX, double inY, double inZ ) { mX = inX; mY = inY; mZ = inZ; } inline void Angle3D::setComponents( Angle3D *inOther ) { setComponents( inOther->mX, inOther->mY, inOther->mZ ); } inline void Angle3D::add( Angle3D *inOther ) { mX += inOther->mX; mY += inOther->mY; mZ += inOther->mZ; } inline void Angle3D::subtract( Angle3D *inOther ) { mX -= inOther->mX; mY -= inOther->mY; mZ -= inOther->mZ; } inline void Angle3D::scale( double inScalar ) { mX *= inScalar; mY *= inScalar; mZ *= inScalar; } inline Angle3D *Angle3D::linearSum( Angle3D *inFirst, Angle3D *inSecond, double inFirstWeight ) { double secondWeight = 1 - inFirstWeight; double x = inFirstWeight * inFirst->mX + secondWeight * inSecond->mX; double y = inFirstWeight * inFirst->mY + secondWeight * inSecond->mY; double z = inFirstWeight * inFirst->mZ + secondWeight * inSecond->mZ; return new Angle3D( x, y, z ); } inline int Angle3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeDouble( mX ); numBytes += inOutputStream->writeDouble( mY ); numBytes += inOutputStream->writeDouble( mZ ); return numBytes; } inline int Angle3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; numBytes += inInputStream->readDouble( &( mX ) ); numBytes += inInputStream->readDouble( &( mY ) ); numBytes += inInputStream->readDouble( &( mZ ) ); return numBytes; } inline void Angle3D::print() { printf( "(%f, %f, %f)", mX, mY, mZ ); } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/Transform3D.h0000640000175000017500000001757307237024761023011 0ustar pabspabs/* * Modification History * * 2001-January-16 Jason Rohrer * Created. * Added a missing getMatrix() function. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. */ #ifndef TRANSFORM_3D_INCLUDED #define TRANSFORM_3D_INCLUDED #include #include #include "minorGems/io/Serializable.h" #include "Vector3D.h" #include "Angle3D.h" /** * An affine transformation in 3D. * * @author Jason Rohrer */ class Transform3D : public Serializable { public: /** * Creates a new identity transform. */ Transform3D(); /** * Creates a new transform by copying another transform. * * @param inOther the transform to copy. */ Transform3D( Transform3D *inOther ); /** * Adds a rotation to the end of this transform. Note that the * rotations specified by inAngle are applied in the following order: * rotX, rotY, rotZ * * @param inAngle angle rotation to add. Must be destructed by caller. */ void rotate( Angle3D *inAngle ); /** * Adds a scaling operation to the end of this transform. * * @param inScale the uniform scale factor. */ void scale( double inScale ); /** * Adds a scaling operation to the end of this transform. * * @param inScaleX the x direction scale factor. * @param inScaleY the y direction scale factor. * @param inScaleZ the z direction scale factor. */ void scale( double inScaleX, double inScaleY, double inScaleZ ); /** * Adds a translation to the end of this transform. * * @param inTranslation angle rotation to add. * Must be destructed by caller. */ void translate( Vector3D *inTranslation ); /** * Adds a transformation to the end of this transformation. * * @param inTransform the transform to add. inTransform is no * modified by this call, and must be destroyed by the caller. */ void transform( Transform3D *inTransform ); /** * Transforms a vector using the built-up transformation. * * @param inTarget the vector to transform. The object passed * in is modified directly, and must be destroyed by the caller. */ void apply( Vector3D *inTarget ); /** * Transforms a vector using the built-up transformation, but skips * the translation part of the transform. This is useful when * applying the transform to normals, when translations will screw * them up. * * @param inTarget the vector to transform. The object passed * in is modified directly, and must be destroyed by the caller. */ void applyNoTranslation( Vector3D *inTarget ); /** * Gets the transformation matrix underlying this transform. * * @return the transformation matrix. */ double *getMatrix(); /** * Prints the transformation matrix to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); private: double mMatrix[4][4]; /** * Multiplies mMatrix by inMatrix, i.e., mMatrix = inMatrix * mMatrix. * * Note that this reversed multiplication order works for concatonating * transformation matrices when we're using column vectors for our 3D * points. Thus, if after the above operation, we compute: * point = mMatrix * point, * we will be transformping point first by the original * mMatrix and then by inMatrix. * * @param inMatrix the matrix to multiply mMatrix by. */ void multiply( double inMatrix[][4] ); /** * Multiplies inMatrixA by inMatrixB, i.e., * inMatrixA = inMatrixA * inMatrixB. * * @param inMatrixA the first matrix in the multiplication, and * the destination of the result. * @param inMatrixB the second matrix in the multiplication. */ //void multiply( double[4][4] inMatrixA, double[4][4] inMatrixB ); }; inline Transform3D::Transform3D() { double tempM[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; memcpy( mMatrix, tempM, 16 * sizeof( double ) ); } inline Transform3D::Transform3D( Transform3D *inOther ) { memcpy( mMatrix, inOther->getMatrix(), 16 * sizeof( double ) ); } inline void Transform3D::rotate( Angle3D *inAngle ) { double aX = inAngle->mX; double rotX[4][4] = { { 1, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; rotX[1][1] = cos( aX ); rotX[1][2] = -sin( aX ); rotX[2][1] = sin( aX ); rotX[2][2] = cos( aX ); double aY = inAngle->mY; double rotY[4][4] = { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; rotY[0][0] = cos( aY ); rotY[0][2] = sin( aY ); rotY[2][0] = -sin( aY ); rotY[2][2] = cos( aY ); double aZ = inAngle->mZ; double rotZ[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; rotZ[0][0] = cos( aZ ); rotZ[0][1] = -sin( aZ ); rotZ[1][0] = sin( aZ ); rotZ[1][1] = cos( aZ ); multiply( rotX ); multiply( rotY ); multiply( rotZ ); } inline void Transform3D::scale( double inScale ) { scale( inScale, inScale, inScale ); } inline void Transform3D::scale( double inScaleX, double inScaleY, double inScaleZ ) { double scaleM[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 1 } }; scaleM[0][0] = inScaleX; scaleM[1][1] = inScaleY; scaleM[2][2] = inScaleZ; multiply( scaleM ); } inline void Transform3D::translate( Vector3D *inTranslation ) { double translateM[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; translateM[0][3] = inTranslation->mX; translateM[1][3] = inTranslation->mY; translateM[2][3] = inTranslation->mZ; multiply( translateM ); } inline void Transform3D::transform( Transform3D *inTransform ) { double tempM[4][4]; memcpy( tempM, inTransform->getMatrix(), 16 * sizeof( double ) ); multiply( tempM ); } inline void Transform3D::apply( Vector3D *inTarget ) { double x = inTarget->mX; double y = inTarget->mY; double z = inTarget->mZ; inTarget->mX = mMatrix[0][0] * x + mMatrix[0][1] * y + mMatrix[0][2] * z + mMatrix[0][3]; inTarget->mY = mMatrix[1][0] * x + mMatrix[1][1] * y + mMatrix[1][2] * z + mMatrix[1][3]; inTarget->mZ = mMatrix[2][0] * x + mMatrix[2][1] * y + mMatrix[2][2] * z + mMatrix[2][3]; } inline void Transform3D::applyNoTranslation( Vector3D *inTarget ) { double x = inTarget->mX; double y = inTarget->mY; double z = inTarget->mZ; inTarget->mX = mMatrix[0][0] * x + mMatrix[0][1] * y + mMatrix[0][2] * z; inTarget->mY = mMatrix[1][0] * x + mMatrix[1][1] * y + mMatrix[1][2] * z; inTarget->mZ = mMatrix[2][0] * x + mMatrix[2][1] * y + mMatrix[2][2] * z; } inline void Transform3D::multiply( double inMatrix[][4] ) { double destM[4][4]; for( int dY=0; dY<4; dY++ ) { for( int dX=0; dX<4; dX++ ) { // take a row of inMatrix corresponding to dY // take a column of mMatrix corresponding to dX destM[dY][dX] = 0; for( int i=0; i<4; i++ ) { destM[dY][dX] += inMatrix[dY][i] * mMatrix[i][dX]; } } } memcpy( mMatrix, destM, 16 * sizeof( double ) ); } inline double *Transform3D::getMatrix() { return &( mMatrix[0][0] ); } inline void Transform3D::print() { for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { printf( "%f ", mMatrix[y][x] ); } printf( "\n" ); } } inline int Transform3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { numBytes += inOutputStream->writeDouble( mMatrix[y][x] ); } } return numBytes; } inline int Transform3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; for( int y=0; y<4; y++ ) { for( int x=0; x<4; x++ ) { numBytes += inInputStream->readDouble( &( mMatrix[y][x] ) ); } } return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/GeometricObject3D.h0000640000175000017500000000230607216162537024070 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-13 Jason Rohrer * Added scale() and copy() functions. */ #ifndef GEOMETRIC_OBJECT_3D_INCLUDED #define GEOMETRIC_OBJECT_3D_INCLUDED #include "Vector3D.h" /** * Interface for any geometric objects in 3-space. * * @author Jason Rohrer */ class GeometricObject3D { public: /** * Moves the object. * * @param inVector a non-normalized vector describing the motion * of the object. */ virtual void move( Vector3D *inVector ) = 0; /** * Rotates the object about the origin. * * @param inAngle the angle to rotate the object by. */ virtual void rotate( Angle3D *inAngle ) = 0; /** * Rotates the object about the origin in reverse direction. * * @param inAngle the angle to rotate the object by * in reverse direction. */ virtual void reverseRotate( Angle3D *inAngle ) = 0; /** * Scales the object about the origin. * * @param inScalar value by which to scale. */ virtual void scale( double inScalar ) = 0; /** * Makes an identical copy of this geometric object. */ virtual GeometricObject3D *copy() = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/Triangle3D.h0000640000175000017500000001710510204365360022561 0ustar pabspabs/* * Modification History * * 2001-April-14 Jason Rohrer * Created. * * 2005-February-15 Jason Rohrer * Made destructor virtual to quell warnings. */ #ifndef TRIANGLE_3D_INCLUDED #define TRIANGLE_3D_INCLUDED #include "GeometricObject3D.h" #include "LineSegment3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Triangle (in 3-space). * * @author Jason Rohrer */ class Triangle3D : public GeometricObject3D, public Serializable { public: Vector3D *mPoints[3]; /** * Constructs a triangle. * * The vectors are copied, so the caller * is responsible for deallocating the passed in vectors. * * @param inPointA the first point. * @param inPointB the second point. * @param inPointC the third point. * * The "front" face of this trangle is defined as the face * viewed when the above vertices are in counter-clockwise order. */ Triangle3D( Vector3D *inPointA, Vector3D *inPointB, Vector3D *inPointC); /** * Constructs a triangle by copying parameters * from another triangle. * * @param inOtherTriangle the triangle to copy. */ Triangle3D( Triangle3D *inOtherTriangle ); virtual ~Triangle3D(); /** * Gets a line segment from this triangle. * * @param inIndex the line segment to get, in [0,2]. * * @return a new line segment, or NULL if the index is out of range. * Must be destroyed by caller. */ LineSegment3D *getLineSegment( int inIndex ); /** * Gets the normal for the plane containing this triangle. * * @return the normal vector. Must be destroyed by caller. */ Vector3D *getNormal(); /** * Computes the normal projection of a point onto the plane * defined by this triangle. A point is returned even * if it is not inside the bounds of the triangle. * * @param inPoint the point to project. Must be destroyed by caller. * * @return a new vector representing the projection of the point. * Must be destroyed by caller. */ Vector3D *projectOntoPlane( Vector3D *inPoint ); /** * Checks if a point is inside the bounds of this triangle. * * @param inPoint the point to check. Must be destroyed by caller. * * @return true iff the point is in bounds. */ char isInBounds( Vector3D *inPoint ); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Triangle3D::Triangle3D( Vector3D *inEndpointA, Vector3D *inEndpointB, Vector3D *inEndpointC ) { mPoints[0] = new Vector3D( inEndpointA ); mPoints[1] = new Vector3D( inEndpointB ); mPoints[2] = new Vector3D( inEndpointC ); } inline Triangle3D::Triangle3D( Triangle3D *inOtherTriangle ) { mPoints[0] = new Vector3D( inOtherTriangle->mPoints[0] ); mPoints[1] = new Vector3D( inOtherTriangle->mPoints[1] ); mPoints[2] = new Vector3D( inOtherTriangle->mPoints[2] ); } inline Triangle3D::~Triangle3D() { delete mPoints[0]; delete mPoints[1]; delete mPoints[2]; } inline LineSegment3D *Triangle3D::getLineSegment( int inIndex ) { if( inIndex < 0 || inIndex > 2 ) { return NULL; } else { // can pass in mPoints directly, since constructor copies them return new LineSegment3D( mPoints[inIndex], mPoints[ ( inIndex + 1 ) % 2 ] ); } } inline Vector3D *Triangle3D::getNormal() { // cross C-B with A-B to get a normal pointing towards // the viewer when seeing the points in counter-clockwise order Vector3D *firstLeg = new Vector3D( mPoints[2] ); firstLeg->subtract( mPoints[1] ); Vector3D *secondLeg = new Vector3D( mPoints[0] ); secondLeg->subtract( mPoints[1] ); Vector3D *normal = firstLeg->cross( secondLeg ); normal->normalize(); delete firstLeg; delete secondLeg; return normal; } inline Vector3D *Triangle3D::projectOntoPlane( Vector3D *inPoint ) { // formula found at: // http://astronomy.swin.edu.au/pbourke/geometry/pointplane/ //minimum distance = //(A (xa - xb) + B (ya - yb) + C (za - zb)) / sqrt(A^2 + B^2 + C^2) Vector3D *normal = getNormal(); double minDistance = normal->mX * ( inPoint->mX - mPoints[0]->mX ) + normal->mY * ( inPoint->mY - mPoints[0]->mY ) + normal->mZ * ( inPoint->mZ - mPoints[0]->mZ ); minDistance = minDistance / sqrt( normal->mX * normal->mX + normal->mY * normal->mY + normal->mZ * normal->mZ ); double dot = inPoint->dot( normal ); Vector3D *returnPoint = new Vector3D( inPoint ); if( dot > 0 ) { // inPoint on front side of plane normal->scale( -minDistance ); } else { // inPoint on back side of plane normal->scale( minDistance ); } returnPoint->add( normal ); delete normal; return returnPoint; } inline char Triangle3D::isInBounds( Vector3D *inPoint ) { // this is a nice formula, found at // http://astronomy.swin.edu.au/pbourke/geometry/insidepoly/ // compute the angle between inPoint and every pair of points in the // triangle (each edge). If the sum is 2*pi, then the point // is inside the triangle. // note that we have a hard-coded epsilon value here double epsilon = 0.000001; double angleSum = 0; Vector3D *firstLeg = new Vector3D( mPoints[0] ); firstLeg->subtract( inPoint ); Vector3D *secondLeg = new Vector3D( mPoints[1] ); secondLeg->subtract( inPoint ); Vector3D *thirdLeg = new Vector3D( mPoints[2] ); thirdLeg->subtract( inPoint ); angleSum += acos( firstLeg->dot( secondLeg ) / ( firstLeg->getLength() * secondLeg->getLength() ) ); angleSum += acos( secondLeg->dot( thirdLeg ) / ( secondLeg->getLength() * thirdLeg->getLength() ) ); angleSum += acos( thirdLeg->dot( firstLeg ) / ( thirdLeg->getLength() * firstLeg->getLength() ) ); delete firstLeg; delete secondLeg; delete thirdLeg; if( angleSum < ( 2 * M_PI - epsilon ) ) { // angle too small for point to be inside plane return false; } else { return true; } } inline void Triangle3D::move( Vector3D *inVector ) { mPoints[0]->add( inVector ); mPoints[1]->add( inVector ); mPoints[2]->add( inVector ); } inline void Triangle3D::rotate( Angle3D *inAngle ) { mPoints[0]->rotate( inAngle ); mPoints[1]->rotate( inAngle ); mPoints[2]->rotate( inAngle ); } inline void Triangle3D::reverseRotate( Angle3D *inAngle ) { mPoints[0]->reverseRotate( inAngle ); mPoints[1]->reverseRotate( inAngle ); mPoints[2]->reverseRotate( inAngle ); } inline void Triangle3D::scale( double inScalar ) { mPoints[0]->scale( inScalar ); mPoints[1]->scale( inScalar ); mPoints[2]->scale( inScalar ); } inline GeometricObject3D *Triangle3D::copy() { Triangle3D *copiedTriangle = new Triangle3D( this ); return (GeometricObject3D*)copiedTriangle; } inline int Triangle3D::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mPoints[0]->serialize( inOutputStream ); numBytesWritten += mPoints[1]->serialize( inOutputStream ); numBytesWritten += mPoints[2]->serialize( inOutputStream ); return numBytesWritten; } inline int Triangle3D::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mPoints[0]->deserialize( inInputStream ); numBytesRead += mPoints[1]->deserialize( inInputStream ); numBytesRead += mPoints[2]->deserialize( inInputStream ); return numBytesRead; } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/Vector3D.h0000640000175000017500000004260510216632607022265 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-13 Jason Rohrer * Added a function for getting the distance between two vectors. * Added implementations for current functions. * * 2000-December-17 Jason Rohrer * Added a normalize function. * * 2000-December-18 Jason Rohrer * Added a print function. * Added a length function. * * 2000-December-20 Jason Rohrer * Added a cross product function. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-February-10 Jason Rohrer * Added a linear sum function. * * 2003-June-20 Jason Rohrer * Added function for getting Z angle between vectors. * * 2005-February-4 Jason Rohrer * Added setCoordinates functions. * * 2005-February-6 Jason Rohrer * Added equals function. * * 2005-February-15 Jason Rohrer * Added function for getting Y angle between vectors. * * 2005-February-22 Jason Rohrer * Added function for getting X angle between vectors. * Added function for rotating about an arbitrary axis. * * 2005-February-25 Jason Rohrer * Fixed bugs in get_AngleTo functions when vectors are close to equal. * * 2005-February-28 Jason Rohrer * Fixed bug in getXAngleTo. * Fixed bugs in get_AngleTo functions when vectors are close to opposite. * * 2005-March-3 Jason Rohrer * Fixed bug in getAngleTo when vectors are close to equal or opposite. * * 2005-March-18 Jason Rohrer * Added a getXZDistance function. */ #ifndef VECTOR_3D_INCLUDED #define VECTOR_3D_INCLUDED #include #include #include "Angle3D.h" #include "minorGems/io/Serializable.h" /** * Geometric vector in 3-space. * * @author Jason Rohrer */ class Vector3D : public Serializable { public: double mX, mY, mZ; /** * Constructs a Vector3D. */ Vector3D( double inX, double inY, double inZ ); /** * Constructs a Vector3D by copying the parameters from * another Vector3D. * * @param inOther vector to copy parameters from. */ Vector3D( Vector3D *inOther ); /** * Sets the values in this vector */ void setCoordinates( double inX, double inY, double inZ ); /** * Sets coordinates by copying the parameters from * another Vector3D. * * @param inOther vector to copy parameters from. * Must be destroyed by caller. */ void setCoordinates( Vector3D *inOther ); /** * Normalizes this vector so that it has a length of 1. */ void normalize(); /** * Tests if another vector is equal to this vector. * * @param inOther vector to test for equality with this vector. * * @return true if equal, false if not. */ char equals( Vector3D *inOther ); /** * Sums another vector with this vector. * * @param inOther vector to add to this vector. */ void add( Vector3D *inOther ); /** * Subtracts a vector from this vector. * * @param inOther vector to subtract from this vector. */ void subtract( Vector3D *inOther ); /** * Computes a dot product of this vector with another. * * @param inOther vector to perform the dot product with. * * @return the dot product of the two vectors. */ double dot( Vector3D *inOther ); /** * Computes a cross product of this vector with another * ( this x other ). The cross computed is right handed. * * @param inOther vector to perform the cross product with. * * @return the cross product of the two vectors. Must be * destroyed by caller. */ Vector3D *cross( Vector3D *inOther ); /** * Computes the angle between this vector and another vector. * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in radians. * This angle is around the axis given by the cross of the two * vectors. * Must be destroyed by caller. */ double getAngleTo( Vector3D *inOther ); /** * Rotates this vector around an arbitrary axis. * * @param inAxis the axis of rotation. Must be normalized. * Must be destroyed by caller. * @param the angle in radians. */ void rotate( Vector3D *inAxis, double inAngle ); /** * Computes the angle between this vector and another vector in * the x-y plane (in other words, the z-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the x-y plane. * Must be destroyed by caller. */ Angle3D *getZAngleTo( Vector3D *inOther ); /** * Computes the angle between this vector and another vector in * the x-z plane (in other words, the y-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the x-z plane. * Must be destroyed by caller. */ Angle3D *getYAngleTo( Vector3D *inOther ); /** * Computes the angle between this vector and another vector in * the y-z plane (in other words, the x-axis rotation angle). * * @param inOther vector to find the angle to. * * @return the angle between the two vectors in the y-z plane. * Must be destroyed by caller. */ Angle3D *getXAngleTo( Vector3D *inOther ); /** * Computes the linear weighted sum of two vectors. * * @param inFirst the first vector. * @param inSecond the second vector. * @param inFirstWeight the weight given to the first vector in the * sum. The second vector is weighted (1-inFirstWeight). * * @return the sum vector. Must be destroyed by caller. */ static Vector3D *linearSum( Vector3D *inFirst, Vector3D *inSecond, double inFirstWeight ); /** * Gets the length of this vector. * * @return this vector's length. */ double getLength(); /** * Multiplies this vector by a scalar. * * @param inScalar scalar to multiply this vector by. */ void scale( double inScalar ); /** * Gets the scalar distance between two vectors. * * @param inOther vector to compute the distance with. * * @return the distance between the two vectors. */ double getDistance( Vector3D *inOther ); /** * Gets the scalar distance between two vectors ignoring the y * components. * * @param inOther vector to compute the distance with. * * @return the xz distance between the two vectors. */ double getXZDistance( Vector3D *inOther ); /** * Rotates the vector about the origin. * * @param inAngle the angle to rotate the vector by. */ void rotate( Angle3D *inAngle ); /** * Rotates the vector about the origin in reverse direction. * * @param inAngle the angle to rotate the object by * in reverse direction. */ void reverseRotate( Angle3D *inAngle ); /** * Prints this vector to standard out. */ void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Vector3D::Vector3D( double inX, double inY, double inZ ) : mX( inX ), mY( inY ), mZ( inZ ) { } inline Vector3D::Vector3D( Vector3D *inOther ) : mX( inOther->mX ), mY( inOther->mY ), mZ( inOther->mZ ) { } inline void Vector3D::setCoordinates( double inX, double inY, double inZ ) { mX = inX; mY = inY; mZ = inZ; } inline void Vector3D::setCoordinates( Vector3D *inOther ) { setCoordinates( inOther->mX, inOther->mY, inOther->mZ ); } inline void Vector3D::normalize() { scale( 1/sqrt( dot( this ) ) ); } inline char Vector3D::equals( Vector3D *inOther ) { return mX == inOther->mX && mY == inOther->mY && mZ == inOther->mZ; } inline void Vector3D::add( Vector3D *inOther ) { mX += inOther->mX; mY += inOther->mY; mZ += inOther->mZ; } inline void Vector3D::subtract( Vector3D *inOther ) { mX -= inOther->mX; mY -= inOther->mY; mZ -= inOther->mZ; } inline double Vector3D::dot( Vector3D *inOther ) { return mX * inOther->mX + mY * inOther->mY + mZ * inOther->mZ; } inline Vector3D *Vector3D::cross( Vector3D *inOther ) { double i = this->mY * inOther->mZ - this->mZ * inOther->mY; double j = this->mZ * inOther->mX - this->mX * inOther->mZ; double k = this->mX * inOther->mY - this->mY * inOther->mX; return new Vector3D( i, j, k ); } inline double Vector3D::getAngleTo( Vector3D *inOther ) { // normalize and remove z component Vector3D *normalThis = new Vector3D( this ); normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->normalize(); double cosineOfAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute cross product of vectors // the magnitude of the cross is the sine of the angle between the two // vectors Vector3D *crossVector = normalThis->cross( normalOther ); double sineOfAngle = crossVector->getLength(); delete crossVector; delete normalThis; delete normalOther; double angle = acos( cosineOfAngle ); if( sineOfAngle > 0 ) { angle = -angle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfAngle >= 1 ) { angle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfAngle <= -1 ) { angle = M_PI; } return angle; } inline void Vector3D::rotate( Vector3D *inAxis, double inAngle ) { // this formula found here: // http://www.gamedev.net/reference/articles/article1199.asp double c = cos( inAngle ); double s = sin( inAngle ); double t = 1 - c; // we assume inAxis is a unit vector (normalized) double x = inAxis->mX; double y = inAxis->mY; double z = inAxis->mZ; double sx = s * x; double sy = s * y; double sz = s * z; double tx = t * x; double ty = t * y; double txx = tx * x; double txy = tx * y; double txz = tx * z; double tyy = ty * y; double tyz = ty * z; double tzz = t * z * z; /* The rotation matrix is: (txx + c) (txy + sz) (txz - sy) (txy - sz) (tyy + c) (tyz + sx) (txz + sy) (tyz - sx) (tzz + c) */ double newX = (txx + c) * mX + (txy + sz) * mY + (txz - sy) * mZ; double newY = (txy - sz) * mX + (tyy + c) * mY + (tyz + sx) * mZ; double newZ = (txz + sy) * mX + (tyz - sx) * mY + (tzz + c) * mZ; mX = newX; mY = newY; mZ = newZ; } inline Angle3D *Vector3D::getZAngleTo( Vector3D *inOther ) { // normalize and remove z component Vector3D *normalThis = new Vector3D( this ); normalThis->mZ = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mZ = 0; normalOther->normalize(); double cosineOfZAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleZ = new Angle3D( 0, 0, M_PI / 2 ); normalThis->rotate( rightAngleZ ); delete rightAngleZ; double sineOfZAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double zAngle = acos( cosineOfZAngle ); if( sineOfZAngle < 0 ) { zAngle = -zAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfZAngle >= 1 ) { zAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfZAngle <= -1 ) { zAngle = M_PI; } return new Angle3D( 0, 0, zAngle ); } inline Angle3D *Vector3D::getYAngleTo( Vector3D *inOther ) { // normalize and remove y component Vector3D *normalThis = new Vector3D( this ); normalThis->mY = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mY = 0; normalOther->normalize(); double cosineOfYAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleY = new Angle3D( 0, M_PI / 2, 0 ); normalThis->rotate( rightAngleY ); delete rightAngleY; double sineOfYAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double yAngle = acos( cosineOfYAngle ); if( sineOfYAngle < 0 ) { yAngle = -yAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfYAngle >= 1 ) { yAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfYAngle <= -1 ) { yAngle = M_PI; } return new Angle3D( 0, yAngle, 0 ); } inline Angle3D *Vector3D::getXAngleTo( Vector3D *inOther ) { // normalize and remove y component Vector3D *normalThis = new Vector3D( this ); normalThis->mX = 0; normalThis->normalize(); Vector3D *normalOther = new Vector3D( inOther ); normalOther->mX = 0; normalOther->normalize(); double cosineOfXAngle = normalThis->dot( normalOther ); // cosine is ambiguous (same for negative and positive angles) // compute dot product with perpendicular vector to get sine // sign of sine will tell us whether angle is positive or negative Angle3D *rightAngleX = new Angle3D( M_PI / 2, 0, 0 ); normalThis->rotate( rightAngleX ); delete rightAngleX; double sineOfXAngle = normalThis->dot( normalOther ); delete normalThis; delete normalOther; double xAngle = acos( cosineOfXAngle ); if( sineOfXAngle < 0 ) { xAngle = -xAngle; } // if vectors are very close, our dot product above might give // a value greater than 1 due to round-off errors // this causes acos to return NAN if( cosineOfXAngle >= 1 ) { xAngle = 0; } // also need to worry if vectors are complete opposites else if( cosineOfXAngle <= -1 ) { xAngle = M_PI; } return new Angle3D( xAngle, 0, 0 ); } inline Vector3D *Vector3D::linearSum( Vector3D *inFirst, Vector3D *inSecond, double inFirstWeight ) { double secondWeight = 1 - inFirstWeight; double x = inFirstWeight * inFirst->mX + secondWeight * inSecond->mX; double y = inFirstWeight * inFirst->mY + secondWeight * inSecond->mY; double z = inFirstWeight * inFirst->mZ + secondWeight * inSecond->mZ; return new Vector3D( x, y, z ); } inline double Vector3D::getLength() { return sqrt( dot( this ) ); } inline void Vector3D::scale( double inScalar ) { mX *= inScalar; mY *= inScalar; mZ *= inScalar; } inline double Vector3D::getDistance( Vector3D *inOther ) { double delX = mX - inOther->mX; double delY = mY - inOther->mY; double delZ = mZ - inOther->mZ; return sqrt( delX * delX + delY * delY + delZ * delZ ); } inline double Vector3D::getXZDistance( Vector3D *inOther ) { double delX = mX - inOther->mX; double delZ = mZ - inOther->mZ; return sqrt( delX * delX + delZ * delZ ); } inline void Vector3D::rotate( Angle3D *inAngle ) { if( inAngle->mX != 0 ) { double cosTheta = cos( inAngle->mX ); double sinTheta = sin( inAngle->mX ); double oldY = mY; mY = mY * cosTheta - mZ * sinTheta; mZ = oldY * sinTheta + mZ * cosTheta; } if( inAngle->mY != 0 ) { double cosTheta = cos( inAngle->mY ); double sinTheta = sin( inAngle->mY ); double oldX = mX; mX = cosTheta * mX + sinTheta * mZ; mZ = -sinTheta * oldX + cosTheta * mZ; } if( inAngle->mZ != 0 ) { double cosTheta = cos( inAngle->mZ ); double sinTheta = sin( inAngle->mZ ); double oldX = mX; mX = cosTheta * mX - sinTheta * mY; mY = sinTheta * oldX + cosTheta * mY; } } inline void Vector3D::reverseRotate( Angle3D *inAngle ) { Angle3D *actualAngle = new Angle3D( -inAngle->mX, -inAngle->mY, -inAngle->mZ ); rotate( actualAngle ); delete actualAngle; } inline void Vector3D::print() { printf( "(%f, %f, %f)", mX, mY, mZ ); } inline int Vector3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeDouble( mX ); numBytes += inOutputStream->writeDouble( mY ); numBytes += inOutputStream->writeDouble( mZ ); return numBytes; } inline int Vector3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; numBytes += inInputStream->readDouble( &( mX ) ); numBytes += inInputStream->readDouble( &( mY ) ); numBytes += inInputStream->readDouble( &( mZ ) ); return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/LineSegment3D.h0000640000175000017500000001134710204365360023230 0ustar pabspabs/* * Modification History * * 2001-April-14 Jason Rohrer * Created. * * 2005-February-15 Jason Rohrer * Made destructor virtual to quell warnings. */ #ifndef LINE_SEGMENT_3D_INCLUDED #define LINE_SEGMENT_3D_INCLUDED #include "GeometricObject3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Line segment (in 3-space). * * @author Jason Rohrer */ class LineSegment3D : public GeometricObject3D, public Serializable { public: Vector3D *mEndpoints[2]; /** * Constructs a line segment. * * The vectors are copied, so the caller * is responsible for deallocating the passed in vectors. * * @param inEndpointA the first endpoint. * @param inEndpointB the second endpoint. */ LineSegment3D( Vector3D *inEndpointA, Vector3D *inEndpointB ); /** * Constructs a line segment by copying parameters * from another line segment. * * @param inOtherSegment the line segment to copy. */ LineSegment3D( LineSegment3D *inOtherSegment ); virtual ~LineSegment3D(); /** * Gets the projection of a point onto this line segment * * @param inPoint the point to project. Must be destroyed * by caller. * * @return a new vector representing the projected point, or * NULL if the projection is not on this line segment. */ Vector3D *projectPoint( Vector3D *inPoint ); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline LineSegment3D::LineSegment3D( Vector3D *inEndpointA, Vector3D *inEndpointB ) { mEndpoints[0] = new Vector3D( inEndpointA ); mEndpoints[1] = new Vector3D( inEndpointB ); } inline LineSegment3D::LineSegment3D( LineSegment3D *inOtherSegment ) { mEndpoints[0] = new Vector3D( inOtherSegment->mEndpoints[0] ); mEndpoints[1] = new Vector3D( inOtherSegment->mEndpoints[1] ); } inline LineSegment3D::~LineSegment3D() { delete mEndpoints[0]; delete mEndpoints[1]; } inline Vector3D *LineSegment3D::projectPoint( Vector3D *inPoint ) { // compute the vector for the underlying line of this segment Vector3D *supportingVector = new Vector3D( mEndpoints[0] ); supportingVector->subtract( mEndpoints[1] ); supportingVector->normalize(); // now find the lengths of the projection of each endpoint // onto this supporting vector double lengthA = supportingVector->dot( mEndpoints[0] ); double lengthB = supportingVector->dot( mEndpoints[1] ); // find the length of the projection of inPoint double lengthInPoint = supportingVector->dot( inPoint ); // projection is off the segment if lengthInPoint is not // between lengthA and lengthB if( lengthInPoint > lengthA && lengthInPoint > lengthB ) { delete supportingVector; return NULL; } else if( lengthInPoint < lengthA && lengthInPoint < lengthB ) { delete supportingVector; return NULL; } else { // length is in between the two // compute difference from A's projection double difference = lengthInPoint - lengthA; Vector3D *returnVector = new Vector3D( mEndpoints[0] ); supportingVector->scale( difference ); returnVector->add( supportingVector ); delete supportingVector; return returnVector; } } inline void LineSegment3D::move( Vector3D *inVector ) { mEndpoints[0]->add( inVector ); mEndpoints[1]->add( inVector ); } inline void LineSegment3D::rotate( Angle3D *inAngle ) { mEndpoints[0]->rotate( inAngle ); mEndpoints[1]->rotate( inAngle ); } inline void LineSegment3D::reverseRotate( Angle3D *inAngle ) { mEndpoints[0]->reverseRotate( inAngle ); mEndpoints[1]->reverseRotate( inAngle ); } inline void LineSegment3D::scale( double inScalar ) { mEndpoints[0]->scale( inScalar ); mEndpoints[1]->scale( inScalar ); } inline GeometricObject3D *LineSegment3D::copy() { LineSegment3D *copiedSegment = new LineSegment3D( this ); return (GeometricObject3D*)copiedSegment; } inline int LineSegment3D::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mEndpoints[0]->serialize( inOutputStream ); numBytesWritten += mEndpoints[1]->serialize( inOutputStream ); return numBytesWritten; } inline int LineSegment3D::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mEndpoints[0]->deserialize( inInputStream ); numBytesRead += mEndpoints[1]->deserialize( inInputStream ); return numBytesRead; } #endif transcend-0.3+dfsg2.orig/minorGems/math/geometry/Sphere.h0000640000175000017500000001702507266142706022067 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2000-December-14 Jason Rohrer * Added implementation for all member functions. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-24 Jason Rohrer * Fixed a bug in the deserialization function. * * 2001-February-2 Jason Rohrer * Added M_PI definition. * * 2001-April-14 Jason Rohrer * Added line segment and triangle intersection tests. */ #ifndef SPHERE_INCLUDED #define SPHERE_INCLUDED #include "GeometricObject3D.h" #include "LineSegment3D.h" #include "Triangle3D.h" #include "minorGems/io/Serializable.h" #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Geometric sphere (in 3-space). * * @author Jason Rohrer */ class Sphere : public GeometricObject3D, public Serializable { public: Vector3D *mCenter; double mRadius; /** * Constructs a Sphere. * * @param inCenter a vector containing the coordinates of the * sphere's center. The vector is copied, so the caller * is responsible for deallocating the passed in vector. * * @param inRadius the radius of the sphere. */ Sphere( Vector3D *inCenter, double inRadius ); /** * Constructs a Sphere by copying parameters from another Sphere. * * @param inOtherSphere the sphere to copy. */ Sphere( Sphere *inOtherSphere ); ~Sphere(); /** * Gets whether this sphere intersects with another. * * @param inOtherSphere the sphere to test for intersection * with this sphere. * * @return true iff this sphere intersects with the other. */ char intersects( Sphere *inOtherSphere ); /** * Gets the normalized direction of intersection of this * sphere with another. * * @param inOtherSphere the sphere to test for intersection * with this sphere. * * @return a normalized vector direction of the intersection, * or NULL if there is no intersection. */ Vector3D *getIntersection( Sphere *inOtherSphere ); /** * Gets whether a line segment intersects or is inside this sphere. * * @param inSegment the segment to test. * * @return true if the segment intersects with or is contained by * this sphere. */ char intersects( LineSegment3D *inSegment ); /** * Gets whether a triangle intesects or is inside this sphere. * * * @param inTriangle the triangle to test. * * @return true if the triangle intersects with or is contained by * this sphere. */ char intersects( Triangle3D *inTriangle ); /** * Gets the volume of this sphere. * * @return the volume of this sphere. */ double getVolume(); // these implement the GeometricObject3D interface void move( Vector3D *inVector ); void rotate( Angle3D *inAngle ); void reverseRotate( Angle3D *inAngle ); void scale( double inScalar ); GeometricObject3D *copy(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); }; inline Sphere::Sphere( Vector3D *inCenter, double inRadius ) : mCenter( new Vector3D( inCenter ) ), mRadius( inRadius ) { } inline Sphere::Sphere( Sphere *inOtherSphere ) : mCenter( new Vector3D( inOtherSphere->mCenter ) ), mRadius( inOtherSphere->mRadius ) { } inline Sphere::~Sphere() { delete mCenter; } inline char Sphere::intersects( Sphere *inOtherSphere ) { double distance = mCenter->getDistance( inOtherSphere->mCenter ); if( distance <= mRadius + inOtherSphere->mRadius ) { return true; } else { return false; } } inline Vector3D *Sphere::getIntersection( Sphere *inOtherSphere ) { if( intersects( inOtherSphere ) ) { Vector3D * returnVector = new Vector3D( inOtherSphere->mCenter ); returnVector->subtract( mCenter ); returnVector->normalize(); return returnVector; } else { return NULL; } } /* * These intersection algorithms were found in the following paper: * * ERIT -- A Collection of Efficient and Reliable Intersection Tests * by Martin Held * Journal of Graphics Tools */ inline char Sphere::intersects( LineSegment3D *inSegment ) { // first check if either endpoint is inside this sphere if( inSegment->mEndpoints[0]->getDistance( mCenter ) <= mRadius ) { return true; } else if( inSegment->mEndpoints[1]->getDistance( mCenter ) <= mRadius ) { return true; } else { // both endpoints outside // project center onto the line Vector3D *projectedCenter = inSegment->projectPoint( mCenter ); if( projectedCenter == NULL ) { // projection is outside of the segment return false; } else { // projection is inside of the segment // check distance from center double distance = projectedCenter->getDistance( mCenter ); delete projectedCenter; if( distance <= mRadius ) { return true; } else { return false; } } } } inline char Sphere::intersects( Triangle3D *inTriangle ) { char intersectionFound; // first, check each triangle edge segment for intersection LineSegment3D *segmentA = inTriangle->getLineSegment( 0 ); intersectionFound = intersects( segmentA ); delete segmentA; if( intersectionFound ) { return true; } LineSegment3D *segmentB = inTriangle->getLineSegment( 1 ); intersectionFound = intersects( segmentB ); delete segmentB; if( intersectionFound ) { return true; } LineSegment3D *segmentC = inTriangle->getLineSegment( 2 ); intersectionFound = intersects( segmentC ); delete segmentC; if( intersectionFound ) { return true; } // if we're at this point, none of the edges intersected. // we still need to check if the interior region of the triangle // is inside the sphere Vector3D *projectedCenter = inTriangle->projectOntoPlane( mCenter ); if( inTriangle->isInBounds( projectedCenter ) ) { if( projectedCenter->getDistance( mCenter ) <= mRadius ) { // the projected center is inside the triangle and // inside the sphere intersectionFound = true; } else { // the projected center is inside the triangle, but outside // the sphere intersectionFound = false; } } else { // center projection not even in triangle bounds, so no intersection intersectionFound = false; } delete projectedCenter; return intersectionFound; } inline double Sphere::getVolume() { return (4/3) * M_PI * mRadius * mRadius * mRadius; } inline void Sphere::move( Vector3D *inVector ) { mCenter->add( inVector ); } inline void Sphere::rotate( Angle3D *inAngle ) { mCenter->rotate( inAngle ); } inline void Sphere::reverseRotate( Angle3D *inAngle ) { mCenter->reverseRotate( inAngle ); } inline void Sphere::scale( double inScalar ) { mCenter->scale( inScalar ); mRadius *= inScalar; } inline GeometricObject3D *Sphere::copy() { Sphere *copiedSphere = new Sphere( this ); return (GeometricObject3D*)copiedSphere; } inline int Sphere::serialize( OutputStream *inOutputStream ) { int numBytesWritten = 0; numBytesWritten += mCenter->serialize( inOutputStream ); unsigned char* doublePointer = (unsigned char *)( &mRadius ); numBytesWritten += inOutputStream->write( doublePointer, sizeof( double ) ); return numBytesWritten; } inline int Sphere::deserialize( InputStream *inInputStream ) { int numBytesRead = 0; numBytesRead += mCenter->deserialize( inInputStream ); unsigned char* doublePointer = (unsigned char *)( &mRadius ); numBytesRead += inInputStream->read( doublePointer, sizeof( double ) ); return numBytesRead; } #endif transcend-0.3+dfsg2.orig/minorGems/math/probability/0000750000175000017500000000000010305077063021136 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/math/probability/ProbabilityMassFunction.h0000640000175000017500000002034707376065743026150 0ustar pabspabs/* * Modification History * * 2001-November-17 Jason Rohrer * Created. * Added a constructor for explicit initialization of elements. * Made serializable. * * 2001-November-18 Jason Rohrer * Added function for finding the minimum probability value. * Added a print function. */ #ifndef PROBABILITY_MASS_FUNCTION_INCLUDED #define PROBABILITY_MASS_FUNCTION_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/io/Serializable.h" /** * A discrete probability distribution. * All probabilities are constrained to sum to 1. * * @author Jason Rohrer */ class ProbabilityMassFunction : public Serializable { public: /** * Constructs a probability mass function. * * @param inRandSource the random source to use when * sampling from this function. * Must be destroyed by caller after this class is destroyed. */ ProbabilityMassFunction( RandomSource *inRandSource ); /** * Constructs a probability mass function with elements * already inserted. * * @param inRandSource the random source to use when * sampling from this function. * Must be destroyed by caller after this class is destroyed. * @param inNumElements the number of elements * this PMF should be initialized with. * @param inProbabilities the probabilities to start with. * Note that these values will be normalized before * being stored. * Will be destroyed by this constructor. */ ProbabilityMassFunction( RandomSource *inRandSource, int inNumElements, double *inProbabilities ); virtual ~ProbabilityMassFunction(); /** * Adds an element and a probability to * this mass function. * * Note that since the distribution * always sums to 1, inProbability will * be normalized and thus the value set will likely * not exactly equal the value specified by the caller. * * @param inProbability the probability of the new element. * * @return the index of the new element in the distribution/ * Note that the actual index of this element * may change in the future as elements are added to * and removed from this mass function. */ virtual unsigned long addElement( double inProbability ); /** * Removes an element from this mass function. * * @param inIndex the index of the element. */ virtual void removeElement( unsigned long inIndex ); /** * Gets the probability of an element. * * @param inIndex the index of the element. * * @return the probability of the element at inIndex, * or 0 if inIndex is not a valid index. */ virtual double getProbability( unsigned long inIndex ); /** * Sets the probability of an element. * * Note that since the distribution * always sums to 1, inProbability will * be normalized and thus the value set will likely * not exactly equal the value specified by the caller. * * @param inIndex the index of the element. * @param inProbability the probability of the element at inIndex. */ virtual void setProbability( unsigned long inIndex, double inProbability ); /** * Samples an element at random according to this distribution. * * @return the index of the sampled element. */ virtual unsigned long sampleElement(); /** * Gets the minimum probability value of this function. * * @return the minimum probability value. */ virtual double getMinimumProbability(); /** * Prints the probabilities in this mass function to standard out. */ virtual void print(); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: RandomSource *mRandSource; SimpleVector< double > *mProbabilityVector; /** * Normalizes this distribution so that it sums to 1. */ virtual void normalize(); /** * Gets the net sum of this probability distribution. * * @return the sum of this distribution. */ virtual double getProbabilitySum(); }; inline ProbabilityMassFunction::ProbabilityMassFunction( RandomSource *inRandSource ) : mRandSource( inRandSource ), mProbabilityVector( new SimpleVector< double >() ) { } inline ProbabilityMassFunction::ProbabilityMassFunction( RandomSource *inRandSource, int inNumElements, double *inProbabilities ) : mRandSource( inRandSource ), mProbabilityVector( new SimpleVector< double >( inNumElements ) ){ for( int i=0; ipush_back( inProbabilities[i] ); } normalize(); } inline ProbabilityMassFunction::~ProbabilityMassFunction() { // we can simply delete this vector, since its elements are primitives delete mProbabilityVector; } inline unsigned long ProbabilityMassFunction::addElement( double inProbability ) { mProbabilityVector->push_back( inProbability ); normalize(); return mProbabilityVector->size(); } inline void ProbabilityMassFunction::removeElement( unsigned long inIndex ) { mProbabilityVector->deleteElement( (int)inIndex ); normalize(); } inline double ProbabilityMassFunction::getProbability( unsigned long inIndex ) { if( inIndex >= 0 && inIndex < mProbabilityVector->size() ) { return *( mProbabilityVector->getElement( inIndex ) ); } else { return 0; } } inline void ProbabilityMassFunction::setProbability( unsigned long inIndex, double inProbability ) { if( inIndex >= 0 && inIndex < mProbabilityVector->size() ) { *( mProbabilityVector->getElement( inIndex ) ) = inProbability; normalize(); } } inline unsigned long ProbabilityMassFunction::sampleElement() { double randVal = mRandSource->getRandomDouble(); // compute a running CDF value until we surpass randVal double currentCDFVal = 0; int currentIndex = -1; while( currentCDFVal < randVal ) { currentIndex++; double currentProb = *( mProbabilityVector->getElement( currentIndex ) ); currentCDFVal += currentProb; } // when we exit the while loop, we are at the index of the // element whose probability pushed the CDF past randVal if( currentIndex == -1 ) { // randVal was 0 return 0; } else { return currentIndex; } } inline double ProbabilityMassFunction::getMinimumProbability() { // works, since all values <= 1 double minValue = 2; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); if( prob < minValue ) { minValue = prob; } } return minValue; } inline void ProbabilityMassFunction::normalize() { double currentSum = getProbabilitySum(); if( currentSum != 1 ) { double invCurrentSum = 1.0 / currentSum; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ); *prob = (*prob) * invCurrentSum; } double newSum = getProbabilitySum(); } } inline double ProbabilityMassFunction::getProbabilitySum() { double sum = 0; int numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); } return sum; } inline void ProbabilityMassFunction::print() { long numElements = mProbabilityVector->size(); for( int i=0; igetElement( i ) ); printf( "%lf ", prob ); } } inline int ProbabilityMassFunction::serialize( OutputStream *inOutputStream ) { int numBytes = 0; long numElements = mProbabilityVector->size(); numBytes += inOutputStream->writeLong( numElements ); for( int i=0; iwriteDouble( *( mProbabilityVector->getElement( i ) ) ); } return numBytes; } inline int ProbabilityMassFunction::deserialize( InputStream *inInputStream ) { int numBytes = 0; delete mProbabilityVector; long numElements; numBytes += inInputStream->readLong( &numElements ); mProbabilityVector = new SimpleVector< double >( numElements ); for( int i=0; ireadDouble( &prob ); mProbabilityVector->push_back( prob ); } normalize(); return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/math/BigInt.cpp0000640000175000017500000003336107474074412020515 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * Changed to remove extra zeros when subtracting. */ #include "BigInt.h" #include "minorGems/util/stringUtils.h" #include #include BigInt::BigInt( int inSign, int inNumBytes, unsigned char *inBytes ) : mSign( inSign ), mNumBytes( inNumBytes ), mBytes( new unsigned char[ inNumBytes ] ) { memcpy( mBytes, inBytes, mNumBytes ); } BigInt::BigInt( int inInt ) { if( inInt > 0 ) { mSign = 1; } else if( inInt == 0 ) { mSign = 0; } else { mSign = -1; // flip sign so conversion works inInt *= -1; } mNumBytes = 4; int bytesToSkip = 0; int baseFactor = 1; mBytes = new unsigned char[ 4 ]; int i; for( i=mNumBytes-1; i>=mNumBytes-4; i-- ) { mBytes[i] = 0xFF & ( inInt / baseFactor ); baseFactor *= 256; } if( mBytes[0] == 0 ) { bytesToSkip++; if( mBytes[1] == 0 ) { bytesToSkip++; if( mBytes[2] == 0 ) { bytesToSkip++; if( mBytes[3] == 0 ) { bytesToSkip++; } } } } mNumBytes -= bytesToSkip; unsigned char *temp = new unsigned char[ mNumBytes ]; memcpy( temp, &( mBytes[ bytesToSkip ] ), mNumBytes ); delete [] mBytes; mBytes = temp; } BigInt::~BigInt() { delete [] mBytes; } BigInt *BigInt::add( BigInt *inOtherInt ) { //char *stringA = convertToHexString(); //char *stringB = inOtherInt->convertToHexString(); //printf( "Adding %s to %s\n", stringA, stringB ); //delete [] stringA; //delete [] stringB; if( mSign == 0 && inOtherInt->mSign == 0 ) { return getZero(); } else if( mSign == 0 && inOtherInt->mSign != 0 ) { return inOtherInt->copy(); } else if( mSign != 0 && inOtherInt->mSign == 0 ) { return copy(); } else if( mSign == -1 && inOtherInt->mSign == -1 ) { // adding two negatives // add them as positives, then flip sign of result BigInt *copyA = copy(); BigInt *copyB = inOtherInt->copy(); copyA->mSign = 1; copyB->mSign = 1; BigInt *result = copyA->add( copyB ); delete copyA; delete copyB; result->mSign = -1; return result; } else if( mSign == 1 && inOtherInt->mSign == -1 ) { // flip other sign and subtract BigInt *copy = inOtherInt->copy(); copy->mSign = 1; BigInt *result = subtract( copy ); delete copy; return result; } else if( mSign == -1 && inOtherInt->mSign == 1 ) { // flip our sign and subtract BigInt *copy = this->copy(); copy->mSign = 1; BigInt *result = inOtherInt->subtract( copy ); delete copy; return result; } else { // both this int and arg int are positive int maxLength = mNumBytes; if( maxLength < inOtherInt->mNumBytes ) { maxLength = inOtherInt->mNumBytes; } // leave room for carry unsigned char *result = new unsigned char[ maxLength + 1 ]; // flip to little-endian order to make adding easier BigInt *intA = flipByteOrder(); BigInt *intB = inOtherInt->flipByteOrder(); int sum = 0; for( int i=0; imNumBytes ) { byteA = intA->mBytes[i]; } if( i < intB->mNumBytes ) { byteB = intB->mBytes[i]; } // add in high-order bits from last sum sum = byteA + byteB + ( sum >> 8 ); // truncate to low-order bits result[i] = (unsigned char)sum; } // stick remaining carry into last byte result[maxLength] = (unsigned char)( sum >> 8 ); BigInt *resultInt; if( result[maxLength] == 0 ) { // result array will be copied skipping carry byte resultInt = new BigInt( 1, maxLength, result ); } else { // keep carry byte resultInt = new BigInt( 1, maxLength + 1, result ); } BigInt *flippedResult = resultInt->flipByteOrder(); delete intA; delete intB; delete resultInt; delete [] result; return flippedResult; } } BigInt *BigInt::subtract( BigInt *inOtherInt ) { //char *stringA = convertToHexString(); //char *stringB = inOtherInt->convertToHexString(); //printf( "Subtracting %s from %s\n", stringB, stringA ); //delete [] stringA; //delete [] stringB; if( mSign == 0 && inOtherInt->mSign == 0 ) { return getZero(); } else if( mSign != 0 && inOtherInt->mSign == 0 ) { return copy(); } else if( mSign == 0 && inOtherInt->mSign != 0 ) { BigInt *copy = inOtherInt->copy(); // flip sign copy->mSign = -( copy->mSign ); return copy; } else if( isEqualTo( inOtherInt ) ) { return getZero(); } else if( inOtherInt->mSign == -1 ) { // flip sign and add BigInt *copy = inOtherInt->copy(); copy->mSign = 1; BigInt *result = add( copy ); delete copy; return result; } else if( mSign == -1 ) { // flip our sign and add BigInt *copy = this->copy(); copy->mSign = 1; BigInt *result = inOtherInt->add( copy ); delete copy; // flip sign of result result->mSign = -1; return result; } else if( isLessThan( inOtherInt ) ) { // trying to subtract a larger number // flip subtraction order, then flip sign of result BigInt *result = inOtherInt->subtract( this ); result->mSign = -( result->mSign ); return result; } else { // we're subtracting a smaller positive number from // a larger positive number int maxLength = mNumBytes; if( maxLength < inOtherInt->mNumBytes ) { maxLength = inOtherInt->mNumBytes; } // result cannot be longer than the larger number unsigned char *result = new unsigned char[ maxLength ]; // flip to little-endian order to make subtracting easier BigInt *intA = this->flipByteOrder(); BigInt *intB = inOtherInt->flipByteOrder(); int borrow = 0; for( int i=0; imNumBytes ) { byteA = intA->mBytes[i]; } if( i < intB->mNumBytes ) { byteB = intB->mBytes[i]; } int diff = byteA - byteB - borrow; // compute next borrow borrow = 0; while( diff < 0 ) { borrow++; diff += 256; } result[i] = (unsigned char)diff; } BigInt *resultInt; if( borrow == 0 ) { // skip any trailing 0 bytes int bytesToSkip = 0; int index = maxLength - 1; while( index >= 0 && result[index] == 0 ) { bytesToSkip ++; index--; } // result array will be copied skipping borrow byte // and skipping any trailing 0 bytes resultInt = new BigInt( true, maxLength - bytesToSkip, result ); } else { printf( "Error: final borrow not zero: %d\n", borrow ); resultInt = getZero(); } BigInt *flippedResult = resultInt->flipByteOrder(); delete intA; delete intB; delete resultInt; delete [] result; return flippedResult; } } char BigInt::isLessThan( BigInt *inOtherInt ) { if( mSign > inOtherInt->mSign ) { return false; } else if( mSign < inOtherInt->mSign ) { return true; } else { // signs are equal if( mSign == 0 ) { return false; } else if( mSign == -1 && mNumBytes > inOtherInt->mNumBytes ) { return true; } else if( mSign == 1 && mNumBytes > inOtherInt->mNumBytes ) { return false; } else if( mSign == 1 && mNumBytes < inOtherInt->mNumBytes ) { return true; } else if( mSign == -1 && mNumBytes < inOtherInt->mNumBytes ) { return false; } else if( isEqualTo( inOtherInt ) ) { return false; } else { // numbers are the same length but are not equal // start with high-order bytes for( int i=0; i inOtherInt->mBytes[i] ) { if( mSign == 1 ) { return false; } else { return true; } } else if ( mBytes[i] < inOtherInt->mBytes[i] ) { if( mSign == 1 ) { return true; } else { return false; } } } // if we made it here, the numbers are equal... // but we tested for equality above: must be an error printf( "Error: equality test in isLessThan failed\n" ); return false; } } } char BigInt::isEqualTo( BigInt *inOtherInt ) { if( mSign != inOtherInt->mSign ) { return false; } else if( mNumBytes != inOtherInt->mNumBytes ) { return false; } else { // same length, same sign for( int i=0; imBytes[i] ) { // mismatch found return false; } } // no mismatch found return true; } } BigInt *BigInt::copy() { return new BigInt( mSign, mNumBytes, mBytes ); } BigInt *BigInt::getZero() { unsigned char bytes[0]; return new BigInt( 0, 0, bytes ); } /* THIS DOES NOT WORK! char *BigInt::convertToDecimalString() { char *tempString = new char[4]; char *resultString = new char[ mNumBytes * 3 + 1 ]; int stringIndex = 0; for( int i=0; i> 4 ); int lowBits = 0xF & ( currentByte ); resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits ); hexStringIndex++; resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits ); hexStringIndex++; } resultHexString[ hexStringIndex ] = '\0'; return resultHexString; } } int BigInt::convertToInt() { int result = 0; int baseFactor = 1; for( int i=mNumBytes-1; i>=mNumBytes-4; i-- ) { if( i >=0 ) { result += mBytes[i] * baseFactor; } baseFactor *= 256; } return mSign * result; } BigInt *BigInt::flipByteOrder() { BigInt *returnInt = copy(); unsigned char *newBytes = returnInt->mBytes; int newIndex = mNumBytes - 1; for( int i=0; i int main() { unsigned char intA[] = { 100, 200, 99, 23, 89, 100, 233 }; unsigned char intB[] = { 10, 78, 243, 9, 0 }; BigInt *bigIntA = new BigInt( 1, 7, intA ); BigInt *bigIntB = new BigInt( -1, 5, intB ); char *hexA = bigIntA->convertToHexString(); char *hexB = bigIntB->convertToHexString(); printf( "A = %s\n", hexA ); printf( "B = %s\n", hexB ); BigInt *sum = bigIntA->subtract( bigIntB ); char *hexSum = sum->convertToHexString(); printf( "Sum = %s\n", hexSum ); delete [] hexA; delete [] hexB; delete [] hexSum; delete bigIntA; delete bigIntB; delete sum; int c = 0x58B11F; BigInt *intC = new BigInt( c ); char *hexC = intC->convertToHexString(); printf( "C = %s\n", hexC ); int extractedC = intC->convertToInt(); if( extractedC == c ) { printf( "equal\n" ); } else { printf( "not equal\n" ); } delete [] hexC; delete intC; int limit = 300; printf( "Testing pair operations for all pairs in -%d..%d ...\n", limit, limit ); char failed = false; for( int i=-limit; iadd( intJ ); BigInt *intDiff = intI->subtract( intJ ); int sum = i + j; int diff = i - j; if( sum != intSum->convertToInt() ) { printf( "sum test failed for %d, %d\n", i, j ); printf( " real sum = %d, computed sum = %d\n", sum, intSum->convertToInt() ); failed = true; } if( diff != intDiff->convertToInt() ) { printf( "diff test failed for %d, %d\n", i, j ); printf( " real diff = %d, computed diff = %d\n", diff, intDiff->convertToInt() ); failed = true; } if( intI->isLessThan( intJ ) && ( i >= j ) ) { printf( "first less than test failed for %d, %d\n", i, j ); failed = true; } if( intJ->isLessThan( intI ) && ( j >= i ) ) { printf( "second less than test failed for %d, %d\n", i, j ); failed = true; } if( intI->isEqualTo( intJ ) && ( i != j ) ) { printf( "equality test failed for %d, %d\n", i, j ); failed = true; } delete intSum; delete intDiff; delete intJ; } delete intI; } if( !failed ) { printf( "test passed\n" ); } return 0; } transcend-0.3+dfsg2.orig/minorGems/common.h0000640000175000017500000000033707554101512017331 0ustar pabspabs/* * Modification History * * 2002-October-18 Jason Rohrer * Created. */ #ifndef MINOR_GEMS_COMMON_INCLUDED #define MINOR_GEMS_COMMON_INCLUDED #include "minorGems/util/development/memory/debugMemory.h" #endif transcend-0.3+dfsg2.orig/minorGems/build/0000750000175000017500000000000010305077061016762 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/build/Makefile.minorGems0000640000175000017500000002707410212051510022360 0ustar pabspabs# # Modification History # # 2004-November-19 Jason Rohrer # Copied from Primrose source. # # 2004-November-21 Jason Rohrer # Added multi-source downloader. # # 2004-December-13 Jason Rohrer # Added socket manager. # # 2005-February-4 Jason Rohrer # Added ScreenGL. # # 2005-February-21 Jason Rohrer # Added SingleTextureGL. # ## # The common portion of Makefiles for all targets that use minorGems, # supplying variable definitions for minorGems. # # Should not be made manually---used by project-specific configure scripts to # build Makefiles. ## # This file is intended to be included in another makefile. # The following variables need to be defined: # # ROOT_PATH the path to the directory containing the minorGems directory # PLATFORM_PATH # PLATFORM # DIRECTORY_PLATFORM_PATH # DIRECTORY_PLATFORM # TIME_PLATFORM_PATH # TIME_PLATFORM # SOCKET_UDP_PLATFORM_PATH # SOCKET_UDP_PLATFORM # Makefile Usage: # -- Include this file # -- Define your project-specific targets and rules # -- Include Makefile.minorGems_targets PLATFORM_DIRECTORY = ${ROOT_PATH}/minorGems/io/file/${DIRECTORY_PLATFORM_PATH}/Directory${DIRECTORY_PLATFORM} PLATFORM_TIME = ${ROOT_PATH}/minorGems/system/${TIME_PLATFORM_PATH}/Time${TIME_PLATFORM} PLATFORM_HOST_ADDRESS = ${ROOT_PATH}/minorGems/network/${PLATFORM_PATH}/HostAddress${PLATFORM} PLATFORM_SOCKET = ${ROOT_PATH}/minorGems/network/${PLATFORM_PATH}/Socket${PLATFORM} PLATFORM_SOCKET_CLIENT = ${ROOT_PATH}/minorGems/network/${PLATFORM_PATH}/SocketClient${PLATFORM} PLATFORM_SOCKET_SERVER = ${ROOT_PATH}/minorGems/network/${PLATFORM_PATH}/SocketServer${PLATFORM} PLATFORM_SOCKET_UDP = ${ROOT_PATH}/minorGems/network/${SOCKET_UDP_PLATFORM_PATH}/SocketUDP${SOCKET_UDP_PLATFORM} PLATFORM_TYPE_IO = ${ROOT_PATH}/minorGems/io/${PLATFORM_PATH}/TypeIO${PLATFORM} PLATFORM_THREAD = ${ROOT_PATH}/minorGems/system/${PLATFORM_PATH}/Thread${PLATFORM} PLATFORM_MUTEX_LOCK = ${ROOT_PATH}/minorGems/system/${PLATFORM_PATH}/MutexLock${PLATFORM} PLATFORM_BINARY_SEMAPHORE = ${ROOT_PATH}/minorGems/system/${PLATFORM_PATH}/BinarySemaphore${PLATFORM} ## # Header, source, and object files. ## SIMPLE_VECTOR_H = ${ROOT_PATH}/minorGems/util/SimpleVector.h OUTPUT_STREAM_H = ${ROOT_PATH}/minorGems/io/OutputStream.h INPUT_STREAM_H = ${ROOT_PATH}/minorGems/io/InputStream.h HOST_ADDRESS_H = ${ROOT_PATH}/minorGems/network/HostAddress.h HOST_ADDRESS_CPP = ${PLATFORM_HOST_ADDRESS}.cpp HOST_ADDRESS_O = ${PLATFORM_HOST_ADDRESS}.o SOCKET_H = ${ROOT_PATH}/minorGems/network/Socket.h SOCKET_CPP = ${PLATFORM_SOCKET}.cpp SOCKET_O = ${PLATFORM_SOCKET}.o SOCKET_CLIENT_H = ${ROOT_PATH}/minorGems/network/SocketClient.h SOCKET_CLIENT_CPP = ${PLATFORM_SOCKET_CLIENT}.cpp SOCKET_CLIENT_O = ${PLATFORM_SOCKET_CLIENT}.o SOCKET_SERVER_H = ${ROOT_PATH}/minorGems/network/SocketServer.h SOCKET_SERVER_CPP = ${PLATFORM_SOCKET_SERVER}.cpp SOCKET_SERVER_O = ${PLATFORM_SOCKET_SERVER}.o SOCKET_UDP_H = ${ROOT_PATH}/minorGems/network/SocketUDP.h SOCKET_UDP_CPP = ${PLATFORM_SOCKET_UDP}.cpp SOCKET_UDP_O = ${PLATFORM_SOCKET_UDP}.o SOCKET_STREAM_H = ${ROOT_PATH}/minorGems/network/SocketStream.h NETWORK_FUNCTION_LOCKS = ${ROOT_PATH}/minorGems/network/NetworkFunctionLocks NETWORK_FUNCTION_LOCKS_H = ${NETWORK_FUNCTION_LOCKS}.h NETWORK_FUNCTION_LOCKS_CPP = ${NETWORK_FUNCTION_LOCKS}.cpp NETWORK_FUNCTION_LOCKS_O = ${NETWORK_FUNCTION_LOCKS}.o SOCKET_MANAGER = ${ROOT_PATH}/minorGems/network/SocketManager SOCKET_MANAGER_H = ${SOCKET_MANAGER}.h SOCKET_MANAGER_CPP = ${SOCKET_MANAGER}.cpp SOCKET_MANAGER_O = ${SOCKET_MANAGER}.o PATH_H = ${ROOT_PATH}/minorGems/io/file/Path.h PATH_CPP = ${ROOT_PATH}/minorGems/io/file/${PLATFORM_PATH}/Path${PLATFORM}.cpp PATH_O = ${ROOT_PATH}/minorGems/io/file/${PLATFORM_PATH}/Path${PLATFORM}.o FILE_H = ${ROOT_PATH}/minorGems/io/file/File.h FILE_OUTPUT_STREAM_H = ${ROOT_PATH}/minorGems/io/file/FileOutputStream.h FILE_INPUT_STREAM_H = ${ROOT_PATH}/minorGems/io/file/FileInputStream.h DIRECTORY_H = ${ROOT_PATH}/minorGems/io/file/Directory.h DIRECTORY_CPP = ${PLATFORM_DIRECTORY}.cpp DIRECTORY_O = ${PLATFORM_DIRECTORY}.o TYPE_IO_H = ${ROOT_PATH}/minorGems/io/TypeIO.h TYPE_IO_CPP = ${PLATFORM_TYPE_IO}.cpp TYPE_IO_O = ${PLATFORM_TYPE_IO}.o TIME_H = ${ROOT_PATH}/minorGems/system/Time.h TIME_CPP = ${PLATFORM_TIME}.cpp TIME_O = ${PLATFORM_TIME}.o THREAD_H = ${ROOT_PATH}/minorGems/system/Thread.h THREAD_CPP = ${PLATFORM_THREAD}.cpp THREAD_O = ${PLATFORM_THREAD}.o MUTEX_LOCK_H = ${ROOT_PATH}/minorGems/system/MutexLock.h MUTEX_LOCK_CPP = ${PLATFORM_MUTEX_LOCK}.cpp MUTEX_LOCK_O = ${PLATFORM_MUTEX_LOCK}.o BINARY_SEMAPHORE_H = ${ROOT_PATH}/minorGems/system/BinarySemaphore.h BINARY_SEMAPHORE_CPP = ${PLATFORM_BINARY_SEMAPHORE}.cpp BINARY_SEMAPHORE_O = ${PLATFORM_BINARY_SEMAPHORE}.o SEMAPHORE_H = ${ROOT_PATH}/minorGems/system/Semaphore.h APP_LOG_H = ${ROOT_PATH}/minorGems/util/log/AppLog.h APP_LOG_CPP = ${ROOT_PATH}/minorGems/util/log/AppLog.cpp APP_LOG_O = ${ROOT_PATH}/minorGems/util/log/AppLog.o PRINT_LOG_H = ${ROOT_PATH}/minorGems/util/log/PrintLog.h PRINT_LOG_CPP = ${ROOT_PATH}/minorGems/util/log/PrintLog.cpp PRINT_LOG_O = ${ROOT_PATH}/minorGems/util/log/PrintLog.o FILE_LOG_H = ${ROOT_PATH}/minorGems/util/log/FileLog.h FILE_LOG_CPP = ${ROOT_PATH}/minorGems/util/log/FileLog.cpp FILE_LOG_O = ${ROOT_PATH}/minorGems/util/log/FileLog.o LOG_H = ${ROOT_PATH}/minorGems/util/log/Log.h LOG_CPP = ${ROOT_PATH}/minorGems/util/log/Log.cpp LOG_O = ${ROOT_PATH}/minorGems/util/log/Log.o PRINT_UTILS_H = ${ROOT_PATH}/minorGems/util/printUtils.h PRINT_UTILS_CPP = ${ROOT_PATH}/minorGems/util/printUtils.cpp PRINT_UTILS_O = ${ROOT_PATH}/minorGems/util/printUtils.o WEB_CLIENT_H = ${ROOT_PATH}/minorGems/network/web/WebClient.h WEB_CLIENT_CPP = ${ROOT_PATH}/minorGems/network/web/WebClient.cpp WEB_CLIENT_O = ${ROOT_PATH}/minorGems/network/web/WebClient.o URL_UTILS_H = ${ROOT_PATH}/minorGems/network/web/URLUtils.h URL_UTILS_CPP = ${ROOT_PATH}/minorGems/network/web/URLUtils.cpp URL_UTILS_O = ${ROOT_PATH}/minorGems/network/web/URLUtils.o MIME_TYPER = ${ROOT_PATH}/minorGems/network/web/MimeTyper MIME_TYPER_H = ${MIME_TYPER}.h MIME_TYPER_CPP = ${MIME_TYPER}.cpp MIME_TYPER_O = ${MIME_TYPER}.o WEB_SERVER_PATH = ${ROOT_PATH}/minorGems/network/web/server WEB_SERVER = ${WEB_SERVER_PATH}/WebServer WEB_SERVER_H = ${WEB_SERVER}.h WEB_SERVER_CPP = ${WEB_SERVER}.cpp WEB_SERVER_O = ${WEB_SERVER}.o REQUEST_HANDLING_THREAD = ${WEB_SERVER_PATH}/RequestHandlingThread REQUEST_HANDLING_THREAD_H = ${REQUEST_HANDLING_THREAD}.h REQUEST_HANDLING_THREAD_CPP = ${REQUEST_HANDLING_THREAD}.cpp REQUEST_HANDLING_THREAD_O = ${REQUEST_HANDLING_THREAD}.o THREAD_HANDLING_THREAD = ${WEB_SERVER_PATH}/ThreadHandlingThread THREAD_HANDLING_THREAD_H = ${THREAD_HANDLING_THREAD}.h THREAD_HANDLING_THREAD_CPP = ${THREAD_HANDLING_THREAD}.cpp THREAD_HANDLING_THREAD_O = ${THREAD_HANDLING_THREAD}.o CONNECTION_PERMISSION_HANDLER = ${WEB_SERVER_PATH}/ConnectionPermissionHandler CONNECTION_PERMISSION_HANDLER_H = ${CONNECTION_PERMISSION_HANDLER}.h CONNECTION_PERMISSION_HANDLER_CPP = ${CONNECTION_PERMISSION_HANDLER}.cpp CONNECTION_PERMISSION_HANDLER_O = ${CONNECTION_PERMISSION_HANDLER}.o STOP_SIGNAL_THREAD = ${ROOT_PATH}/minorGems/system/StopSignalThread STOP_SIGNAL_THREAD_H = ${STOP_SIGNAL_THREAD}.h STOP_SIGNAL_THREAD_CPP = ${STOP_SIGNAL_THREAD}.cpp STOP_SIGNAL_THREAD_O = ${STOP_SIGNAL_THREAD}.o FINISHED_SIGNAL_THREAD = ${ROOT_PATH}/minorGems/system/FinishedSignalThread FINISHED_SIGNAL_THREAD_H = ${FINISHED_SIGNAL_THREAD}.h FINISHED_SIGNAL_THREAD_CPP = ${FINISHED_SIGNAL_THREAD}.cpp FINISHED_SIGNAL_THREAD_O = ${FINISHED_SIGNAL_THREAD}.o FINISHED_SIGNAL_THREAD_MANAGER = ${ROOT_PATH}/minorGems/system/FinishedSignalThreadManager FINISHED_SIGNAL_THREAD_MANAGER_H = ${FINISHED_SIGNAL_THREAD_MANAGER}.h FINISHED_SIGNAL_THREAD_MANAGER_CPP = ${FINISHED_SIGNAL_THREAD_MANAGER}.cpp FINISHED_SIGNAL_THREAD_MANAGER_O = ${FINISHED_SIGNAL_THREAD_MANAGER}.o STRING_BUFFER_OUTPUT_STREAM_H = ${ROOT_PATH}/minorGems/util/StringBufferOutputStream.h STRING_BUFFER_OUTPUT_STREAM_CPP = ${ROOT_PATH}/minorGems/util/StringBufferOutputStream.cpp STRING_BUFFER_OUTPUT_STREAM_O = ${ROOT_PATH}/minorGems/util/StringBufferOutputStream.o XML_UTILS_H = ${ROOT_PATH}/minorGems/formats/xml/XMLUtils.h XML_UTILS_CPP = ${ROOT_PATH}/minorGems/formats/xml/XMLUtils.cpp XML_UTILS_O = ${ROOT_PATH}/minorGems/formats/xml/XMLUtils.o HTML_UTILS_H = ${ROOT_PATH}/minorGems/formats/html/HTMLUtils.h HTML_UTILS_CPP = ${ROOT_PATH}/minorGems/formats/html/HTMLUtils.cpp HTML_UTILS_O = ${ROOT_PATH}/minorGems/formats/html/HTMLUtils.o SETTINGS_MANAGER_H = ${ROOT_PATH}/minorGems/util/SettingsManager.h SETTINGS_MANAGER_CPP = ${ROOT_PATH}/minorGems/util/SettingsManager.cpp SETTINGS_MANAGER_O = ${ROOT_PATH}/minorGems/util/SettingsManager.o TRANSLATION_MANAGER_H = ${ROOT_PATH}/minorGems/util/TranslationManager.h TRANSLATION_MANAGER_CPP = ${ROOT_PATH}/minorGems/util/TranslationManager.cpp TRANSLATION_MANAGER_O = ${ROOT_PATH}/minorGems/util/TranslationManager.o STRING_UTILS = ${ROOT_PATH}/minorGems/util/stringUtils STRING_UTILS_H = ${STRING_UTILS}.h STRING_UTILS_CPP = ${STRING_UTILS}.cpp STRING_UTILS_O = ${STRING_UTILS}.o SHA1 = ${ROOT_PATH}/minorGems/crypto/hashes/sha1 SHA1_H = ${SHA1}.h SHA1_CPP = ${SHA1}.cpp SHA1_O = ${SHA1}.o MEMORY_TRACK_H = ${ROOT_PATH}/minorGems/util/development/memory/MemoryTrack.h MEMORY_TRACK_CPP = ${ROOT_PATH}/minorGems/util/development/memory/MemoryTrack.cpp MEMORY_TRACK_O = ${ROOT_PATH}/minorGems/util/development/memory/MemoryTrack.o DEBUG_MEMORY = ${ROOT_PATH}/minorGems/util/development/memory/debugMemory DEBUG_MEMORY_H = ${DEBUG_MEMORY}.h DEBUG_MEMORY_CPP = ${DEBUG_MEMORY}.cpp DEBUG_MEMORY_O = ${DEBUG_MEMORY}.o # variable pointing to both necessary .o files for memory tracking MEMORY_TRACKER_O = ${MEMORY_TRACK_O} ${DEBUG_MEMORY_O} # p2p parts HOST_CATCHER = ${ROOT_PATH}/minorGems/network/p2pParts/HostCatcher HOST_CATCHER_H = ${HOST_CATCHER}.h HOST_CATCHER_CPP = ${HOST_CATCHER}.cpp HOST_CATCHER_O = ${HOST_CATCHER}.o OUTBOUND_CHANNEL = ${ROOT_PATH}/minorGems/network/p2pParts/OutboundChannel OUTBOUND_CHANNEL_H = ${OUTBOUND_CHANNEL}.h OUTBOUND_CHANNEL_CPP = ${OUTBOUND_CHANNEL}.cpp OUTBOUND_CHANNEL_O = ${OUTBOUND_CHANNEL}.o DUPLICATE_MESSAGE_DETECTOR = ${ROOT_PATH}/minorGems/network/p2pParts/DuplicateMessageDetector DUPLICATE_MESSAGE_DETECTOR_H = ${DUPLICATE_MESSAGE_DETECTOR}.h DUPLICATE_MESSAGE_DETECTOR_CPP = ${DUPLICATE_MESSAGE_DETECTOR}.cpp DUPLICATE_MESSAGE_DETECTOR_O = ${DUPLICATE_MESSAGE_DETECTOR}.o PROTOCOL_UTILS = ${ROOT_PATH}/minorGems/network/p2pParts/protocolUtils PROTOCOL_UTILS_H = ${PROTOCOL_UTILS}.h PROTOCOL_UTILS_CPP = ${PROTOCOL_UTILS}.cpp PROTOCOL_UTILS_O = ${PROTOCOL_UTILS}.o MESSAGE_PER_SECOND_LIMITER = ${ROOT_PATH}/minorGems/network/p2pParts/MessagePerSecondLimiter MESSAGE_PER_SECOND_LIMITER_H = ${MESSAGE_PER_SECOND_LIMITER}.h MESSAGE_PER_SECOND_LIMITER_CPP = ${MESSAGE_PER_SECOND_LIMITER}.cpp MESSAGE_PER_SECOND_LIMITER_O = ${MESSAGE_PER_SECOND_LIMITER}.o MULTI_SOURCE_DOWNLOADER = ${ROOT_PATH}/minorGems/network/p2pParts/MultiSourceDownloader MULTI_SOURCE_DOWNLOADER_H = ${MULTI_SOURCE_DOWNLOADER}.h MULTI_SOURCE_DOWNLOADER_CPP = ${MULTI_SOURCE_DOWNLOADER}.cpp MULTI_SOURCE_DOWNLOADER_O = ${MULTI_SOURCE_DOWNLOADER}.o ENCODING_UTILS = ${ROOT_PATH}/minorGems/formats/encodingUtils ENCODING_UTILS_H = ${ENCODING_UTILS}.h ENCODING_UTILS_CPP = ${ENCODING_UTILS}.cpp ENCODING_UTILS_O = ${ENCODING_UTILS}.o SCREEN_GL = ${ROOT_PATH}/minorGems/graphics/openGL/ScreenGL SCREEN_GL_H = ${SCREEN_GL}.h SCREEN_GL_CPP = ${SCREEN_GL}.cpp SCREEN_GL_O = ${SCREEN_GL}.o SINGLE_TEXTURE_GL = ${ROOT_PATH}/minorGems/graphics/openGL/SingleTextureGL SINGLE_TEXTURE_GL_H = ${SINGLE_TEXTURE_GL}.h SINGLE_TEXTURE_GL_CPP = ${SINGLE_TEXTURE_GL}.cpp SINGLE_TEXTURE_GL_O = ${SINGLE_TEXTURE_GL}.o transcend-0.3+dfsg2.orig/minorGems/build/Makefile.minorGems_targets0000640000175000017500000001156510212051510024107 0ustar pabspabs# # Modification History # # 2004-November-19 Jason Rohrer # Copied from Primrose source. # # 2004-November-21 Jason Rohrer # Added multi-source downloader. # # 2004-December-13 Jason Rohrer # Added socket manager. # # 2005-February-4 Jason Rohrer # Added ScreenGL. # # 2005-February-21 Jason Rohrer # Added SingleTextureGL. # ## # The common portion of Makefiles for all targets that use minorGems, # supplying target dependencies for minorGems targets. # # # Should not be made manually---used by project-specific configure scripts to # build Makefiles. ## # Makefile Usage: # -- Include Makefile.minorGems # -- Define your project-specific targets and rules # -- Include this file ## # Dependencies. ## MINOR_GEMS_SOURCE = \ ${HOST_ADDRESS_CPP} \ ${SOCKET_CPP} \ ${SOCKET_SERVER_CPP} \ ${SOCKET_CLIENT_CPP} \ ${SOCKET_UDP_CPP} \ ${NETWORK_FUNCTION_LOCKS_CPP} \ ${SOCKET_MANAGER_CPP} \ ${PATH_CPP} \ ${DIRECTORY_CPP} \ ${TYPE_IO_CPP} \ ${TIME_CPP} \ ${THREAD_CPP} \ ${MUTEX_LOCK_CPP} \ ${BINARY_SEMAPHORE_CPP} \ ${APP_LOG_CPP} \ ${PRINT_LOG_CPP} \ ${FILE_LOG_CPP} \ ${LOG_CPP} \ ${PRINT_UTILS_CPP} \ ${WEB_CLIENT_CPP} \ ${URL_UTILS_CPP} \ ${MIME_TYPER_CPP} \ ${STRING_BUFFER_OUTPUT_STREAM_CPP} \ ${XML_UTILS_CPP} \ ${HTML_UTILS_CPP} \ ${SETTINGS_MANAGER_CPP} \ ${TRANSLATION_MANAGER_CPP} \ ${STRING_UTILS_CPP} \ ${SHA1_CPP} \ ${MEMORY_TRACK_CPP} \ ${DEBUG_MEMORY_CPP} \ ${HOST_CATCHER_CPP} \ ${OUTBOUND_CHANNEL_CPP} \ ${DUPLICATE_MESSAGE_DETECTOR_CPP} \ ${PROTOCOL_UTILS_CPP} \ ${MESSAGE_PER_SECOND_LIMITER_CPP} \ ${MULTI_SOURCE_DOWNLOADER_CPP} \ ${ENCODING_UTILS_CPP} \ ${WEB_SERVER_CPP} \ ${REQUEST_HANDLING_THREAD_CPP} \ ${THREAD_HANDLING_THREAD_CPP} \ ${CONNECTION_PERMISSION_HANDLER_CPP} \ ${STOP_SIGNAL_THREAD_CPP} \ ${FINISHED_SIGNAL_THREAD_CPP} \ ${FINISHED_SIGNAL_THREAD_MANAGER_CPP} \ ${SCREEN_GL_CPP} \ ${SINGLE_TEXTURE_GL_CPP} # sed command for fixing up the dependencies generated by g++ # g++ (pre-3.0) leaves the path off of the .o target # look for a .o file at the beginning of a line (in other words, one # without a path), and replace it with the full-path version. # This should be compatible with g++ 3.0, since we only replace .o names # that occur at the beginning of a line (using the "^" modifier) MINOR_GEMS_SED_FIX_COMMAND = sed ' \ s/^HostAddress.*\.o/$${HOST_ADDRESS_O}/; \ s/^SocketServer.*\.o/$${SOCKET_SERVER_O}/; \ s/^SocketClient.*\.o/$${SOCKET_CLIENT_O}/; \ s/^SocketUDP.*\.o/$${SOCKET_UDP_O}/; \ s/^SocketManager.*\.o/$${SOCKET_MANAGER_O}/; \ s/^Socket.*\.o/$${SOCKET_O}/; \ s/^NetworkFunctionLocks.*\.o/$${NETWORK_FUNCTION_LOCKS_O}/; \ s/^Path.*\.o/$${PATH_O}/; \ s/^Directory.*\.o/$${DIRECTORY_O}/; \ s/^TypeIO.*\.o/$${TYPE_IO_O}/; \ s/^Time.*\.o/$${TIME_O}/; \ s/^MutexLock.*\.o/$${MUTEX_LOCK_O}/; \ s/^BinarySemaphore.*\.o/$${BINARY_SEMAPHORE_O}/; \ s/^AppLog.*\.o/$${APP_LOG_O}/; \ s/^PrintLog.*\.o/$${PRINT_LOG_O}/; \ s/^FileLog.*\.o/$${FILE_LOG_O}/; \ s/^Log.*\.o/$${LOG_O}/; \ s/^PrintUtils.*\.o/$${PRINT_UTILS_O}/; \ s/^WebClient.*\.o/$${WEB_CLIENT_O}/; \ s/^URLUtils.*\.o/$${URL_UTILS_O}/; \ s/^MimeTyper.*\.o/$${MIME_TYPER_O}/; \ s/^StringBufferOutputStream.*\.o/$${STRING_BUFFER_OUTPUT_STREAM_O}/; \ s/^XMLUtils.*\.o/$${XML_UTILS_O}/; \ s/^HTMLUtils.*\.o/$${HTML_UTILS_O}/; \ s/^SettingsManager.*\.o/$${SETTINGS_MANAGER_O}/; \ s/^TranslationManager.*\.o/$${TRANSLATION_MANAGER_O}/; \ s/^stringUtils.*\.o/$${STRING_UTILS_O}/; \ s/^sha1.*\.o/$${SHA1_O}/; \ s/^MemoryTrack.*\.o/$${MEMORY_TRACK_O}/; \ s/^DebugMemory.*\.o/$${DEBUG_MEMORY_O}/; \ s/^HostCatcher.*\.o/$${HOST_CATCHER_O}/; \ s/^OutboundChannel.*\.o/$${OUTBOUND_CHANNEL_O}/; \ s/^DuplicateMessageDetector.*\.o/$${DUPLICATE_MESSAGE_DETECTOR_O}/; \ s/^protocolUtils.*\.o/$${PROTOCOL_UTILS_O}/; \ s/^MessagePerSecondLimiter.*\.o/$${MESSAGE_PER_SECOND_LIMITER_O}/; \ s/^MultiSourceDownloader.*\.o/$${MULTI_SOURCE_DOWNLOADER_O}/; \ s/^encodingUtils.*\.o/$${ENCODING_UTILS_O}/; \ s/^WebServer.*\.o/$${WEB_SERVER_O }/; \ s/^RequestHandlingThread.*\.o/$${REQUEST_HANDLING_THREAD_O}/; \ s/^ThreadHandlingThread.*\.o/$${THREAD_HANDLING_THREAD_O}/; \ s/^Thread.*\.o/$${THREAD_O}/; \ s/^ConnectionPermissionHandler.*\.o/$${CONNECTION_PERMISSION_HANDLER_O}/; \ s/^StopSignalThread.*\.o/$${STOP_SIGNAL_THREAD_O}/; \ s/^FinishedSignalThreadManager.*\.o/$${FINISHED_SIGNAL_THREAD_MANAGER_O}/; \ s/^FinishedSignalThread.*\.o/$${FINISHED_SIGNAL_THREAD_O}/; \ s/^ScreenGL.*\.o/$${SCREEN_GL_O}/; \ s/^SingleTextureGL.*\.o/$${SINGLE_TEXTURE_GL_O}/; \ ' MINOR_GEMS_DEPENDENCY_FILE = Makefile.minorGems_dependencies # build the dependency file ${MINOR_GEMS_DEPENDENCY_FILE}: ${MINOR_GEMS_SOURCE} rm -f ${MINOR_GEMS_DEPENDENCY_FILE} ${COMPILE} -I${ROOT_PATH} -MM ${MINOR_GEMS_SOURCE} >> ${MINOR_GEMS_DEPENDENCY_FILE}.temp cat ${MINOR_GEMS_DEPENDENCY_FILE}.temp | ${MINOR_GEMS_SED_FIX_COMMAND} >> ${MINOR_GEMS_DEPENDENCY_FILE} rm -f ${MINOR_GEMS_DEPENDENCY_FILE}.temp include ${MINOR_GEMS_DEPENDENCY_FILE} transcend-0.3+dfsg2.orig/minorGems/sound/0000750000175000017500000000000010305077065017017 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/sound/midi/0000750000175000017500000000000010305077065017741 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/sound/midi/pic/0000750000175000017500000000000010305077065020514 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/sound/midi/pic/testMidi.c0000640000175000017500000000357607307545617022471 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. * * 2001-June-5 Jason Rohrer * Changed to trigger notes on an input signal. * Changed to dynamically set note velocity based on * trigger time. * * 2001-June-6 Jason Rohrer * Made note velocity pluck times twice as long. * Added a randomized pitch element. */ #include "midi.h" // avoid local variables char tempI; char noteHoldTime; char tempVelocity; char tempPitch; char betweenNoteTime; /** * Tests midi implementation by sending a stream of notes. * * @author Jason Rohrer */ void main( void ) { midiSetup(); // set B0 to output set_bit( STATUS, RP0 ); // set B0 to output clear_bit( TRISB, 0 ); // set B1 to input set_bit( TRISB, 1 ); clear_bit( STATUS, RP0 ); noteHoldTime = 0; tempVelocity = 0; betweenNoteTime = 0; while( 1 ) { noteHoldTime = 0; // if pin is on if( PORTB & 00000010B ) { turnNoteOff( 0, tempPitch, tempVelocity ); // time how long the pin is on in 2's of ms while( PORTB & 00000010B ) { delay_ms( 2 ); if( noteHoldTime < 127 ) { noteHoldTime++; } } // pin is off // sound a note with a velocity // corresponding to how long the pin was on tempVelocity = 127 - noteHoldTime; // betweenNoteTime contains a rather random // number in 0-255 tempPitch = betweenNoteTime >> 1; turnNoteOn( 0, tempPitch, tempVelocity ); // start counting again betweenNoteTime = 0; } else { // pin is off // this will cycle when it hits 255 // we will be essentially generating // a random number by timing how // long the note is off betweenNoteTime++; // do nothing } /* for( tempI=0; tempI<=127; tempI++ ) { set_bit( PORTB, 0 ); turnNoteOn( 0, tempI, 64 ); delay_s( 1 ); clear_bit( PORTB, 0 ); delay_s( 1 ); turnNoteOff( 0, tempI, 64 ); } */ } } transcend-0.3+dfsg2.orig/minorGems/sound/midi/pic/midi.h0000640000175000017500000000313407307317446021621 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. * * 2001-June-5 Jason Rohrer * Changed back to non-inverted serial output. * This output type has been tested and works with MIDI. */ /** * A collection of midi utility functions. * * Uses C2C serial to send MIDI. Note that * MIDI operates at TTL [0-5] levels, not RS232 [0-10] levels, so * no external MAX-232 chip is needed. * * @author Jason Rohrer */ // settings for software serial // code copied from: // http://www.iptel-now.de/HOWTO/PIC/pic.html //RS232 settings #pragma RS232_TXPORT PORTA #pragma RS232_RXPORT PORTA #pragma RS232_TXPIN 1 #pragma RS232_RXPIN 4 // // BAUD for MIDI is 31.25 Kbps #pragma RS232_BAUD 31250 // inverted bit values for midi #pragma TRUE_RS232 1 //Timing settings #pragma CLOCK_FREQ 10000000 // end copied code /** * Sets up the chip for midi transmission. Must be called * before calling any of the other midi routines. */ void midiSetup(); /** * Turns a note on. * * @param inChannel the channel number, in [0,15] * (which correspond to midi channels 1-16). * @param inKey the key number, in [0,127]. * Middle C is 60. * @param inVelocity the note velocity, in [0,127]. */ void turnNoteOn( char inChannel, char inKey, char inVelocity ); /** * Turns a note off. * * @param inChannel the channel number, in [0,15] * (which correspond to midi channels 1-16). * @param inKey the key number, in [0,127]. * Middle C is 60. * @param inVelocity the note velocity, in [0,127]. */ void turnNoteOff( char inChannel, char inKey, char inVelocity ); transcend-0.3+dfsg2.orig/minorGems/sound/midi/pic/midi.c0000640000175000017500000000323707307057505021615 0ustar pabspabs/* * Modification History * * 2001-June-4 Jason Rohrer * Created. */ #include "midi.h" // avoid local function variables char midiTempCharA; void midiSetup() { // Hardware Initialization for software serial // code copied from: // http://www.iptel-now.de/HOWTO/PIC/pic.html disable_interrupt( GIE ); set_bit( STATUS, RP0 ); // turn on pin 3 for reading, and leave others as output pins set_tris_a( 00010000b ); set_tris_b( 0 ); clear_bit( STATUS, RP0 ); output_port_a( 0 ); output_port_b( 0 ); // end copied code } void turnNoteOn( char inChannel, char inKey, char inVelocity ) { // the on code midiTempCharA = 10010000B; // make sure the channel is in range if( inChannel > 15 ) { return; } // stick the channel into the last 4 bits midiTempCharA = midiTempCharA | inChannel; // make sure the key is in range if( inKey > 127 ) { return; } // make sure the velocity is in range if( inVelocity > 127 ) { return; } // send the channel putchar( midiTempCharA ); // send the key putchar( inKey ); // send the velocity putchar( inVelocity ); } void turnNoteOff( char inChannel, char inKey, char inVelocity ) { // the off code midiTempCharA = 10000000B; // make sure the channel is in range if( inChannel > 15 ) { return; } // stick the channel into the last 4 bits midiTempCharA = midiTempCharA | inChannel; // make sure the key is in range if( inKey > 127 ) { return; } // make sure the velocity is in range if( inVelocity > 127 ) { return; } // send the channel putchar( midiTempCharA ); // send the key putchar( inKey ); // send the velocity putchar( inVelocity ); } transcend-0.3+dfsg2.orig/minorGems/sound/formats/0000750000175000017500000000000010305077065020472 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/sound/formats/aiff.h0000640000175000017500000000144610047466057021564 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #ifndef AIFF_INCLUDED #define AIFF_INCLUDED /** * Constructs an AIFF header. * * @param inNumChannels the number of channels. * @param inSampleSizeInBits the size of each sound sample in bits. * @param inSampleRateInHertz the number of sample frames per second. * @param inNumSampleFrames the total number of sample frames in the file. * @param outHeaderLength pointer to where the length of the header, in bytes, * should be returned. * * @return the header. * Must be destroyed by caller. */ unsigned char *getAIFFHeader( int inNumChannels, int inSampleSizeInBits, int inSampleRateInHertz, int inNumSampleFrames, int *outHeaderLength ); #endif transcend-0.3+dfsg2.orig/minorGems/sound/formats/testAIFF.cpp0000640000175000017500000000451210047466057022614 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #include "aiff.h" #include #include #include "minorGems/util/random/StdRandomSource.h" int main() { int numChannels = 2; int numNotes = 5; int noteLengthInSeconds = 1; int sampleSize = 16; int sampleRate = 44100; int samplesPerNote = noteLengthInSeconds * sampleRate; // generate the header int headerSize; unsigned char *aiffHeader = getAIFFHeader( 2, sampleSize, sampleRate, numNotes * samplesPerNote, &headerSize ); FILE *aiffFile = fopen( "test.aiff", "wb" ); printf( "Header size = %d\n", headerSize ); fwrite( aiffHeader, 1, headerSize, aiffFile ); delete [] aiffHeader; StdRandomSource randSource; for( int i=0; i> 8 & 0xFF; unsigned char lsb = val && 0xFF; // output twice, once for each channel fwrite( &msb, 1, 1, aiffFile ); fwrite( &lsb, 1, 1, aiffFile ); fwrite( &msb, 1, 1, aiffFile ); fwrite( &lsb, 1, 1, aiffFile ); } } fclose( aiffFile ); return 0; } transcend-0.3+dfsg2.orig/minorGems/sound/formats/aiff.cpp0000640000175000017500000000564610047466057022125 0ustar pabspabs/* * Modification History * * 2004-May-9 Jason Rohrer * Created. */ #include "aiff.h" #include "minorGems/util/StringBufferOutputStream.h" unsigned char *getAIFFHeader( int inNumChannels, int inSampleSizeInBits, int inSampleRateInHertz, int inNumSampleFrames, int *outHeaderLength ) { /* Header Information 32 bits 'FORM' 32 bits ckSize 32 bits 'AIFF' 32 bits 'COMM' 32 bits ckSize '18' 16 bits numChannels 32 bits num SampleFrames 16 bits sampleSize '16' 80 bits sampleRate { 16 bits = '16398' 16 bits = '44100' remaining 48 bits = '0' } 32 bits 'SSND' 32 bits ckSize 32 bits offset '0' 32 bits block size '0' FINALLY: sound data #bytes in 'FORM' chunk = bytes in sound data + 46 #bytes in 'SSND' chunk = bytes in sound data + 8 */ int soundSizeInBytes = ( inNumChannels * inNumSampleFrames * inSampleSizeInBits ) / 8; StringBufferOutputStream *headerStream = new StringBufferOutputStream(); headerStream->writeString( "FORM" ); // form chunk ID headerStream->writeLong( 46 + soundSizeInBytes ); // size of form chunk headerStream->writeString( "AIFF" ); // form type headerStream->writeString( "COMM" ); // common chunk ID headerStream->writeLong( 18 ); // common chunk size headerStream->writeShort( inNumChannels ); headerStream->writeLong( inNumSampleFrames ); // number of frames in sound // data headerStream->writeShort( inSampleSizeInBits ); // size of each sample headerStream->writeLong( inSampleRateInHertz | 16398<<16 ); // sample rate in Hz plus // mysterious most significant // bits headerStream->writeLong( 0 ); // 48 bits of 0 padding headerStream->writeShort( 0 ); headerStream->writeString( "SSND" ); // Sound chunk ID headerStream->writeLong( 8 + soundSizeInBytes ); // size of sound chunk headerStream->writeLong( 0 ); // offset headerStream->writeLong( 0 ); // block size unsigned char *returnBuffer = headerStream->getBytes( outHeaderLength ); delete headerStream; return returnBuffer; } transcend-0.3+dfsg2.orig/minorGems/io/0000750000175000017500000000000010305077063016274 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/Serializable.h0000640000175000017500000000304507554101513021056 0ustar pabspabs/* * Modification History * * 2001-January-10 Jason Rohrer * Created. * Added untility functions for converting integers to and from byte arrays. * * 2001-January-15 Jason Rohrer * Made utility functions static. * Fixed a major bug in the longToBytes function. * * 2001-February-3 Jason Rohrer * Removed the long io functions, which are now contained in the * input- and outputStream classes. */ #include "minorGems/common.h" #ifndef SERIALIZABLE_CLASS_INCLUDED #define SERIALIZABLE_CLASS_INCLUDED #include "InputStream.h" #include "OutputStream.h" /** * Interface for an object that can be serialized to and deserialized * from a stream. * * Note that the deserialize function is meant to be called from an already * constructed object (to set object parameters using data read from the * stream), and is not a method of obtaining an object. * * All multi-byte data members should be encoded in using a big endian format. * * @author Jason Rohrer */ class Serializable { public: /** * Writes this object out to a stream. * * @param inOutputStream the stream to write to. * * @return the number of bytes written successfully, * or -1 for a stream error. */ virtual int serialize( OutputStream *inOutputStream ) = 0; /** * Reads this object in from a stream. * * @param inInputStream the stream to read from. * * @return the number of bytes read successfully, * or -1 for a stream error. */ virtual int deserialize( InputStream *inInputStream ) = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/io/file/0000750000175000017500000000000010305077063017213 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/file/unix/0000750000175000017500000000000010305077063020176 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/file/unix/DirectoryUnix.cpp0000640000175000017500000000141207753761036023526 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/io/file/Directory.h" #include char Directory::removeDirectory( File *inFile ) { char *fileName = inFile->getFullFileName(); int result = rmdir( fileName ); delete [] fileName; if( result == 0 ) { return true; } else { return false; } } char Directory::makeDirectory( File *inFile ) { char *stringName = inFile->getFullFileName(); int result = mkdir( stringName, 0xFFFF ); delete [] stringName; if( 0 == result ) { return true; } else { return false; } } transcend-0.3+dfsg2.orig/minorGems/io/file/FileInputStream.h0000640000175000017500000000646707554101513022455 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-April-12 Jason Rohrer * Changed so that File is not destroyed when this stream is destroyed. * * 2001-April-29 Jason Rohrer * Fixed a bug in the use of fread * (num elements and element size swapped). * Fixed a memory leak in the error message handling. */ #include "minorGems/common.h" #ifndef FILE_INPUT_STREAM_CLASS_INCLUDED #define FILE_INPUT_STREAM_CLASS_INCLUDED #include "minorGems/io/InputStream.h" #include "File.h" #include /** * File implementation of an InputStream. * * @author Jason Rohrer */ class FileInputStream : public InputStream { public: /** * Constructs an input stream. * * @param inFile the file to open for reading. * If the file does not exist, all calls to read will fail. * inFile is NOT destroyed when this class is destroyed. */ FileInputStream( File *inFile ); /** * Destroys this stream and closes the file. */ ~FileInputStream(); /** * Gets the file attached to this stream. * * @return the file used by this stream. * Should not be modified or destroyed by caller until after * this class is destroyed. */ File *getFile(); // implementst InputStream interface virtual long read( unsigned char *inBuffer, long inNumBytes ); private: File *mFile; FILE *mUnderlyingFile; }; inline FileInputStream::FileInputStream( File *inFile ) : mFile( inFile ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); mUnderlyingFile = fopen( fileName, "rb" ); if( mUnderlyingFile == NULL ) { // file open failed. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Opening file %s failed.", fileName ); setNewLastError( stringBuffer ); } delete [] fileName; } inline FileInputStream::~FileInputStream() { if( mUnderlyingFile != NULL ) { fclose( mUnderlyingFile ); } } inline File *FileInputStream::getFile() { return mFile; } inline long FileInputStream::read( unsigned char *inBuffer, long inNumBytes ) { if( mUnderlyingFile != NULL ) { long numRead = fread( inBuffer, 1, inNumBytes, mUnderlyingFile ); if( numRead < inNumBytes ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); if( feof( mUnderlyingFile ) ) { // we reached the end of the file. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Reached end of file %s on read.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; } else { // some other kind of error occured char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Reading from file %s failed.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; if( numRead == 0 ) { // a complete read failure return -1; } } } return numRead; } else { // file was not opened properly int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "File %s was not opened properly before reading.", fileName ); setNewLastError( stringBuffer ); return -1; } } #endif transcend-0.3+dfsg2.orig/minorGems/io/file/FileOutputStream.h0000640000175000017500000000643307554101513022647 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-April-12 Jason Rohrer * Changed so that File is not destroyed when this stream is destroyed. * * 2001-April-29 Jason Rohrer * Fixed a bug in the use of fwrite * (num elements and element size swapped). * Fixed a memory leak in the error message handling. */ #include "minorGems/common.h" #ifndef FILE_OUTPUT_STREAM_CLASS_INCLUDED #define FILE_OUTPUT_STREAM_CLASS_INCLUDED #include "minorGems/io/OutputStream.h" #include "File.h" #include /** * File implementation of an OutputStream. * * @author Jason Rohrer */ class FileOutputStream : public OutputStream { public: /** * Constructs an output stream. * * @param inFile the file to open for writing. * If the file does not exist, it will be created. * inFile is NOT destroyed when this class is destroyed. * @param inAppend set to true to append to file. If * file does not exist, file is still created. Defaults * to false. */ FileOutputStream( File *inFile, char inAppend = false ); /** * Destroys this stream and closes the file. */ ~FileOutputStream(); /** * Gets the file attached to this stream. * * @return the file used by this stream. * Should not be modified or destroyed by caller until after * this class is destroyed. */ File *getFile(); // implementst OutputStream interface virtual long write( unsigned char *inBuffer, long inNumBytes ); private: File *mFile; FILE *mUnderlyingFile; }; inline FileOutputStream::FileOutputStream( File *inFile, char inAppend ) : mFile( inFile ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); if( inAppend ) { mUnderlyingFile = fopen( fileName, "ab" ); } else { mUnderlyingFile = fopen( fileName, "wb" ); } if( mUnderlyingFile == NULL ) { // file open failed. char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Opening file %s failed.", fileName ); setNewLastError( stringBuffer ); } delete [] fileName; } inline FileOutputStream::~FileOutputStream() { if( mUnderlyingFile != NULL ) { fclose( mUnderlyingFile ); } } inline File *FileOutputStream::getFile() { return mFile; } inline long FileOutputStream::write( unsigned char *inBuffer, long inNumBytes ) { if( mUnderlyingFile != NULL ) { long numWritten = fwrite( inBuffer, 1, inNumBytes, mUnderlyingFile ); if( numWritten < inNumBytes ) { int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); // some other kind of error occured char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "Writing to file %s failed.", fileName ); setNewLastError( stringBuffer ); delete [] fileName; if( numWritten == 0 ) { // a complete write failure return -1; } } return numWritten; } else { // file was not opened properly int fileNameLength; char *fileName = mFile->getFullFileName( &fileNameLength ); char *stringBuffer = new char[ fileNameLength + 50 ]; sprintf( stringBuffer, "File %s was not opened properly before writing.", fileName ); setNewLastError( stringBuffer ); return -1; } } #endif transcend-0.3+dfsg2.orig/minorGems/io/file/testPath.cpp0000640000175000017500000000112207522316361021514 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. */ #include "Path.h" #include int main() { char *pathString = "/test/this/thing"; printf( "using path string = %s\n", pathString ); printf( "Constructing path.\n" ); Path *path = new Path( pathString ); printf( "Extracting path string.\n" ); char *extractedPathString = path->getPathStringTerminated(); printf( "extracted path string = %s\n", extractedPathString ); delete [] extractedPathString; delete path; return 1; } transcend-0.3+dfsg2.orig/minorGems/io/file/File.h0000640000175000017500000005024110304641041020236 0ustar pabspabs/* * Modification History * * 2001-February-11 Jason Rohrer * Created. * * 2001-February-25 Jason Rohrer * Fixed file name bugs in length and existence functions. * * 2001-May-11 Jason Rohrer * Added a missing include. * * 2001-November-3 Jason Rohrer * Added a function for checking if a file is a directory. * Added a function for getting the child files of a directory. * Added a function for getting a pathless file name. * * 2001-November-13 Jason Rohrer * Made name length parameter optional in constructor. * Made return length parameter optional in name getting functions. * * 2001-November-17 Jason Rohrer * Added a functions for removing a file and for copying a file. * * 2002-March-11 Jason Rohrer * Added destruction comment to getFullFileName(). * * 2002-March-13 Jason Rohrer * Changed mName to be \0-terminated to fix interaction bugs with Path. * Fixed a missing delete. * Added a function for creating a directory. * * 2002-March-31 Jason Rohrer * Fixed some bad syntax. * * 2002-April-6 Jason Rohrer * Replaced use of strdup. * * 2002-April-8 Jason Rohrer * Fixed fopen bug. * * 2002-April-11 Jason Rohrer * Fixed a memory leak. * Fixed a casting error. * * 2002-June-28 Jason Rohrer * Added a function for copying a file class. * * 2002-August-3 Jason Rohrer * Added a function for getting the parent file. * * 2002-August-5 Jason Rohrer * Used an unused error variable. * * 2002-September-11 Jason Rohrer * Added return value to remove. * * 2003-January-27 Jason Rohrer * Added a function for reading file contents. * * 2003-February-3 Jason Rohrer * Added a function for writing a string to a file. * * 2003-March-13 Jason Rohrer * Added a function for getting a child file from a directory. * * 2003-June-2 Jason Rohrer * Fixed parent directory behavior when current file is root directory. * Fixed a bug in getting child files of root directory. * * 2003-November-6 Jason Rohrer * Added function for getting last modification time. * * 2003-November-10 Jason Rohrer * Changed to use platform-dependent makeDirectory function. * * 2004-January-4 Jason Rohrer * Added recursive child file functions. * * 2005-August-29 Jason Rohrer * Fixed an uninitialized variable warning. */ #include "minorGems/common.h" #ifndef FILE_CLASS_INCLUDED #define FILE_CLASS_INCLUDED #include #include #include #include #include "Path.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/stringUtils.h" /** * File interface. Provides access to information about a * file. * * @author Jason Rohrer */ class File { public: /** * Constructs a file. * * @param inPath the path for this file. * Is destroyed when this class is destroyed. * Pass in NULL to specify * no path (the current working directory). * @param inName the name of the file to open. * Must be destroyed by caller if not const. * Copied internally. * @param inNameLength length of the name in chars, * or -1 to use the c-string length of inName * (assuming that inName is \0-terminated). * Defaults to -1. */ File( Path *inPath, char *inName, int inNameLength = -1 ); ~File(); /** * Gets whether this file is a directory. * * @return true iff this file is a directory. */ char isDirectory(); /** * Makes a directory in the location of this file. * * Can only succeed if exists() is false. * * @return true iff directory creation succeeded. */ char makeDirectory(); /** * Gets the files contained in this file if it is a directory. * * @param outNumFiles pointer to where the number of * files will be returned. * * @return an array of files, or NULL if this * file is not a directory, is an empty directory, or doesn't exist. * Must be destroyed by caller if non-NULL. */ File **getChildFiles( int *outNumFiles ); /** * Gets the files contained in this file if it is a directory and * recursively in subdirectories of this file. * * @param inDepthLimit the maximum subdirectory depth to recurse into. * If inDepthLimit is 0, then only child files in this directory * will be returned. * @param outNumFiles pointer to where the number of * files will be returned. * * @return an array of files, or NULL if this * file is not a directory, is an empty directory (or a directory * containing empty subdirectories), or doesn't exist. * Must be destroyed by caller if non-NULL. */ File **getChildFilesRecursive( int inDepthLimit, int *outNumFiles ); /** * Gets a child of this directory. * * @param inChildFileName the name of the child file. * Must be destroyed by caller if non-const. * * @return the child file (even if it does not exist), or NULL if * this file is not a directory. * Must be destroyed by caller if non-NULL. */ File *getChildFile( char *inChildFileName ); /** * Gets the parent directory of this file. * * @return the parent directory of this file. * Must be destroyed by caller. */ File *getParentDirectory(); /** * Gets the length of this file. * * @return the length of this file in bytes. Returns * 0 if the file does not exist. */ long getLength(); /** * Gets whether a file exists. * * @return true if the file exists. */ char exists(); /** * Gets the last modification time of this file. * * @return the modification time in seconds based on the * system clock. Returns 0 if the file does not exist. */ unsigned long getModificationTime(); /** * Removes this file from the disk, if it exists. * * @return true iff the remove succeeded, false if the removal * fails or the file does not exist. */ char remove(); /** * Copies this file object (does not copy the file described by * this object). * * @return a deep copy of this file object. */ File *copy(); /** * Copies the contents of this file into another file. * * @param inDestination the file to copy this file into. * If it exists, it will be overwritten. * If it does not exist, it will be created. * Must be destroyed by caller. * @param inBlockSize the block size to use when copying. * Defaults to blocks of 5000 bytes. */ void copy( File *inDestination, long inBlockSize = 5000 ); /** * Gets the full-path file name sufficient * to access this file from the current working * directory. * * @param outLength pointer to where the name length, in * characters, will be returned. Set to NULL to ignore * the output length. Defaults to NULL. * * @return the full path file name for this file, * in platform-specific form. Must be destroyed by caller. * The returned string is '\0' terminated, but this * extra character is not included in the length. * Must be destroyed by caller. */ char *getFullFileName( int *outLength = NULL ); /** * Gets the pathless name of this file. * * @param outLength pointer to where the name length, in * characters, will be returned. Set to NULL to ignore * the output length. Defaults to NULL. * * @return the name of this file. Must be destroyed by caller. */ char *getFileName( int *outLength = NULL ); /** * Reads the contents of this file. * * @return a \0-terminated string containing the file contents, * or NULL if reading the file into memory failed. * Must be destroyed by caller. */ char *readFileContents(); /** * Writes a string to this file. * * @param inString the \0-terminated string to write. * Must be destroyed by caller if non-const. * * @return true if the file was written to successfully, or * false otherwise. */ char writeToFile( char *inString ); private: Path *mPath; char *mName; int mNameLength; /** * Gets the files contained in this file if it is a directory and * recursively in subdirectories of this file. * * @param inDepthLimit the maximum subdirectory depth to recurse into. * If inDepthLimit is 0, then only child files in this directory * will be returned. * @param inResultVector vector to add the discovered files to. * Must be destroyed by caller. */ void getChildFilesRecursive( int inDepthLimit, SimpleVector *inResultVector ); }; inline File::File( Path *inPath, char *inName, int inNameLength ) : mPath( inPath ), mNameLength( inNameLength ) { if( inNameLength == -1 ) { inNameLength = strlen( inName ); mNameLength = inNameLength; } // copy name internally mName = stringDuplicate( inName ); } inline File::~File() { delete [] mName; if( mPath != NULL ) { delete mPath; } } inline long File::getLength() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return fileInfo.st_size; } else { // file does not exist return 0; } } inline char File::isDirectory() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == -1 ) { return false; } else { return S_ISDIR( fileInfo.st_mode ); } } inline File **File::getChildFiles( int *outNumFiles ) { int length; char *stringName = getFullFileName( &length ); DIR *directory = opendir( stringName ); if( directory != NULL ) { SimpleVector< File* > *fileVector = new SimpleVector< File* >(); struct dirent *entry = readdir( directory ); if( entry == NULL ) { delete fileVector; closedir( directory ); delete [] stringName; *outNumFiles = 0; return NULL; } while( entry != NULL ) { // skip parentdir and thisdir files, if they occur if( strcmp( entry->d_name, "." ) && strcmp( entry->d_name, ".." ) ) { Path *newPath; if( mPath != NULL ) { newPath = mPath->append( mName ); } else { if( Path::isRoot( mName ) ) { // use name as a string path newPath = new Path( mName ); } else { char **folderPathArray = new char*[1]; folderPathArray[0] = mName; // a non-absolute path to this directory's contents int numSteps = 1; char absolute = false; newPath = new Path( folderPathArray, numSteps, absolute ); delete [] folderPathArray; } } // safe to pass d_name in directly because it is copied // internally by the constructor fileVector->push_back( new File( newPath, entry->d_name, strlen( entry->d_name ) ) ); } entry = readdir( directory ); } // now we have a vector full of this directory's files int vectorSize = fileVector->size(); *outNumFiles = vectorSize; if( vectorSize == 0 ) { delete fileVector; closedir( directory ); delete [] stringName; return NULL; } else { File **returnFiles = new File *[vectorSize]; for( int i=0; igetElement( i ) ); } delete fileVector; closedir( directory ); delete [] stringName; return returnFiles; } } else { delete [] stringName; *outNumFiles = 0; return NULL; } } inline File **File::getChildFilesRecursive( int inDepthLimit, int *outNumFiles ) { // create a vector for results SimpleVector *resultVector = new SimpleVector(); // call the recursive function getChildFilesRecursive( inDepthLimit, resultVector ); // extract results from vector File **resultArray = NULL; int numResults = resultVector->size(); if( numResults > 0 ) { resultArray = resultVector->getElementArray(); } delete resultVector; *outNumFiles = numResults; return resultArray; } inline void File::getChildFilesRecursive( int inDepthLimit, SimpleVector *inResultVector ) { // get our child files int numChildren; File **childFiles = getChildFiles( &numChildren ); if( childFiles != NULL ) { // for each child, add it to vector and // recurse into it if it is a directory for( int i=0; ipush_back( child ); if( child->isDirectory() ) { // skip recursion if we have hit our depth limit if( inDepthLimit > 0 ) { // recurse into this subdirectory child->getChildFilesRecursive( inDepthLimit - 1, inResultVector ); } } } delete [] childFiles; } } inline File *File::getChildFile( char *inChildFileName ) { // make sure we are a directory if( !isDirectory() ) { return NULL; } // get a path to this directory Path *newPath; if( mPath != NULL ) { newPath = mPath->append( mName ); } else { char **folderPathArray = new char*[1]; folderPathArray[0] = mName; // a non-absolute path to this directory's contents int numSteps = 1; char absolute = false; newPath = new Path( folderPathArray, numSteps, absolute ); delete [] folderPathArray; } return new File( newPath, inChildFileName ); } inline File *File::getParentDirectory() { if( mPath != NULL ) { char *parentName; Path *parentPath; if( strcmp( mName, ".." ) == 0 ) { // already a parent dir reference // append one more parent dir reference with parentName below parentPath = mPath->append( ".." ); parentName = stringDuplicate( ".." ); } else { // not a parent dir reference, so we can truncate parentPath = mPath->truncate(); parentName = mPath->getLastStep(); } File *parentFile = new File( parentPath, parentName ); delete [] parentName; return parentFile; } else { if( Path::isRoot( mName ) ) { // we are already at the root return new File( NULL, mName ); } else { // append parent dir symbol to path char **parentPathSteps = new char*[1]; parentPathSteps[0] = mName; Path *parentPath = new Path( parentPathSteps, 1, false ); char *parentName = ".."; File *parentFile = new File( parentPath, parentName ); delete [] parentPathSteps; return parentFile; } } } inline char File::exists() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return true; } else { // file does not exist return false; } } inline unsigned long File::getModificationTime() { struct stat fileInfo; // get full file name int length; char *stringName = getFullFileName( &length ); int statError = stat( stringName, &fileInfo ); delete [] stringName; if( statError == 0 ) { return fileInfo.st_mtime; } else { // file does not exist return 0; } } inline char File::remove() { char returnVal = false; if( exists() ) { char *stringName = getFullFileName(); int error = ::remove( stringName ); if( error == 0 ) { returnVal = true; } delete [] stringName; } return returnVal; } inline File *File::copy() { Path *pathCopy = NULL; if( mPath != NULL ) { pathCopy = mPath->copy(); } return new File( pathCopy, mName ); } inline void File::copy( File *inDestination, long inBlockSize ) { char *thisFileName = getFullFileName(); char *destinationFileName = inDestination->getFullFileName(); FILE *thisFile = fopen( thisFileName, "rb" ); FILE *destinationFile = fopen( destinationFileName, "wb" ); long length = getLength(); long bytesCopied = 0; char *buffer = new char[ inBlockSize ]; while( bytesCopied < length ) { long bytesToCopy = inBlockSize; // end of file case if( length - bytesCopied < bytesToCopy ) { bytesToCopy = length - bytesCopied; } fread( buffer, 1, bytesToCopy, thisFile ); fwrite( buffer, 1, bytesToCopy, destinationFile ); bytesCopied += bytesToCopy; } fclose( thisFile ); fclose( destinationFile ); delete [] buffer; delete [] thisFileName; delete [] destinationFileName; } inline char *File::getFileName( int *outLength ) { char *returnName = stringDuplicate( mName ); if( outLength != NULL ) { *outLength = mNameLength; } return returnName; } inline char *File::getFullFileName( int *outLength ) { int length = mNameLength; int pathLength = 0; char *path = NULL; if( mPath != NULL ) { path = mPath->getPathString( &pathLength ); length += pathLength; } // extra character for '\0' termination char *returnString = new char[ length + 1 ]; if( path != NULL ) { memcpy( returnString, path, pathLength ); memcpy( &( returnString[pathLength] ), mName, mNameLength ); delete [] path; } else { // no path, so copy the name directly in memcpy( returnString, mName, mNameLength ); } // terminate the string returnString[ length ] = '\0'; if( outLength != NULL ) { *outLength = length; } return returnString; } #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" inline char *File::readFileContents() { if( exists() ) { int length = getLength(); char *returnString = new char[ length + 1 ]; if( returnString != NULL ) { FileInputStream *input = new FileInputStream( this ); int numRead = input->read( (unsigned char *)returnString, length ); delete input; if( numRead == length ) { returnString[ length ] = '\0'; return returnString; } else { delete [] returnString; return NULL; } } else { // failed to allocate this much memory return NULL; } } else { return NULL; } } inline char File::writeToFile( char *inString ) { long stringLength = strlen( inString ); FileOutputStream *output = new FileOutputStream( this ); long numWritten = output->writeString( inString ); delete output; if( stringLength == numWritten ) { return true; } else { return false; } } #include "Directory.h" inline char File::makeDirectory() { if( exists() ) { return false; } else { return Directory::makeDirectory( this ); } } #endif transcend-0.3+dfsg2.orig/minorGems/io/file/UniversalFileIO.h0000640000175000017500000000572407205611501022371 0ustar pabspabs// Jason Rohrer // UniversalFileIO.h /** * * Object that handles universal file reading and writing * Writes files as big endian, even on little endian machines * * Assumes that longs and floats are 32-bits * * Created 1-12-99 * Mods: * Jason Rohrer 1-30-2000 Fixed fwrite functions to work on little endian machines */ #ifndef UNIVERSAL_FILE_IO_INCLUDED #define UNIVERSAL_FILE_IO_INCLUDED #include class UniversalFileIO { public: UniversalFileIO(); long freadLong( FILE *f ); float freadFloat( FILE *f ); void fwriteLong( FILE *f, long x ); void fwriteFloat( FILE *f, float x ); private: char machineBigEndian; char bytesInLong; char bytesInFloat; }; inline UniversalFileIO::UniversalFileIO() : machineBigEndian( true ), bytesInLong( 4 ), bytesInFloat( 4 ) { // test whether machine is big endian long test = 1; char testChopped = (*(char*)&test); if( testChopped == 1 ) machineBigEndian = false; } inline long UniversalFileIO::freadLong( FILE *f ) { if( machineBigEndian ) { long returnVal; fread((void *)&returnVal, sizeof(long), 1, f); return returnVal; } else { unsigned char *buffer = new unsigned char[bytesInLong]; fread((void *)buffer, sizeof(char), bytesInLong, f); // now put the bytes into a long long returnVal = (long)( buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3] ); delete [] buffer; return returnVal; } } inline float UniversalFileIO::freadFloat( FILE *f ) { if( machineBigEndian ) { float returnVal; fread( (void *)&returnVal, sizeof(float), 1, f ); return returnVal; } else { unsigned char *buffer = new unsigned char[bytesInFloat]; fread( (void *)buffer, sizeof(char), bytesInFloat, f ); // now put the bytes into a long long temp = (long)(buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]); delete [] buffer; return *((float *) &temp); // convert long into a float } } inline void UniversalFileIO::fwriteLong( FILE *f, long x ) { if( machineBigEndian ) { fwrite( (void *)&x, sizeof(long), 1, f ); } else { unsigned char *buffer = new unsigned char[bytesInLong]; buffer[0] = (unsigned char)(x >> 24); // put bytes from long into char buffer buffer[1] = (unsigned char)(x >> 16); buffer[2] = (unsigned char)(x >> 8); buffer[3] = (unsigned char)(x); fwrite( (void *)buffer, sizeof(char), bytesInLong, f ); } } inline void UniversalFileIO::fwriteFloat( FILE *f, float x ) { if( machineBigEndian ) { fwrite( (void *)&x, sizeof(float), 1, f ); } else { unsigned char *buffer = new unsigned char[bytesInFloat]; long temp = *((long*)&x); // convert float into long so that bit-wise ops can be performed buffer[0] = (unsigned char)(temp >> 24); // put bytes from float into char buffer buffer[1] = (unsigned char)(temp >> 16); buffer[2] = (unsigned char)(temp >> 8); buffer[3] = (unsigned char)(temp); fwrite( (void *)buffer, sizeof(char), bytesInFloat, f ); } } #endiftranscend-0.3+dfsg2.orig/minorGems/io/file/Path.h0000640000175000017500000003536210304641041020262 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-May-11 Jason Rohrer * Added a version of getPathString that * returns a '\0' terminated string. * * 2001-September-21 Jason Rohrer * Added a missing include. * * 2001-September-23 Jason Rohrer * Added a copy function. * Made some comments more explicit. * Changed the constructor to allow for const path step strings. * * 2001-November-3 Jason Rohrer * Added a function for appending a string to a path. * Changed the interface to the main constructor. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-April-11 Jason Rohrer * Fixed a variable scoping bug. * * 2002-July-2 Jason Rohrer * Fixed a major memory leak in copy(). * * 2002-August-1 Jason Rohrer * Added support for path truncation. * Added support for parsing platform-dependent path strings. * * 2003-May-29 Jason Rohrer * Fixed a bug when an extra delimeters are at the end of the path. * Fixed a bug when string path consists only of root. * * 2003-June-2 Jason Rohrer * Fixed a bug in absolute path detection. * Added platform-specific functions for root and absolute path detection. * Fixed a memory bug when string path contains root only. * Fixed a path step bug when path is root. * Fixed bugs in truncate and append when non-default root string is used. * * 2005-August-29 Jason Rohrer * Fixed an uninitialized variable warning. */ #include "minorGems/common.h" #ifndef PATH_CLASS_INCLUDED #define PATH_CLASS_INCLUDED #include #include "minorGems/util/stringUtils.h" #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Platform-independent file path interface. Contains * all of path except for file name. Thus, appending * a file name to the path will produce a complete file path. * * E.g., on Linux, file path: * temp/files/ * file name: * test.txt * full path: * temp/files/test.txt * * @author Jason Rohrer */ class Path { public: /** * Constructs a path. * * @param inPathSteps an array of c-strings representing * each step in the path, with no delimeters. * For example, { "temp", "files" } to represent * the linux path temp/files. * Must be destroyed by caller since copied internally. * @param inNumSteps the number of strings in the path. * @param inAbsolute set to true to make this an absolute * path. For example, in Linux, an absolute path * is one that starts with '/', as in /usr/include/. * The effects of inAbsolute vary by platform. * @param inRootString the root string for this path if it * is absolute, or NULL to specify a default root. * Defaults to NULL. * Must be destroyed by caller if non-NULL. */ Path( char **inPathSteps, int inNumSteps, char inAbsolute, char *inRootString = NULL ); /** * Constructs a path by parsing a platform-dependent path string. * * @param inPathSteps a \0-terminated string representing the path. * Must be destroyed by caller. */ Path( char *inPathString ); ~Path(); /** * Returns a complete, platform-dependent string path. * * @param outLength pointer to where the path length, in * characters, will be returned. * * @return a new char array containing the path. Note * that the string is not terminated by '\0'. Must * be destroyed by the caller. */ char *getPathString( int *outLength ); /** * Returns a complete, platform-dependent string path, terminated * bye '\0'. * * @return a new char array containing the path. Note * that the string IS terminated by '\0'. Must * be destroyed by the caller. */ char *getPathStringTerminated(); /** * Gets the platform-specific path delimeter. * * Note that this function is implemented separately for * each supported platform. * * @return the path delimeter. */ static char getDelimeter(); /** * Gets start characters for an absolute path. * * Note that this function is implemented separately for * each supported platform. * * @param outLength pointer to where the string length, in * characters, will be returned. * * @return the absolute path start string characters. For * example, on Linux, this would be the string "/". * Must be destroyed by the caller. */ static char *getAbsoluteRoot( int *outLength ); /** * Gets whether a path string is absolute. * * Note that this function is implemented separately for * each supported platform. * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return true if the string is absolute, or false otherwise. */ static char isAbsolute( char *inPathString ); /** * Extracts the root string from a path string. * * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return the root string, or NULL if inPathString is not * absolute. Must be destroyed by caller if non-NULL. */ static char *extractRoot( char *inPathString ); /** * Gets whether a path string is a root path. * * Note that this function is implemented separately for * each supported platform. For example, on Unix, only "/" * is the root path, while on Windows, both "c:\" and "d:\" might * be root paths. * * @param inPathString the string to check. * Must be destroyed by caller if non-const. * * @return true if the string is a root string, or false otherwise. */ static char isRoot( char *inPathString ); /** * Gets start string for an absolute path. * * @return the absolute path start string in \0-terminated form. * Must be destroyed by the caller. */ static char *getAbsoluteRootString(); /** * Copies this path. * * @return a new path that is a deep copy of this path. */ Path *copy(); /** * Constructs a new path by appending an additional * step onto this path. * * @param inStepString the step to add to this path. * Must be destroyed by caller if non-const. * * @return a new path with the extra step. * Must be destroyed by caller. */ Path *append( char *inStepString ); /** * Constructs a new path by removing the last step from this path. * * @return a new path, or NULL if there is only one step in this path. * Must be destroyed by caller. */ Path *truncate(); /** * Gets the last step in this path. * * @return the last step. Must be destroyed by caller. */ char *getLastStep(); private: char **mPathSteps; int mNumSteps; int *mStepLength; char mAbsolute; // the root string of this path, if it is absolute char *mRootString; }; inline Path::Path( char **inPathSteps, int inNumSteps, char inAbsolute, char *inRootString ) : mNumSteps( inNumSteps ), mAbsolute( inAbsolute ), mRootString( NULL ) { if( inRootString != NULL ) { mRootString = stringDuplicate( inRootString ); } // copy the path steps mPathSteps = new char*[ mNumSteps ]; mStepLength = new int[ mNumSteps ]; for( int i=0; i 1 ) { // don't count tail end delimeters delimCount++; } currentDelimPointer = strstr( &( currentDelimPointer[1] ), delimString ); } // no delimeter at end of path mNumSteps = delimCount + 1; mPathSteps = new char*[ mNumSteps ]; mStepLength = new int[ mNumSteps ]; // now extract the chars between delimeters as path steps currentDelimPointer = strstr( pathRootSkipped, delimString ); int stepIndex = 0; currentDelimPointer[0] = '\0'; mPathSteps[ stepIndex ] = stringDuplicate( pathRootSkipped ); mStepLength[ stepIndex ] = strlen( mPathSteps[ stepIndex ] ); stepIndex++; while( currentDelimPointer != NULL ) { char *nextDelimPointer = strstr( &( currentDelimPointer[1] ), delimString ); if( nextDelimPointer != NULL ) { nextDelimPointer[0] = '\0'; } mPathSteps[ stepIndex ] = stringDuplicate( &( currentDelimPointer[1] ) ); mStepLength[ stepIndex ] = strlen( mPathSteps[ stepIndex ] ); stepIndex++; currentDelimPointer = nextDelimPointer; } } else { // no delimeters if( strlen( pathRootSkipped ) > 0 ) { mNumSteps = 1; mPathSteps = new char*[1]; mPathSteps[0] = stringDuplicate( pathRootSkipped ); mStepLength = new int[1]; mStepLength[0] = strlen( mPathSteps[0] ); } else { // path with root only mNumSteps = 0; mPathSteps = new char*[0]; mStepLength = new int[0]; } } delete [] delimString; delete [] pathStringCopy; } inline Path::~Path() { // delete each step for( int i=0; i= 1 ) { return stringDuplicate( mPathSteps[ mNumSteps - 1 ] ); } else { if( mAbsolute ) { if( mRootString != NULL ) { return stringDuplicate( mRootString ); } else { return getAbsoluteRootString(); } } else { // no path steps and not absolute... return stringDuplicate( "" ); } } } inline char *Path::getAbsoluteRootString() { int rootLength; char *root = getAbsoluteRoot( &rootLength ); char *rootString = new char[ rootLength + 1 ]; strncpy( rootString, root, rootLength ); // strncopy won't add termination if length limit reached rootString[ rootLength ] = '\0'; delete [] root; return rootString; } #endif transcend-0.3+dfsg2.orig/minorGems/io/file/linux/0000750000175000017500000000000010305077063020352 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/file/linux/PathLinux.cpp0000640000175000017500000000220607666762723023017 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-August-1 Jason Rohrer * Added missing length return value. * * 2003-June-2 Jason Rohrer * Added support for new path checking functions. */ #include "minorGems/io/file/Path.h" #include "minorGems/util/stringUtils.h" /* * Linux-specific path implementation. May be compatible * with other posix-complient systems. */ char Path::getDelimeter() { return '/'; } char *Path::getAbsoluteRoot( int *outLength ) { char *returnString = new char[1]; returnString[0] = '/'; *outLength = 1; return returnString; } char Path::isAbsolute( char *inPathString ) { if( inPathString[0] == '/' ) { return true; } else { return false; } } char *Path::extractRoot( char *inPathString ) { if( isAbsolute( inPathString ) ){ return stringDuplicate( "/" ); } else { return NULL; } } char Path::isRoot( char *inPathString ) { if( strcmp( inPathString, "/" ) == 0 ) { return true; } else { return false; } } transcend-0.3+dfsg2.orig/minorGems/io/file/Directory.h0000640000175000017500000000262207753761021021342 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/common.h" #include "minorGems/io/file/File.h" #ifndef DIRECTORY_INCLUDED #define DIRECTORY_INCLUDED /** * Class of static directory functions. * * This class exists because most directory operations are * platform-dependent, and a large body of existing code * depends on a platform-independent File.h. * * @author Jason Rohrer. */ class Directory { public: /** * Removes a directory. * * The directory must be empty for this call to succeed. * * @param inFile the file representing the directory. * Must be destroyed by caller. * * @return true if the directory is removed successfully, or * false otherwise (for example, if the directory is not empy). */ static char removeDirectory( File *inFile ); /** * Makes a directory. * * @param inFile the file representing the directory. * Must be destroyed by caller. * * @return true if the directory is removed successfully, or * false otherwise (for example, if the directory is not empy). */ static char makeDirectory( File *inFile ); }; #endif transcend-0.3+dfsg2.orig/minorGems/io/file/win32/0000750000175000017500000000000011476353073020165 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/file/win32/DirectoryWin32.cpp0000640000175000017500000000140507753761036023466 0ustar pabspabs/* * Modification History * * 2003-January-23 Jason Rohrer * Created. * * 2003-November-10 Jason Rohrer * Added makeDirectory function. */ #include "minorGems/io/file/Directory.h" #include char Directory::removeDirectory( File *inFile ) { char *fileName = inFile->getFullFileName(); int result = _rmdir( fileName ); delete [] fileName; if( result == 0 ) { return true; } else { return false; } } char Directory::makeDirectory( File *inFile ) { char *stringName = inFile->getFullFileName(); int result = mkdir( stringName ); delete [] stringName; if( 0 == result ) { return true; } else { return false; } } transcend-0.3+dfsg2.orig/minorGems/io/file/win32/PathWin32.cpp0000640000175000017500000000321507666767217022431 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Fixed delimeter constants. * * 2001-August-1 Jason Rohrer * Added missing length return value. * * 2003-June-2 Jason Rohrer * Added support for new path checking functions. */ #include "minorGems/io/file/Path.h" #include "minorGems/util/stringUtils.h" /* * Windows-specific path implementation. */ char Path::getDelimeter() { return '\\'; } char *Path::getAbsoluteRoot( int *outLength) { // C:\ is the only root we can generically return char *returnString = new char[3]; returnString[0] = 'C'; returnString[1] = ':'; returnString[2] = '\\'; *outLength = 3; return returnString; } char Path::isAbsolute( char *inPathString ) { // ignore first character, which will be drive letter if( inPathString[1] == ':' && inPathString[2] == '\\' ) { return true; } else { return false; } } char *Path::extractRoot( char *inPathString ) { if( isAbsolute( inPathString ) ){ // copy path, then trim to only three characters char *pathCopy = stringDuplicate( inPathString ); pathCopy[ 3 ] = '\0'; char *trimmedCopy = stringDuplicate( pathCopy ); delete [] pathCopy; return trimmedCopy; } else { return NULL; } } char Path::isRoot( char *inPathString ) { // must be of form "c:\" if( strlen( inPathString ) == 3 && inPathString[1] == ':' && inPathString[2] == '\\' ) { return true; } else { return false; } } transcend-0.3+dfsg2.orig/minorGems/io/file/test/0000750000175000017500000000000010305077063020172 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/file/test/testPath.cpp0000640000175000017500000000112207776046015022502 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. */ #include "Path.h" #include int main() { char *pathString = "/test/this/thing"; printf( "using path string = %s\n", pathString ); printf( "Constructing path.\n" ); Path *path = new Path( pathString ); printf( "Extracting path string.\n" ); char *extractedPathString = path->getPathStringTerminated(); printf( "extracted path string = %s\n", extractedPathString ); delete [] extractedPathString; delete path; return 1; } transcend-0.3+dfsg2.orig/minorGems/io/file/test/testChildFiles.cpp0000640000175000017500000000223107776046015023616 0ustar pabspabs/* * Modification History * * 2004-January-4 Jason Rohrer * Created. */ /** * A test program for the various child file functions in File.h * * @author Jason Rohrer. */ #include "minorGems/io/file/File.h" int main( int inNumArgs, char **inArgs ) { char *fileName = "linux"; if( inNumArgs > 1 ) { fileName = inArgs[1]; } File *testFile = new File( NULL, fileName ); int numChildren; File **childFiles = testFile->getChildFiles( &numChildren ); printf( "child files:\n" ); for( int i=0; igetFullFileName(); printf( " %s\n", fullName ); delete [] fullName; delete childFiles[i]; } delete [] childFiles; childFiles = testFile->getChildFilesRecursive( 10, &numChildren ); printf( "recursive child files:\n" ); for( int i=0; igetFullFileName(); printf( " %s\n", fullName ); delete [] fullName; delete childFiles[i]; } delete [] childFiles; delete testFile; return 0; } transcend-0.3+dfsg2.orig/minorGems/io/serialPort/0000750000175000017500000000000010305077063020420 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/serialPort/testSerialPort.cpp0000640000175000017500000000101007624272636024116 0ustar pabspabs #include "SerialPort.h" #include int main() { printf( "Constructing serial port.\n" ); SerialPort *port = new SerialPort( 4800, SerialPort::PARITY_NONE, 8, 1 ); char *line = port->receiveLine(); // specific to GPS unit port->sendLine( "ASTRAL" ); while( line != NULL ) { printf( "received: %s\n", line ); delete [] line; line = port->receiveLine(); } printf( "Deleting serial port.\n" ); delete port; return 0; } transcend-0.3+dfsg2.orig/minorGems/io/serialPort/linux/0000750000175000017500000000000010305077063021557 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/serialPort/linux/SerialPortLinux.cpp0000640000175000017500000001477507643326107025415 0ustar pabspabs/* * Modification History * * 2003-February-17 Jason Rohrer * Created. * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" // Much of the code in this implementation was copied // from the Serial Programming FAQ, by Gary Frefking // http://en.tldp.org/HOWTO/Serial-Programming-HOWTO/ #include #include #include #include #include #include // baudrate settings are defined in , which is // included by class LinuxSerialPortObject { public: int mFileHandle; struct termios mOldTermIO; }; SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { int fileHandle = open( "/dev/ttyS0", O_RDWR | O_NOCTTY ); if( fileHandle < 0 ) { mNativeObjectPointer = NULL; } else { LinuxSerialPortObject *serialPortObject = new LinuxSerialPortObject(); serialPortObject->mFileHandle = fileHandle; mNativeObjectPointer = (void *)serialPortObject; struct termios newTermIO; //save current serial port settings tcgetattr( fileHandle, &( serialPortObject->mOldTermIO ) ); // clear struct for new port settings bzero( &newTermIO, sizeof( newTermIO ) ); unsigned int baudRate; switch( inBaud ) { case 1200: baudRate = B1200; break; case 2400: baudRate = B2400; break; case 4800: baudRate = B4800; break; case 9600: baudRate = B9600; break; case 19200: baudRate = B19200; break; case 38400: baudRate = B38400; break; case 57600: baudRate = B57600; break; default: break; } unsigned int parity = 0; switch( inParity ) { case SerialPort::PARITY_NONE: // disable parity parity = 0; break; case SerialPort::PARITY_EVEN: // enable parity, defaults to even parity = PARENB; break; case SerialPort::PARITY_ODD: // enable parity, and set to odd parity = PARENB | PARODD; break; default: break; } unsigned int dataBits = 0; switch( inDataBits ) { case 5: dataBits = CS5; break; case 6: dataBits = CS6; break; case 7: dataBits = CS7; break; case 8: dataBits = CS8; break; default: break; } unsigned int stopBits = 0; switch( inStopBits ) { case 1: stopBits = 0; break; case 2: stopBits = CSTOPB; break; default: break; } newTermIO.c_cflag = baudRate | parity | dataBits | stopBits | CLOCAL | CREAD; /* IGNPAR : ignore bytes with parity errors ICRNL : map CR to NL (otherwise a CR input on the other computer will not terminate input) IGNCR : ignore CR, so only one line is read when other machine sends CRLF otherwise make device raw (no other input processing) */ newTermIO.c_iflag = IGNPAR | ICRNL | IGNCR; /* Raw output. */ newTermIO.c_oflag = 0; /* ICANON : enable canonical input disable all echo functionality, and don't send signals to calling program */ newTermIO.c_lflag = ICANON; /* now clean the modem line and activate the settings for the port */ tcflush( fileHandle, TCIFLUSH ); tcsetattr( fileHandle, TCSANOW, &newTermIO ); } } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; tcsetattr( fileHandle, TCSANOW, &( serialPortObject->mOldTermIO ) ); delete serialPortObject; } } int SerialPort::sendLine( char *inLine ) { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; int stringLength = strlen( inLine ); int numWritten = write( fileHandle, inLine, stringLength ); char *endLineString = "\n"; int numWrittenEndLine = write( fileHandle, endLineString, 1 ); if( numWritten == stringLength && numWrittenEndLine == 1 ) { return 1; } else { return -1; } } else { return -1; } } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; char *buffer = new char[500]; int numRead = read( fileHandle, buffer, 500 ); char *returnString; if( numRead != -1 ) { buffer[ numRead ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } void SerialPort::dumpReceiveBuffer() { if( mNativeObjectPointer != NULL ) { LinuxSerialPortObject *serialPortObject = (LinuxSerialPortObject *)mNativeObjectPointer; int fileHandle = serialPortObject->mFileHandle; // from man page: // flushes data received but not read tcflush( fileHandle, TCIFLUSH ); } } transcend-0.3+dfsg2.orig/minorGems/io/serialPort/SerialPortFromFile.cpp0000640000175000017500000000266207641141423024644 0ustar pabspabs/* * Modification History * * 2003-March-28 Jason Rohrer * Created. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" #include SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { FILE *file = fopen( "gpscap.txt", "r" ); mNativeObjectPointer = file; } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { FILE *file = (FILE *)mNativeObjectPointer; fclose( file ); } } int SerialPort::sendLine( char *inLine ) { return 1; } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { FILE *file = (FILE *)mNativeObjectPointer; char *buffer = new char[500]; // read up to first newline int index = 0; char lastCharRead = (char)getc( file ); while( lastCharRead != '\n' && index < 499 ) { buffer[index] = lastCharRead; lastCharRead = (char)getc( file ); index++; } char *returnString; if( index > 0 ) { buffer[ index ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } transcend-0.3+dfsg2.orig/minorGems/io/serialPort/win32/0000750000175000017500000000000010305077063021362 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/serialPort/win32/COMPort.h0000640000175000017500000000672307643326110023030 0ustar pabspabs/* * Modification History * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ //============================================================================= // General component library for WIN32 // Copyright (C) 2000, UAB BBDSoft ( http://www.bbdsoft.com/ ) // // This material is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // Permission to use or copy this software for any purpose is hereby granted // without fee, provided the above notices are retained on all copies. // Permission to modify the code and to distribute modified code is granted, // provided the above notices are retained, and a notice that the code was // modified is included with the above copyright notice. // // The author of this program may be contacted at developers@bbdsoft.com //============================================================================= #ifndef _COMPORT_ #define _COMPORT_ //----------------------------------------------------------------------------- class COMPort { public: enum Parity { None = 0 , Odd , Even , Mark , Space }; enum DataBits { db4 = 4 , db5 , db6 , db7 , db8 }; enum StopBits { sb1 = 0, sb15, sb2 }; enum BitRate { br110 = 110, br300 = 300, br600 = 600, br1200 = 1200, br2400 = 2400, br4800 = 4800, br9600 = 9600, br19200 = 19200, br38400 = 38400, br56000 = 56000, br57600 = 57600, br115200 = 115200, br256000 = 256000 }; // for function getModemSignals struct MSPack { unsigned char DTR : 1; unsigned char RTS : 1; unsigned char : 2; unsigned char CTS : 1; unsigned char DSR : 1; unsigned char RI : 1; unsigned char DCD : 1; }; COMPort ( const char * const portName ); ~COMPort (); // I/O operations char read (); COMPort & write (const char inChar); unsigned long read ( void * , const unsigned long count ); unsigned long write ( const void * , const unsigned long count ); // dumps unread characters in the receive buffer void dumpReceiveBuffer(); COMPort& setBitRate ( unsigned long Param ); unsigned long bitRate () const; COMPort& setParity ( Parity Param ); Parity parity () const; COMPort& setDataBits ( DataBits Param ); DataBits dataBits () const; COMPort& setStopBits ( StopBits Param ); StopBits stopBits () const; COMPort & setHandshaking ( bool inHandshaking = true ); COMPort& setLineCharacteristics ( char * Param ); unsigned long getMaximumBitRate () const; COMPort & setxONxOFF ( bool Param = true); bool isxONxOFF () const; MSPack getModemSignals () const; COMPort& setBlockingMode ( unsigned long inReadInterval = 0 , unsigned long inReadMultiplyer = 0 , unsigned long inReadConstant = 0 ); protected: private: // disable copy constructor and assignment operator COMPort (const COMPort &); COMPort& operator= (const COMPort &); void getState () const; COMPort& setState (); unsigned thePortHandle; char * theDCB; }; // End of COMPort class declaration #endif transcend-0.3+dfsg2.orig/minorGems/io/serialPort/win32/SerialPortWin32.cpp0000640000175000017500000001036507643326111025005 0ustar pabspabs/* * Modification History * * 2003-March-24 Jason Rohrer * Created. * * 2003-March-26 Jason Rohrer * Fixed a line parsing bug (lines end with newline, not CR). * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #include "minorGems/io/serialPort/SerialPort.h" #include "minorGems/util/stringUtils.h" // For now, we are using BBDSoft's COM port code #include "COMPort.h" SerialPort::SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ) { COMPort *port = new COMPort( "COM1" ); port->setHandshaking( false ); unsigned long baudRate = COMPort::br2400; switch( inBaud ) { case 1200: baudRate = COMPort::br1200; break; case 2400: baudRate = COMPort::br2400; break; case 4800: baudRate = COMPort::br4800; break; case 9600: baudRate = COMPort::br9600; break; case 19200: baudRate = COMPort::br19200; break; case 38400: baudRate = COMPort::br38400; break; case 57600: baudRate = COMPort::br57600; break; default: break; } port->setBitRate( baudRate ); switch( inParity ) { case SerialPort::PARITY_NONE: port->setParity( COMPort::None ); break; case SerialPort::PARITY_EVEN: port->setParity( COMPort::Even ); break; case SerialPort::PARITY_ODD: port->setParity( COMPort::Odd ); break; default: port->setParity( COMPort::None ); break; } switch( inDataBits ) { case 5: port->setDataBits( COMPort::db5 ); break; case 6: port->setDataBits( COMPort::db6 ); break; case 7: port->setDataBits( COMPort::db7 ); break; case 8: port->setDataBits( COMPort::db8 ); break; default: port->setDataBits( COMPort::db8 ); break; } switch( inStopBits ) { case 1: port->setStopBits( COMPort::sb1 ); break; case 2: port->setStopBits( COMPort::sb2 ); break; default: port->setStopBits( COMPort::sb1 ); break; } mNativeObjectPointer = (void *)port; } SerialPort::~SerialPort() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; delete port; } } int SerialPort::sendLine( char *inLine ) { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; int stringLength = strlen( inLine ); int numWritten = port->write( (void *)inLine, stringLength ); char *endLineString = "\n"; int numWrittenEndLine = port->write( (void *)endLineString, 1 ); if( numWritten == stringLength && numWrittenEndLine == 1 ) { return 1; } else { return -1; } } else { return -1; } } char *SerialPort::receiveLine() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; char *buffer = new char[500]; // read up to first newline int index = 0; char lastCharRead = port->read(); while( lastCharRead != '\n' && index < 499 ) { buffer[index] = lastCharRead; lastCharRead = port->read(); index++; } char *returnString; if( index > 0 ) { buffer[ index ] = '\0'; returnString = stringDuplicate( buffer ); } else { returnString = NULL; } delete [] buffer; return returnString; } else { return NULL; } } void SerialPort::dumpReceiveBuffer() { if( mNativeObjectPointer != NULL ) { COMPort *port = (COMPort *)mNativeObjectPointer; port->dumpReceiveBuffer(); } } transcend-0.3+dfsg2.orig/minorGems/io/serialPort/win32/COMPort.cpp0000640000175000017500000002471107643367544023400 0ustar pabspabs/* * Modification History * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ //============================================================================= // General component library for WIN32 // Copyright (C) 2000, UAB BBDSoft ( http://www.bbdsoft.com/ ) // // This material is provided "as is", with absolutely no warranty expressed // or implied. Any use is at your own risk. // // Permission to use or copy this software for any purpose is hereby granted // without fee, provided the above notices are retained on all copies. // Permission to modify the code and to distribute modified code is granted, // provided the above notices are retained, and a notice that the code was // modified is included with the above copyright notice. // // The author of this program may be contacted at developers@bbdsoft.com //============================================================================= #ifndef _COMPORT_ #include "ComPort.h" #endif #ifndef _WINDOWS_ #define WIN32_LEAN_AND_MEAN #include #endif #ifndef _STDEXCEPT_ #include #endif using namespace std; //---------------------------------------------------------------------------- COMPort::COMPort ( const char * const portName ) : theDCB (NULL) { thePortHandle = (unsigned ) CreateFile ( portName , GENERIC_READ | GENERIC_WRITE , 0 , NULL , OPEN_EXISTING , FILE_FLAG_NO_BUFFERING , NULL ); if (thePortHandle == HFILE_ERROR) { throw runtime_error ("COMPort: failed to open."); } // endif theDCB = new char [sizeof(DCB)]; getState(); setBlockingMode(); setHandshaking(); } // end constructor //---------------------------------------------------------------------------- COMPort::~COMPort() { delete [] theDCB; // close serial port device if (CloseHandle ((HANDLE)thePortHandle) == FALSE ) { throw runtime_error ("COMPort: failed to close."); } // endif } // end destructor //---------------------------------------------------------------------------- void COMPort::getState () const { if (!GetCommState ( (HANDLE) thePortHandle , (LPDCB) theDCB ) ) { throw runtime_error ("COMPort: could not retrieve serial port state."); } // endif } // end COMPort::getState () const //---------------------------------------------------------------------------- COMPort& COMPort::setState () { if (!SetCommState ( (HANDLE) thePortHandle , (LPDCB) theDCB ) ) { throw runtime_error ("COMPort: could not modify serial port state."); } // endif return *this; } // end COMPort::setState () //----------------------------------------------------------------------------- COMPort& COMPort::setBitRate ( unsigned long Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.BaudRate = Param; return setState(); } // end COMPort::setBitRate (..) //----------------------------------------------------------------------------- unsigned long COMPort::bitRate() const { DCB & aDCB = *((LPDCB)theDCB); return aDCB.BaudRate; } // end COMPort::bitRate () const //----------------------------------------------------------------------------- COMPort& COMPort::setLineCharacteristics( char * inConfig ) { COMMTIMEOUTS aTimeout; if ( !BuildCommDCBAndTimeouts ( inConfig , (LPDCB)theDCB , &aTimeout ) ) { throw runtime_error ("COMPort: could not set line characteristics."); } // endif if ( ! SetCommTimeouts ( (HANDLE(thePortHandle)) , &aTimeout ) ) { throw runtime_error ("COMPort: could not set line characteristics."); } // endif return setState(); } //---------------------------------------------------------------------------- char COMPort::read () { char buffer; DWORD charsRead = 0; do { if (! ReadFile ( (HANDLE(thePortHandle)) , &buffer , sizeof(char) , &charsRead , NULL ) ) { throw runtime_error ("COMPort: read failed."); } // endif } while ( !charsRead ); return buffer; } // end COMPort::read() //---------------------------------------------------------------------------- void COMPort::dumpReceiveBuffer () { PurgeComm( (HANDLE(thePortHandle)) , PURGE_RXCLEAR ); } // end COMPort::dumpReceiveBuffer() //---------------------------------------------------------------------------- unsigned long COMPort::read ( void *inBuffer , const unsigned long inCharsReq ) { DWORD charsRead = 0; if ( !ReadFile ( (HANDLE(thePortHandle)) , inBuffer , inCharsReq , &charsRead , NULL ) ) { throw runtime_error ("COMPort: read failed."); } // endif return charsRead; } // end COMPort::read (..) //---------------------------------------------------------------------------- COMPort & COMPort::write ( const char inChar ) { char buffer = inChar; DWORD charsWritten = 0; if ( !WriteFile ( (HANDLE(thePortHandle)) , &buffer , sizeof(char) , &charsWritten , NULL ) ) { throw runtime_error ("COMPort: write failed."); } // endif return *this; } // end COMPort::write (..) //---------------------------------------------------------------------------- unsigned long COMPort::write ( const void *inBuffer , const unsigned long inBufSize ) { DWORD charsWritten = 0; if ( !WriteFile ( (HANDLE(thePortHandle)) , inBuffer , inBufSize , &charsWritten , NULL ) ) { throw runtime_error ("COMPort: write failed."); } // endif return charsWritten; } // end COMPort::write() //----------------------------------------------------------------------------- COMPort& COMPort::setxONxOFF ( bool Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.fOutX = Param ? 1 : 0; aDCB.fInX = Param ? 1 : 0; return setState(); } // end COMPort::setxONxOFF (..) //----------------------------------------------------------------------------- bool COMPort::isxONxOFF () const { DCB & aDCB = *((LPDCB)theDCB); return (aDCB.fOutX && aDCB.fInX); } // end COMPort::isxONxOFF () const //---------------------------------------------------------------------------- COMPort& COMPort::setBlockingMode ( unsigned long inReadInterval , unsigned long inReadMultiplyer , unsigned long inReadConstant ) { COMMTIMEOUTS commTimeout; if ( !GetCommTimeouts ( (HANDLE(thePortHandle)) , &commTimeout ) ) { throw runtime_error ("COMPort: failed to retrieve timeouts."); } // endif commTimeout.ReadIntervalTimeout = inReadInterval; if ( inReadInterval==MAXDWORD ) { commTimeout.ReadTotalTimeoutMultiplier = 0; commTimeout.ReadTotalTimeoutConstant = 0; } else { commTimeout.ReadTotalTimeoutMultiplier = inReadMultiplyer; commTimeout.ReadTotalTimeoutConstant = inReadConstant; } // endifelse if ( !SetCommTimeouts ( (HANDLE(thePortHandle)) , &commTimeout ) ) { throw runtime_error ("COMPort: failed to modify timeouts."); } // endif return *this; } // end COMPort::setBlockingMode (..) //----------------------------------------------------------------------------- COMPort & COMPort::setHandshaking ( bool inHandshaking ) { DCB & aDCB = *((LPDCB)theDCB); if (inHandshaking) { aDCB.fOutxCtsFlow = TRUE; aDCB.fOutxDsrFlow = FALSE; aDCB.fRtsControl = RTS_CONTROL_HANDSHAKE; } else { aDCB.fOutxCtsFlow = FALSE; aDCB.fOutxDsrFlow = FALSE; aDCB.fRtsControl = RTS_CONTROL_ENABLE; } // endifelse return setState(); } // end COMPort::setHandshaking (..) //----------------------------------------------------------------------------- unsigned long COMPort::getMaximumBitRate() const { COMMPROP aProp; if ( !GetCommProperties ( (HANDLE)thePortHandle , &aProp ) ) { throw runtime_error ("COMPort: failed to retrieve port properties."); } // endif return aProp.dwMaxBaud; } // end COMPort::getMaximumBitRate () const //----------------------------------------------------------------------------- COMPort::MSPack COMPort::getModemSignals() const { MSPack aPack; // 1 bit - DTR, 2 - bit RTS (output signals) // 4 bit - CTS, 5 bit - DSR, 6 bit - RI, 7 bit - DCD (input signals) if ( !GetCommModemStatus ( (HANDLE)thePortHandle , (LPDWORD)&aPack ) ) { throw runtime_error ("COMPort: failed to retrieve modem signals."); } // endif return aPack; } // end COMPort::getModemSignals () const //----------------------------------------------------------------------------- COMPort& COMPort::setParity ( Parity Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.Parity = Param; return setState(); } // end COMPort::setParity (..) //----------------------------------------------------------------------------- COMPort& COMPort::setDataBits ( DataBits Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.ByteSize = Param; return setState(); } // end COMPort::setDataBits (..) //----------------------------------------------------------------------------- COMPort& COMPort::setStopBits ( StopBits Param ) { DCB & aDCB = *((LPDCB)theDCB); aDCB.StopBits = Param; return setState(); } // end COMPort::setStopBits (..) //----------------------------------------------------------------------------- COMPort::Parity COMPort::parity () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::Parity)aDCB.Parity; } // end COMPort::parity () const //----------------------------------------------------------------------------- COMPort::DataBits COMPort::dataBits () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::DataBits)aDCB.ByteSize; } // end COMPort::dataBits () const //----------------------------------------------------------------------------- COMPort::StopBits COMPort::stopBits () const { DCB & aDCB = *((LPDCB)theDCB); return (COMPort::StopBits)aDCB.StopBits; } // end COMPort::stopBits () cosnt transcend-0.3+dfsg2.orig/minorGems/io/serialPort/testSerialPortCompile0000750000175000017500000000011507624270527024652 0ustar pabspabsg++ -o testSerialPort -I../../.. linux/SerialPortLinux.cpp testSerialPort.cpptranscend-0.3+dfsg2.orig/minorGems/io/serialPort/SerialPort.h0000640000175000017500000000430607643326042022666 0ustar pabspabs/* * Modification History * * 2003-February-17 Jason Rohrer * Created. * * 2003-April-4 Jason Rohrer * Added function for dumping the read buffer. */ #ifndef SERIAL_PORT_INCLUDED #define SERIAL_PORT_INCLUDED /** * Serial port. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class SerialPort { public: static const int PARITY_NONE = 0; static const int PARITY_EVEN = 1; static const int PARITY_ODD = 2; /** * Constructs a serial port. * * @param inBaud the baud rate. * @param inParity the parity, one of SerialPort:: PARITY_NONE, * PARITY_EVEN, or PARITY_ODD. * @param inDataBits the number of data bits, 5, 6, 7, or 8. * @param inStopBits the number of stop bits, 1 or 2. */ SerialPort( int inBaud, int inParity, int inDataBits, int inStopBits ); ~SerialPort(); /** * Sends a line of text through this serial port. * * @param inLine the \0-terminated line of text to send. * Should not contain newline characters. * Must be destroyed by caller if non-const. * * @return 1 if the line was sent successfully, * or -1 for a port error. */ int sendLine( char *inLine ); /** * Receives a line of text from this serial port. * * @return the read line as a \0-terminated string with end of * line characters included, or NULL for a port error. * Must be destroyed by caller if non-NULL. */ char *receiveLine(); /** * Discards all characters in the receive buffer, including * unread characters. * * Can be used to recover from buffer overflow problems. */ void dumpReceiveBuffer(); private: /** * Used for platform-specific implementations. */ void *mNativeObjectPointer; }; #endif transcend-0.3+dfsg2.orig/minorGems/io/InputStream.h0000640000175000017500000000735010047466201020725 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed the "number of bytes read" parameter and return value * to longs. * * 2001-February-3 Jason Rohrer * Added a readDouble function to fix platform-specific double problems. * Also added a readLong function for completeness. Implemented * these functions, making use of the new TypeIO interface. * * 2001-February-12 Jason Rohrer * Changed to subclass Stream. * * 2002-March-9 Jason Rohrer * Added a readByte() function. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * * 2004-May-9 Jason Rohrer * Added support for shorts. */ #include "minorGems/common.h" #ifndef INPUT_STREAM_CLASS_INCLUDED #define INPUT_STREAM_CLASS_INCLUDED #include "Stream.h" #include "TypeIO.h" /** * Interface for a byte input stream. * * @author Jason Rohrer */ class InputStream : public Stream { public: InputStream(); virtual ~InputStream(); /** * Reads bytes from this stream. * * @param inBuffer the buffer where read bytes will be put. * Must be pre-allocated memory space. * @param inNumBytes the number of bytes to read from the stream. * * @return the number of bytes read successfully, * or -1 for a stream error. */ virtual long read( unsigned char *inBuffer, long inNumBytes ) = 0; /** * Reads a byte from this stream. * * @param outByte pointer to where the byte should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readByte( unsigned char *outByte ); /** * Reads a double from the stream in a platform-independent way. * * @param outDouble pointer to where the double should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readDouble( double *outDouble ); /** * Reads a long from the stream in a platform-independent way. * * @param outLong pointer to where the long should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readLong( long *outLong ); /** * Reads a short from the stream in a platform-independent way. * * @param outShort pointer to where the short should be stored. * * @return the number of bytes read successfully, or -1 for a * stream error. */ long readShort( short *outShort ); private: unsigned char *mDoubleBuffer; unsigned char *mLongBuffer; unsigned char *mShortBuffer; unsigned char *mByteBuffer; }; inline InputStream::InputStream() : mDoubleBuffer( new unsigned char[8] ), mLongBuffer( new unsigned char[4] ), mShortBuffer( new unsigned char[2] ), mByteBuffer( new unsigned char[1] ) { } inline InputStream::~InputStream() { delete [] mDoubleBuffer; delete [] mShortBuffer; delete [] mLongBuffer; delete [] mByteBuffer; } inline long InputStream::readByte( unsigned char *outByte ) { int numBytes = read( mByteBuffer, 1 ); *outByte = mByteBuffer[0]; return numBytes; } inline long InputStream::readDouble( double *outDouble ) { int numBytes = read( mDoubleBuffer, 8 ); *outDouble = TypeIO::bytesToDouble( mDoubleBuffer ); return numBytes; } inline long InputStream::readLong( long *outLong ) { int numBytes = read( mLongBuffer, 4 ); *outLong = TypeIO::bytesToLong( mLongBuffer ); return numBytes; } inline long InputStream::readShort( short *outShort ) { int numBytes = read( mShortBuffer, 4 ); *outShort = TypeIO::bytesToShort( mShortBuffer ); return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/io/TypeIO.h0000640000175000017500000000650610047466202017626 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed parameter names to match convention. * * 2003-January-11 Jason Rohrer * Added missing casts. * * 2004-May-9 Jason Rohrer * Added support for shorts. */ #include "minorGems/common.h" #ifndef TYPE_IO_INCLUDED #define TYPE_IO_INCLUDED /** * Interfaces for platform-independent type input and output. * * The specification for the input/output format for types is as follows: * * Types should be output in the order and format that a big-endian Linux * outputs them by default. * * Note that minorGems/numtest.cpp can be used to test how doubles are * stored on a specific platform. * * @author Jason Rohrer */ class TypeIO { public: /** * Converts an 32-bit integer to a byte array in a * platform-independent fashion. * * @param inInt the integer to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void longToBytes( long inInt, unsigned char *outBytes ); /** * Converts a 4-byte array to a 32-bit integer * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the integer represented by the bytes. */ static long bytesToLong( unsigned char *inBytes ); /** * Converts an 16-bit integer to a byte array in a * platform-independent fashion. * * @param inInt the integer to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void shortToBytes( short inInt, unsigned char *outBytes ); /** * Converts a 2-byte array to a 16-bit integer * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the integer represented by the bytes. */ static short bytesToShort( unsigned char *inBytes ); /** * Converts an 64-bit float to a byte array in a * platform-independent fashion. * * @param inDouble the double to convert to a byte array. * @param outBytes preallocated array where bytes will be returned. */ static void doubleToBytes( double inDouble, unsigned char *outBytes ); /** * Converts a 8-byte array to a 64-bit float * platform-independent fashion. * * @param inBytes array of bytes to convert. * * @return the double represented by the bytes. */ static double bytesToDouble( unsigned char *inBytes ); }; // for now, these long IO functions can be implemented in the same way // on every platform. inline void TypeIO::longToBytes( long inInt, unsigned char *outBytes ) { // use a big-endian conversion outBytes[0] = (unsigned char)( inInt >> 24 & 0xFF ); outBytes[1] = (unsigned char)( inInt >> 16 & 0xFF ); outBytes[2] = (unsigned char)( inInt >> 8 & 0xFF ); outBytes[3] = (unsigned char)( inInt & 0xFF ); } inline long TypeIO::bytesToLong( unsigned char *inBytes ) { return (long)( inBytes[0] << 24 | inBytes[1] << 16 | inBytes[2] << 8 | inBytes[3] ); } inline void TypeIO::shortToBytes( short inInt, unsigned char *outBytes ) { // use a big-endian conversion outBytes[0] = (unsigned char)( inInt >> 8 & 0xFF ); outBytes[1] = (unsigned char)( inInt & 0xFF ); } inline short TypeIO::bytesToShort( unsigned char *inBytes ) { return (short)( inBytes[0] << 8 | inBytes[1] ); } #endif transcend-0.3+dfsg2.orig/minorGems/io/pipedStreamTest.cpp0000640000175000017500000000305607244373534022134 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. */ #include #include "PipedStream.h" #include "minorGems/util/random/StdRandomSource.h" // test function for piped streams int main() { PipedStream *stream = new PipedStream; InputStream *inStream = stream; OutputStream *outStream = stream; StdRandomSource *randSource = new StdRandomSource(); unsigned char *bufferA = new unsigned char[10]; unsigned char *bufferB = new unsigned char[15]; int i; printf( "bufferA = \n" ); for( i=0; i<10; i++ ) { bufferA[i] = (unsigned char)( randSource->getRandomBoundedInt(0, 255) ); printf( "%d\n", bufferA[i] ); } printf( "bufferB = \n" ); for( i=0; i<15; i++ ) { bufferB[i] = (unsigned char)( randSource->getRandomBoundedInt(0, 255) ); printf( "%d\n", bufferB[i] ); } unsigned char *bufferC = new unsigned char[ 10 + 15]; outStream->write( bufferA, 10 ); outStream->write( bufferB, 15 ); inStream->read( bufferC, 10 + 10 ); char *error = inStream->getLastError(); if( error != NULL ) { printf( "Stream error %s\n", error ); delete [] error; } printf( "bufferc = \n" ); for( i=0; i<10 + 15; i++ ) { printf( "%d\n", bufferC[i] ); } inStream->read( bufferC, 10 ); error = inStream->getLastError(); if( error != NULL ) { printf( "Stream error %s\n", error ); delete [] error; } printf( "bufferc = \n" ); for( i=0; i<10 + 15; i++ ) { printf( "%d\n", bufferC[i] ); } delete [] bufferA; delete [] bufferB; delete [] bufferC; delete randSource; delete stream; } transcend-0.3+dfsg2.orig/minorGems/io/linux/0000750000175000017500000000000010305077063017433 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/linux/TypeIOLinux.cpp0000640000175000017500000000524507473770515022355 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed a bug in the big-endian code. * Fixed parameter names to match convention. * * 2001-February-26 Jason Rohrer * Fixed a bug in the little-endian implementation. * * 2001-August-28 Jason Rohrer * Changed to be FreeBSD compatible. * * 2002-March-13 Jason Rohrer * Started to change to work with solaris. * Finished changing to work with solaris. * * 2002-April-11 Jason Rohrer * Added a default BSD case to work with OSX. * * 2002-May-25 Jason Rohrer * Changed to use minorGems endian.h */ #include "minorGems/io/TypeIO.h" #include "minorGems/system/endian.h" /* * Linux-specific type input and output. * Note that all types are output in the order that * a big-endian linux machine outputs them with no conversion. */ #if __BYTE_ORDER == __LITTLE_ENDIAN void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output second word first outBytes[0] = doubleBuffer[7]; outBytes[1] = doubleBuffer[6]; outBytes[2] = doubleBuffer[5]; outBytes[3] = doubleBuffer[4]; outBytes[4] = doubleBuffer[3]; outBytes[5] = doubleBuffer[2]; outBytes[6] = doubleBuffer[1]; outBytes[7] = doubleBuffer[0]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // put first word at the end of this double doubleBuffer[7] = inBytes[0]; doubleBuffer[6] = inBytes[1]; doubleBuffer[5] = inBytes[2]; doubleBuffer[4] = inBytes[3]; doubleBuffer[3] = inBytes[4]; doubleBuffer[2] = inBytes[5]; doubleBuffer[1] = inBytes[6]; doubleBuffer[0] = inBytes[7]; return returnValue; } #endif #if __BYTE_ORDER == __BIG_ENDIAN void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output in stored order outBytes[0] = doubleBuffer[0]; outBytes[1] = doubleBuffer[1]; outBytes[2] = doubleBuffer[2]; outBytes[3] = doubleBuffer[3]; outBytes[4] = doubleBuffer[4]; outBytes[5] = doubleBuffer[5]; outBytes[6] = doubleBuffer[6]; outBytes[7] = doubleBuffer[7]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // store in input order doubleBuffer[0] = inBytes[0]; doubleBuffer[1] = inBytes[1]; doubleBuffer[2] = inBytes[2]; doubleBuffer[3] = inBytes[3]; doubleBuffer[4] = inBytes[4]; doubleBuffer[5] = inBytes[5]; doubleBuffer[6] = inBytes[6]; doubleBuffer[7] = inBytes[7]; return returnValue; } #endif transcend-0.3+dfsg2.orig/minorGems/io/OutputStream.h0000640000175000017500000000702310047466201021123 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed the "number of bytes written" parameter and return value * to longs. * * 2001-February-3 Jason Rohrer * Added a writeDouble function to fix platform-specific double problems. * Also added a writeLong function for completeness. Implemented * these functions, making use of the new TypeIO interface. * * 2001-February-12 Jason Rohrer * Changed to subclass Stream. * * 2002-February-25 Jason Rohrer * Added a function for writing a string. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * * 2004-May-9 Jason Rohrer * Added support for shorts. */ #include "minorGems/common.h" #ifndef OUTPUT_STREAM_CLASS_INCLUDED #define OUTPUT_STREAM_CLASS_INCLUDED #include "Stream.h" #include "TypeIO.h" #include /** * Interface for a byte output stream. * * @author Jason Rohrer */ class OutputStream : public Stream { public: OutputStream(); virtual ~OutputStream(); /** * Writes bytes to this stream. * * @param inBuffer the buffer of bytes to send. * @param inNumBytes the number of bytes to send. * * @return the number of bytes written successfully, * or -1 for a stream error. */ virtual long write( unsigned char *inBuffer, long inNumBytes ) = 0; /** * Writes a string to this stream. * * @param inString a \0-terminated string. * Must be destroyed by caller. * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeString( char *inString ); /** * Writes a double to the stream in a platform-independent way. * * @param inDouble the double to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeDouble( double inDouble ); /** * Writes a long to the stream in a platform-independent way. * * @param inLong the long to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeLong( long inLong ); /** * Writes a short to the stream in a platform-independent way. * * @param inShort the long to write * * @return the number of bytes written successfully, or -1 for a * stream error. */ long writeShort( short inShort ); private: unsigned char *mDoubleBuffer; unsigned char *mLongBuffer; unsigned char *mShortBuffer; }; inline OutputStream::OutputStream() : mDoubleBuffer( new unsigned char[8] ), mLongBuffer( new unsigned char[4] ), mShortBuffer( new unsigned char[2] ) { } inline OutputStream::~OutputStream() { delete [] mDoubleBuffer; delete [] mLongBuffer; delete [] mShortBuffer; } inline long OutputStream::writeString( char *inString ) { int numBytes = write( (unsigned char *)inString, strlen( inString ) ); return numBytes; } inline long OutputStream::writeDouble( double inDouble ) { TypeIO::doubleToBytes( inDouble, mDoubleBuffer ); int numBytes = write( mDoubleBuffer, 8 ); return numBytes; } inline long OutputStream::writeLong( long inLong ) { TypeIO::longToBytes( inLong, mLongBuffer ); int numBytes = write( mLongBuffer, 4 ); return numBytes; } inline long OutputStream::writeShort( short inShort ) { TypeIO::shortToBytes( inShort, mShortBuffer ); int numBytes = write( mShortBuffer, 2 ); return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/io/PipedStream.h0000640000175000017500000000765107554101513020674 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-February-20 Jason Rohrer * Added a missing return value. */ #include "minorGems/common.h" #ifndef PIPED_STREAM_CLASS_INCLUDED #define PIPED_STREAM_CLASS_INCLUDED #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" #include "minorGems/util/SimpleVector.h" #include /** * An input/output stream that can server as a pipe between * two components that read from and write to streams. * * Buffered internally to prevent blocking, so is compatible * with non-threaded components. Note, however, that * ever buffer written to the stream is copied internally, * so max memory usage is doubled. * * IS NOT THREAD-SAFE! * * @author Jason Rohrer */ class PipedStream : public InputStream, public OutputStream { public: /** * Constructs a PipedStream. */ PipedStream(); /** * Destroys any pending unread buffers. */ ~PipedStream(); // implements the InputStream interface long read( unsigned char *inBuffer, long inNumBytes ); // implements the OutputStream interface long write( unsigned char *inBuffer, long inNumBytes ); protected: SimpleVector *mBuffers; SimpleVector *mBufferSizes; long mCurrentBufferIndex; }; inline PipedStream::PipedStream() : mBuffers( new SimpleVector() ), mBufferSizes( new SimpleVector() ), mCurrentBufferIndex( 0 ) { } inline PipedStream::~PipedStream() { int numRemaining = mBuffers->size(); for( int i=0; igetElement( i ) ); delete [] buffer; } delete mBuffers; delete mBufferSizes; } inline long PipedStream::read( unsigned char *inBuffer, long inNumBytes ) { if( mBuffers->size() == 0 ) { // none read, since no buffers available InputStream:: setNewLastErrorConst( "No data available on piped stream read." ); return 0; } else { unsigned char *currentBuffer = *( mBuffers->getElement( 0 ) ); long currentBufferSize = *( mBufferSizes->getElement( 0 ) ); long outputIndex = 0; while( outputIndex < inNumBytes ) { long bytesRemaining = inNumBytes - outputIndex; long bytesRemainingInCurrent = currentBufferSize - mCurrentBufferIndex; // if current buffer isn't big enough to fill output if( bytesRemaining >= bytesRemainingInCurrent ) { // copy all of current buffer into inBuffer memcpy( &( inBuffer[outputIndex] ), &( currentBuffer[mCurrentBufferIndex] ), bytesRemainingInCurrent ); outputIndex += bytesRemainingInCurrent; // delete the current buffer mBuffers->deleteElement( 0 ); mBufferSizes->deleteElement( 0 ); delete [] currentBuffer; mCurrentBufferIndex = 0; if( outputIndex != inNumBytes && mBuffers->size() == 0 ) { // partial read, since no more buffers available InputStream::setNewLastErrorConst( "Partial data available on piped stream read." ); return outputIndex; } if( mBuffers->size() != 0 ) { // get the new current buffer currentBuffer = *( mBuffers->getElement( 0 ) ); currentBufferSize = *( mBufferSizes->getElement( 0 ) ); } } else { // current buffer is bigger memcpy( &( inBuffer[outputIndex] ), &( currentBuffer[mCurrentBufferIndex] ), bytesRemaining ); mCurrentBufferIndex += bytesRemaining; outputIndex += bytesRemaining; } } // end while // if we made it out of the while loop, we read all bytes return inNumBytes; } // end else } inline long PipedStream::write( unsigned char *inBuffer, long inNumBytes ) { // add a copy of the buffer to the vector of buffers unsigned char *copy = new unsigned char[ inNumBytes ]; memcpy( copy, inBuffer, inNumBytes ); mBuffers->push_back( copy ); mBufferSizes->push_back( inNumBytes ); return inNumBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/io/Stream.h0000640000175000017500000000513707554101513017707 0ustar pabspabs/* * Modification History * * 2001-February-12 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-March-31 Jason Rohrer * Made destructor virtual so it works with subclasses. * Fixed several bugs in deletion of mLastError. */ #include "minorGems/common.h" #ifndef STREAM_CLASS_INCLUDED #define STREAM_CLASS_INCLUDED #include #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Base class for all streams. * * @author Jason Rohrer */ class Stream { public: /** * Constructs a stream. */ Stream(); virtual ~Stream(); /** * Gets the last error associated with this stream. * Calling this function clears the error until * another error occurs. * * @return the last stream error in human-readable form. * Must be destroyed by caller. Returns NULL if * there is no error. Note that this string is '\0' terminated. */ char *getLastError(); protected: /** * Called by subclasses to specify a new last error. Useful * when error doesn't contain information specifiable by * a constant string. * * @param inString human-readable string representing the error. * Note that this string must be '\0' terminated. * Will be destroyed by this class. */ void setNewLastError( char *inString ); /** * Called by subclasses to specify a new last error. Useful * when the error can be described by a constant string * * @param inString human-readable constant string representing * the error. * Note that this string must be '\0' terminated. */ void setNewLastErrorConst( const char *inString ); private: // set to NULL when there is no error char *mLastError; }; inline Stream::Stream() : mLastError( NULL ) { } inline Stream::~Stream() { if( mLastError != NULL ) { delete [] mLastError; } } inline char *Stream::getLastError() { char *returnString = mLastError; mLastError = NULL; return returnString; } inline void Stream::setNewLastError( char *inString ) { if( mLastError != NULL ) { delete [] mLastError; } mLastError = inString; } inline void Stream::setNewLastErrorConst( const char *inString ) { int length = 0; char lastChar = 'a'; while( lastChar != '\0' ) { lastChar = inString[length]; length++; } // add one more to length to accommodate '\0' terminination length++; if( mLastError != NULL ) { delete [] mLastError; } mLastError = new char[ length ]; memcpy( mLastError, inString, length ); } #endif transcend-0.3+dfsg2.orig/minorGems/io/win32/0000750000175000017500000000000010305077063017236 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/io/win32/TypeIOWin32.cpp0000640000175000017500000000247107237354514021754 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * Fixed parameter names to match convention. * * 2001-February-4 Jason Rohrer * Fixed a byte-order bug. */ #include "minorGems/io/TypeIO.h" /* * Win32-specific type input and output. * Note that all types are output in the order that * a big-endian linux machine outputs them with no conversion. */ // windows machines are all little-endian void TypeIO::doubleToBytes( double inDouble, unsigned char *outBytes ) { unsigned char *doubleBuffer = (unsigned char*)( &inDouble ); // output second word first outBytes[0] = doubleBuffer[7]; outBytes[1] = doubleBuffer[6]; outBytes[2] = doubleBuffer[5]; outBytes[3] = doubleBuffer[4]; outBytes[4] = doubleBuffer[3]; outBytes[5] = doubleBuffer[2]; outBytes[6] = doubleBuffer[1]; outBytes[7] = doubleBuffer[0]; } double TypeIO::bytesToDouble( unsigned char *inBytes ) { double returnValue; unsigned char *doubleBuffer = (unsigned char*)( &returnValue ); // put first word at the end of this double doubleBuffer[7] = inBytes[0]; doubleBuffer[6] = inBytes[1]; doubleBuffer[5] = inBytes[2]; doubleBuffer[4] = inBytes[3]; doubleBuffer[3] = inBytes[4]; doubleBuffer[2] = inBytes[5]; doubleBuffer[1] = inBytes[6]; doubleBuffer[0] = inBytes[7]; return returnValue; } transcend-0.3+dfsg2.orig/minorGems/formats/0000750000175000017500000000000010305077062017337 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/formats/xml/0000750000175000017500000000000010305077062020137 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/formats/xml/XMLUtils.cpp0000640000175000017500000000470207540235637022343 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #include "XMLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *XMLUtils::escapeDisallowedCharacters( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i; for( i=0; ipush_back( '&' ); returnStringVector->push_back( 'a' ); returnStringVector->push_back( 'm' ); returnStringVector->push_back( 'p' ); returnStringVector->push_back( ';' ); break; case '<': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'l' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '>': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'g' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '\"': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'q' ); returnStringVector->push_back( 'u' ); returnStringVector->push_back( 'o' ); returnStringVector->push_back( 't' ); returnStringVector->push_back( ';' ); break; case '\'': returnStringVector->push_back( '&' ); returnStringVector->push_back( 'a' ); returnStringVector->push_back( 'p' ); returnStringVector->push_back( 'o' ); returnStringVector->push_back( 's' ); returnStringVector->push_back( ';' ); break; default: returnStringVector->push_back( inString[i] ); break; } } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } transcend-0.3+dfsg2.orig/minorGems/formats/xml/XMLUtils.h0000640000175000017500000000134107540235637022004 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #ifndef XML_UTILS_INCLUDED #define XML_UTILS_INCLUDED /** * Utilities for processing XML. * * @author Jason Rohrer */ class XMLUtils { public: /** * Escapes characters disallowed in XML character data. * * @param the string to escape as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return string with characters escaped as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *escapeDisallowedCharacters( char *inString ); }; #endif transcend-0.3+dfsg2.orig/minorGems/formats/html/0000750000175000017500000000000010305077062020303 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/formats/html/HTMLUtils.cpp0000640000175000017500000000215707540240755022612 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #include "HTMLUtils.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SimpleVector.h" #include char *HTMLUtils::removeAllTags( char *inString ) { SimpleVector *returnStringVector = new SimpleVector(); int stringLength = strlen( inString ); int i = 0; while( i < stringLength ) { if( inString[i] == '<' ) { // the start of a tag // skip all until (and including) close of tag while( i < stringLength && inString[i] != '>' ) { // do nothing i++; } } else { returnStringVector->push_back( inString[i] ); } i++; } int numChars = returnStringVector->size(); char *returnString = new char[ numChars + 1 ]; for( i=0; igetElement( i ) ); } returnString[ numChars ] = '\0'; delete returnStringVector; return returnString; } transcend-0.3+dfsg2.orig/minorGems/formats/html/HTMLUtils.h0000640000175000017500000000131007540240755022245 0ustar pabspabs/* * Modification History * * 2002-September-12 Jason Rohrer * Created. */ #ifndef HTML_UTILS_INCLUDED #define HTML_UTILS_INCLUDED /** * Utilities for processing HTML. * * @author Jason Rohrer */ class HTMLUtils { public: /** * Removes all HTML tags from an HTML string. * * @param the HTML data as a \0-terminated string. * Must be destroyed by caller if non-const. * * @return data with all HTML tags removed as a newly allocated * \0-terminated string. * Must be destroyed by caller. */ static char *removeAllTags( char *inString ); }; #endif transcend-0.3+dfsg2.orig/minorGems/formats/encodingUtilsTestCompile0000750000175000017500000000021007733613350024245 0ustar pabspabsg++ -g -I../.. -o encodingUtilsTest encodingUtilsTest.cpp encodingUtils.cpp ../util/stringUtils.cpp ../util/StringBufferOutputStream.cpptranscend-0.3+dfsg2.orig/minorGems/formats/encodingUtils.h0000640000175000017500000000423207733613350022330 0ustar pabspabs/* * Modification History * * 2003-August-22 Jason Rohrer * Created. * * 2003-September-22 Jason Rohrer * Added base64 encoding. */ #ifndef ENCODING_UTILS_INCLUDED #define ENCODING_UTILS_INCLUDED /** * A collection of functions for representing data in various encoding formats. * * @author Jason Rohrer */ /** * Encodes data as a ASCII hexidecimal string. * * @param inData the data to encode. * Must be destroyed by caller. * @param inDataLength the length of inData in bytes. * * @return a \0-terminated ASCII hexidecimal string containing * characters in the range [0-9] and [A-F]. * Will be twice as long as inData. * Must be destroyed by caller. */ char *hexEncode( unsigned char *inData, int inDataLength ); /** * Decodes raw data from an ASCII hexidecimal string. * * @param inHexString a \0-terminated hexidecimal string. * Must be destroyed by caller. * * @return an array of raw data, or NULL if decoding fails. * Will be half as long as inHexString. * Must be destroyed by caller if non-NULL. */ unsigned char *hexDecode( char *inHexString ); /** * Encodes data as a ASCII base64 string. * * @param inData the data to encode. * Must be destroyed by caller. * @param inDataLength the length of inData in bytes. * @param inBreakLines set to true to break lines every 76 characters, * or false to produce an unbroken base64 string. * * @return a \0-terminated ASCII base64 string containing * characters in the range [0-9], [A-Z], [a-z], and [+,/,=]. * Must be destroyed by caller. */ char *base64Encode( unsigned char *inData, int inDataLength, char inBreakLines = true ); /** * Decodes raw data from an ASCII base64 string. * * @param inBase64String a \0-terminated base64 string. Can optionally contain * linebreaks. * Must be destroyed by caller. * @param outDataLength pointer to where the length of the decoded data * should be returned. * * @return an array of raw data, or NULL if decoding fails. * Must be destroyed by caller if non-NULL. */ unsigned char *base64Decode( char *inBase64String, int *outDataLength ); #endif transcend-0.3+dfsg2.orig/minorGems/formats/encodingUtils.cpp0000640000175000017500000003143510027336105022656 0ustar pabspabs/* * Modification History * * 2003-August-22 Jason Rohrer * Created. * * 2003-September-22 Jason Rohrer * Added base64 encoding. * * 2004-March-21 Jason Rohrer * Fixed a variable scoping and redefinition bug pointed out by Benjamin Meyer. */ #include "encodingUtils.h" #include "minorGems/util/SimpleVector.h" #include #include char fourBitIntToHex( int inInt ) { char outChar[2]; if( inInt < 10 ) { sprintf( outChar, "%d", inInt ); } else { switch( inInt ) { case 10: outChar[0] = 'A'; break; case 11: outChar[0] = 'B'; break; case 12: outChar[0] = 'C'; break; case 13: outChar[0] = 'D'; break; case 14: outChar[0] = 'E'; break; case 15: outChar[0] = 'F'; break; default: outChar[0] = '0'; break; } } return outChar[0]; } // returns -1 if inHex is not a valid hex character int hexToFourBitInt( char inHex ) { int returnInt; switch( inHex ) { case '0': returnInt = 0; break; case '1': returnInt = 1; break; case '2': returnInt = 2; break; case '3': returnInt = 3; break; case '4': returnInt = 4; break; case '5': returnInt = 5; break; case '6': returnInt = 6; break; case '7': returnInt = 7; break; case '8': returnInt = 8; break; case '9': returnInt = 9; break; case 'A': case 'a': returnInt = 10; break; case 'B': case 'b': returnInt = 11; break; case 'C': case 'c': returnInt = 12; break; case 'D': case 'd': returnInt = 13; break; case 'E': case 'e': returnInt = 14; break; case 'F': case 'f': returnInt = 15; break; default: returnInt = -1; break; } return returnInt; } char *hexEncode( unsigned char *inData, int inDataLength ) { char *resultHexString = new char[ inDataLength * 2 + 1 ]; int hexStringIndex = 0; for( int i=0; i> 4 ); int lowBits = 0xF & ( currentByte ); resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits ); hexStringIndex++; resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits ); hexStringIndex++; } resultHexString[ hexStringIndex ] = '\0'; return resultHexString; } unsigned char *hexDecode( char *inHexString ) { int hexLength = strlen( inHexString ); if( hexLength % 2 != 0 ) { // hex strings must be even in length return NULL; } int dataLength = hexLength / 2; unsigned char *rawData = new unsigned char[ dataLength ]; for( int i=0; i *encodingVector = new SimpleVector(); int numInLine = 0; // take groups of 3 data bytes and map them to 4 base64 digits for( int i=0; i> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); unsigned int digitD = 0x3F & ( block ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( binaryToAscii[ digitD ] ); numInLine += 4; if( inBreakLines && numInLine == 76 ) { // break the line encodingVector->push_back( '\r' ); encodingVector->push_back( '\n' ); numInLine = 0; } } else { // at end int numLeft = inDataLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // two digits, two pads unsigned int block = inData[i] << 16 | 0; unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( '=' ); encodingVector->push_back( '=' ); break; } case 2: { // three digits, one pad unsigned int block = inData[i] << 16 | inData[i+1] << 8 | 0; // base64 digits, with digitA at left unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( '=' ); break; } default: break; } // done with all data i = inDataLength; } } char *returnString = encodingVector->getElementString(); delete encodingVector; return returnString; } unsigned char *base64Decode( char *inBase64String, int *outDataLength ) { SimpleVector *decodedVector = new SimpleVector(); int encodingLength = strlen( inBase64String ); SimpleVector *binaryEncodingVector = new SimpleVector(); int i; for( i=0; ipush_back( currentBinary ); } } int binaryEncodingLength = binaryEncodingVector->size(); unsigned char *binaryEncoding = binaryEncodingVector->getElementArray(); delete binaryEncodingVector; int blockCount = binaryEncodingLength / 4; if( binaryEncodingLength % 4 != 0 ) { // extra, 0-padded block blockCount += 1; } // take groups of 4 encoded digits and map them to 3 data bytes for( i=0; i> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); unsigned int digitC = 0xFF & ( block ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); decodedVector->push_back( digitC ); } else { // at end int numLeft = binaryEncodingLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // impossible break; } case 2: { // two base64 digits, one data byte unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); decodedVector->push_back( digitA ); break; } case 3: { // three base64 digits, two data bytes unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | binaryEncoding[i+2] << 6 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); break; } default: break; } // done with all data i = binaryEncodingLength; } } delete [] binaryEncoding; *outDataLength = decodedVector->size(); unsigned char* returnData = decodedVector->getElementArray(); delete decodedVector; return returnData; } transcend-0.3+dfsg2.orig/minorGems/formats/encodingUtilsTest.cpp0000640000175000017500000000232107733613350023520 0ustar pabspabs/* * Modification History * * 2003-September-22 Jason Rohrer * Created. */ #include "encodingUtils.h" #include #include int main() { const char *dataString = "*#$&$(@KFI#*$(SDBM@#*!(@%" "*#$&$(@KFI#*$(SDBM@#*!(@%" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@F" "*#$&$(@KFI#*$(SDBM@#*!(@%a"; printf( "base64 encoding the string: %s\n", dataString ); char *encoding = base64Encode( (unsigned char *)dataString, strlen( dataString ), true ); printf( "Encoded as:\n%s\n", encoding ); int decodedLength; unsigned char *decoding = base64Decode( encoding, &decodedLength ); char *buffer = new char[ decodedLength + 1 ]; memcpy( (void *)buffer, (void *)decoding, decodedLength ); buffer[ decodedLength ] = '\0'; printf( "Decoded as: %s\n", buffer ); if( strcmp( buffer, dataString ) == 0 ) { printf( "Test passed\n" ); } else { printf( "Test failed\n" ); } delete [] buffer; delete [] decoding; delete [] encoding; return 0; } transcend-0.3+dfsg2.orig/minorGems/util/0000750000175000017500000000000010305077117016642 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/development/0000750000175000017500000000000011476353073021174 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/0000750000175000017500000000000010305077117023241 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/LeakCheckAnalyze0000750000175000017500000000031510001775461026324 0ustar pabspabs#!/bin/sh CHECKER=`dirname $0`/LeakCheck" $@" ANALYZER=`dirname $0`/leak-analyze" $1 leak.out" echo "Checking with: $CHECKER" echo "" $CHECKER echo "" echo "Analyzing with: $ANALYZER" echo "" $ANALYZER transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/leak-analyze0000750000175000017500000000636410002231613025540 0ustar pabspabs#!/usr/bin/perl # # Modification History # # 2004-January-17 Jason Rohrer # Fixed regexps to match both A-F and a-f for hex address strings. # # Erwin S. Andreasen # Henner Zeller # # Homepage: http://www.andreasen.org/LeakTracer/ # This program is Public Domain use IO::Handle; die "You must supply at least one argument.\n" unless $#ARGV >= 0; $ExeFile = shift @ARGV; $LeaksFile = $#ARGV >= 0 ? shift @ARGV : "leak.out"; open (LEAKS, $LeaksFile) or die "Could not open leaks data file $LeaksFile: $!"; if ($#ARGV >= 0) { $BreakOn = shift @ARGV; # Rest in @ARGV are program arguments } $n = $u = 0; while () { chop; next if (m/^\s*#/); # 1 2 3 4 5 6 7 #if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) { # Allocations, which have not been freed or deallocations which have not # been allocated. # 1 2 3 if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) { $addr="$2"; # ",$4,$6"; $u++ if not exists $Type{$addr}; $Count{$addr}++; $Size{$addr} += $3; # $7; $Type{$addr} = "Leak"; $n++; } elsif (/^\s*D\s+(0x)?([0-9a-fA-F]+)/) { $addr="$2"; # ",$4,$6"; $u++ if not exists $Type{$addr}; $Count{$addr}++; $Type{$addr} = "delete on not allocated memory"; $n++; } # allocations/deallocations with other errornous conditions # 1 2 3 4 5 elsif (/^\s*([SO])\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)/) { $addrs = "$3,$5,$1"; $AllocDealloc{$addrs} = ("$1" =~ m/S/) ? "Different allocation schemes" : "This Memory was overwritten"; } } print STDERR "Gathered $n ($u unique) points of data.\n"; close (LEAKS); # Instead of using -batch, we just run things as usual. with -batch, # we quit on the first error, which we don't want. open (PIPE, "|gdb -q $ExeFile") or die "Cannot start gdb"; #open (PIPE, "|cat"); # Change set listsize 2 to something else to show more lines print PIPE "set prompt\nset complaints 1000\nset height 0\n"; # Optionally, run the program if (defined($BreakOn)) { print PIPE "break $BreakOn\n"; print PIPE "run ", join(" ", @ARGV), " \n"; } print PIPE "set listsize 2\n"; foreach (sort keys %AllocDealloc) { print PIPE "echo \\n#-- Alloc: $AllocDealloc{$_}\\nalloc here :\n"; @addrs = split(/,/,$_); print PIPE "l *0x" . (shift @addrs) . "\necho ..free here :\n"; print PIPE "set listsize 1\n"; print PIPE "l *0x" . (shift @addrs) . "\n"; } foreach (sort keys %Type) { print PIPE "echo \\n#-- $Type{$_}: counted $Count{$_}x"; if ($Size{$_} > 0) { print PIPE " / total Size: $Size{$_}"; } print PIPE "\\n\n"; @addrs = split(/,/,$_); print PIPE "set listsize 2\n"; print PIPE "l *0x" . (shift @addrs) . "\n"; #print PIPE "echo ..called from :\n"; #print PIPE "set listsize 1\n"; # gdb bails out, if it cannot find an address. #print PIPE "l *0x" . (shift @addrs) . "\necho ..called from :\n"; #print PIPE "l *0x" . (shift @addrs) . "\n"; } if (defined($BreakOn)) { print PIPE "kill\n"; } print PIPE "quit\n"; PIPE->flush(); wait(); close (PIPE); transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/VERSION0000640000175000017500000000000410001775461024304 0ustar pabspabs2.3 transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/LeakCheck0000750000175000017500000000064010001775461025001 0ustar pabspabs#!/bin/sh if [ $# -lt 1 ] ; then echo "Usage: $0 " exit 1 fi # this looks in the same directory, this # LeakCheck script resides; modify to your # needs: SHLIB=`dirname $0`/LeakTracer.so if [ ! -x $SHLIB ] ; then echo "$SHLIB not found" exit 1 fi if [ -z "$LEAKTRACE_FILE" ] ; then rm -f leak.out else rm -f "$LEAKTRACE_FILE" fi export LD_PRELOAD=$SHLIB exec $@ transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/test.cc0000640000175000017500000000054110001775461024530 0ustar pabspabs/* * Modification History * * 2002-March-31 Jason Rohrer * Added test of strdup support. */ #include // Small leaky test program void foo() { int *x = new int; } int main() { char *str = strdup( "Test String" ); int *z = new int[10]; foo(); foo(); delete z; delete z; // delete value twice } transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/README.html0000640000175000017500000002270510001775461025073 0ustar pabspabs

Table of contents

Introduction

LeakTracer is a small tool I wrote when checking a C++ program for memory
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
__builtin_return_address gcc-extension mentioned.

To use LeakTracer, run your program using the provided LeakCheck script. It
uses the LD_PRELOAD feature to "overlay" some functions on top of your
functions (no recompile needed). If your platform does not support LD_PRELOAD,
you can add the LeakTracer.o object file to the objects in your Makefile and
run your application. 

LeakTracer uses gdb to print out the exact line where the memory was allocated
and not freed - this of course means you have to free all dynamically
allocated data. LeakTracer also overrides the global operator new and operator
delete - this will give problems if you override them as well.

LeakTracer traces only new/new[] and delete calls - it does not look at
malloc/free/realloc.

Here is some example output:

Gathered 8 (8 unique) points of data.
(gdb)
Allocations: 1 / Size: 36
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
39      public:
40          NullArcableInstance() : ArcableInstance(new NullArcable) {}

Allocations: 1 / Size: 8
0x8055b02 is in init_types(void) (Type.cc:119).
118     void init_types() {
119         Type::Integer = new IntegerType;

Allocations: 1 / Size: 132 (new[])
0x805f4ab is in Hashtable::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
14          Hashtable (uint _size = 32) : size(_size), count(0) {
15              table = new List [size];

[...]

Requirements

You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
currently use 2.95 but have used it with previous older versions without
problems.
You also need to run this on an architecture which supports
__builtin_return_address arguments that are greater than 0 - there may be
some problems on MIPS there. 

So far this code has been tested under Linux 2.2, x86 system, Solaris and
HP-UX.

Installation

Just type make. There is no install target; you should put LeakTracer
some place you can remember.

Since version 2.0, it is possible to preload the LeakTracer object on
architectures that support LD_PRELOAD (this is at least Linux and probably
others -- please report success/failure). This means it is much easier to use
the program: you do not need to relink your program with LeakTracer.o.

In case your platform does not support LD_PRELOAD, you can use LeakTracer in
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
of them (also after -llibrary lines).

In any case your application must also be compiled with debugging enabled
(i.e. -g).

Running with LeakTracer

If you are using the shared object, run the LeakCheck script. This script
should stay in the directory where you install LeakCheck -- it will search for
LeakTracer.so file there and load it. E.g.:

~/src/LeakTracer/LeakCheck yourApplication

(if you put LeakTracer in ~/src/LeakTracer/)

Run your application as normal, performing tasks that you want to be traced
for memory leaks. While the application runs, LeakTracer will write data about
memory allocation to the file "leak.out" in the current directory. You can
override the location of that file by setting the LEAKTRACE_FILE environment
variable.

If you cannot use LD_PRELOAD, just run your application as normal after
relinking it. It will also produce a "leak.out" file when it finishes.

Detectable errors

LeakTracer is capable to detect the following problems with your program

  1) memory which is allocated but not freed
  2) (limited support for) overwritten memory at the end of the allocated
     block  ( reason = 1 )
  3) memory which is tried to be deleted but which is not allocated
     (either because of a garbage pointer or twice deletion)
     (reason = 2)
  4) memory which is allocated with new[] but deleted with simple delete
     and vice versa (reason = 4)

For the last three problems, LeakTracer can abort() your program if you
tell it so; the resulting core-dump allows to debug the problem. By default,
only the overwrite memory condition results in an abort of the program
because it is inherently critical. The two other conditions are not critical.
You can influence what LeakTracer does with the environment variable
   LT_ABORTREASON
which you can set to some numeric value which is the result of the
sum of the reasons you find in the parentesis in the enumeration above.
To abort on any reason, for example, you would set LT_ABORTREASON to 7.

Analyzing output

You should then run leak-analyze, since looking at the raw leak.out file will
not help you much. To run leak-analyze, you need Perl as well as gdb
installed (any version of gdb will do). For example:

leak-analyze myprog leak.out

You don't have to specify the leak.out filename if you just use the default
one. leak-analyze will run gdb on the file, sending it a number of commands
that will show the source lines with the memory leaks.

leak-analyze should show you something like this:

Gathered 2 (2 unique) points of data.

#-- Alloc: Different allocation schemes
alloc here :0x80485b7 is in main (test.cc:6).
5
6               int *wrong = new int[10];
..free here :0x80485d9 is in main (test.cc:11).
11              delete wrong;

#-- Leak: Allocations: 1 / Size: 168 
0x8048593 is in main (test.cc:3).
2       int main() {
3               int *array = new int [42] ;

#-- Leak: Allocations: 1 / Size: 4 
0x80485a5 is in main (test.cc:4).
3               int *array = new int [42] ;
4               int *foo = new int;
This means that total of two allocations happened, in two different places.

First a delete error is shown: you allocated some memory using new[] but you
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.

Afterwards each allocation is shown in turn. There was 1 allocation from this
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
lines of code shown, it's the bottom one that created the allocation.

That's all there is to it - now you should find those memory leaks, fix them
and rerun Leak tracer.

Shared libraries and objects

If you want to analyze the leaks in shared libraries in your file, it may be
necessary to make leak-analyze run your program and thus load the shared
libraries before searching for addresses.

To do that, run leak-analyze with the program name, leak name AND another
argument which is where to set the breakpoint, e.g.:

leak-analyze myprog leak.out main

This will make leak-analyze tell gdb to set a breakpoint on "main" and then
run the program. After the analysis is complete, the program will be killed.

If you want to load some shared libraries, you can set a breakpoint on a
different location, e.g. main.cc:42 if you know that once line 42 is reached,
all shared objects have been loaded.

If your program needs some command line arguments, supply them after "main".

Licensing

LeakTracer is public domain (i.e. do with it whatever you feel like).

Credits

Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
(foobar@to.com) contributed a rewrite of the code which
introduced dynamic loading of LeakTracer and more.

Revision history

February 21, 1999       v1.0 - only tested internally
February 23, 1999       v1.1 - added operator new[] / delete[]
February 23, 1999           v1.2 - Oops, forgot to free() the memory..
February 26, 1999       v1.3 - allow delete 0
March 27, 1999          v1.4 - Allow %p format without leading 0x for non-GNU 
                                       libc. Option to leak-analyze to run the program.
July 21, 1999               v1.5 - Fix for the above suggested by Alan Gonzalez
August 21, 2000         v1.6 - use a destructor instead of 
                                       __attribute__(destructor)
November 19, 2000               v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
                                       and much more
February 27, 2001               v2.1 - Further update by Henner: optional thread safety,
                                       choose what should make LeakTracer abort(), better
                                       tracing of delete on non-new'ed pointers
March 2, 2001                   v2.2 - Another updated by Henner: hash table to increase
                                       performance with many allocations
June 13, 2001                   v2.3 - Made LT more resistant to being called before init
                                       and after destruction

Authors:    Erwin Andreasen 
        Henner Zeller 
Homepage:   http://www.andreasen.org/LeakTracer/

transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/Makefile0000640000175000017500000000262510001777407024711 0ustar pabspabs# # Modification History # # 2004-January-16 Jason Rohrer # Switched to use minorGems platform-independed mutexes. # CC = g++ # Source files SRC := LeakTracer.cc ROOT_PATH = ../../../.. # Switch comment to select a platform # PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/win32/MutexLockWin32.cpp PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/linux/MutexLockLinux.cpp -pthread # Comment both of these out to disable thread safetly C_THREAD=-DTHREAD_SAVE -D_REENTRANT -D_THREAD_SAFE O_THREAD = $(PLATFORM_MUTEX) # Common flags C_FLAGS = -g -pipe -Wall -W -I$(ROOT_PATH) $(C_THREAD) O_FLAGS = $(C_FLAGS) $(O_THREAD) # Object files OBJ_DIR = . OBJ := $(patsubst %.cc,$(OBJ_DIR)/%.o,$(SRC)) SHOBJ := $(patsubst %.o,$(OBJ_DIR)/%.so,$(OBJ)) .PHONY: all clean tidy distrib test all: $(OBJ) $(SHOBJ) clean: tidy rm -f $(OBJ) leak.out tidy: rm -f *~ *orig *bak *rej tags: $(SRC) $(INCL) ctags $(SRC) $(INCL) distrib: clean all README.html (cd .. && tar cvfz /root/drylock/LeakTracer/LeakTracer.tar.gz --exclude LeakTracer/CVS --exclude LeakTracer/old --exclude LeakTracer/test LeakTracer/) $(OBJ_DIR)/%.o: %.cc $(CC) -fPIC -c $(C_FLAGS) $< -o $@ $(OBJ_DIR)/%.so : $(OBJ_DIR)/%.o $(CC) $(O_FLAGS) -shared -o $@ $< README.html: README /root/ed/mcl/util/htmlize.pl README test: $(CC) $(C_FLAGS) test.cc -o test ./test ./LeakCheck ./test ./leak-analyze ./test # ./compare-test test.template test.result transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/README0000640000175000017500000002130110001775461024117 0ustar pabspabsIntroduction ------------ LeakTracer is a small tool I wrote when checking a C++ program for memory leaks. I couldn't get dmalloc to display what I wanted, and I just saw the __builtin_return_address gcc-extension mentioned. To use LeakTracer, run your program using the provided LeakCheck script. It uses the LD_PRELOAD feature to "overlay" some functions on top of your functions (no recompile needed). If your platform does not support LD_PRELOAD, you can add the LeakTracer.o object file to the objects in your Makefile and run your application. LeakTracer uses gdb to print out the exact line where the memory was allocated and not freed - this of course means you have to free all dynamically allocated data. LeakTracer also overrides the global operator new and operator delete - this will give problems if you override them as well. LeakTracer traces only new/new[] and delete calls - it does not look at malloc/free/realloc. Here is some example output: Gathered 8 (8 unique) points of data. (gdb) Allocations: 1 / Size: 36 0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40). 39 public: 40 NullArcableInstance() : ArcableInstance(new NullArcable) {} Allocations: 1 / Size: 8 0x8055b02 is in init_types(void) (Type.cc:119). 118 void init_types() { 119 Type::Integer = new IntegerType; Allocations: 1 / Size: 132 (new[]) 0x805f4ab is in Hashtable::Hashtable(unsigned int) (ea/h/Hashtable.h:15). 14 Hashtable (uint _size = 32) : size(_size), count(0) { 15 table = new List [size]; [...] Requirements ------------ You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I currently use 2.95 but have used it with previous older versions without problems. You also need to run this on an architecture which supports __builtin_return_address arguments that are greater than 0 - there may be some problems on MIPS there. So far this code has been tested under Linux 2.2, x86 system, Solaris and HP-UX. Installation ------------ Just type make. There is no install target; you should put LeakTracer some place you can remember. Since version 2.0, it is possible to preload the LeakTracer object on architectures that support LD_PRELOAD (this is at least Linux and probably others -- please report success/failure). This means it is much easier to use the program: you do not need to relink your program with LeakTracer.o. In case your platform does not support LD_PRELOAD, you can use LeakTracer in the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end of them (also after -llibrary lines). In any case your application must also be compiled with debugging enabled (i.e. -g). Running with LeakTracer ----------------------- If you are using the shared object, run the LeakCheck script. This script should stay in the directory where you install LeakCheck -- it will search for LeakTracer.so file there and load it. E.g.: ~/src/LeakTracer/LeakCheck yourApplication (if you put LeakTracer in ~/src/LeakTracer/) Run your application as normal, performing tasks that you want to be traced for memory leaks. While the application runs, LeakTracer will write data about memory allocation to the file "leak.out" in the current directory. You can override the location of that file by setting the LEAKTRACE_FILE environment variable. If you cannot use LD_PRELOAD, just run your application as normal after relinking it. It will also produce a "leak.out" file when it finishes. Detectable errors ----------------- LeakTracer is capable to detect the following problems with your program 1) memory which is allocated but not freed 2) (limited support for) overwritten memory at the end of the allocated block ( reason = 1 ) 3) memory which is tried to be deleted but which is not allocated (either because of a garbage pointer or twice deletion) (reason = 2) 4) memory which is allocated with new[] but deleted with simple delete and vice versa (reason = 4) For the last three problems, LeakTracer can abort() your program if you tell it so; the resulting core-dump allows to debug the problem. By default, only the overwrite memory condition results in an abort of the program because it is inherently critical. The two other conditions are not critical. You can influence what LeakTracer does with the environment variable LT_ABORTREASON which you can set to some numeric value which is the result of the sum of the reasons you find in the parentesis in the enumeration above. To abort on any reason, for example, you would set LT_ABORTREASON to 7. Analyzing output ---------------- You should then run leak-analyze, since looking at the raw leak.out file will not help you much. To run leak-analyze, you need Perl as well as gdb installed (any version of gdb will do). For example: leak-analyze myprog leak.out You don't have to specify the leak.out filename if you just use the default one. leak-analyze will run gdb on the file, sending it a number of commands that will show the source lines with the memory leaks. leak-analyze should show you something like this: Gathered 2 (2 unique) points of data. #-- Alloc: Different allocation schemes alloc here :0x80485b7 is in main (test.cc:6). 5 6 int *wrong = new int[10]; ..free here :0x80485d9 is in main (test.cc:11). 11 delete wrong; #-- Leak: Allocations: 1 / Size: 168 0x8048593 is in main (test.cc:3). 2 int main() { 3 int *array = new int [42] ; #-- Leak: Allocations: 1 / Size: 4 0x80485a5 is in main (test.cc:4). 3 int *array = new int [42] ; 4 int *foo = new int; This means that total of two allocations happened, in two different places. First a delete error is shown: you allocated some memory using new[] but you freed it using delete. leak-analyze will show where you allocated the memory and where you freed it. Afterwards each allocation is shown in turn. There was 1 allocation from this line of code (test.cc:3), and it was 168 bytes in size. Note that of the two lines of code shown, it's the bottom one that created the allocation. That's all there is to it - now you should find those memory leaks, fix them and rerun Leak tracer. Shared libraries and objects ---------------------------- If you want to analyze the leaks in shared libraries in your file, it may be necessary to make leak-analyze run your program and thus load the shared libraries before searching for addresses. To do that, run leak-analyze with the program name, leak name AND another argument which is where to set the breakpoint, e.g.: leak-analyze myprog leak.out main This will make leak-analyze tell gdb to set a breakpoint on "main" and then run the program. After the analysis is complete, the program will be killed. If you want to load some shared libraries, you can set a breakpoint on a different location, e.g. main.cc:42 if you know that once line 42 is reached, all shared objects have been loaded. If your program needs some command line arguments, supply them after "main". Licensing --------- LeakTracer is public domain (i.e. do with it whatever you feel like). Credits ------- Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller (foobar@to.com) contributed a rewrite of the code which introduced dynamic loading of LeakTracer and more. Revision history ---------------- February 21, 1999 v1.0 - only tested internally February 23, 1999 v1.1 - added operator new[] / delete[] February 23, 1999 v1.2 - Oops, forgot to free() the memory.. February 26, 1999 v1.3 - allow delete 0 March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU libc. Option to leak-analyze to run the program. July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez August 21, 2000 v1.6 - use a destructor instead of __attribute__(destructor) November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD and much more February 27, 2001 v2.1 - Further update by Henner: optional thread safety, choose what should make LeakTracer abort(), better tracing of delete on non-new'ed pointers March 2, 2001 v2.2 - Another updated by Henner: hash table to increase performance with many allocations June 13, 2001 v2.3 - Made LT more resistant to being called before init and after destruction Authors: Erwin Andreasen Henner Zeller Homepage: http://www.andreasen.org/LeakTracer/ transcend-0.3+dfsg2.orig/minorGems/util/development/leakTracer/LeakTracer.cc0000640000175000017500000003331410002005355025557 0ustar pabspabs/* * Modification History * * 2002-March-31 Jason Rohrer * Added support for strdup. * Fixed bug in strdup. * * 2003-October-3 Jason Rohrer * Added printout of leak contents in report. * * 2004-January-16 Jason Rohrer * Switched to use minorGems platform-independed mutexes. * Changed to use simpler fopen call to open report. */ /* * Homepage: * * Authors: * Erwin S. Andreasen * Henner Zeller * * This program is Public Domain */ #ifdef THREAD_SAVE #define _THREAD_SAVE //#include #include "minorGems/system/MutexLock.h" #endif #include #include #include #include #include #include #include #include #include #include /* * underlying allocation, de-allocation used within * this tool */ #define LT_MALLOC malloc #define LT_FREE free #define LT_REALLOC realloc /* * prime number for the address lookup hash table. * if you have _really_ many memory allocations, use a * higher value, like 343051 for instance. */ #define SOME_PRIME 35323 #define ADDR_HASH(addr) ((unsigned long) addr % SOME_PRIME) /** * Filedescriptor to write to. This should not be a low number, * because these often have special meanings (stdin, out, err) * and may be closed by the program (daemons) * So choose an arbitrary higher FileDescriptor .. e.g. 42 */ #define FILEDESC 42 /** * allocate a bit more memory in order to check if there is a memory * overwrite. Either 0 or more than sizeof(unsigned int). Note, you can * only detect memory over_write_, not _reading_ beyond the boundaries. Better * use electric fence for these kind of bugs * */ typedef unsigned long magic_t; #define MAGIC ((magic_t) 0xAABBCCDDLu) /** * this may be more than sizeof(magic_t); if you want more, then * sepecify it like #define SAVESIZE (sizeof(magic_t) + 12) */ #define SAVESIZE (sizeof(magic_t) + 0) /** * on 'new', initialize the memory with this value. * if not defined - uninitialized. This is very helpful because * it detects if you initialize your classes correctly .. if not, * this helps you faster to get the segmentation fault you're * implicitly asking for :-). * * Set this to some value which is likely to produce a * segmentation fault on your platform. */ #define SAVEVALUE 0xAA /** * on 'delete', clean memory with this value. * if not defined - no memory clean. * * Set this to some value which is likely to produce a * segmentation fault on your platform. */ #define MEMCLEAN 0xEE /** * Initial Number of memory allocations in our list. * Doubles for each re-allocation. */ #define INITIALSIZE 32768 static class LeakTracer { struct Leak { const void *addr; size_t size; const void *allocAddr; bool type; int nextBucket; }; int newCount; // how many memory blocks do we have int leaksCount; // amount of entries in the leaks array int firstFreeSpot; // Where is the first free spot in the leaks array? int currentAllocated; // currentAllocatedMemory int maxAllocated; // maximum Allocated unsigned long totalAllocations; // total number of allocations. stats. unsigned int abortOn; // resons to abort program (see abortReason_t) /** * Have we been initialized yet? We depend on this being * false before constructor has been called! */ bool initialized; bool destroyed; // Has our destructor been called? FILE *report; // filedescriptor to write to /** * pre-allocated array of leak info structs. */ Leak *leaks; /** * fast hash to lookup the spot where an allocation is * stored in case of an delete. map */ int *leakHash; // fast lookup #ifdef THREAD_SAVE MutexLock mutex; #endif enum abortReason_t { OVERWRITE_MEMORY = 0x01, DELETE_NONEXISTENT = 0x02, NEW_DELETE_MISMATCH = 0x04 }; public: LeakTracer() { initialize(); } void initialize() { // Unfortunately we might be called before our constructor has actualy fired if (initialized) return; // fprintf(stderr, "LeakTracer::initialize()\n"); initialized = true; newCount = 0; leaksCount = 0; firstFreeSpot = 1; // index '0' is special currentAllocated = 0; maxAllocated = 0; totalAllocations = 0; abortOn = OVERWRITE_MEMORY; // only _severe_ reason report = 0; leaks = 0; leakHash = 0; char uniqFilename[256]; const char *filename = getenv("LEAKTRACE_FILE") ? : "leak.out"; struct stat dummy; if (stat(filename, &dummy) == 0) { sprintf(uniqFilename, "%s.%d", filename, getpid()); fprintf(stderr, "LeakTracer: file exists; using %s instead\n", uniqFilename); } else { sprintf(uniqFilename, "%s", filename); } // not sure why this "open" code is here // (it doesn't open the file properly in MinGW on win32) /* int reportfd = open(uniqFilename, O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE); if (reportfd < 0) { fprintf(stderr, "LeakTracer: cannot open %s: %m\n", filename); report = stderr; } else { int dupfd = dup2(reportfd, FILEDESC); close(reportfd); report = fdopen(dupfd, "w"); if (report == NULL) { report = stderr; } } */ // simpler version using only fopen report = fopen( uniqFilename, "w" ); if( report == NULL ) { fprintf( stderr, "LeakTracer: cannot open %s\n", uniqFilename ); report = stderr; } time_t t = time(NULL); fprintf (report, "# starting %s", ctime(&t)); leakHash = (int*) LT_MALLOC(SOME_PRIME * sizeof(int)); memset ((void*) leakHash, 0x00, SOME_PRIME * sizeof(int)); #ifdef MAGIC fprintf (report, "# memory overrun protection of %d Bytes " "with magic 0x%4lX\n", SAVESIZE, MAGIC); #endif #ifdef SAVEVALUE fprintf (report, "# initializing new memory with 0x%2X\n", SAVEVALUE); #endif #ifdef MEMCLEAN fprintf (report, "# sweeping deleted memory with 0x%2X\n", MEMCLEAN); #endif if (getenv("LT_ABORTREASON")) { abortOn = atoi(getenv("LT_ABORTREASON")); } #define PRINTREASON(x) if (abortOn & x) fprintf(report, "%s ", #x); fprintf (report, "# aborts on "); PRINTREASON( OVERWRITE_MEMORY ); PRINTREASON( DELETE_NONEXISTENT ); PRINTREASON( NEW_DELETE_MISMATCH ); fprintf (report, "\n"); #undef PRINTREASON #ifdef THREAD_SAVE fprintf (report, "# thread save\n"); /* * create default, non-recursive ('fast') mutex * to lock our datastructure where we keep track of * the user's new/deletes */ /*if (pthread_mutex_init(&mutex, NULL) < 0) { fprintf(report, "# couldn't init mutex ..\n"); fclose(report); _exit(1); }*/ #else fprintf(report, "# not thread save; if you use threads, recompile with -DTHREAD_SAVE\n"); #endif fflush(report); } /* * the workhorses: */ void *registerAlloc(size_t size, bool type); void registerFree (void *p, bool type); /** * write a hexdump of the given area. */ void hexdump(const unsigned char* area, int size); /** * Terminate current running progam. */ void progAbort(abortReason_t reason) { if (abortOn & reason) { fprintf(report, "# abort; DUMP of current state\n"); writeLeakReport(); fclose(report); abort(); } else fflush(report); } /** * write a Report over leaks, e.g. still pending deletes */ void writeLeakReport(); ~LeakTracer() { // fprintf(stderr, "LeakTracer::destroy()\n"); time_t t = time(NULL); fprintf (report, "# finished %s", ctime(&t)); writeLeakReport(); fclose(report); free(leaks); #ifdef THREAD_SAVE //pthread_mutex_destroy(&mutex); #endif destroyed = true; } } leakTracer; void* LeakTracer::registerAlloc (size_t size, bool type) { initialize(); // fprintf(stderr, "LeakTracer::registerAlloc()\n"); if (destroyed) { fprintf(stderr, "Oops, registerAlloc called after destruction of LeakTracer (size=%d)\n", size); return LT_MALLOC(size); } void *p = LT_MALLOC(size + SAVESIZE); // Need to call the new-handler if (!p) { fprintf(report, "LeakTracer malloc %m\n"); _exit (1); } #ifdef SAVEVALUE /* initialize with some defined pattern */ memset(p, SAVEVALUE, size + SAVESIZE); #endif #ifdef MAGIC /* * the magic value is a special pattern which does not need * to be uniform. */ if (SAVESIZE >= sizeof(magic_t)) { magic_t *mag; mag = (magic_t*)((char*)p + size); *mag = MAGIC; } #endif #ifdef THREAD_SAVE //pthread_mutex_lock(&mutex); mutex.lock(); #endif ++newCount; ++totalAllocations; currentAllocated += size; if (currentAllocated > maxAllocated) maxAllocated = currentAllocated; for (;;) { for (int i = firstFreeSpot; i < leaksCount; i++) if (leaks[i].addr == NULL) { leaks[i].addr = p; leaks[i].size = size; leaks[i].type = type; leaks[i].allocAddr=__builtin_return_address(1); firstFreeSpot = i+1; // allow to lookup our index fast. int *hashPos = &leakHash[ ADDR_HASH(p) ]; leaks[i].nextBucket = *hashPos; *hashPos = i; #ifdef THREAD_SAVE //pthread_mutex_unlock(&mutex); mutex.unlock(); #endif return p; } // Allocate a bigger array // Note that leaksCount starts out at 0. int new_leaksCount = (leaksCount == 0) ? INITIALSIZE : leaksCount * 2; leaks = (Leak*)LT_REALLOC(leaks, sizeof(Leak) * new_leaksCount); if (!leaks) { fprintf(report, "# LeakTracer realloc failed: %m\n"); _exit(1); } else { fprintf(report, "# internal buffer now %d\n", new_leaksCount); fflush(report); } memset(leaks+leaksCount, 0x00, sizeof(Leak) * (new_leaksCount-leaksCount)); leaksCount = new_leaksCount; } } void LeakTracer::hexdump(const unsigned char* area, int size) { fprintf(report, "# "); for (int j=0; j < size ; ++j) { fprintf (report, "%02x ", *(area+j)); if (j % 16 == 15) { fprintf(report, " "); for (int k=-15; k < 0 ; k++) { char c = (char) *(area + j + k); fprintf (report, "%c", isprint(c) ? c : '.'); } fprintf(report, "\n# "); } } fprintf(report, "\n"); } void LeakTracer::registerFree (void *p, bool type) { initialize(); if (p == NULL) return; if (destroyed) { fprintf(stderr, "Oops, allocation destruction of LeakTracer (p=%p)\n", p); return; } #ifdef THREAD_SAVE //pthread_mutex_lock(&mutex); mutex.lock(); #endif int *lastPointer = &leakHash[ ADDR_HASH(p) ]; int i = *lastPointer; while (i != 0 && leaks[i].addr != p) { lastPointer = &leaks[i].nextBucket; i = *lastPointer; } if (leaks[i].addr == p) { *lastPointer = leaks[i].nextBucket; // detach. newCount--; leaks[i].addr = NULL; currentAllocated -= leaks[i].size; if (i < firstFreeSpot) firstFreeSpot = i; if (leaks[i].type != type) { fprintf(report, "S %10p %10p # new%s but delete%s " "; size %d\n", leaks[i].allocAddr, __builtin_return_address(1), ((!type) ? "[]" : " normal"), ((type) ? "[]" : " normal"), leaks[i].size); progAbort( NEW_DELETE_MISMATCH ); } #ifdef MAGIC if ((SAVESIZE >= sizeof(magic_t)) && *((magic_t*)((char*)p + leaks[i].size)) != MAGIC) { fprintf(report, "O %10p %10p " "# memory overwritten beyond allocated" " %d bytes\n", leaks[i].allocAddr, __builtin_return_address(1), leaks[i].size); fprintf(report, "# %d byte beyond area:\n", SAVESIZE); hexdump((unsigned char*)p+leaks[i].size, SAVESIZE); progAbort( OVERWRITE_MEMORY ); } #endif #ifdef THREAD_SAVE # ifdef MEMCLEAN int allocationSize = leaks[i].size; # endif //pthread_mutex_unlock(&mutex); mutex.unlock(); #else #define allocationSize leaks[i].size #endif #ifdef MEMCLEAN // set it to some garbage value. memset((unsigned char*)p, MEMCLEAN, allocationSize + SAVESIZE); #endif LT_FREE(p); return; } #ifdef THREAD_SAVE //pthread_mutex_unlock(&mutex); mutex.unlock(); #endif fprintf(report, "D %10p # delete non alloc or twice pointer %10p\n", __builtin_return_address(1), p); progAbort( DELETE_NONEXISTENT ); } void LeakTracer::writeLeakReport() { initialize(); if (newCount > 0) { fprintf(report, "# LeakReport\n"); fprintf(report, "# %10s | %9s # Pointer Addr\n", "from new @", "size"); } for (int i = 0; i < leaksCount; i++) if (leaks[i].addr != NULL) { // This ought to be 64-bit safe? char *memContents = (char *)LT_MALLOC( leaks[i].size + 1 ); memcpy( (void *)memContents, (void *)( leaks[i].addr ), leaks[i].size ); memContents[ leaks[i].size ] = '\0'; fprintf(report, "L %10p %9ld # %p %s\n", leaks[i].allocAddr, (long) leaks[i].size, leaks[i].addr, memContents ); LT_FREE( memContents ); } fprintf(report, "# total allocation requests: %6ld ; max. mem used" " %d kBytes\n", totalAllocations, maxAllocated / 1024); fprintf(report, "# leak %6d Bytes\t:-%c\n", currentAllocated, (currentAllocated == 0) ? ')' : '('); if (currentAllocated > 50 * 1024) { fprintf(report, "# .. that is %d kByte!! A lot ..\n", currentAllocated / 1024); } } /** -- The actual new/delete operators -- **/ void* operator new(size_t size) { return leakTracer.registerAlloc(size,false); } void* operator new[] (size_t size) { return leakTracer.registerAlloc(size,true); } void operator delete (void *p) { leakTracer.registerFree(p,false); } void operator delete[] (void *p) { leakTracer.registerFree(p,true); } // added by Jason Rohrer char *strdup( const char *inString ) { char *outString = (char*)leakTracer.registerAlloc( strlen( inString ) + 1, true ); strcpy( outString, inString ); return outString; } /* Emacs: * Local variables: * c-basic-offset: 8 * End: * vi:set tabstop=8 shiftwidth=8 nowrap: */ transcend-0.3+dfsg2.orig/minorGems/util/development/memory/0000750000175000017500000000000010305077117022474 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/development/memory/debugMemory.cpp0000640000175000017500000000421307554553735025501 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Added static initialization counting class for MemoryTrack. * * 2002-October-19 Jason Rohrer * Added more detail to error message. * Improved printing behavior. * Moved include of debugMemory.h to work better with IDE compilers. * Fixed to deal with differences between malloc and new[] on some platforms. * * 2002-October-20 Jason Rohrer * Removed delete macro trick that was causing crashes in tinyxml. * Removed function that was no longer being used. */ #include "minorGems/util/development/memory/debugMemory.h" #ifdef DEBUG_MEMORY #include "minorGems/util/development/memory/MemoryTrack.h" #include "stdlib.h" #include "stdio.h" void *debugMemoryNew( unsigned int inSize, const char *inFileName, int inLine ) { void *allocatedPointer = (void *)malloc( inSize ); MemoryTrack::addAllocation( allocatedPointer, inSize, SINGLE_ALLOCATION, inFileName, inLine ); return allocatedPointer; } void *debugMemoryNewArray( unsigned int inSize, const char *inFileName, int inLine ) { unsigned int mallocSize = inSize; if( inSize == 0 ) { // always allocate at least one byte to circumvent differences // between malloc and new[] on some platforms // (new int[0] returns a pointer to an array of length 0, while // malloc( 0 ) can return NULL on some platforms) mallocSize = 1; } void *allocatedPointer = (void *)malloc( mallocSize ); MemoryTrack::addAllocation( allocatedPointer, inSize, ARRAY_ALLOCATION, inFileName, inLine ); return allocatedPointer; } void debugMemoryDelete( void *inPointer ) { MemoryTrack::addDeallocation( inPointer, SINGLE_ALLOCATION ); free( inPointer ); } void debugMemoryDeleteArray( void *inPointer ) { MemoryTrack::addDeallocation( inPointer, ARRAY_ALLOCATION ); free( inPointer ); } #endif transcend-0.3+dfsg2.orig/minorGems/util/development/memory/MemoryTrack.cpp0000640000175000017500000001620507554553734025462 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Changed to use custom list instead of SimpleVector because SimpleVector * uses debugMemory. * Added static initialization counting class. * Changed to use struct and malloc for AllocationList to avoid * circular new and delete calls. * * 2002-October-19 Jason Rohrer * Fixed a bug in adding to the alloc list. * Improved printing behavior. * Added support for clearing memory on allocation and deallocation. * Fixed to ignore deallocation of our own static lock. * Fixed bug in allocation count. * Added message for NULL pointer deallocation. * Put locks in place around print statements, which are not atomic on win32. * Changed to use hex notation when printing pointers. * * 2002-October-19 Jason Rohrer * Added ifdef for DEBUG_MEMORY. * * 2002-October-20 Jason Rohrer * Removed file and line arguments from deallocation calls. */ #ifdef DEBUG_MEMORY #include "minorGems/util/development/memory/MemoryTrack.h" #include #include #include int MemoryTrackStaticInitCounter::mCount = 0; MutexLock *MemoryTrack::mLock; AllocationList *MemoryTrack::mListHead; char MemoryTrack::mTracking = false; int MemoryTrack::mTotalAllocationSize = 0; int MemoryTrack::mTotalDeallocationSize = 0; int MemoryTrack::mNumberOfAllocations = 0; void MemoryTrack::addAllocation( void *inPointer, unsigned int inAllocationSize, int inAllocationType, const char *inFileName, int inLineNumber ) { mLock->lock(); if( !mTracking ) { printf( "Tracking off on allocation (0x%x) [%d bytes] %s:%d.\n", (unsigned int)inPointer, inAllocationSize, inFileName, inLineNumber ); mLock->unlock(); return; } // insert after head of list AllocationList *element = (AllocationList *)malloc( sizeof( AllocationList ) ); element->mPrevious = (void *)mListHead; element->mNext = mListHead->mNext; mListHead->mNext = (void *)element; AllocationList *nextElement = (AllocationList *)( element->mNext ); if( nextElement != NULL ) { nextElement->mPrevious = (void *)element; } element->mPointer = inPointer; element->mAllocationSize = inAllocationSize; element->mAllocationType = inAllocationType; element->mFileName = inFileName; element->mLineNumber = inLineNumber; mTotalAllocationSize += inAllocationSize; mNumberOfAllocations ++; // wipe this block of memory clearMemory( inPointer, inAllocationSize ); mLock->unlock(); } int MemoryTrack::addDeallocation( void *inPointer, int inDeallocationType ) { mLock->lock(); if( inPointer == NULL ) { printf( "NULL pointer (0x%x) deallocated\n", (unsigned int)inPointer ); } if( inPointer == (void *)mLock ) { // we're seeing the deallocation of our own static lock as // the system exits // ignore it mLock->unlock(); return 0; } if( !mTracking ) { printf( "Tracking off on deallocation (0x%x)\n", (unsigned int)inPointer ); mLock->unlock(); return 0; } AllocationList *element = (AllocationList *)( mListHead->mNext ); while( element != NULL ) { void *pointer = element->mPointer; if( pointer == inPointer ) { unsigned int allocationSize = element->mAllocationSize; int allocationType = element->mAllocationType; const char *allocFileName = element->mFileName; int allocLineNumber = element->mLineNumber; // remove from list, whether or not types match AllocationList *previousElement = (AllocationList *)( element->mPrevious ); AllocationList *nextElement = (AllocationList *)( element->mNext ); // patch list previousElement->mNext = (void *)( nextElement ); if( nextElement != NULL ) { nextElement->mPrevious = (void *)( previousElement ); } free( element ); mTotalDeallocationSize += allocationSize; if( allocationType == inDeallocationType ) { // found and types match mLock->unlock(); // wipe this block of memory clearMemory( inPointer, allocationSize ); return 0; } else { // allocation types don't match printf( "Attempt to deallocate (0x%x) [%d bytes] with wrong" " delete form\n" " %s:%d (location of original allocation)\n", (unsigned int)inPointer, allocationSize, allocFileName, allocLineNumber ); mLock->unlock(); return 2; } } element = (AllocationList *)( element->mNext ); } // not found (delete of unallocated memory) printf( "Attempt to deallocate (0x%x) unallocated memory\n", (unsigned int)inPointer ); mLock->unlock(); return 1; } void MemoryTrack::printLeaks() { mLock->lock(); printf( "\n\n---- debugMemory report ----\n" ); printf( "Number of Allocations: %d\n", mNumberOfAllocations ); printf( "Total allocations: %d bytes\n", mTotalAllocationSize ); printf( "Total deallocations: %d bytes\n", mTotalDeallocationSize ); int leakEstimate = mTotalAllocationSize - mTotalDeallocationSize; AllocationList *element = (AllocationList *)( mListHead->mNext ); if( element == NULL ) { printf( "No leaks detected.\n" ); } else { printf( "Leaks detected:\n" ); } int leakSum = 0; while( element != NULL ) { printf( "Not deallocated (0x%x) [%d bytes]\n" " %s:%d (location of original allocation)\n", (unsigned int)( element->mPointer ), element->mAllocationSize, element->mFileName, element->mLineNumber ); leakSum += element->mAllocationSize; element = (AllocationList *)( element->mNext ); } if( leakSum != leakEstimate ) { printf( "Warning: Leak sum does not equal leak estimate.\n" ); } printf( "Leaked memory: %d bytes\n", leakSum ); printf( "---- END debugMemory report ----\n\n" ); mLock->unlock(); } void MemoryTrack::clearMemory( void *inPointer, unsigned int inSize ) { unsigned char *charArray = (unsigned char *)inPointer; for( unsigned int i=0; i #include #define SINGLE_ALLOCATION 0 #define ARRAY_ALLOCATION 1 /** * Linked list of memory allocations. * * @author Jason Rohrer */ typedef struct { void *mPrevious; void *mNext; void *mPointer; unsigned int mAllocationSize; int mAllocationType; const char *mFileName; int mLineNumber; } AllocationList; /** * Class that tracks memory allocations and deallocations. * * @author Jason Rohrer */ class MemoryTrack { public: /** * Adds an allocation to this tracker and clears the allocated * memory block. * * @param inPointer a pointer to the allocated memory. * @param inAllocationType the type of allocation, * either SINGLE_ALLOCATION or ARRAY_ALLOCATION. * @param inAllocationSize the size of the allocation in bytes. * @param inFileName the name of the source file in which the * allocation took place. * @param inLineNumber the line number in the source file * on which the allocation took place. */ static void addAllocation( void *inPointer, unsigned int inAllocationSize, int inAllocationType, const char *inFileName, int inLineNumber ); /** * Adds a deallocation to this tracker and clears the block * to be deallocated. * Must be called *before* the memory is deallocated * * @param inPointer a pointer to the memory being deallocated. * @param inDeallocationType the type of deallocation, * either SINGLE_ALLOCATION or ARRAY_ALLOCATION. * @return 0 if the deallocation deallocates * an allocated block of memory, or 1 if it * deallocates a block of memory that is not currently allocated, * and 2 if it is the wrong deallocation type for the specified * block. */ static int addDeallocation( void *inPointer, int inDeallocationType ); /** * Prints a list of all memory leaks (allocations that have never * been deallocated). */ static void printLeaks(); // these are public so initializer can get to them static MutexLock *mLock; // dummy place holder for list head static AllocationList *mListHead; // true if we're tracking static char mTracking; static int mTotalAllocationSize; static int mTotalDeallocationSize; static int mNumberOfAllocations; protected: /** * Clears memory so that reading from it will not produce * anything useful. Good for checking for reads to memory that * has been deallocated. * * @param inPointer pointer to the memory to clear. * @Param inSize the number of bytes to clear starting at inPointer. */ static void clearMemory( void *inPointer, unsigned int inSize ); }; /** * Class that initializes MemoryTrack's static members. * * *All* files that use MemoryTrack will instantiate a static * instance of this class (see static instance below). * * This class counts how many static instantiations have happened so * far, making sure to init/destroy MemoryTrack's static members only once. * * Adapted from: * http://www.hlrs.de/organization/par/services/tools/docu/kcc/ * tutorials/static_initialization.html */ class MemoryTrackStaticInitCounter { public: MemoryTrackStaticInitCounter() { if( mCount == 0 ) { // allocate static members MemoryTrack::mLock = new MutexLock(); MemoryTrack::mListHead = (AllocationList *) malloc( sizeof( AllocationList ) ); MemoryTrack::mListHead->mPrevious = NULL; MemoryTrack::mListHead->mNext = NULL; MemoryTrack::mTotalAllocationSize = 0; MemoryTrack::mTotalDeallocationSize = 0; MemoryTrack::mNumberOfAllocations = 0; MemoryTrack::mTracking = true; } mCount++; } ~MemoryTrackStaticInitCounter() { mCount--; if( mCount == 0 ) { // print leaks... we should only get here after // all static members of classes that use MemoryTrack // have been destroyed. MemoryTrack::printLeaks(); MemoryTrack::mTracking = false; // deallocate static members free( MemoryTrack::mListHead ); delete MemoryTrack::mLock; } } private: // only allocate/deallocate when mCount == 0 static int mCount; }; // This will be included in *every* file that includes MemoryTrack.h static MemoryTrackStaticInitCounter memoryTrackInitializer; #endif transcend-0.3+dfsg2.orig/minorGems/util/development/memory/testDebugMemory.cpp0000640000175000017500000000267707554421343026342 0ustar pabspabs/* * Modification History * * 2002-October-17 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Added static initialization counting class for MemoryTrack. * * 2002-October-19 Jason Rohrer * Removed call to debugMemoryPrintLeaksAndStopTracking. * Made test cases more interesting. * Added test cases for deleting NULL pointers. */ #include "minorGems/util/development/memory/debugMemory.h" #include class TestClass { public: TestClass() { mX = new int[5]; } ~TestClass() { delete [] mX; } int *mX; }; int main() { int *x = new int[5]; printf( "array contents before initializing elements:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); x[0] = 1; x[1] = 2; x[2] = 3; x[3] = 4; x[4] = 5; printf( "array contents before deleting:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); delete [] x; printf( "array contents after deleting:\n" "%d, %d, %d, %d, %d\n\n", x[0], x[1], x[2], x[3], x[4] ); int *y = new int[4]; y[0] = 1; TestClass *t = new TestClass(); delete t; int *z = new int[7]; delete z; //delete t; int *badPointer = NULL; delete badPointer; int *badPointer2 = NULL; delete [] badPointer2; return 0; } transcend-0.3+dfsg2.orig/minorGems/util/SettingsManager.h0000640000175000017500000001456607722152350022125 0ustar pabspabs/* * Modification History * * 2002-September-16 Jason Rohrer * Created. * Fixed a memory leak. * * 2002-September-25 Jason Rohrer * Added a setSetting function that takes a string value. * * 2002-September-26 Jason Rohrer * Added functions for integer values. * * 2003-August-24 Jason Rohrer * Fixed to remove 499-character limit for a setting value. */ #include "minorGems/common.h" #ifndef SETTINGS_MANAGER_INCLUDED #define SETTINGS_MANAGER_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/system/MutexLock.h" #include // utility class for dealing with static member dealocation class SettingsManagerStaticMembers; /** * Class that manages program settings. * * @author Jason Rohrer */ class SettingsManager { public: /** * Sets the directory name where settings are stored. * * @param inName the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static void setDirectoryName( char *inName ); /** * Gets the directory name where settings are stored. * * @return the name of the directory (relative to the * program's working directory). * Must be destroyed by caller if non-const. */ static char *getDirectoryName(); /** * Gets a setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * * @return a vector of strings representing the setting value. * The vector and the strings it contains must be destroyed * by the caller. */ static SimpleVector *getSetting( char *inSettingName ); /** * Gets a string setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * * @return the setting value string, or NULL if no setting * value can be read. * Must be destroyed by caller if non-NULL. */ static char *getStringSetting( char *inSettingName ); /** * Gets a float setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * @param outValueFound pointer to where flag should be returned * indicating whether or not the value was found. * Set to true if the value was found, false otherwise. * * @return the setting value. */ static float getFloatSetting( char *inSettingName, char *outValueFound ); /** * Gets an integer setting. * * @param inSettingName the name of the setting to get. * Must be destroyed by caller if non-const. * @param outValueFound pointer to where flag should be returned * indicating whether or not the value was found. * Set to true if the value was found, false otherwise. * * @return the setting value. */ static int getIntSetting( char *inSettingName, char *outValueFound ); /** * Sets a setting. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingVector a vector of strings representing the * setting value. * The vector and the strings it contains must be destroyed * by the caller. */ static void setSetting( char *inSettingName, SimpleVector *inSettingVector ); /** * Sets a setting to a single float value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. */ static void setSetting( char *inSettingName, float inSettingValue ); /** * Sets a setting to a single int value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. */ static void setSetting( char *inSettingName, int inSettingValue ); /** * Sets a setting to a single string value. * * @param inSettingName the name of the setting to set. * Must be destroyed by caller if non-const. * @param inSettingValue the value to set. * Must be destroyed by caller. */ static void setSetting( char *inSettingName, char *inSettingValue ); protected: static SettingsManagerStaticMembers mStaticMembers; /** * Gets the file for a setting name. * * @param inSettingName the name of the setting. * Must be destroyed by caller if non-const. * @param inReadWriteFlags the flags to pass into the * fopen call. For example, "r" or "w". * * @return the file descriptor, or NULL if the open failed. * Must be fclose()'d by caller if non-NULL. */ static FILE *getSettingsFile( char *inSettingName, const char *inReadWriteFlags ); /** * Gets the file name for a setting. * * @param inSettingName the name of the setting. * Must be destroyed by caller if non-const. * * @return the name of the settings file. * Must be destroyed by caller. */ static char *getSettingsFileName( char *inSettingName ); }; /** * Container for static members to allow for their proper destruction * on program termination. * * @author Jason Rohrer */ class SettingsManagerStaticMembers { public: SettingsManagerStaticMembers(); ~SettingsManagerStaticMembers(); char *mDirectoryName; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/CircularBuffer.h0000640000175000017500000000614107554101514021715 0ustar pabspabs/* * Modification History * * 2001-Janary-11 Jason Rohrer * Created. * * 2001-Janary-12 Jason Rohrer * Added canRead and canWrite functions. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. */ #include "minorGems/common.h" #ifndef CIRCULAR_BUFFER_INCLUDED #define CIRCULAR_BUFFER_INCLUDED #include "minorGems/system/Semaphore.h" /** * Thread-safe circular buffer. * * @author Jason Rohrer */ class CircularBuffer { public: /** * Constructs a CircularBuffer. * * @param inSize the number of objects in this buffer. */ CircularBuffer( unsigned long inSize ); ~CircularBuffer(); /** * Writes an object into the next free position in the buffer. * Blocks if no free positions are available. * * @param inObject the object pointer to write. */ void writeObject( void *inObject ); /** * Reads the next available object from the buffer. * Blocks if now objects are available. * * @return the object pointer read. */ void *readNextObject(); /** * Returns true if an object can be read from this buffer * without blocking. */ char canRead(); /** * Returns true if an object can be written to this buffer * without blocking. */ char canWrite(); private: unsigned long mBufferSize; void **mObjects; unsigned long mReadIndex, mWriteIndex; // initialized to 0 Semaphore *mReadSemaphore; // initialized to mBufferSize; Semaphore *mWriteSemaphore; MutexLock *mLock; }; inline CircularBuffer::CircularBuffer( unsigned long inSize ) : mBufferSize( inSize ), mObjects( new void*[inSize] ), mReadIndex( 0 ), mWriteIndex( 0 ), mReadSemaphore( new Semaphore( 0 ) ), mWriteSemaphore( new Semaphore( inSize ) ), mLock( new MutexLock() ) { } inline CircularBuffer::~CircularBuffer() { delete [] mObjects; delete mReadSemaphore; delete mWriteSemaphore; delete mLock; } // note that in this implementation, no mutex is needed, since // reader and writer are never accessing the same data members // simultaneously inline void CircularBuffer::writeObject( void *inObject ) { // wait to for a space to write into mWriteSemaphore->wait(); // write, and increment our write location mObjects[ mWriteIndex ] = inObject; mWriteIndex++; mWriteIndex = mWriteIndex % mBufferSize; // signal the reader that an new object is ready mReadSemaphore->signal(); } inline void *CircularBuffer::readNextObject() { void *returnObject; // wait for an object to read mReadSemaphore->wait(); // read the object, and increment our read location returnObject = mObjects[ mReadIndex ]; mReadIndex++; mReadIndex = mReadIndex % mBufferSize; // signal the writer mWriteSemaphore->signal(); // now return the object that we retrieved from the buffer return returnObject; } inline char CircularBuffer::canRead() { // can read if the read semaphore won't block return ! mReadSemaphore->willBlock(); } inline char CircularBuffer::canWrite() { // can write if the write semaphore won't block return ! mWriteSemaphore->willBlock(); } #endif transcend-0.3+dfsg2.orig/minorGems/util/SettingsManager.cpp0000640000175000017500000001336507722152350022454 0ustar pabspabs/* * Modification History * * 2002-September-16 Jason Rohrer * Created. * Fixed a memory leak. * * 2002-September-25 Jason Rohrer * Added a setSetting function that takes a string value. * * 2002-September-26 Jason Rohrer * Added functions for integer values. * * 2003-January-11 Jason Rohrer * Added default values for float and int settings. * * 2003-August-24 Jason Rohrer * Fixed to remove 499-character limit for a setting value. */ #include "SettingsManager.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/Path.h" // will be destroyed automatically at program termination SettingsManagerStaticMembers SettingsManager::mStaticMembers; void SettingsManager::setDirectoryName( char *inName ) { delete [] mStaticMembers.mDirectoryName; mStaticMembers.mDirectoryName = stringDuplicate( inName ); } char *SettingsManager::getDirectoryName() { return stringDuplicate( mStaticMembers.mDirectoryName ); } SimpleVector *SettingsManager::getSetting( char *inSettingName ) { char *fileName = getSettingsFileName( inSettingName ); File *settingsFile = new File( NULL, fileName ); delete [] fileName; char *fileContents = settingsFile->readFileContents(); delete settingsFile; if( fileContents == NULL ) { // return empty vector return new SimpleVector(); } // else tokenize the file contents SimpleVector *returnVector = tokenizeString( fileContents ); delete [] fileContents; return returnVector; } char *SettingsManager::getStringSetting( char *inSettingName ) { char *value = NULL; SimpleVector *settingsVector = getSetting( inSettingName ); int numStrings = settingsVector->size(); if( numStrings >= 1 ) { char *firstString = *( settingsVector->getElement( 0 ) ); value = stringDuplicate( firstString ); } for( int i=0; igetElement( i ) ); delete [] nextString; } delete settingsVector; return value; } float SettingsManager::getFloatSetting( char *inSettingName, char *outValueFound ) { char valueFound = false; float value = 0; char *stringValue = getStringSetting( inSettingName ); if( stringValue != NULL ) { int numRead = sscanf( stringValue, "%f", &value ); if( numRead == 1 ) { valueFound = true; } delete [] stringValue; } *outValueFound = valueFound; return value; } int SettingsManager::getIntSetting( char *inSettingName, char *outValueFound ) { char valueFound = false; int value = 0; char *stringValue = getStringSetting( inSettingName ); if( stringValue != NULL ) { int numRead = sscanf( stringValue, "%d", &value ); if( numRead == 1 ) { valueFound = true; } delete [] stringValue; } *outValueFound = valueFound; return value; } void SettingsManager::setSetting( char *inSettingName, SimpleVector *inSettingVector ) { FILE *file = getSettingsFile( inSettingName, "w" ); if( file != NULL ) { int numSettings = inSettingVector->size(); for( int i=0; igetElement( i ) ); fprintf( file, "%s\n", setting ); } fclose( file ); } // else do nothing } void SettingsManager::setSetting( char *inSettingName, float inSettingValue ) { char *valueString = new char[ 15 ]; sprintf( valueString, "%f", inSettingValue ); setSetting( inSettingName, valueString ); delete [] valueString; } void SettingsManager::setSetting( char *inSettingName, int inSettingValue ) { char *valueString = new char[ 15 ]; sprintf( valueString, "%d", inSettingValue ); setSetting( inSettingName, valueString ); delete [] valueString; } void SettingsManager::setSetting( char *inSettingName, char *inSettingValue ) { SimpleVector *settingsVector = new SimpleVector( 1 ); settingsVector->push_back( inSettingValue ); setSetting( inSettingName, settingsVector ); delete settingsVector; } FILE *SettingsManager::getSettingsFile( char *inSettingName, const char *inReadWriteFlags ) { char *fullFileName = getSettingsFileName( inSettingName ); FILE *file = fopen( fullFileName, inReadWriteFlags ); delete [] fullFileName; return file; } char *SettingsManager::getSettingsFileName( char *inSettingName ) { char **pathSteps = new char*[1]; pathSteps[0] = mStaticMembers.mDirectoryName; char *fileName = new char[ strlen( inSettingName ) + 10 ]; sprintf( fileName, "%s.ini", inSettingName ); File *settingsFile = new File( new Path( pathSteps, 1, false ), fileName ); delete [] fileName; // pathSteps copied internally by Path constructor delete [] pathSteps; char *fullFileName = settingsFile->getFullFileName(); delete settingsFile; return fullFileName; } SettingsManagerStaticMembers::SettingsManagerStaticMembers() : mDirectoryName( stringDuplicate( "settings" ) ) { } SettingsManagerStaticMembers::~SettingsManagerStaticMembers() { delete [] mDirectoryName; } transcend-0.3+dfsg2.orig/minorGems/util/random/0000750000175000017500000000000010305077117020122 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/random/Noise.h0000640000175000017500000000473207554101514021360 0ustar pabspabs// Jason Rohrer // Noise.h /** * * Noise generation interface * * * Created 11-3-99 * Mods: * Jason Rohrer 12-20-2000 Added a fractal noise function * that fills a double array. * */ #include "minorGems/common.h" #ifndef NOISE_INCLUDED #define NOISE_INCLUDED #include #include #include "RandomSource.h" // fills 2d image with RGBA noise void genRandNoise2d(unsigned long *buff, int buffHigh, int buffWide); // returns a random floating point between 0 and 1 inline float floatRand() { float invRandMax = 1 / ((float)RAND_MAX); return (float)(rand()) * invRandMax; } // fills 2d image with ARGB fractal noise void genFractalNoise2d(unsigned long *buff, int buffHigh, int buffWide); /** * Fills a 2d array with 1/f fractal noise. * * @param inBuffer a pre-allocated buffer to fill. * @param inWidth the width and height of the 2d array * contained in the buffer. * Must be a power of 2. * @param inMaxFrequency the maximum frequency of noise modulation to * include, in [2,inWidth]. Lower values produce more "blurry" or * blocky noise. * @param inFPower power to raise F to whene generating noise. * Amplitude of modulation = 1 / (F^inFPower). * @param inInterpolate set to true to perform interpolation of * each frequency modulation. Setting to false produces a "blockier" * noise, while setting to true makes the noise more cloud-like. * @param inRandSource the source to use for random numbers. */ void genFractalNoise2d( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ); /** * Fills a 1d array with 1/f fractal noise. * * @param inBuffer a pre-allocated buffer to fill. * @param inWidth the width of the 2d array * contained in the buffer. * Must be a power of 2. * @param inMaxFrequency the maximum frequency of noise modulation to * include, in [2,inWidth]. Lower values produce more "blurry" or * blocky noise. * @param inFPower power to raise F to whene generating noise. * Amplitude of modulation = 1 / (F^inFPower). * @param inInterpolate set to true to perform interpolation of * each frequency modulation. Setting to false produces a "blockier" * noise, while setting to true makes the noise more cloud-like. * @param inRandSource the source to use for random numbers. */ void genFractalNoise( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ); #endif transcend-0.3+dfsg2.orig/minorGems/util/random/RandomSource.h0000640000175000017500000000253007554101514022676 0ustar pabspabs// Jason Rohrer // RandomSource.h /** * * abstract interface for random number generation * * Can be implemented by: * --stdlib rand() function calls * --seed file full of random numbers * * Created 12-7-99 * Mods: * Jason Rohrer 9-28-2000 Added a getRandomBoundedInt() * interface to faciliate retrieving * an integer in a given range [a,b] * where each integer in the range * has the same probability of being * returned. * Jason Rohrer 12-16-2000 Added a getRandomDouble() interface. */ #include "minorGems/common.h" #ifndef RANDOM_SOURCE_INCLUDED #define RANDOM_SOURCE_INCLUDED class RandomSource { public: // pure virtual functions implemented by inheriting classes virtual float getRandomFloat() = 0; // in interval [0,1.0] virtual double getRandomDouble() = 0; // in interval [0,1.0] virtual long getRandomInt() = 0; // in interval [0,MAX] virtual long getIntMax() = 0; // returns MAX /** * Returns a random integer in [rangeStart,rangeEnd] * where each integer in the range has an equal * probability of occuring. */ virtual long getRandomBoundedInt( long inRangeStart, long inRangeEnd ) = 0; protected: long MAX; // maximum integer random number float invMAX; // floating point inverse of MAX double invDMAX; // double invers of MAX }; #endif transcend-0.3+dfsg2.orig/minorGems/util/random/StdRandomSource.h0000640000175000017500000000517507554101514023361 0ustar pabspabs// Jason Rohrer // StdRandomSource.h /** * * Implementation of random number generation that uses stdlib calls * * * Created 12-7-99 * Mods: * Jason Rohrer 9-28-2000 Added a getRandomBoundedInt() * implementation * Jason Rohrer 12-7-2000 Overloaded constructor to support * specifying a seed. * Jason Rohrer 12-16-2000 Added a getRandomDouble() function. * Jason Rohrer 12-17-2000 Fixed bug in initialization of invDMAX * in default constructor. * Jason Rohrer 9-13-2001 Fixed a bug in getRandomBoundedInt() * as floats were being used, and they * don't provide enough resolution. * Jason Rohrer 10-11-2002 Fixed some type casting warnings. */ #include "minorGems/common.h" #ifndef STD_RANDOM_SOURCE_INCLUDED #define STD_RANDOM_SOURCE_INCLUDED #include #include #include "RandomSource.h" class StdRandomSource : public RandomSource { public: StdRandomSource(); // needed to seed stdlib generator // specify the seed for the stdlib generator StdRandomSource( unsigned long inSeed ); // implements these functions float getRandomFloat(); // in interval [0,1.0] double getRandomDouble(); // in interval [0,1.0] long getRandomInt(); // in interval [0,MAX] long getIntMax(); // returns MAX /** * Returns a random integer in [rangeStart,rangeEnd] * where each integer in the range has an equal * probability of occuring. */ long getRandomBoundedInt( long inRangeStart, long inRangeEnd ); private: double mInvMAXPlusOne; // 1 / ( MAX + 1 ) }; inline StdRandomSource::StdRandomSource() { MAX = RAND_MAX; srand( (unsigned)time(NULL) ); invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (float)MAX ) + 1.0 ); } inline StdRandomSource::StdRandomSource( unsigned long inSeed ) { MAX = RAND_MAX; srand( inSeed ); invMAX = (float)1.0 / ((float)MAX); invDMAX = 1.0 / ((double)MAX); mInvMAXPlusOne = 1.0 / ( ( (double)MAX ) + 1.0 ); } inline float StdRandomSource::getRandomFloat() { return (float)(rand()) * invMAX; } inline double StdRandomSource::getRandomDouble() { return (double)(rand()) * invDMAX; } inline long StdRandomSource::getRandomInt() { return rand(); } inline long StdRandomSource::getIntMax() { return MAX; } inline long StdRandomSource::getRandomBoundedInt( long inRangeStart, long inRangeEnd ) { // float in range [0,1) double randFloat = (double)( rand() ) * mInvMAXPlusOne; long onePastRange = inRangeEnd + 1; long magnitude = (int)( randFloat * ( onePastRange - inRangeStart ) ); return magnitude + inRangeStart; } #endif transcend-0.3+dfsg2.orig/minorGems/util/random/Noise.cpp0000640000175000017500000001625407220517221021711 0ustar pabspabs// Jason Rohrer // Noise.cpp /** * * Noise generation implementation * * * Created 11-3-99 * Mods: * Jason Rohrer 12-20-2000 Changed genFractalNoise2d function to make * it less blocky. * */ #include "Noise.h" // fills 2d image with ARGB noise void genRandNoise2d(unsigned long *buff, int buffHigh, int buffWide) { int *yOffset = new int[buffHigh]; // precalc y offsets for( int y=0; y> 24 & 0xFF) + alpha; int buffRed = (buffARGB >> 16 & 0xFF) + red; int buffGreen = (buffARGB >> 8 & 0xFF) + green; int buffBlue = (buffARGB & 0xFF) + blue; if( buffAlpha < 0) buffAlpha = 0; if( buffRed < 0) buffRed = 0; if( buffGreen < 0) buffGreen = 0; if( buffBlue < 0) buffBlue = 0; if( buffAlpha > 255) buffAlpha = 255; if( buffRed > 255) buffRed = 255; if( buffGreen > 255) buffGreen = 255; if( buffBlue > 255) buffBlue = 255; buff[ yOffset[buffY] + buffX] = buffBlue | buffGreen << 8 | buffRed << 16 | buffAlpha << 24; buffX++; blockX++; if( buffX >= buffWide ) blockX = blockWide; // if this block hangs outside buffer } buffY++; blockY++; if( buffY >= buffHigh ) blockY = blockHigh; // if this block hangs outside buffer } buffX = startX + blockWide; } buffY = startY + blockHigh; } } delete [] yOffset; } void genFractalNoise2d( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ) { RandomSource *r = inRandSource; int w = inWidth; int i, x, y, f; int numPoints = w * w; // first, fill surface with a uniform 0.5 value for( i=0; igetRandomDouble() - 1 ) * weight; } // now walk though 2d array and perform // bilinear interpolation between blocks for( y=0; y 1.0 ) { inBuffer[y * w + x] = 1.0; } else if( inBuffer[y * w + x] < 0.0 ) { inBuffer[y * w + x] = 0.0; } } } delete [] blockValues; } } void genFractalNoise( double *inBuffer, int inWidth, int inMaxFrequency, double inFPower, char inInterpolate, RandomSource *inRandSource ) { RandomSource *r = inRandSource; int w = inWidth; int i, x, f; // first, fill array with uniform 0.5 values for( i=0; igetRandomDouble() - 1 ) * weight; } // now walk though array and perform linear interpolation between blocks for( x=0; x 1.0 ) { inBuffer[x] = 1.0; } else if( inBuffer[x] < 0.0 ) { inBuffer[x] = 0.0; } } delete [] blockValues; } } transcend-0.3+dfsg2.orig/minorGems/util/stringUtils.h0000640000175000017500000002030407726643110021347 0ustar pabspabs/* * Modification History * * 2002-April-6 Jason Rohrer * Created. * * 2002-April-8 Jason Rohrer * Fixed multiple inclusion bug * * 2002-May-7 Jason Rohrer * Added functions for case-insensitive substring finding and case * conversion. * * 2002-May-9 Jason Rohrer * Fixed a bug when string not found. * * 2002-May-26 Jason Rohrer * Added a function for string comparison ignoring cases. * * 2003-March-28 Jason Rohrer * Added split function. * * 2003-May-1 Jason Rohrer * Added replacement functions. * * 2003-May-4 Jason Rohrer * Added list replacement function. * * 2003-May-10 Jason Rohrer * Moved implementations into a .cpp file. This will break several * projects. * Added a tokenization function. * * 2003-June-14 Jason Rohrer * Added a join function. * * 2003-June-22 Jason Rohrer * Added an autoSprintf function. * * 2003-August-12 Jason Rohrer * Added a concatonate function. * * 2003-September-7 Jason Rohrer * Improved split comment. */ #include "minorGems/common.h" #include "minorGems/util/SimpleVector.h" #ifndef STRING_UTILS_INCLUDED #define STRING_UTILS_INCLUDED // ANSI does not support strdup, strcasestr, or strcasecmp #include #include #include /** * Duplicates a string into a newly allocated string. * * @param inString the \0-terminated string to duplicate. * Must be destroyed by caller if non-const. * * @return a \0-terminated duplicate of inString. * Must be destroyed by caller. */ inline char *stringDuplicate( const char *inString ) { char *returnBuffer = new char[ strlen( inString ) + 1 ]; strcpy( returnBuffer, inString ); return returnBuffer; } /** * Converts a string to lower case. * * @param inString the \0-terminated string to convert. * Must be destroyed by caller if non-const. * * @return a newly allocated \0-terminated string * that is a lowercase version of inString. * Must be destroyed by caller. */ char *stringToLowerCase( const char *inString ); /** * Searches for the first occurrence of one string in another. * * @param inHaystack the \0-terminated string to search in. * Must be destroyed by caller if non-const. * @param inNeedle the \0-terminated string to search for. * Must be destroyed by caller if non-const. * * @return a string pointer into inHaystack where the * first occurrence of inNeedle starts, or NULL if inNeedle is not found. */ char *stringLocateIgnoreCase( const char *inHaystack, const char *inNeedle ); /** * Compares two strings, ignoring case. * * @param inStringA the first \0-terminated string. * Must be destroyed by caller if non-const. * @param inStringB the second \0-terminated string. * Must be destroyed by caller if non-const. * * @return an integer less than, equal to, or greater than zero if * inStringA is found, respectively, to be less than, to match, or be * greater than inStringB. */ int stringCompareIgnoreCase( const char *inStringA, const char *inStringB ); /** * Splits a string into parts around a separator string. * * Note that splitting strings that start and/or end with the separator * results in "empty" strings being returned at the start and/or end * of the parts array. * * For example, splitting "a#short#test" around the "#" separator will * result in the array { "a", "short", "test" }, but splitting * "#another#short#test#" will result in the array * { "", "another", "short", "test", "" }. * * This differs somewhat from the perl version of split, but it gives the * caller more information about the string being split. * * @param inString the string to split. * Must be destroyed by caller if non-const. * @param inSeparator the separator string. * Must be destroyed by caller if non-const. * @param outNumParts pointer to where the the number of parts (the length of * the returned array) should be returned. * * @return an array of split parts. * Must be destroyed by caller. */ char **split( char *inString, char *inSeparator, int *outNumParts ); /** * Joins a collection of strings using a separator string. * * @param inStrings the strings to join. * Array and strings must be destroyed by caller. * @param inNumParts the number of strings to join. * @param inSeparator the separator string. * Must be destroyed by caller if non-const. * * @return the joined string. * Must be destroyed by caller. */ char *join( char **inStrings, int inNumParts, char *inGlue ); /** * Concatonates two strings. * * @param inStringA the first string in the concatonation. * Must be destroyed by caller if non-const. * @param inStringB the second string in the concatonation. * Must be destroyed by caller if non-const. * * @return the concatonation. * Must be destroyed by caller. */ char *concatonate( char *inStringA, char *inStringB ); /** * Replaces the first occurrence of a target string with * a substitute string. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for inTarget in. * @param inTarget the string to search for. * @param inSubstitute the string to replace the first occurrence * of the target with. * @param outFound a pre-allocated character which will be filled * with true if the target is found, and filled with false * otherwise. * * @return a newly allocated string with the substitution performed. */ char *replaceOnce( char *inHaystack, char *inTarget, char *inSubstitute, char *outFound ); /** * Replaces the all occurrences of a target string with * a substitute string. * * Note that this function is not self-insertion-safe: * If inSubstitute contains inTarget, this function will * enter an infinite loop. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for inTarget in. * @param inTarget the string to search for. * @param inSubstitute the string to replace the all occurrences * of the target with. * @param outFound a pre-allocated character which will be filled * with true if the target is found at least once, * and filled with false otherwise. * * @return a newly allocated string with the substitutions performed. */ char *replaceAll( char *inHaystack, char *inTarget, char *inSubstitute, char *outFound ); /** * Replaces the all occurrences of each target string on a list with * a corresponding substitute string. * * Note that this function is not self-insertion-safe: * If inSubstituteVector contains elements from inTargetVector, * this function will enter an infinite loop. * * All parameters and return value must be destroyed by caller. * * @param inHaystack the string to search for targets in. * @param inTargetVector the list of strings to search for. * Vector and contained strings must be destroyed by caller. * @param inSubstituteVector the corresponding list of strings to * replace the all occurrences of the targets with. * Vector and contained strings must be destroyed by caller. * * @return a newly allocated string with the substitutions performed. */ char *replaceTargetListWithSubstituteList( char *inHaystack, SimpleVector *inTargetVector, SimpleVector *inSubstituteVector ); /** * Split a string into tokens using whitespace as separators. * * @param inString the string to tokenize. * Must be destroyed by caller. * * @return a vector of extracted strings. * Vector and strings must be destroyed by caller. */ SimpleVector *tokenizeString( char *inString ); /** * Prints formatted data elements into a newly allocated string buffer. * * Similar to sprintf, except that buffer sizing is automatic (and therefore * safer than manual sizing). * * @param inFormatString the format string to print from. * @param variable argument list data values to fill in the format string * with (uses same conventions as printf). * * @return a newly allocated buffer containing the \0-terminated printed * string. * Must be destroyed by caller. */ char *autoSprintf( const char* inFormatString, ... ); #endif transcend-0.3+dfsg2.orig/minorGems/util/log/0000750000175000017500000000000010305077117017423 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/log/PrintLog.h0000640000175000017500000000326407554101514021341 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-April-8 Jason Rohrer * Changed to be thread-safe. */ #include "minorGems/common.h" #ifndef PRINT_LOG_INCLUDED #define PRINT_LOG_INCLUDED #include "Log.h" #include "minorGems/system/MutexLock.h" /** * A console-based implementation of the Log interface. * * @author Jason Rohrer */ class PrintLog : public Log { public: /** * Constructs a print log. */ PrintLog(); virtual ~PrintLog(); // implement the Log interface virtual void setLoggingLevel( int inLevel ); virtual int getLoggingLevel(); virtual void logString( char *inString, int inLevel ); virtual void logString( char *inLoggerName, char *inString, int inLevel ); protected: int mLoggingLevel; static const char *mDefaultLoggerName; MutexLock *mLock; /** * Generates a string representation of a log message. * * @param inLoggerName the name of the logger * as a \0-terminated string. * Must be destroyed by caller. * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. * * @return the log message as a \0-terminated string. * Must be destroyed by caller. */ char *generateLogMessage( char *inLoggerName, char *inString, int inLevel ); }; #endif transcend-0.3+dfsg2.orig/minorGems/util/log/AppLog.h0000640000175000017500000000661007554101514020763 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-30 Jason Rohrer * Wrapped our dynamically allocated static member in a statically * allocated class to avoid memory leaks at program termination. */ #include "minorGems/common.h" #ifndef APP_LOG_INCLUDED #define APP_LOG_INCLUDED #include "Log.h" #include "PrintLog.h" // internally used class class LogPointerWrapper; /** * A singleton wrapper for implementations of the Log interface. * * This class should be the interface for log-access for applications. * * @author Jason Rohrer */ class AppLog { public: /** * These log errors at various levels. * * All char* parameters must be \0-terminated and destroyed by caller. */ static void criticalError( char *inString ); static void criticalError( char *inLoggerName, char *inString ); static void error( char *inString ); static void error( char *inLoggerName, char *inString ); static void warning( char *inString ); static void warning( char *inLoggerName, char *inString ); static void info( char *inString ); static void info( char *inLoggerName, char *inString ); static void detail( char *inString ); static void detail( char *inLoggerName, char *inString ); static void trace( char *inString ); static void trace( char *inLoggerName, char *inString ); /** * Sets the log to use. * Note that this call destroys the current log. * * @param inLog the log to use. * Will be destroyed by this class. */ static void setLog( Log *inLog ); /** * Gets the log being used. * * @return the log being used. * Will be destroyed by this class. */ static Log *getLog(); /** * Sets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @param inLevel one of the defined logging levels. */ static void setLoggingLevel( int inLevel ); /** * Gets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @return one of the defined logging levels. */ static int getLoggingLevel(); protected: // note that all static objects // are destroyed at program termination //static Log *mLog; static LogPointerWrapper mLogPointerWrapper; }; /** * Wrapper for static member pointer to ensure * deletion of static member object at program termination. */ class LogPointerWrapper { public: /** * Constructor allows specification of the object * to wrap and destroy at program termination. * * @param inLog the log object to wrap. * Will be destroyed at program termination if non-NULL. */ LogPointerWrapper( Log *inLog ); /** * Destructor will get called at program termination * if the object is statically allocated. */ ~LogPointerWrapper(); Log *mLog; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/log/FileLog.h0000640000175000017500000000337707561776211021143 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-November-5 Jason Rohrer * Added support for backing up logs and deleting old log data. */ #include "minorGems/common.h" #ifndef FILE_LOG_INCLUDED #define FILE_LOG_INCLUDED #include "PrintLog.h" #include /** * A file-based implementation of the Log interface. * * @author Jason Rohrer */ class FileLog : public PrintLog { public: /** * Constructs a file log. * * @param inFileName the name of the file to write log messages to. * Must be destroyed by caller. * @param inSecondsBetweenBackups the number of seconds to wait * before making a backup of the current log file (deleting any * old backups), clearing the current log file, and starting * a fresh log in the current log file. Defaults to 3600 * seconds (one hour). Backup logs are saved to inFileName.bakup */ FileLog( char *inFileName, unsigned long inSecondsBetweenBackups = 3600 ); virtual ~FileLog(); /** * Makes a backup of the current log file, deletes old backups, * and clears the current log file. */ void makeBackup(); // overrides PrintLog::logString virtual void logString( char *inLoggerName, char *inString, int inLevel ); protected: FILE *mLogFile; char *mLogFileName; unsigned long mSecondsBetweenBackups; unsigned long mTimeOfLastBackup; static const char *mDefaultLogFileName; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/log/Makefile0000640000175000017500000000235707436570640021105 0ustar pabspabs# # Modification History # # 2002-February-25 Jason Rohrer # Created. # Changed to be more succinct. # GXX=g++ ROOT_PATH = ../../.. DEBUG_ON_FLAG = -g DEBUG_OFF_FLAG = DEBUG_FLAG = ${DEBUG_OFF_FLAG} TIME_PLATFORM_PATH = unix TIME_PLATFORM = Unix TIME_O = ${ROOT_PATH}/minorGems/system/${TIME_PLATFORM_PATH}/Time${TIME_PLATFORM}.o TIME_H = ${ROOT_PATH}/minorGems/system/Time.h TIME_CPP = ${ROOT_PATH}/minorGems/system/${TIME_PLATFORM_PATH}/Time${TIME_PLATFORM}.cpp testLog: AppLog.o FileLog.o PrintLog.o Log.o testLog.o ${TIME_O} ${GXX} ${DEBUG_FLAG} -o testLog AppLog.o FileLog.o PrintLog.o Log.o testLog.o ${TIME_O} clean: rm -f *.o ${TIME_O} testLog.o: testLog.cpp AppLog.h FileLog.h Log.h ${GXX} ${DEBUG_FLAG} -o testLog.o -c testLog.cpp AppLog.o: AppLog.h AppLog.cpp Log.h PrintLog.h ${GXX} ${DEBUG_FLAG} -o AppLog.o -c AppLog.cpp PrintLog.o: PrintLog.h PrintLog.cpp Log.h PrintLog.h ${TIME_H} ${GXX} ${DEBUG_FLAG} -I${ROOT_PATH} -o PrintLog.o -c PrintLog.cpp FileLog.o: PrintLog.h FileLog.cpp FileLog.h Log.h ${GXX} ${DEBUG_FLAG} -o FileLog.o -c FileLog.cpp Log.o: Log.h Log.cpp ${GXX} ${DEBUG_FLAG} -o Log.o -c Log.cpp ${TIME_O}: ${TIME_H} ${TIME_CPP} ${GXX} ${DEBUG_FLAG} -I${ROOT_PATH} -o ${TIME_O} -c ${TIME_CPP} transcend-0.3+dfsg2.orig/minorGems/util/log/FileLog.cpp0000640000175000017500000000576607561776211021502 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-13 Jason Rohrer * Added a flush after every write. * * 2002-April-8 Jason Rohrer * Changed to be thread-safe. * * 2002-November-5 Jason Rohrer * Added support for backing up logs and deleting old log data. */ #include "FileLog.h" #include "PrintLog.h" #include "minorGems/util/stringUtils.h" #include "minorGems/io/file/File.h" #include #include const char *FileLog::mDefaultLogFileName = "default.log"; FileLog::FileLog( char *inFileName, unsigned long inSecondsBetweenBackups ) : mLogFile( NULL ), mLogFileName( stringDuplicate( inFileName ) ), mSecondsBetweenBackups( inSecondsBetweenBackups ), mTimeOfLastBackup( time( NULL ) ) { mLogFile = fopen( mLogFileName, "a" ); if( mLogFile == NULL ) { printf( "Log file %s failed to open.\n", mLogFileName ); printf( "Writing log to default file: %s\n", mDefaultLogFileName ); // switch to default log file name delete [] mLogFileName; mLogFileName = stringDuplicate( mDefaultLogFileName ); mLogFile = fopen( mLogFileName, "a" ); if( mLogFile == NULL ) { printf( "Default log file %s failed to open.\n", mLogFileName ); } } } FileLog::~FileLog() { if( mLogFile != NULL ) { fclose( mLogFile ); } delete [] mLogFileName; } void FileLog::logString( char *inLoggerName, char *inString, int inLevel ) { if( mLogFile != NULL ) { if( inLevel <= mLoggingLevel ) { char *message = PrintLog::generateLogMessage( inLoggerName, inString, inLevel ); mLock->lock(); fprintf( mLogFile, "%s\n", message ); fflush( mLogFile ); if( time( NULL ) - mTimeOfLastBackup > mSecondsBetweenBackups ) { makeBackup(); } mLock->unlock(); delete [] message; } } } void FileLog::makeBackup() { fclose( mLogFile ); File *currentLogFile = new File( NULL, mLogFileName ); char *backupFileName = new char[ strlen( mLogFileName ) + 10 ]; sprintf( backupFileName, "%s.backup", mLogFileName ); File *backupLogFile = new File( NULL, backupFileName ); delete [] backupFileName; // copy into backup log file, which will overwrite it currentLogFile->copy( backupLogFile ); delete currentLogFile; delete backupLogFile; // clear main log file and start writing to it again mLogFile = fopen( mLogFileName, "w" ); if( mLogFile == NULL ) { printf( "Log file %s failed to open.\n", mLogFileName ); } mTimeOfLastBackup = time( NULL ); } transcend-0.3+dfsg2.orig/minorGems/util/log/AppLog.cpp0000640000175000017500000000553507451444376021337 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-30 Jason Rohrer * Wrapped our dynamically allocated static member in a statically * allocated class to avoid memory leaks at program termination. */ #include "AppLog.h" #include "Log.h" #include // wrap our static member in a statically allocated class LogPointerWrapper AppLog::mLogPointerWrapper( new PrintLog ); LogPointerWrapper::LogPointerWrapper( Log *inLog ) : mLog( inLog ) { } LogPointerWrapper::~LogPointerWrapper() { if( mLog != NULL ) { delete mLog; } } void AppLog::criticalError( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::CRITICAL_ERROR_LEVEL ); } void AppLog::criticalError( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::CRITICAL_ERROR_LEVEL ); } void AppLog::error( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::ERROR_LEVEL ); } void AppLog::error( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::ERROR_LEVEL ); } void AppLog::warning( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::WARNING_LEVEL ); } void AppLog::warning( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::WARNING_LEVEL ); } void AppLog::info( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::INFO_LEVEL ); } void AppLog::info( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::INFO_LEVEL ); } void AppLog::detail( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::DETAIL_LEVEL ); } void AppLog::detail( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::DETAIL_LEVEL ); } void AppLog::trace( char *inString ) { mLogPointerWrapper.mLog->logString( inString, Log::TRACE_LEVEL ); } void AppLog::trace( char *inLoggerName, char *inString ) { mLogPointerWrapper.mLog->logString( inLoggerName, inString, Log::TRACE_LEVEL ); } void AppLog::setLog( Log *inLog ) { int currentLoggingLevel = getLoggingLevel(); if( inLog != mLogPointerWrapper.mLog ) { delete mLogPointerWrapper.mLog; } mLogPointerWrapper.mLog = inLog; setLoggingLevel( currentLoggingLevel ); } Log *AppLog::getLog() { return mLogPointerWrapper.mLog; } void AppLog::setLoggingLevel( int inLevel ) { mLogPointerWrapper.mLog->setLoggingLevel( inLevel ); } int AppLog::getLoggingLevel() { return mLogPointerWrapper.mLog->getLoggingLevel(); } transcend-0.3+dfsg2.orig/minorGems/util/log/Log.cpp0000640000175000017500000000057107436570021020657 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. */ #include "Log.h" const int Log::DEACTIVATE_LEVEL = 0; const int Log::CRITICAL_ERROR_LEVEL = 1; const int Log::ERROR_LEVEL = 2; const int Log::WARNING_LEVEL = 3; const int Log::INFO_LEVEL = 4; const int Log::DETAIL_LEVEL = 5; const int Log::TRACE_LEVEL = 6; Log::~Log() { } transcend-0.3+dfsg2.orig/minorGems/util/log/PrintLog.cpp0000640000175000017500000000615010006200435021655 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-11 Jason Rohrer * Added a missing include. * * 2002-April-8 Jason Rohrer * Fixed a casting, dereferencing Win32 compile bug. * Changed to be thread-safe. * Changed to use thread-safe printing function. * * 2002-April-8 Jason Rohrer * Fixed a signed-unsigned mismatch. * * 2004-January-11 Jason Rohrer * Added lock around asctime call. * Increased scope of lock. * * 2004-January-29 Jason Rohrer * Changed to use ctime instead of localtime and asctime. * Improved locking scope. * Changed to use autoSprintf. */ #include "PrintLog.h" #include "minorGems/system/Time.h" #include "minorGems/util/printUtils.h" #include "minorGems/util/stringUtils.h" #include #include #include const char *PrintLog::mDefaultLoggerName = "general"; PrintLog::PrintLog() : mLoggingLevel( Log::TRACE_LEVEL ), mLock( new MutexLock() ) { } PrintLog::~PrintLog() { delete mLock; } void PrintLog::setLoggingLevel( int inLevel ) { mLock->lock(); mLoggingLevel = inLevel; mLock->unlock(); } int PrintLog::getLoggingLevel() { mLock->lock(); int level = mLoggingLevel; mLock->unlock(); return level; } void PrintLog::logString( char *inString, int inLevel ) { logString( (char *)mDefaultLoggerName, inString, inLevel ); } void PrintLog::logString( char *inLoggerName, char *inString, int inLevel ) { // not thread-safe to read mLoggingLevel here // without synchronization. // However, we want logging calls that are above // our level to execute with nearly no overhead. // mutex might be too much overhead.... // Besides, not being thread-safe in this case might // (worst case) result in a missed log entry or // an extra log entry... but setting the logging level should be rare. if( inLevel <= mLoggingLevel ) { char *message = generateLogMessage( inLoggerName, inString, inLevel ); threadPrintF( "%s\n", message ); delete [] message; } } char *PrintLog::generateLogMessage( char *inLoggerName, char *inString, int inLevel ) { unsigned long seconds, milliseconds; Time::getCurrentTime( &seconds, &milliseconds ); // lock around ctime call, since it returns a static buffer mLock->lock(); char *dateString = stringDuplicate( ctime( (time_t *)( &seconds ) ) ); // done with static buffer, since we made a copy mLock->unlock(); // this date string ends with a newline... // get rid of it dateString[ strlen(dateString) - 1 ] = '\0'; char *messageBuffer = autoSprintf( "L%d | %s (%ld ms) | %s | %s", inLevel, dateString, milliseconds, inLoggerName, inString ); delete [] dateString; return messageBuffer; } transcend-0.3+dfsg2.orig/minorGems/util/log/testLog.cpp0000640000175000017500000000147207436570021021560 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. */ #include "AppLog.h" #include "FileLog.h" #include "Log.h" int main() { AppLog::warning( "A test warning" ); AppLog::warning( "Another test warning" ); AppLog::warning( "mainApp", "And Another test warning" ); AppLog::setLoggingLevel( Log::ERROR_LEVEL ); AppLog::warning( "Yet Another test warning" ); AppLog::error( "mainApp", "A test error" ); AppLog::setLog( new FileLog( "test.log" ) ); // this should be skipped AppLog::warning( "A second test warning" ); // this should not be AppLog::criticalError( "A critical error" ); AppLog::setLog( new FileLog( "test2.log" ) ); // this should be skipped AppLog::warning( "A third test warning" ); return 0; } transcend-0.3+dfsg2.orig/minorGems/util/log/Log.h0000640000175000017500000000457107554101514020326 0ustar pabspabs/* * Modification History * * 2002-February-25 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. */ #include "minorGems/common.h" #ifndef LOG_INCLUDED #define LOG_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * An interface for a class that can perform logging functions. * * @author Jason Rohrer */ class Log { public: // These levels were copied from the JLog framework // by Todd Lauinger static const int DEACTIVATE_LEVEL; static const int CRITICAL_ERROR_LEVEL; static const int ERROR_LEVEL; static const int WARNING_LEVEL; static const int INFO_LEVEL; static const int DETAIL_LEVEL; static const int TRACE_LEVEL; // provided so that virtual deletes work properly virtual ~Log(); /** * Sets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @param inLevel one of the defined logging levels. */ virtual void setLoggingLevel( int inLevel ) = 0; /** * Gets the logging level of the current log. * * Messages with levels above the current level will not be logged. * * @return one of the defined logging levels. */ virtual int getLoggingLevel() = 0; /** * Logs a string in this log under the default logger name. * * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. */ virtual void logString( char *inString, int inLevel ) = 0; /** * Logs a string in this log, specifying a logger name. * * @param inLoggerName the name of the logger * as a \0-terminated string. * Must be destroyed by caller. * @param inString the string to log as a \0-terminated string. * Must be destroyed by caller. * @param inLevel the level to log inString at. */ virtual void logString( char *inLoggerName, char *inString, int inLevel ) = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/StringBufferOutputStream.h0000640000175000017500000000277210047451312024015 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. * * 2004-May-9 Jason Rohrer * Added function for getting data as a byte array. */ #include "minorGems/common.h" #ifndef STRING_BUFFER_OUTPUT_STREAM_INCLUDED #define STRING_BUFFER_OUTPUT_STREAM_INCLUDED #include "minorGems/util/SimpleVector.h" #include "minorGems/io/OutputStream.h" /** * An output stream that fills a string buffer. * * @author Jason Rohrer */ class StringBufferOutputStream : public OutputStream { public: StringBufferOutputStream(); ~StringBufferOutputStream(); /** * Gets the data writen to this stream since construction as a * \0-terminated string. * * @return a string containing all data written to this stream. * Must be destroyed by caller. */ char *getString(); /** * Gets the data writen to this stream since construction as a * byte array. * * @param outNumBytes pointer to where the array length should be * returned. * * @return an array containingdata written to this stream. * Must be destroyed by caller. */ unsigned char *getBytes( int *outNumBytes ); // implements the OutputStream interface long write( unsigned char *inBuffer, long inNumBytes ); protected: SimpleVector *mCharacterVector; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/TranslationManager.cpp0000640000175000017500000002023610131504075023136 0ustar pabspabs/* * Modification History * * 2004-October-7 Jason Rohrer * Created. */ #include "TranslationManager.h" #include #include "minorGems/io/file/File.h" // will be destroyed automatically at program termination TranslationManagerStaticMembers TranslationManager::mStaticMembers; void TranslationManager::setDirectoryName( char *inName ) { mStaticMembers.setDirectoryAndLanguage( inName, mStaticMembers.mLanguageName ); } char *TranslationManager::getDirectoryName() { return stringDuplicate( mStaticMembers.mDirectoryName ); } char **TranslationManager::getAvailableLanguages( int *outNumLanguages ) { File *languageDirectory = new File( NULL, mStaticMembers.mDirectoryName ); if( languageDirectory->exists() && languageDirectory->isDirectory() ) { int numChildFiles; File **childFiles = languageDirectory->getChildFiles( &numChildFiles ); if( childFiles != NULL ) { SimpleVector *languageNames = new SimpleVector(); for( int i=0; igetFileName(); char *extensionPointer = strstr( name, ".txt" ); if( extensionPointer != NULL ) { // terminate string, cutting off extension extensionPointer[0] = '\0'; languageNames->push_back( stringDuplicate( name ) ); } delete [] name; delete childFiles[i]; } delete [] childFiles; char **returnArray = languageNames->getElementArray(); *outNumLanguages = languageNames->size(); delete languageNames; delete languageDirectory; return returnArray; } } delete languageDirectory; // default, if we didn't return above *outNumLanguages = 0; return new char*[0]; } void TranslationManager::setLanguage( char *inLanguageName ) { mStaticMembers.setDirectoryAndLanguage( mStaticMembers.mDirectoryName, inLanguageName ); } const char *TranslationManager::translate( char *inTranslationKey ) { char *translatedString = NULL; SimpleVector *keys = mStaticMembers.mTranslationKeys; SimpleVector *naturalLanguageStrings = mStaticMembers.mNaturalLanguageStrings; if( keys != NULL ) { int numKeys = keys->size(); for( int i=0; igetElement( i ) ) ) == 0 ) { // keys match translatedString = *( naturalLanguageStrings->getElement( i ) ); } } } if( translatedString == NULL ) { translatedString = inTranslationKey; } return translatedString; } TranslationManagerStaticMembers::TranslationManagerStaticMembers() : mDirectoryName( NULL ), mLanguageName( NULL ), mTranslationKeys( NULL ), mNaturalLanguageStrings( NULL ) { // default setDirectoryAndLanguage( "languages", "English" ); } TranslationManagerStaticMembers::~TranslationManagerStaticMembers() { if( mDirectoryName != NULL ) { delete [] mDirectoryName; } if( mLanguageName != NULL ) { delete [] mLanguageName; } if( mTranslationKeys != NULL ) { int numKeys = mTranslationKeys->size(); for( int i=0; igetElement( i ) ); } delete mTranslationKeys; } if( mNaturalLanguageStrings != NULL ) { int numKeys = mNaturalLanguageStrings->size(); for( int i=0; igetElement( i ) ); } delete mNaturalLanguageStrings; } } void TranslationManagerStaticMembers::setDirectoryAndLanguage( char *inDirectoryName, char *inLanguageName ) { // save temp copies first to allow caller to pass our own members in to us char *tempDirectoryName = stringDuplicate( inDirectoryName ); char *tempLanguageName = stringDuplicate( inLanguageName ); if( mDirectoryName != NULL ) { delete [] mDirectoryName; } if( mLanguageName != NULL ) { delete [] mLanguageName; } mDirectoryName = tempDirectoryName; mLanguageName = tempLanguageName; // clear the old translation table if( mTranslationKeys != NULL ) { int numKeys = mTranslationKeys->size(); for( int i=0; igetElement( i ) ); } delete mTranslationKeys; } if( mNaturalLanguageStrings != NULL ) { int numKeys = mNaturalLanguageStrings->size(); for( int i=0; igetElement( i ) ); } delete mNaturalLanguageStrings; } // now read in the translation table mTranslationKeys = new SimpleVector(); mNaturalLanguageStrings = new SimpleVector(); File *directoryFile = new File( NULL, mDirectoryName ); if( directoryFile->exists() && directoryFile->isDirectory() ) { char *languageFileName = autoSprintf( "%s.txt", mLanguageName ); File *languageFile = directoryFile->getChildFile( languageFileName ); delete [] languageFileName; if( languageFile != NULL ) { char *languageFileFullPath = languageFile->getFullFileName(); FILE *languageFILE = fopen( languageFileFullPath, "r" ); if( languageFILE != NULL ) { char readError = false; while( ! readError ) { char *key = new char[ 100 ]; int numRead = fscanf( languageFILE, "%99s", key ); if( numRead == 1 ) { // skip the first " int readChar = ' '; while( readChar != '"' && readChar != EOF ) { readChar = fgetc( languageFILE ); } if( readChar != EOF ) { char *naturalLanguageString = new char[1000]; // scan a string of up to 999 characters, stopping // at the first " character numRead = fscanf( languageFILE, "%999[^\"]", naturalLanguageString ); if( numRead == 1 ) { // trim the key and string and save them mTranslationKeys->push_back( stringDuplicate( key ) ); mNaturalLanguageStrings->push_back( stringDuplicate( naturalLanguageString ) ); } else { readError = true; } delete [] naturalLanguageString; // skip the trailing " readChar = ' '; while( readChar != '"' && readChar != EOF ) { readChar = fgetc( languageFILE ); } } else { readError = true; } } else { readError = true; } delete [] key; } fclose( languageFILE ); } delete [] languageFileFullPath; delete languageFile; } } delete directoryFile; } transcend-0.3+dfsg2.orig/minorGems/util/StringBufferOutputStream.cpp0000640000175000017500000000227410047451312024345 0ustar pabspabs/* * Modification History * * 2002-August-1 Jason Rohrer * Created. * * 2004-May-9 Jason Rohrer * Added function for getting data as a byte array. */ #include "minorGems/util/StringBufferOutputStream.h" StringBufferOutputStream::StringBufferOutputStream() : mCharacterVector( new SimpleVector() ) { } StringBufferOutputStream::~StringBufferOutputStream() { delete mCharacterVector; } char *StringBufferOutputStream::getString() { int numChars = mCharacterVector->size(); char *returnArray = new char[ numChars + 1 ]; for( int i=0; igetElement( i ) ) ); } returnArray[ numChars ] = '\0'; return returnArray; } unsigned char *StringBufferOutputStream::getBytes( int *outNumBytes ) { *outNumBytes = mCharacterVector->size(); return mCharacterVector->getElementArray(); } long StringBufferOutputStream::write( unsigned char *inBuffer, long inNumBytes ) { for( int i=0; ipush_back( inBuffer[ i ] ); } return inNumBytes; } transcend-0.3+dfsg2.orig/minorGems/util/stringUtils.cpp0000640000175000017500000002433710001576545021711 0ustar pabspabs/* * Modification History * * 2003-May-10 Jason Rohrer * Created. * Added a tokenization function. * * 2003-June-14 Jason Rohrer * Added a join function. * * 2003-June-22 Jason Rohrer * Added an autoSprintf function. * * 2003-July-27 Jason Rohrer * Fixed bugs in autoSprintf return values for certain cases. * * 2003-August-12 Jason Rohrer * Added a concatonate function. * * 2003-September-7 Jason Rohrer * Changed so that split returns last part, even if it is empty. * * 2004-January-15 Jason Rohrer * Added work-around for MinGW vsnprintf bug. */ #include "stringUtils.h" #include "StringBufferOutputStream.h" #include char *stringToLowerCase( const char *inString ) { int length = strlen( inString ); char *returnString = stringDuplicate( inString ); for( int i=0; i *parts = new SimpleVector(); char *workingString = stringDuplicate( inString ); char *workingStart = workingString; int separatorLength = strlen( inSeparator ); char *foundSeparator = strstr( workingString, inSeparator ); while( foundSeparator != NULL ) { // terminate at separator foundSeparator[0] = '\0'; parts->push_back( stringDuplicate( workingString ) ); // skip separator workingString = &( foundSeparator[ separatorLength ] ); foundSeparator = strstr( workingString, inSeparator ); } // add the remaining part, even if it is the empty string parts->push_back( stringDuplicate( workingString ) ); delete [] workingStart; *outNumParts = parts->size(); char **returnArray = parts->getElementArray(); delete parts; return returnArray; } char *join( char **inStrings, int inNumParts, char *inGlue ) { StringBufferOutputStream *outStream = new StringBufferOutputStream(); for( int i=0; iwriteString( inStrings[i] ); outStream->writeString( inGlue ); } // no glue after last string outStream->writeString( inStrings[ inNumParts - 1 ] ); char *returnString = outStream->getString(); delete outStream; return returnString; } char *concatonate( char *inStringA, char *inStringB ) { char **tempArray = new char*[2]; tempArray[ 0 ] = inStringA; tempArray[ 1 ] = inStringB; char *glue = ""; char *result = join( tempArray, 2, glue ); delete [] tempArray; return result; } char *replaceOnce( char *inHaystack, char *inTarget, char *inSubstitute, char *outFound ) { char *haystackCopy = stringDuplicate( inHaystack ); char *fieldTargetPointer = strstr( haystackCopy, inTarget ); if( fieldTargetPointer == NULL ) { // target not found *outFound = false; return haystackCopy; } else { // target found // prematurely terminate haystack copy string at // start of target occurence // (okay, since we're working with a copy of the haystack argument) fieldTargetPointer[0] = '\0'; // pointer to first char after target occurrence char *fieldPostTargetPointer = &( fieldTargetPointer[ strlen( inTarget ) ] ); char *returnString = new char[ strlen( inHaystack ) - strlen( inTarget ) + strlen( inSubstitute ) + 1 ]; sprintf( returnString, "%s%s%s", haystackCopy, inSubstitute, fieldPostTargetPointer ); delete [] haystackCopy; *outFound = true; return returnString; } } char *replaceAll( char *inHaystack, char *inTarget, char *inSubstitute, char *outFound ) { // repeatedly replace once until replacing fails char lastFound = true; char atLeastOneFound = false; char *returnString = stringDuplicate( inHaystack ); while( lastFound ) { char *nextReturnString = replaceOnce( returnString, inTarget, inSubstitute, &lastFound ); delete [] returnString; returnString = nextReturnString; if( lastFound ) { atLeastOneFound = true; } } *outFound = atLeastOneFound; return returnString; } char *replaceTargetListWithSubstituteList( char *inHaystack, SimpleVector *inTargetVector, SimpleVector *inSubstituteVector ) { int numTargets = inTargetVector->size(); char *newHaystack = stringDuplicate( inHaystack ); char tagFound; for( int i=0; igetElement( i ) ), *( inSubstituteVector->getElement( i ) ), &tagFound ); delete [] newHaystack; newHaystack = newHaystackWithReplacements; } return newHaystack; } SimpleVector *tokenizeString( char *inString ) { char *tempString = stringDuplicate( inString ); char *restOfString = tempString; SimpleVector *foundTokens = new SimpleVector(); SimpleVector *currentToken = new SimpleVector(); while( restOfString[0] != '\0' ) { // characters remain // skip whitespace char nextChar = restOfString[0]; while( nextChar == ' ' || nextChar == '\n' || nextChar == '\r' || nextChar == '\t' ) { restOfString = &( restOfString[1] ); nextChar = restOfString[0]; } if( restOfString[0] != '\0' ) { // a token while( nextChar != ' ' && nextChar != '\n' && nextChar != '\r' && nextChar != '\t' && nextChar != '\0' ) { // still not whitespace currentToken->push_back( nextChar ); restOfString = &( restOfString[1] ); nextChar = restOfString[0]; } // reached end of token foundTokens->push_back( currentToken->getElementString() ); currentToken->deleteAll(); } } delete [] tempString; delete currentToken; return foundTokens; } char *autoSprintf( const char* inFormatString, ... ) { int bufferSize = 50; va_list argList; va_start( argList, inFormatString ); char *buffer = new char[ bufferSize ]; int stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); if( stringLength != -1 ) { // follows C99 standard... // stringLength is the length of the string that would have been // written if the buffer was big enough // not room for string and terminating \0 in bufferSize bytes if( stringLength >= bufferSize ) { // need to reprint with a bigger buffer delete [] buffer; bufferSize = stringLength + 1; va_list argList; va_start( argList, inFormatString ); buffer = new char[ bufferSize ]; // can simply use vsprintf now vsprintf( buffer, inFormatString, argList ); va_end( argList ); return buffer; } else { // buffer was big enough // trim the buffer to fit the string char *returnString = stringDuplicate( buffer ); delete [] buffer; return returnString; } } else { // follows old ANSI standard // -1 means the buffer was too small // Note that some buggy non-C99 vsnprintf implementations // (notably MinGW) // do not return -1 if stringLength equals bufferSize (in other words, // if there is not enough room for the trailing \0). // Thus, we need to check for both // (A) stringLength == -1 // (B) stringLength == bufferSize // below. // keep doubling buffer size until it's big enough while( stringLength == -1 || stringLength == bufferSize) { delete [] buffer; if( stringLength == bufferSize ) { // only occurs if vsnprintf implementation is buggy // might as well use the information, though // (instead of doubling the buffer size again) bufferSize = bufferSize + 1; } else { // double buffer size again bufferSize = 2 * bufferSize; } va_list argList; va_start( argList, inFormatString ); buffer = new char[ bufferSize ]; stringLength = vsnprintf( buffer, bufferSize, inFormatString, argList ); va_end( argList ); } // trim the buffer to fit the string char *returnString = stringDuplicate( buffer ); delete [] buffer; return returnString; } } transcend-0.3+dfsg2.orig/minorGems/util/SimpleVector.h0000640000175000017500000001431510271414276021437 0ustar pabspabs// Jason Rohrer // SimpleVector.h /** * * Simple vector template class. Supports pushing at end and random-access deletions. * Dynamically sized. * * * Created 10-24-99 * Mods: * Jason Rohrer 12-11-99 Added deleteAll function * Jason Rohrer 1-30-2000 Changed to return NULL if get called on non-existent element * Jason Rohrer 12-20-2000 Added a function for deleting a particular * element. * Jason Rohrer 12-14-2001 Added a function for getting the index of * a particular element. * Jason Rohrer 1-24-2003 Added a functions for getting an array or * string of all elements. * Jason Rohrer 7-26-2005 Added template <> to explicitly specialized * getElementString. */ #include "minorGems/common.h" #ifndef SIMPLEVECTOR_INCLUDED #define SIMPLEVECTOR_INCLUDED #include // for memory moving functions const int defaultStartSize = 50; template class SimpleVector { public: SimpleVector(); // create an empty vector SimpleVector(int sizeEstimate); // create an empty vector with a size estimate ~SimpleVector(); void push_back(Type x); // add x to the end of the vector Type *getElement(int index); // get a ptr to element at index in vector int size(); // return the number of allocated elements in the vector bool deleteElement(int index); // delete element at an index in vector /** * Deletes a particular element. Deletes the first element * in vector == to inElement. * * @param inElement the element to delete. * * @return true iff an element was deleted. */ bool deleteElement( Type inElement ); /** * Gets the index of a particular element. Gets the index of the * first element in vector == to inElement. * * @param inElement the element to get the index of. * * @return the index if inElement, or -1 if inElement is not found. */ int getElementIndex( Type inElement ); void deleteAll(); // delete all elements from vector /** * Gets the elements as an array. * * @return the a new array containing all elements in this vector. * Must be destroyed by caller, though elements themselves are * not copied. */ Type *getElementArray(); /** * Gets the char elements as a \0-terminated string. * * @return a \0-terminated string containing all elements in this * vector. * Must be destroyed by caller. */ char *getElementString(); private: Type *elements; int numFilledElements; int maxSize; int minSize; // number of allocated elements when vector is empty }; template inline SimpleVector::SimpleVector() { elements = new Type[defaultStartSize]; numFilledElements = 0; maxSize = defaultStartSize; minSize = defaultStartSize; } template inline SimpleVector::SimpleVector(int sizeEstimate) { elements = new Type[sizeEstimate]; numFilledElements = 0; maxSize = sizeEstimate; minSize = sizeEstimate; } template inline SimpleVector::~SimpleVector() { delete [] elements; } template inline int SimpleVector::size() { return numFilledElements; } template inline Type *SimpleVector::getElement(int index) { if( index < numFilledElements && index >=0 ) { return &(elements[index]); } else return NULL; } template inline bool SimpleVector::deleteElement(int index) { if( index < numFilledElements) { // if index valid for this vector if( index != numFilledElements - 1) { // this spot somewhere in middle // move memory towards front by one spot int sizeOfElement = sizeof(Type); int numBytesToMove = sizeOfElement*(numFilledElements - (index+1)); Type *destPtr = &(elements[index]); Type *srcPtr = &(elements[index+1]); memmove((void *)destPtr, (void *)srcPtr, numBytesToMove); } numFilledElements--; // one less element in vector return true; } else { // index not valid for this vector return false; } } template inline bool SimpleVector::deleteElement( Type inElement ) { int index = getElementIndex( inElement ); if( index != -1 ) { return deleteElement( index ); } else { return false; } } template inline int SimpleVector::getElementIndex( Type inElement ) { // walk through vector, looking for first match. for( int i=0; i inline void SimpleVector::deleteAll() { numFilledElements = 0; if( maxSize > minSize ) { // free memory if vector has grown delete [] elements; elements = new Type[minSize]; // reallocate an empty vector maxSize = minSize; } } template inline void SimpleVector::push_back(Type x) { if( numFilledElements < maxSize) { // still room in vector elements[numFilledElements] = x; numFilledElements++; } else { // need to allocate more space for vector int newMaxSize = maxSize << 1; // double size Type *newAlloc = new Type[newMaxSize]; int sizeOfElement = sizeof(Type); int numBytesToMove = sizeOfElement*(numFilledElements); // move into new space memcpy((void *)newAlloc, (void *) elements, numBytesToMove); // delete old space delete [] elements; elements = newAlloc; maxSize = newMaxSize; elements[numFilledElements] = x; numFilledElements++; } } template inline Type *SimpleVector::getElementArray() { Type *newAlloc = new Type[ numFilledElements ]; int sizeOfElement = sizeof( Type ); int numBytesToCopy = sizeOfElement * numFilledElements; // copy into new space memcpy( (void *)newAlloc, (void *)elements, numBytesToCopy ); return newAlloc; } template <> inline char *SimpleVector::getElementString() { char *newAlloc = new char[ numFilledElements + 1 ]; int sizeOfElement = sizeof( char ); int numBytesToCopy = sizeOfElement * numFilledElements; // copy into new space memcpy( (void *)newAlloc, (void *)elements, numBytesToCopy ); newAlloc[ numFilledElements ] = '\0'; return newAlloc; } #endif transcend-0.3+dfsg2.orig/minorGems/util/TranslationManager.h0000640000175000017500000001037010131504075022601 0ustar pabspabs/* * Modification History * * 2004-October-7 Jason Rohrer * Created. * Copied structure from SettingsManager. */ #include "minorGems/common.h" #ifndef TRANSLATION_MANAGER_INCLUDED #define TRANSLATION_MANAGER_INCLUDED #include "minorGems/util/SimpleVector.h" // utility class for dealing with static member dealocation class TranslationManagerStaticMembers; /** * Class that manages natural language translation of user interface strings. * * @author Jason Rohrer */ class TranslationManager { public: /** * Sets the directory name where translation files are stored. * Defaults to "languages". * * @param inName the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static void setDirectoryName( char *inName ); /** * Gets the directory name where translation files are stored. * * @return the name of the directory (relative to the * program's working directory). * Must be destroyed by caller. */ static char *getDirectoryName(); /** * Gets a list of available languages. * * @param outNumLanguages pointer to where the number of languages * should be returned. * * @return an array of language names. * Array and the strings it contains must be destroyed by caller. */ static char **getAvailableLanguages( int *outNumLanguages ); /** * Sets the natural language to translate keys into. * Defaults to "English". * * @param inLanguageName the name of the language. * This name, when .txt is appended, is the name of * the translation file. Thus, setLanguage( "English" ) would * select the English.txt language file. * Language names must not contain spaces. * Must be destroyed by caller. */ static void setLanguage( char *inLanguageName ); /** * Gets the natural language translation of a key. * * NOTE: if a translation does not exist for the key, the key * itself will be returned. * * @param inTranslationKey the translation key string. * Must be destroyed by caller if non-const. * * @return the translated natural language string. * The string MUST NOT be destroyed by the caller, as it will * be destroyed by this class upon program termination. * * This specification allows the translate function to be used * inline, whether or not a correct translation exists. Thus
         *
         * printf( "%s", translate( myKey ) );
         * delete [] myKey;
         * 
* * will always be correct, whether or not a translation exists, as * will
         *
         * printf( "%s", translate( "MY_KEY" ) );
         * 
*/ static const char *translate( char *inTranslationKey ); protected: static TranslationManagerStaticMembers mStaticMembers; }; /** * Container for static members to allow for their proper destruction * on program termination. * * @author Jason Rohrer */ class TranslationManagerStaticMembers { public: TranslationManagerStaticMembers(); ~TranslationManagerStaticMembers(); /** * Sets the directory name and language name to use, and reads * the translation table from file. * * @param inDirectoryName the directory name. * Must be destroyed by caller. * @param inLanguageName the language name. * Must be destroyed by caller. */ void setDirectoryAndLanguage( char *inDirectoryName, char *inLanguageName ); char *mDirectoryName; char *mLanguageName; // vectors mapping keys to strings SimpleVector *mTranslationKeys; SimpleVector *mNaturalLanguageStrings; }; #endif transcend-0.3+dfsg2.orig/minorGems/util/printUtils.h0000640000175000017500000000102707554101514021172 0ustar pabspabs/* * Modification History * * 2002-April-8 Jason Rohrer * Created. */ #include "minorGems/common.h" #ifndef PRINT_UTILS_INCLUDED #define PRINT_UTILS_INCLUDED /** * A thread-safe version of printf. * * Note that printf is already thread-safe on certain platforms, * but does not seem to be thread-safe on Win32. * * @param inFormatString the format string to use. * @param ... a variable argument list, with the same usage * pattern as printf. */ int threadPrintF( const char* inFormatString, ... ); #endif transcend-0.3+dfsg2.orig/minorGems/util/printUtils.cpp0000640000175000017500000000107407455450264021540 0ustar pabspabs/* * Modification History * * 2002-April-8 Jason Rohrer * Created. * * 2002-April-11 Jason Rohrer * Added a missing return value. */ #include "printUtils.h" #include "minorGems/system/MutexLock.h" #include // for variable argument lists #include MutexLock threadPrintFLock; int threadPrintF( const char* inFormatString, ... ) { threadPrintFLock.lock(); va_list argList; va_start( argList, inFormatString ); int returnVal = vprintf( inFormatString, argList ); threadPrintFLock.unlock(); return returnVal; } transcend-0.3+dfsg2.orig/minorGems/util/test/0000750000175000017500000000000010305077117017621 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/util/test/testSnprintf.mingw.out0000640000175000017500000000055610001574214024175 0ustar pabspabsPrinted string of length 3 to buffer of size 4 with snprintf. Return value = 3 Buffer was null-terminated by snprintf Printed string of length 4 to buffer of size 4 with snprintf. Return value = 4 Buffer was NOT null-terminated by snprintf Printed string of length 5 to buffer of size 4 with snprintf. Return value = -1 Buffer was NOT null-terminated by snprintf transcend-0.3+dfsg2.orig/minorGems/util/test/testSnprintf.cpp0000640000175000017500000000232410001572273023026 0ustar pabspabs/* * Modification History * * 2004-January-15 Jason Rohrer * Created. */ /** * A test program for snprintf behavior. */ #include #include #include int main() { int numStrings = 3; // test strings of length 3, 4, and 5 const char *testStrings[3] = { "tst", "test", "testt" }; int result; // a buffer of length 4, which IS NOT large // enough to hold the last two testStrings char *buffer = (char*)( malloc( 4 ) ); int i; for( i=0; i * * Modified by Aaron D. Gifford * * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN * * The original unmodified version is available at: * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sha1.h" #include #include // for hex encoding #include "minorGems/formats/encodingUtils.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&(sha1_quadbyte)0xFF00FF00) \ |(rol(block->l[i],8)&(sha1_quadbyte)0x00FF00FF)) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); typedef union _BYTE64QUAD16 { sha1_byte c[64]; sha1_quadbyte l[16]; } BYTE64QUAD16; /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1_Transform(sha1_quadbyte state[5], sha1_byte buffer[64]) { sha1_quadbyte a, b, c, d, e; BYTE64QUAD16 *block; block = (BYTE64QUAD16*)buffer; /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1_Init - Initialize new context */ void SHA1_Init(SHA_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1_Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) { sha1_quadbyte i, j; sha1_byte finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1_Update(context, (sha1_byte *)"\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (sha1_byte *)"\0", 1); } /* Should cause a SHA1_Transform() */ SHA1_Update(context, finalcount, 8); for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { digest[i] = (sha1_byte) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = j = 0; memset(context->buffer, 0, SHA1_BLOCK_LENGTH); memset(context->state, 0, SHA1_DIGEST_LENGTH); memset(context->count, 0, 8); memset(&finalcount, 0, 8); } unsigned char *computeRawSHA1Digest( unsigned char *inData, int inDataLength ) { SHA_CTX context; SHA1_Init( &context ); // copy into buffer, since this SHA1 implementation seems to overwrite // parts of the data buffer. unsigned char *buffer = new unsigned char[ inDataLength ]; memcpy( (void *)buffer, (void *)inData, inDataLength ); SHA1_Update( &context, buffer, inDataLength ); delete [] buffer; unsigned char *digest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( digest, &context ); return digest; } unsigned char *computeRawSHA1Digest( char *inString ) { SHA_CTX context; SHA1_Init( &context ); // copy into buffer, since this SHA1 implementation seems to overwrite // parts of the data buffer. int dataLength = strlen( inString ); unsigned char *buffer = new unsigned char[ dataLength ]; memcpy( (void *)buffer, (void *)inString, dataLength ); SHA1_Update( &context, buffer, strlen( inString ) ); delete [] buffer; unsigned char *digest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( digest, &context ); return digest; } char *computeSHA1Digest( char *inString ) { unsigned char *digest = computeRawSHA1Digest( inString ); char *digestHexString = hexEncode( digest, SHA1_DIGEST_LENGTH ); delete [] digest; return digestHexString; } char *computeSHA1Digest( unsigned char *inData, int inDataLength ) { unsigned char *digest = computeRawSHA1Digest( inData, inDataLength ); char *digestHexString = hexEncode( digest, SHA1_DIGEST_LENGTH ); delete [] digest; return digestHexString; } transcend-0.3+dfsg2.orig/minorGems/crypto/hashes/sha1sumCompile0000750000175000017500000000011610053166076023301 0ustar pabspabsg++ -I../../.. -o sha1sum sha1sum.cpp sha1.cpp ../../formats/encodingUtils.cpptranscend-0.3+dfsg2.orig/minorGems/crypto/hashes/sha1sum.cpp0000640000175000017500000000353610053166076022560 0ustar pabspabs/* * Modification History * * 2004-May-20 Jason Rohrer * Created. */ #include "sha1.h" #include "minorGems/formats/encodingUtils.h" #include /** * Prints usage message and exits. * * @param inAppName the name of the app. */ void usage( char *inAppName ); int main( int inNumArgs, char **inArgs ) { if( inNumArgs != 2 ) { usage( inArgs[0] ); } FILE *file = fopen( inArgs[1], "rb" ); if( file == NULL ) { printf( "Failed to open file %s for reading\n\n", inArgs[1] ); usage( inArgs[0] ); } SHA_CTX shaContext; SHA1_Init( &shaContext ); int bufferSize = 5000; unsigned char *buffer = new unsigned char[ bufferSize ]; int numRead = bufferSize; char error = false; // read bytes from file until we run out while( numRead == bufferSize && !error ) { numRead = fread( buffer, 1, bufferSize, file ); if( numRead > 0 ) { SHA1_Update( &shaContext, buffer, numRead ); } else{ error = true; } } fclose( file ); delete [] buffer; if( error ) { printf( "Error reading from file %s\n", inArgs[1] ); } else { unsigned char *rawDigest = new unsigned char[ SHA1_DIGEST_LENGTH ]; SHA1_Final( rawDigest, &shaContext ); // else hash is correct char *digestHexString = hexEncode( rawDigest, SHA1_DIGEST_LENGTH ); printf( "%s %s\n", digestHexString, inArgs[1] ); delete [] rawDigest; delete [] digestHexString; } return 0; } void usage( char *inAppName ) { printf( "Usage:\n\n" ); printf( "\t%s file_to_sum\n", inAppName ); printf( "example:\n" ); printf( "\t%s test.txt\n", inAppName ); exit( 1 ); } transcend-0.3+dfsg2.orig/minorGems/crypto/hashes/sha1.h0000640000175000017500000000740307731352255021502 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Changed to use minorGems endian.h * Added a function for hashing an entire string to a hex digest. * Added a function for getting a raw digest. * * 2003-September-15 Jason Rohrer * Added support for hashing raw (non-string) data. * Removed legacy C code. */ /* * sha.h * * Originally taken from the public domain SHA1 implementation * written by by Steve Reid * * Modified by Aaron D. Gifford * * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN * * The original unmodified version is available at: * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __SHA1_H__ #define __SHA1_H__ #include "minorGems/system/endian.h" /* Make sure you define these types for your architecture: */ typedef unsigned int sha1_quadbyte; /* 4 byte type */ typedef unsigned char sha1_byte; /* single byte type */ /* * Be sure to get the above definitions right. For instance, on my * x86 based FreeBSD box, I define LITTLE_ENDIAN and use the type * "unsigned long" for the quadbyte. On FreeBSD on the Alpha, however, * while I still use LITTLE_ENDIAN, I must define the quadbyte type * as "unsigned int" instead. */ #define SHA1_BLOCK_LENGTH 64 #define SHA1_DIGEST_LENGTH 20 /* The SHA1 structure: */ typedef struct _SHA_CTX { sha1_quadbyte state[5]; sha1_quadbyte count[2]; sha1_byte buffer[SHA1_BLOCK_LENGTH]; } SHA_CTX; void SHA1_Init(SHA_CTX *context); void SHA1_Update(SHA_CTX *context, sha1_byte *data, unsigned int len); void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX* context); /** * Computes a unencoded 20-byte digest from data. * * @param inData the data to hash. * Must be destroyed by caller. * @param inDataLength the length of the data to hash. * Must be destroyed by caller. * * @return the digest as a byte array of length 20. * Must be destroyed by caller. */ unsigned char *computeRawSHA1Digest( unsigned char *inData, int inDataLength ); /** * Computes a unencoded 20-byte digest from an arbitrary string message. * * @param inString the message as a \0-terminated string. * Must be destroyed by caller. * * @return the digest as a byte array of length 20. * Must be destroyed by caller. */ unsigned char *computeRawSHA1Digest( char *inString ); /** * Computes a hex-encoded string digest from data. * * @param inData the data to hash. * Must be destroyed by caller. * @param inDataLength the length of the data to hash. * Must be destroyed by caller. * * @return the digest as a \0-terminated string. * Must be destroyed by caller. */ char *computeSHA1Digest( unsigned char *inData, int inDataLength ); /** * Computes a hex-encoded string digest from an arbitrary string message. * * @param inString the message as a \0-terminated string. * Must be destroyed by caller. * * @return the digest as a \0-terminated string. * Must be destroyed by caller. */ char *computeSHA1Digest( char *inString ); #endif transcend-0.3+dfsg2.orig/minorGems/crypto/hashes/sha1Test.cpp0000640000175000017500000000476707733372451022711 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * * 2003-September-21 Jason Rohrer * Added test of long string. */ #include "sha1.h" #include #include /** * All parameters must be destroyed by caller. */ void checkHash( char *inString, char *inTestName, char *inCorrectHash ); int main() { /* * Test vectors: * * "abc" * A999 3E36 4706 816A BA3E 2571 7850 C26C 9CD0 D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 8498 3E44 1C3B D26E BAAE 4AA1 F951 29E5 E546 70F1 * * A million repetitions of "a" * 34AA 973C D4C4 DAA4 F61E EB2B DBAD 2731 6534 016F */ char *abc = "abc"; char *correctABCHash = "A9993E364706816ABA3E25717850C26C9CD0D89D"; char *mixedAlpha = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; char *correctMixedAlphaHash = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"; char *correctMillionHash = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"; int oneMillion = 1000000; char *millionAs = new char[ oneMillion + 1 ]; for( int i=0; i getKeyDown( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? true : false); } getKeyUp( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? false : true); } Sample Mac implementation #include getKeyDown( int vKeyCode ) { KeyMapByteArray keyArray; GetKeys( keyArray ); int arrayInd = vKeyCode >> 3; // divide by 8 to get start array index of key code unsigned char neededByte = keyArray[ arrayInd ]; return (neededByte >> vKeyCode % 8) && 0x01; // trim off bit needed } getKeyUp( int vKeyCode ) { KeyMapByteArray keyArray; GetKeys( keyArray ); int arrayInd = vKeyCode >> 3; // divide by 8 to get start array index of key code unsigned char neededByte = keyArray[ arrayInd ]; return !((neededByte >> vKeyCode % 8) && 0x01); // trim off bit needed, and invert } */transcend-0.3+dfsg2.orig/minorGems/graphics/filters/0000750000175000017500000000000010305077062021134 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/filters/InvertFilter.h0000640000175000017500000000123307220517106023722 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. */ #ifndef INVERT_FILTER_INCLUDED #define INVERT_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Filter that inverts the values in a channel. * * @author Jason Rohrer */ class InvertFilter : public ChannelFilter { public: // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); }; inline void InvertFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numPixels = inWidth * inHeight; for( int i=0; i *mFilterVector; }; inline MultiFilter::MultiFilter() : mFilterVector( new SimpleVector() ) { } inline MultiFilter::~MultiFilter() { delete mFilterVector; } inline void MultiFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numFilters = mFilterVector->size(); for( int i=0; igetElement( i ) ); thisFilter->apply( inChannel, inWidth, inHeight ); } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/filters/MedianFilter.h0000640000175000017500000000672407301027016023656 0ustar pabspabs/* * Modification History * * 2001-May-15 Jeremy Tavan * Created. * * 2001-May-16 Jeremy Tavan * Modified to use the much faster quick_select * median-finding algorithm. * * 2001-May-17 Jaosn Rohrer * Tried to optimize by moving stuff out of the inner-inner loop. * It helped a bit, but not much. * */ #ifndef MEDIAN_FILTER_INCLUDED #define MEDIAN_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" #include "quickselect.h" int medianFilterCompareInt( const void *x, const void *y ); /** * Median convolution filter. * * @author Jeremy Tavan */ class MedianFilter : public ChannelFilter { public: /** * Constructs a median filter. * * @param inRadius the radius of the box in pixels. */ MedianFilter( int inRadius ); /** * Sets the box radius. * * @param inRadius the radius of the box in pixels. */ void setRadius( int inRadius ); /** * Gets the box radius. * * @return the radius of the box in pixels. */ int getRadius(); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: int mRadius; }; inline MedianFilter::MedianFilter( int inRadius ) : mRadius( inRadius ) { } inline void MedianFilter::setRadius( int inRadius ) { mRadius = inRadius; } inline int MedianFilter::getRadius() { return mRadius; } inline void MedianFilter::apply( double *inChannel, int inWidth, int inHeight ) { // pre-compute an integer version of the channel for the // median alg to use int numPixels = inWidth * inHeight; int *intChannel = new int[ numPixels ]; for( int p=0; p= inHeight ) { endBoxY = inHeight - 1; } int boxSizeY = endBoxY - startBoxY + 1; for( int x=0; x= inWidth ) { endBoxX = inWidth - 1; } int boxSizeX = endBoxX - startBoxX + 1; int *buffer = new int[boxSizeX * boxSizeY]; // sum all pixels in the box around this pixel for( int boxY = startBoxY; boxY<=endBoxY; boxY++ ) { int yBoxIndexContrib = boxY * inWidth; int yBoxContrib = boxSizeX * ( boxY-startBoxY ); for( int boxX = startBoxX; boxX<=endBoxX; boxX++ ) { //buffer[boxSizeX*(boxY-startBoxY)+(boxX-startBoxX)] = (int)(1000.0 * inChannel[yBoxIndexContrib + boxX]); buffer[ yBoxContrib + ( boxX-startBoxX ) ] = intChannel[ yBoxIndexContrib + boxX ]; } } medianChannel[ yIndexContrib + x ] = (double)quick_select( buffer, boxSizeX*boxSizeY ) / 1000.0; delete [] buffer; } } // copy blurred image back into passed-in image memcpy( inChannel, medianChannel, sizeof(double) * inWidth * inHeight ); delete [] medianChannel; delete [] intChannel; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/filters/quickselect.h0000640000175000017500000000320607300520500023612 0ustar pabspabs/* * This Quickselect routine is based on the algorithm described in * "Numerical recipies in C", Second Edition, * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 */ #define ELEM_SWAP(a,b) { register int t=(a);(a)=(b);(b)=t; } int quick_select(int arr[], int n) { int low, high; int median; int middle, ll, hh; low = 0 ; high = n-1 ; median = (low + high) / 2; for (;;) { if (high <= low) /* One element only */ return arr[median] ; if (high == low + 1) { /* Two elements only */ if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; return arr[median] ; } /* Find median of low, middle and high items; swap into position low */ middle = (low + high) / 2; if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; /* Swap low item (now in position middle) into position (low+1) */ ELEM_SWAP(arr[middle], arr[low+1]) ; /* Nibble from each end towards middle, swapping items when stuck */ ll = low + 1; hh = high; for (;;) { do ll++; while (arr[low] > arr[ll]) ; do hh--; while (arr[hh] > arr[low]) ; if (hh < ll) break; ELEM_SWAP(arr[ll], arr[hh]) ; } /* Swap middle item (in position low) back into correct position */ ELEM_SWAP(arr[low], arr[hh]) ; /* Re-set active partition */ if (hh <= median) low = ll; if (hh >= median) high = hh - 1; } } #undef ELEM_SWAP transcend-0.3+dfsg2.orig/minorGems/graphics/filters/BoxBlurFilter.h0000640000175000017500000000472207225666471024055 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Added a getRadius function for completeness. */ #ifndef BOX_BLUR_FILTER_INCLUDED #define BOX_BLUR_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Blur convolution filter that uses a box for averaging. * * @author Jason Rohrer */ class BoxBlurFilter : public ChannelFilter { public: /** * Constructs a box filter. * * @param inRadius the radius of the box in pixels. */ BoxBlurFilter( int inRadius ); /** * Sets the box radius. * * @param inRadius the radius of the box in pixels. */ void setRadius( int inRadius ); /** * Gets the box radius. * * @return the radius of the box in pixels. */ int getRadius(); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: int mRadius; }; inline BoxBlurFilter::BoxBlurFilter( int inRadius ) : mRadius( inRadius ) { } inline void BoxBlurFilter::setRadius( int inRadius ) { mRadius = inRadius; } inline int BoxBlurFilter::getRadius() { return mRadius; } inline void BoxBlurFilter::apply( double *inChannel, int inWidth, int inHeight ) { double *blurredChannel = new double[ inWidth * inHeight ]; for( int y=0; y= inHeight ) { endBoxY = inHeight - 1; } int boxSizeY = endBoxY - startBoxY + 1; for( int x=0; x= inWidth ) { endBoxX = inWidth - 1; } int boxSizeX = endBoxX - startBoxX + 1; // sum all pixels in the box around this pixel for( int boxY = startBoxY; boxY<=endBoxY; boxY++ ) { int yBoxIndexContrib = boxY * inWidth; for( int boxX = startBoxX; boxX<=endBoxX; boxX++ ) { blurredChannel[ yIndexContrib + x ] += inChannel[ yBoxIndexContrib + boxX ]; } } // divide to complete the average blurredChannel[ yIndexContrib + x ] /= (boxSizeX * boxSizeY); } } // copy blurred image back into passed-in image memcpy( inChannel, blurredChannel, sizeof(double) * inWidth * inHeight ); delete [] blurredChannel; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/filters/ThresholdFilter.h0000640000175000017500000000265107220517106024414 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. */ #ifndef THRESHOLD_FILTER_INCLUDED #define THRESHOLD_FILTER_INCLUDED #include "minorGems/graphics/ChannelFilter.h" /** * Threshold filter. * * @author Jason Rohrer */ class ThresholdFilter : public ChannelFilter { public: /** * Constructs a threshold filter. * * @param inThreshold threshold value. Channel values * above or equal to inThreshold are set to 1, while * all other values are set to 0. */ ThresholdFilter( double inThreshold ); /** * Sets the threshold. * * @param inThreshold threshold value. Channel values * above or equal to inThreshold are set to 1, while * all other values are set to 0. */ void setThreshold( double inThreshold ); // implements the ChannelFilter interface void apply( double *inChannel, int inWidth, int inHeight ); private: double mThreshold; }; inline ThresholdFilter::ThresholdFilter( double inThreshold ) : mThreshold( inThreshold ) { } inline void ThresholdFilter::setThreshold( double inThreshold ) { mThreshold = inThreshold; } inline void ThresholdFilter::apply( double *inChannel, int inWidth, int inHeight ) { int numPixels = inWidth * inHeight; for( int i=0; i= mThreshold ) { inChannel[i] = 1.0; } else { inChannel[i] = 0.0; } } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/GraphicBuffer.h0000640000175000017500000001327607205646752022373 0ustar pabspabs// Jason Rohrer // GraphicBufffer.h /** * * Graphic buffer, with functions for manipulation * * * Created 11-8-99 * Mods: * Jason Rohrer 12-12-99 Added support for drawing IconMap objects * Jason Rohrer 11-18-2000 Added a getBuffer function. */ #ifndef GRAPHIC_BUFFER_INCLUDED #define GRAPHIC_BUFFER_INCLUDED #include "Color.h" #include #include "IconMap.h" class GraphicBuffer { public: GraphicBuffer( unsigned long *buff, int buffW, int buffH ); ~GraphicBuffer(); // draw a solid image into the buffer void drawImage( unsigned long *image, int *imageYOffset, int xPos, int yPos, int wide, int high ); // draw a transparent image into the buffer void drawImageAlpha( unsigned long *image, int *imageYOffset, int xPos, int yPos, int wide, int high ); // replace a rectangle in the buffer with bgColor void eraseImage( int xPos, int yPos, int wide, int high, Color &bgColor ); // draw a solid IconMap into the buffer void drawIconMap( IconMap *icon, int xPos, int yPos ); // draw a transparent image into the buffer void drawIconMapAlpha( IconMap *icon, int xPos, int yPos ); // replace a rectangle (over icon) in the buffer with bgColor void eraseIconMap( IconMap *icon, int xPos, int yPos, Color &bgColor ); // set buffer to a new buffer of the same size void setBuffer( unsigned long *buff ); // get the pixel buffer unsigned long *getBuffer(); int getWidth(); int getHeight(); // fill buffer with a color void fill( Color &fillC ); // take a screen shot, save to disc void screenShot( FILE *f ); private: unsigned long *buffer; int bufferHigh; int bufferWide; int *buffYOffset; float invChannelMax; Color utilColor; // color for using composite functions }; inline GraphicBuffer::GraphicBuffer( unsigned long *buff, int buffW, int buffH) : utilColor(0,0,0,0) { invChannelMax = 1 / 255.0; bufferHigh = buffH; bufferWide = buffW; buffer = buff; // precalc'ed y contributions to linear indexing of buffer buffYOffset = new int[bufferHigh]; for( int y=0; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } for( int y=minY; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } unsigned long composite = bgColor.composite; for( int y=minY; y bufferHigh ) { maxY = bufferHigh; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + wide; if( maxX > bufferWide ) { maxX = bufferWide; } for( int y=minY; y> 24) * invChannelMax; float oneMinusAlpha = 1-alpha; unsigned long buffARGB = buffer[ buffYContrib + x ]; argb = utilColor.getWeightedComposite( argb, alpha ); buffARGB = utilColor.getWeightedComposite( buffARGB, oneMinusAlpha ); unsigned long sum = utilColor.sumComposite( argb, buffARGB ); buffer[ buffYContrib + x ] = sum; } } } inline void GraphicBuffer::drawIconMap( IconMap *icon, int xPos, int yPos ) { drawImage( icon->imageMap, icon->yOffset, xPos, yPos, icon->wide, icon->high ); } inline void GraphicBuffer::drawIconMapAlpha( IconMap *icon, int xPos, int yPos ) { drawImageAlpha( icon->imageMap, icon->yOffset, xPos, yPos, icon->wide, icon->high ); } inline void GraphicBuffer::eraseIconMap( IconMap *icon, int xPos, int yPos, Color &bgColor ) { eraseImage( xPos, yPos, icon->wide, icon->high, bgColor ); } inline void GraphicBuffer::fill( Color &fillC ) { unsigned long composite = fillC.composite; for( int y=0; y #include #include "Image.h" /** * An RGBA extension of Image. * * @author Jason Rohrer */ class RGBAImage : public Image { public: /** * Constructs an RGBA image. All channels are initialized to 0. * * @param inWidth width of image in pixels. * @param inHeight height of image in pixels. */ RGBAImage( int inWidth, int inHeight ); /** * Constructs an RGBAImage by copying a given image. * The image data is truncated or expaned (with black channels) * to fit the 4 channel RGBA model, * and any selection in inImage is ignored. * * @param inImage the image to copy. Copied internally, so must be * destroyed by the caller. */ RGBAImage( Image *inImage ); /** * Gets the pixel data from this image as a byte array. * * @return a byte array containing pixel data for this image. * Stored in row-major order, with each pixel represented * by 4 bytes in the order RGBA. * Must be destroyed by caller. */ virtual unsigned char *getRGBABytes(); // overrides the Image::filter function virtual void filter( ChannelFilter *inFilter ); virtual void filter( ChannelFilter *inFilter, int inChannel ); // overrides the Image::copy function // since the function is non-virtual in the parent class, // the pointer type should determine which function gets called. RGBAImage *copy(); }; inline RGBAImage::RGBAImage( int inWidth, int inHeight ) : Image( inWidth, inHeight, 4 ) { } inline RGBAImage::RGBAImage( Image *inImage ) : Image( inImage->getWidth(), inImage->getHeight(), 4 ) { int minNumChannels = 4; if( inImage->getNumChannels() < minNumChannels ) { minNumChannels = inImage->getNumChannels(); } // if inImage does not have at least 4 channels, // leave our additional channels black // if inImage has extra channels, skip them // copy channels from inImage for( int c=0; cgetChannel( c ); memcpy( mChannels[c], inChannel, mNumPixels * sizeof( double ) ); } } inline unsigned char *RGBAImage::getRGBABytes() { int numBytes = mNumPixels * 4; unsigned char *bytes = new unsigned char[ numBytes ]; int pixel = 0; for( int i=0; ipaste( this ); return copiedImage; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/0000750000175000017500000000000011476353073020661 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/openGL/NoLightingGL.h0000640000175000017500000000121007220267641023307 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef NO_LIGHTING_GL_INCLUDED #define NO_LIGHTING_GL_INCLUDED #include "LightingGL.h" /** * LightingGL implementation that uses a constant white light everywhere. * * @author Jason Rohrer */ class NoLightingGL : public LightingGL{ public: // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); }; inline void NoLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { outColor->r = 1.0; outColor->g = 1.0; outColor->b = 1.0; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/RadiosGLView.cpp0000640000175000017500000004061307205611501023655 0ustar pabspabs// Jason Rohrer // RadiosGLView.cpp /** * * OpenGL viewer for RadiosGL rendered scenes * * Created 1-14-2000 * Mods: * Jason Rohrer 1-15-2000 Added timer function to make keyboard * interaction smoother and more intuitive * * Jason Rohrer 1-18-2000 Moved SetMouse call from within mouse handler * to inside re-display function * SetMouse was posting a new mouse event too * quickly, putting the delta-angles back to 0 * before the posted redisplay happened. * * This made view motion jerky on the PC * Fixed it by only centering mouse when it leaves window * (or hits window border) * Added mouse capture to window on PC so that * mouse events outside window border are captured * * Added "o" key control to switch into fullscreen mode */ #define APP_NAME "RadiosGLView" #define SETTINGS_FILE_NAME "RadiosGLView.ini" #define FORWARD_MOTION_TIMER 200 #include #include #include #include #include "gl.h" #include "glut.h" #include "RadiosityConstants.h" #include "Vertex.h" #include "Dither.h" #include "UniversalFileIO.h" // function for force-setting mouse position #include "SetMouse.h" // prototypes void cleanup(); int loadSettings(); void initModel(); void glutMotion(int x, int y); void draw (void); void glutDisplay (void); void glutResize (int w, int h); void glutKeyboard (unsigned char key, int x, int y); void glInit (void); void glutTimer( int value ); void glutIdle( void ); void glutEntry( int state ); // vector typedef float vec3_t[3]; // these are loaded from the ini file int screenCenterX; int screenCenterY; float invScreenCenterX; float invScreenCenterY; int dithering; int invMouse; int fullscreen; char *modelFileName = new char[99]; // window width and height int winW; int winH; long numPatches; Vertex *triangles[3]; Vertex *textAnchors[3]; int startTextID = 13; float translationX = 0; float translationY = 0; float translationZ = 0; // position for view float positionX = 0; float positionY = 25; float positionZ = 0; float speed = 0.5; // speed of motion float rotationSpeed = 1.0; // speed of view rotation // two vectors specifying view direction float lookX = 0; float lookY = 0; float lookZ = 1; float upX = 0; float upY = 1; float upZ = 0; // vector pointing to left to help with rotations float leftX = -1; float leftY = 0; float leftZ = 0; // change in position float deltaPosition = 0; // direction of motion char forwardMode = false; char backwardMode = false; // is mouse engaged for view angle movement char mouseEngaged = false; int lastMouseX = screenCenterX; int lastMouseY = screenCenterY; // set to true during recentering of mouse char ignoreEvent = false; // amount to rotate about axis float rotate = 0.0f; // vector which describes the axis to rotate about vec3_t axis = {1.0, 0.0, 0.0}; // global delta rotation, for use with the mouse vec3_t gRot = {0,0,0}; // global total rotation vec3_t gRotNet = {0,0,0}; FILE *logFile; void draw (void) { glPushMatrix (); glRotatef (-rotate, axis[0], axis[1], axis[2]); glTranslatef (-15.0, 10.0, 5.0); // draws radiosity polygons int textID = startTextID; for( int p=0; p -halfPI && gRotNet[0] + gRot[0] < halfPI ) { float oldUpX = upX; float oldUpY = upY; float oldUpZ = upZ; upX += lookX * gRot[0]; upY += lookY * gRot[0]; upZ += lookZ * gRot[0]; // normalize up float invLength = 1.0 / sqrt(upX*upX + upY*upY + upZ*upZ); upX = upX * invLength; upY = upY * invLength; upZ = upZ * invLength; lookX += oldUpX * -gRot[0]; lookY += oldUpY * -gRot[0]; lookZ += oldUpZ * -gRot[0]; // normalize look invLength = 1.0 / sqrt(lookX*lookX + lookY*lookY + lookZ*lookZ); lookX *= invLength; lookY *= invLength; lookZ *= invLength; gRotNet[0] += gRot[0]; } // now rotate left-right // this should by done by rotating all three vectors around y-axis // only X and Z components will change float cosTheta = cos( gRot[1] ); float sinTheta = sin( gRot[1] ); // first, look direction float oldLookX = lookX; float oldLookZ = lookZ; lookX = cosTheta * oldLookX + sinTheta * oldLookZ; lookZ = -sinTheta * oldLookX + cosTheta * oldLookZ; // then, up direction float oldUpX = upX; float oldUpZ = upZ; upX = cosTheta * oldUpX + sinTheta * oldUpZ; upZ = -sinTheta * oldUpX + cosTheta * oldUpZ; gRotNet[1] += gRot[1]; lastUpX = upX; lastUpY = upY; lastUpZ = upZ; lastLookX = lookX; lastLookY = lookY; lastLookZ = lookZ; } gluLookAt( positionX, positionY, positionZ, positionX + lookX, positionY + lookY, positionZ + lookZ, upX, upY, upZ ); draw (); glutSwapBuffers(); } void glutIdle( void ) { glutPostRedisplay(); } char timing = false; // are we timing interval between key strokes? char keyPress = false; // are we in the middle of a key-down session? void glutTimer( int value ) { // NOTE: This seems to rely on keyboard repeat speed.... // would like to find a better method of using keyboard to move // forward and backward in a 3d environment // Want a key-up, key-down event handler, but this is not available in GLUT!!! if( !timing && (backwardMode || forwardMode) ) { timing = true; // wait one more timer cycle glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // reset timer return; } if( timing ) { // expire timers and current motion if( backwardMode ) { backwardMode = false; } if( forwardMode ) { forwardMode = false; } timing = false; keyPress = false; } glutPostRedisplay(); } // called on app start and on resize. void glutResize (int w, int h) { winW = w; winH = h; screenCenterX = winW / 2; screenCenterY = winH / 2; invScreenCenterX = 1/ (float)screenCenterX; invScreenCenterY = 1/ (float)screenCenterY; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glViewport( 0, 0, winW, winH ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 90, winW / winH, 1, 9999 ); glutPostRedisplay(); } // Keyboard handler void glutKeyboard (unsigned char key, int x, int y) { switch (key){ case 'q': case 'Q': exit (1); break; case 'o': case 'O': glutFullScreen(); break; case 'e': case 'E': // move forward forwardMode = true; backwardMode = false; if( !keyPress ) { // start of a new key down keyPress = true; glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // set timer } timing = false; // reset key timer break; case 'd': case 'D': // move backward //translationZ -= 1; forwardMode = false; backwardMode = true; if( !keyPress ) { // start of a new key down keyPress = true; glutTimerFunc(FORWARD_MOTION_TIMER, glutTimer, 1); // set timer } timing = false; // reset key timer break; case 'a': case 'A': // increase speed speed += 0.1; break; case 's': case 'S': // decrease speed if( speed >= 0.1 ) speed -= 0.1; else speed = 0; break; case 'z': case 'Z': // increase rotation speed rotationSpeed += 0.1; break; case 'x': case 'X': // decrease rotation speed if( rotationSpeed >= 0.1 ) rotationSpeed -= 0.1; else rotationSpeed = 0; break; case 'm': case 'M': if( mouseEngaged ) { glutSetCursor( GLUT_CURSOR_LEFT_ARROW ); // mouse back on to normal mode mouseEngaged = false; ReleaseMouse(); // stop rotation on mouse disengage gRot[0] = 0; gRot[1] = 0; } else { glutSetCursor( GLUT_CURSOR_NONE ); // cursor off, mouse in view control mode mouseEngaged = true; ignoreEvent = true; CaptureMouse(); SetMouse( screenCenterX, screenCenterY ); } break; break; } glutPostRedisplay (); } // Called when the mouse moves in our app area. void glutMotion(int x, int y) { if( mouseEngaged ) { // only change view if mouse in view mode if( ignoreEvent ) { lastMouseX = x; lastMouseY = y; // make sure mouse back in screen area if( x < winW-5 && x > 5 && y < winH-5 && y > 5 ) { ignoreEvent = false; } else { // try centering again, user might be fiesty SetMouse( screenCenterX, screenCenterY ); } return; } gRot[0] = (lastMouseY - y)*rotationSpeed * invScreenCenterY * invMouse; gRot[1] = (lastMouseX - x)*rotationSpeed * invScreenCenterX; lastMouseX = x; lastMouseY = y; glutPostRedisplay (); // watch for mouse hitting edges of screen if( x >= winW-5 || x <= 5 || y >= winH-5 || y <= 5 ) { ignoreEvent = true; SetMouse( screenCenterX, screenCenterY ); } } } void glutEntry( int state ) { // reign mouse in if it leaves window while engaged if( state == GLUT_LEFT && mouseEngaged ) { ignoreEvent = true; SetMouse( screenCenterX, screenCenterY ); } } // Sets up various OpenGL stuff. void glInit (void) { glEnable (GL_DEPTH_TEST); glEnable (GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW); } int main (void) { logFile = fopen("RadiosGLView.log", "w"); if( loadSettings() != 0 ) { fprintf( logFile, "Settings file %s failed to open\n", SETTINGS_FILE_NAME ); cleanup(); return 2; // settings didn't load } if( fullscreen ) { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH |GLUT_FULLSCREEN ); glutFullScreen(); } else { glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH |GLUT_MULTISAMPLE); } glutInitWindowSize( winW, winH ); glutCreateWindow( APP_NAME ); glutKeyboardFunc( glutKeyboard ); glutDisplayFunc( glutDisplay ); glutReshapeFunc( glutResize ); glutPassiveMotionFunc( glutMotion ); glutIdleFunc( glutIdle ); glutEntryFunc( glutEntry ); glInit (); initModel(); glutSetCursor( GLUT_CURSOR_NONE ); // cursor off, mouse in view control mode mouseEngaged = true; ignoreEvent = true; CaptureMouse(); SetMouse( screenCenterX, screenCenterY ); glutMainLoop( ); cleanup(); } void cleanup() { fclose(logFile); delete [] modelFileName; } int loadSettings() { FILE *settingsFile = fopen( SETTINGS_FILE_NAME, "r" ); if( settingsFile == NULL ) { return -1; } char *stringBuffer = new char[99]; // assumes that all parameters are in a specific order // read FULLSCREEN setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &fullscreen ); // read DITHER setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &dithering ); // read SCREENWIDE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &winW ); // read SCREENHIGH setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &winH ); screenCenterX = winW / 2; screenCenterY = winH / 2; invScreenCenterX = 1/ (float)screenCenterX; invScreenCenterY = 1/ (float)screenCenterY; // read MODELFILE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%s", modelFileName ); // read INVERTMOUSE setting fscanf( settingsFile, "%s", stringBuffer ); fscanf( settingsFile, "%d", &invMouse ); // convert 0/1 switch to -1/1 needed for inverting mouse if( invMouse == 0 ) invMouse = 1; else invMouse = -1; fclose( settingsFile ); return 0; } void initModel() { UniversalFileIO fileIO; FILE *sceneFile = fopen( modelFileName, "rb" ); if( sceneFile != NULL ) { fprintf( logFile, "Scene file opened.\n"); } else { return; } numPatches = fileIO.freadLong( sceneFile ); fprintf( logFile, "num patches = %d\n", numPatches); for( int v=0; v<3; v++ ) { triangles[v] = new Vertex[numPatches]; textAnchors[v] = new Vertex[numPatches]; } int textID = startTextID; for( int p=0; p #include #include "TextureGL.h" #include "LightingGL.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/graphics/RGBAImage.h" #include "minorGems/graphics/3d/Primitive3D.h" #include "minorGems/graphics/openGL/NoLightingGL.h" // try and include extensions if multitexture isn't // built-in to gl.h #ifndef GL_ARB_multitexture #include #endif /** * OpenGL primitive object. * * Comprised of a triangle mesh, texture map, and anchor points. * * @author Jason Rohrer */ class PrimitiveGL { public: /** * Constructs a PrimitiveGL. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inPrimitive the primitive to base this GL version on. */ PrimitiveGL( Primitive3D *inPrimitive ); ~PrimitiveGL(); /** * Draws the primitive (assumes an active openGL screen). * * @param inTransform the transform to perform on the primitive * before drawing it. * @param inLighting the lighting to use when drawing the primitive. */ void draw( Transform3D *inTransform, LightingGL *inLighting ); /** * Gets the number of parameters associated with this primitive. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this primitive. * * @return the number of animations. */ virtual int getNumAnimations(); /** * Sets a parameter for this primative. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this primative. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this primitive forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this primitive. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this primitive. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); protected: Primitive3D *mPrimitive; TextureGL *mTextureGL; // Equivalent to the public draw(), but does manual multi-texturing // by multi-pass rendering. Does not depend on ARB calls. void drawNoMultitexture( Transform3D *inTransform, LightingGL *inLighting ); }; inline PrimitiveGL::PrimitiveGL( Primitive3D *inPrimitive ) : mPrimitive( inPrimitive ) { int numTextures = mPrimitive->mNumTextures; mTextureGL = new TextureGL( numTextures ); for( int i=0; imTexture[i]->getRGBABytes(); mTextureGL->setTextureData( i, rgba, mPrimitive->mTexture[i]->getWidth(), mPrimitive->mTexture[i]->getHeight() ); delete [] rgba; } } inline PrimitiveGL::~PrimitiveGL() { delete mPrimitive; delete mTextureGL; } // this is the ARB version of draw inline void PrimitiveGL::draw( Transform3D *inTransform, LightingGL *inLighting ) { // check for multi-texture availability before proceeding if( !TextureGL::isMultiTexturingSupported() ) { drawNoMultitexture( inTransform, inLighting ); return; } // first, copy the vertices and translate/rotate/scale them Vector3D **worldVertices = new Vector3D*[ mPrimitive->mNumVertices ]; Vector3D **worldNormals = new Vector3D*[ mPrimitive->mNumVertices ]; for( int i=0; imNumVertices; i++ ) { // copy worldVertices[i] = new Vector3D( mPrimitive->mVertices[i] ); worldNormals[i] = new Vector3D( mPrimitive->mNormals[i] ); // translate/rotate/scale inTransform->apply( worldVertices[i] ); // only rotate normals inTransform->applyNoTranslation( worldNormals[i] ); // normalize to get rid of any scaling worldNormals[i]->normalize(); } // now draw vertices as triangle strips mTextureGL->enable(); int numTextureLayers = mTextureGL->getNumLayers(); int t; if( mPrimitive->isTransparent() ) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else { glDisable( GL_BLEND ); } if( mPrimitive->isBackVisible() ) { glDisable( GL_CULL_FACE ); } else { glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } Color *lightColor = new Color( 0, 0, 0, 1.0 ); // for each strip of triangles for( int y=0; ymHigh-1; y++ ) { // note that we skip the last row of verts, since they are the bottom // of the last strip. int thisRow = y * mPrimitive->mWide; int nextRow = ( y + 1 ) * mPrimitive->mWide; glBegin( GL_TRIANGLE_STRIP ); // draw first triangle // first vert in next row int index = nextRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = nextRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // draw next vertex to complete first "rectangle" index = thisRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // then add rest of vertices as part of strip for( int x=2; xmWide; x++ ) { // every additional pair of vertices completes // another "rectangle" index = nextRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); // pass in each layer's anchor points for( t=0; tmAnchorX[t][ index ], mPrimitive->mAnchorY[t][ index ] ); } glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); } glEnd(); } mTextureGL->disable(); // cleanup for( int i=0; imNumVertices; i++ ) { delete worldVertices[i]; delete worldNormals[i]; } delete [] worldVertices; delete [] worldNormals; delete lightColor; } // this is the non-ARB version of draw inline void PrimitiveGL::drawNoMultitexture( Transform3D *inTransform, LightingGL *inLighting ) { // first, copy the vertices and translate/rotate/scale them Vector3D **worldVertices = new Vector3D*[ mPrimitive->mNumVertices ]; Vector3D **worldNormals = new Vector3D*[ mPrimitive->mNumVertices ]; for( int i=0; imNumVertices; i++ ) { // copy worldVertices[i] = new Vector3D( mPrimitive->mVertices[i] ); worldNormals[i] = new Vector3D( mPrimitive->mNormals[i] ); // translate/rotate/scale inTransform->apply( worldVertices[i] ); // only rotate normals inTransform->applyNoTranslation( worldNormals[i] ); // normalize to get rid of any scaling worldNormals[i]->normalize(); } Color *lightColor = new Color( 0, 0, 0, 1.0 ); // now draw vertices as triangle strips mTextureGL->enable(); if( mPrimitive->isTransparent() ) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else { glDisable( GL_BLEND ); } if( mPrimitive->isBackVisible() ) { glDisable( GL_CULL_FACE ); } else { glEnable( GL_CULL_FACE ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } // for each strip of triangles for( int y=0; ymHigh-1; y++ ) { // note that we skip the last row of verts, since they are the bottom // of the last strip. int thisRow = y * mPrimitive->mWide; int nextRow = ( y + 1 ) * mPrimitive->mWide; glBegin( GL_TRIANGLE_STRIP ); // draw first triangle // first vert in next row int index = nextRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + 0; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = nextRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // draw next vertex to complete first "rectangle" index = thisRow + 1; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); // then add rest of vertices as part of strip for( int x=2; xmWide; x++ ) { // every additional pair of vertices completes // another "rectangle" index = nextRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); index = thisRow + x; inLighting->getLighting( worldVertices[index], worldNormals[index], lightColor ); glColor4f( lightColor->r, lightColor->g, lightColor->b, 1.0 ); glTexCoord2d( mPrimitive->mAnchorX[0][ index ], mPrimitive->mAnchorY[0][ index ] ); glVertex3d( worldVertices[index]->mX, worldVertices[index]->mY, worldVertices[index]->mZ ); } glEnd(); } mTextureGL->disable(); // cleanup for( int i=0; imNumVertices; i++ ) { delete worldVertices[i]; delete worldNormals[i]; } delete [] worldVertices; delete [] worldNormals; delete lightColor; } // these all just wrap the functions of the underlying Primitive3D inline int PrimitiveGL::getNumParameters() { return mPrimitive->getNumParameters(); } inline int PrimitiveGL::getNumAnimations() { return mPrimitive->getNumAnimations(); } inline void PrimitiveGL::setParameter( int inParameterIndex, double inValue ) { mPrimitive->setParameter( inParameterIndex, inValue ); } inline double PrimitiveGL::getParameter( int inParameterIndex ) { return mPrimitive->getParameter( inParameterIndex ); } inline void PrimitiveGL::step( double inStepSize ) { mPrimitive->step( inStepSize ); } inline void PrimitiveGL::startAnimation( int inAnimationIndex ) { mPrimitive->startAnimation( inAnimationIndex ); } inline void PrimitiveGL::stopAnimation( int inAnimationIndex ) { mPrimitive->stopAnimation( inAnimationIndex ); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/AmbientLightingGL.h0000640000175000017500000000243107232125073024313 0ustar pabspabs/* * Modification History * * 2001-January-17 Jason Rohrer * Created. */ #ifndef AMBIENT_LIGHTING_GL_INCLUDED #define AMBIENT_LIGHTING_GL_INCLUDED #include "LightingGL.h" /** * LightingGL implementation that uses constant, specifiable * light values everywhere. Most useful when combined with * directional or point lightings using MultiLighting. * * Setting color to full white makes this lighting equivalent * to a NoLightingGL. * * @author Jason Rohrer */ class AmbientLightingGL : public LightingGL{ public: /** * Constructs an AmbientLighting. * * @param inColor color and intensity of lighting. * Is not copied, so cannot be accessed again by caller. */ AmbientLightingGL( Color *inColor ); ~AmbientLightingGL(); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: Color *mColor; }; inline AmbientLightingGL::AmbientLightingGL( Color *inColor ) : mColor( inColor ) { } inline AmbientLightingGL::~AmbientLightingGL() { delete mColor; } inline void AmbientLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { outColor->r = mColor->r; outColor->g = mColor->g; outColor->b = mColor->b; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/SingleTextureGL.cpp0000640000175000017500000000355710206430336024411 0ustar pabspabs/* * Modification History * * 2005-February-21 Jason Rohrer * Created. */ #include "SingleTextureGL.h" void SingleTextureGL::setTextureData( Image *inImage ) { // first, convert our image to an RGBAImage RGBAImage *rgbaImage = new RGBAImage( inImage ); if( inImage->getNumChannels() < 4 ) { // we should fill in 1.0 for the alpha channel // since the input image doesn't have an alpha channel double *channel = rgbaImage->getChannel( 3 ); int numPixels = inImage->getWidth() * inImage->getHeight(); for( int i=0; igetRGBABytes(); int error; GLenum texFormat = GL_RGBA; glBindTexture( GL_TEXTURE_2D, mTextureID ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error binding to texture id %d, error = %d\n", mTextureID, error ); } glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexImage2D( GL_TEXTURE_2D, 0, texFormat, inImage->getWidth(), inImage->getHeight(), 0, texFormat, GL_UNSIGNED_BYTE, textureData ); error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error setting texture data for id %d, error = %d\n", mTextureID, error ); printf( "Perhaps texture image width or height is not a power of 2\n" "Width = %lu, Height = %lu\n", inImage->getWidth(), inImage->getHeight() ); } delete rgbaImage; delete [] textureData; } transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/MouseHandlerGL.h0000640000175000017500000000216307220162371023635 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. */ #ifndef MOUSE_HANDLER_GL_INCLUDED #define MOUSE_HANDLER_GL_INCLUDED /** * Interface for an object that can field OpenGL mouse events. * * @author Jason Rohrer */ class MouseHandlerGL { public: /** * Callback function for when mouse moves. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseMoved( int inX, int inY ) = 0; /** * Callback function for when mouse moves with button depressed. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseDragged( int inX, int inY ) = 0; /** * Callback function for when the mouse button is depressed. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mousePressed( int inX, int inY ) = 0; /** * Callback function for when the mouse button is released. * * @param inX x position of mouse. * @param inY y position of mouse. */ virtual void mouseReleased( int inX, int inY ) = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/ScreenGL.h0000640000175000017500000002527110063310267022471 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-15 Jason Rohrer * Added compile instructions for Linux to the comments. * * 2001-February-4 Jason Rohrer * Added a function for getting the view angle. * Added support for keyboard up functions. * Added support for redraw listeners. * Added a missing destructor. * * 2001-August-29 Jason Rohrer * Added support for adding and removing mouse, keyboard, and scene handlers. * * 2001-August-30 Jason Rohrer * Fixed some comments. * * 2001-September-15 Jason Rohrer * Added functions for accessing the screen size. * * 2001-October-13 Jason Rohrer * Added a function for applying the view matrix transformation. * * 2001-October-29 Jason Rohrer * Added a private function that checks for focused keyboard handlers. * * 2004-June-11 Jason Rohrer * Added functions for getting/setting view position. * * 2004-June-14 Jason Rohrer * Added comment about need for glutInit call. */ #ifndef SCREEN_GL_INCLUDED #define SCREEN_GL_INCLUDED #include #include #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "RedrawListenerGL.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/util/SimpleVector.h" // prototypes void glutResize( int inW, int inH ); void glutKeyboard( unsigned char inKey, int inX, int inY ); void glutKeyboardUp( unsigned char inKey, int inX, int inY ); void glutSpecialKeyboard( int inKey, int inX, int inY ); void glutSpecialKeyboardUp( int inKey, int inX, int inY ); void glutMotion( int inX, int inY ); void glutPassiveMotion( int inX, int inY ); void glutMouse( int inButton, int inState, int inX, int inY ); void glutDisplay(); void glutIdle(); /** * Object that handles general initialization of an OpenGL screen. * * Compile note: For Linux, add these library flags: * -lGL -lglut -lGLU -L/usr/X11R6/lib * * @author Jason Rohrer */ class ScreenGL { public: /** * Constructs a ScreenGL. * * Before calling this constructor, glutInit must be called with * the application's command-line arguments. For example, if * the application's main function looks like: * * int main( int inNumArgs, char **inArgs ) { ... } * * Then you must call glutInit( &inNumArgs, inArgs ) before * constructing a screen. * * @param inWide width of screen. * @param inHigh height of screen. * @param inFullScreen set to true for full screen mode. * @param inWindowName name to be displayed on title bar of window. * @param inKeyHandler object that will receive keyboard events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. * @param inMouseHandler object that will receive mouse events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. * @param inSceneHandler object that will be called to draw * the scene during in response to redraw events. * NULL specifies no handler (defaults to NULL). * Must be destroyed by caller. */ ScreenGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, KeyboardHandlerGL *inKeyHandler = NULL, MouseHandlerGL *inMouseHandler = NULL, SceneHandlerGL *inSceneHandler = NULL ); /** * Destructor closes and releases the screen. */ ~ScreenGL(); /** * Starts the GLUT main loop. * * Note that this function call never returns. */ void start(); /** * Moves the view position. * * @param inPositionChange directional vector describing motion. * Must be destroyed by caller. */ void moveView( Vector3D *inPositionChange ); /** * Rotates the view. * * @param inOrientationChange angle to rotate view by. * Must be destroyed by caller. */ void rotateView( Angle3D *inOrientationChange ); /** * Gets the angle of the current view direction. * * @return the angle of the current view direction. * Not a copy, so shouldn't be modified or destroyed by caller. */ Angle3D *getViewOrientation(); /** * Gets the current view position. * * @return the position of the current view. * Must be destroyed by caller. */ Vector3D *getViewPosition(); /** * Sets the current view position. * * @param inPosition the new position. * Must be destroyed by caller. */ void setViewPosition( Vector3D *inPosition ); /** * Gets the width of the screen. * * @return the width of the screen, in pixels. */ int getWidth(); /** * Gets the height of the screen. * * @return the height of the screen, in pixels. */ int getHeight(); /** * Adds a mouse handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addMouseHandler( MouseHandlerGL *inHandler ); /** * Removes a mouse handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeMouseHandler( MouseHandlerGL *inHandler ); /** * Adds a keyboard handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addKeyboardHandler( KeyboardHandlerGL *inHandler ); /** * Removes a keyboard handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeKeyboardHandler( KeyboardHandlerGL *inHandler ); /** * Adds a scene handler. * * @param inHandler the handler to add Must * be destroyed by caller. * * Must not be called after calling start(). */ void addSceneHandler( SceneHandlerGL *inHandler ); /** * Removes a scene handler. * * @param inHandler the handler to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeSceneHandler( SceneHandlerGL *inHandler ); /** * Adds a redraw listener. * * @param inListener the listener to add. Must * be destroyed by caller. * * Must not be called after calling start(). */ void addRedrawListener( RedrawListenerGL *inListener ); /** * Removes a redraw listener. * * @param inListener the listener to remove. Must * be destroyed by caller. * * Must not be called after calling start(). */ void removeRedrawListener( RedrawListenerGL *inListener ); /** * Applies the current view matrix transformation * to the matrix at the top of the GL_PROJECTION stack. */ void applyViewTransform(); /** * Access the various handlers. */ //KeyboardHandlerGL *getKeyHandler(); //MouseHandlerGL *getMouseHandler(); //SceneHandlerGL *getSceneHandler(); private : // GLUT callbacks /*void glutResize( int inW, int inH ); void glutKeyboard( unsigned char inKey, int inX, int inY ); void glutMotion( int inX, int inY ); void glutPassiveMotion( int inX, int inY ); void glutMouse( int inButton, int inState, int inX, int inY ); */ friend void glutResize( int inW, int inH ); friend void glutKeyboard( unsigned char inKey, int inX, int inY ); friend void glutKeyboardUp( unsigned char inKey, int inX, int inY ); friend void glutSpecialKeyboard( int inKey, int inX, int inY ); friend void glutSpecialKeyboardUp( int inKey, int inX, int inY ); friend void glutMotion( int inX, int inY ); friend void glutPassiveMotion( int inX, int inY ); friend void glutMouse( int inButton, int inState, int inX, int inY ); friend void glutDisplay(); friend void glutIdle(); int mWide; int mHigh; char mFullScreen; Vector3D *mViewPosition; // orientation of 0,0,0 means looking in the direction (0,0,1) // with an up direction of (0,1,0) Angle3D *mViewOrientation; // vectors of handlers and listeners SimpleVector *mMouseHandlerVector; SimpleVector *mKeyboardHandlerVector; SimpleVector *mSceneHandlerVector; SimpleVector *mRedrawListenerVector; /** * Gets whether at least one of our keyboard handlers is focused. * * @return true iff at least one keyboard handler is focused. */ char isKeyboardHandlerFocused(); }; inline ScreenGL::~ScreenGL() { delete mViewPosition; delete mViewOrientation; delete mRedrawListenerVector; delete mMouseHandlerVector; delete mKeyboardHandlerVector; delete mSceneHandlerVector; } inline void ScreenGL::moveView( Vector3D *inPositionChange ) { mViewPosition->add( inPositionChange ); } inline void ScreenGL::rotateView( Angle3D *inOrientationChange ) { mViewOrientation->add( inOrientationChange ); } inline Angle3D *ScreenGL::getViewOrientation() { return mViewOrientation; } inline Vector3D *ScreenGL::getViewPosition() { return new Vector3D( mViewPosition ); } inline void ScreenGL::setViewPosition( Vector3D *inPosition ) { delete mViewPosition; mViewPosition = new Vector3D( inPosition ); } inline int ScreenGL::getWidth() { return mWide; } inline int ScreenGL::getHeight() { return mHigh; } inline void ScreenGL::addRedrawListener( RedrawListenerGL *inListener ) { mRedrawListenerVector->push_back( inListener ); } inline void ScreenGL::removeRedrawListener( RedrawListenerGL *inListener ) { mRedrawListenerVector->deleteElement( inListener ); } inline void ScreenGL::addMouseHandler( MouseHandlerGL *inListener ) { mMouseHandlerVector->push_back( inListener ); } inline void ScreenGL::removeMouseHandler( MouseHandlerGL *inListener ) { mMouseHandlerVector->deleteElement( inListener ); } inline void ScreenGL::addKeyboardHandler( KeyboardHandlerGL *inListener ) { mKeyboardHandlerVector->push_back( inListener ); } inline void ScreenGL::removeKeyboardHandler( KeyboardHandlerGL *inListener ) { mKeyboardHandlerVector->deleteElement( inListener ); } inline void ScreenGL::addSceneHandler( SceneHandlerGL *inListener ) { mSceneHandlerVector->push_back( inListener ); } inline void ScreenGL::removeSceneHandler( SceneHandlerGL *inListener ) { mSceneHandlerVector->deleteElement( inListener ); } inline char ScreenGL::isKeyboardHandlerFocused() { for( int h=0; hsize(); h++ ) { KeyboardHandlerGL *handler = *( mKeyboardHandlerVector->getElement( h ) ); if( handler->isFocused() ) { return true; } } // else none were focused return false; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/testNavigatorGL.cpp0000640000175000017500000000102707343255271024442 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. */ #include "TestSceneHandlerGL.h" #include "SceneNavigatorDisplayGL.h" #include "minorGems/util/random/StdRandomSource.h" // simple test function int main() { StdRandomSource *randSource = new StdRandomSource( 2 ); TestSceneHandlerGL *handler = new TestSceneHandlerGL(); char *name = "test window"; SceneNavigatorDisplayGL *screen = new SceneNavigatorDisplayGL( 200, 200, false, name, handler ); screen->start(); return 0; } transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/SceneNavigatorDisplayGL.h0000640000175000017500000002046107673013511025511 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. * * 2001-August-30 Jason Rohrer * Fixed some comments. * * 2001-October-29 Jason Rohrer * Changed to move and rotate view at a fixed * time rate, regardless of framerate. * * 2003-June-14 Jason Rohrer * Fixed namespace for exit call. * Added M_PI backup definition. */ #ifndef SCENE_NAVIGATOR_DISPLAY_GL_INCLUDED #define SCENE_NAVIGATOR_DISPLAY_GL_INCLUDED #include "ScreenGL.h" #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "RedrawListenerGL.h" #include "minorGems/system/Time.h" #include #include // make sure M_PI is defined #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /** * Subclass of ScreenGL that handles general-purpose 3D scene navigation * rendered through OpenGL. * * Motion is unlimited, though forward-backward motion is confinded * to the x-z plane (the user can move up and down explicitly, however). * * To constrict motion, subclass SceneNavigatorDisplayGL and override * the moveView() function (a member of ScreenGL). * * Compile note: For Linux, add these library flags: * -lGL -lglut -lGLU -L/usr/X11R6/lib * Be sure to include ScreenGL.cpp in the file list passed to the compiler. * * @author Jason Rohrer */ class SceneNavigatorDisplayGL : public ScreenGL, public MouseHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL { public: /** * Constructs a navigator. * * @param inWide the width of the screen in pixels. * @param inHigh the height of the screen in pixels. * @param inFullScreen true iff the display should * fill the screen. * @param inWindowName the name of the display window. * @param inSceenHandler the handler responsible for * drawing the scene. * Must be destroyed by caller. * @param inMoveUnitsPerSecond the number of world * units to move per second when the user makes a move. * Defaults to 1.0 units per second. * @param inRotateRadiansPerSecond the number of * radians to rotate per second when the user makes a rotation. * Defaults to 1.0 radians per second. */ SceneNavigatorDisplayGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, SceneHandlerGL *inSceneHandler, double inMoveUnitsPerSecond = 1.0, double inRotateRadiansPerSecond = 1.0 ); ~SceneNavigatorDisplayGL(); // implements the MouseHandlerGL interface virtual void mouseMoved( int inX, int inY ); virtual void mouseDragged( int inX, int inY ); virtual void mousePressed( int inX, int inY ); virtual void mouseReleased( int inX, int inY ); // implements the KeyboardHandlerGL interface virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); private : int mLastMouseX, mLastMouseY; // amount to move and rotate view during each redraw Vector3D *mMoveDirection; Angle3D *mRotationOffset; // keep y movement separate from view direction. double mYMovement; // true if a moving key is currently depressed char mMoving; // true if a rotation key is currently depressed char mRotating; unsigned long mTimeLastRedrawS; unsigned long mTimeLastRedrawMS; double mMoveUnitsPerSecond; double mRotateRadiansPerSecond; }; inline SceneNavigatorDisplayGL::SceneNavigatorDisplayGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, SceneHandlerGL *inSceneHandler, double inMoveUnitsPerSecond, double inRotateRadiansPerSecond ) : ScreenGL( inWide, inHigh, inFullScreen, inWindowName, NULL, NULL, inSceneHandler ), mMoveDirection( new Vector3D( 0, 0, 0 ) ), mRotationOffset( new Angle3D( 0, 0, 0 ) ), mMoving( false ), mRotating( false ), mYMovement( 0 ), mMoveUnitsPerSecond( inMoveUnitsPerSecond ), mRotateRadiansPerSecond( inRotateRadiansPerSecond ) { // add ourself to the superclass' listener lists addMouseHandler( this ); addKeyboardHandler( this ); addRedrawListener( this ); Time::getCurrentTime( &mTimeLastRedrawS, &mTimeLastRedrawMS ); } inline void SceneNavigatorDisplayGL::mouseMoved( int inX, int inY ) { //printf( "mouse moved to (%d, %d)\n", inX, inY ); } inline void SceneNavigatorDisplayGL::mouseDragged( int inX, int inY ) { int delX = inX - mLastMouseX; int delY = inY - mLastMouseY; Angle3D *rotAngle = new Angle3D( delY / 50, delX/50, 0 ); //mScreen->rotateView( rotAngle ); delete rotAngle; mLastMouseX = inX; mLastMouseY = inY; } inline void SceneNavigatorDisplayGL::mousePressed( int inX, int inY ) { } inline void SceneNavigatorDisplayGL::mouseReleased( int inX, int inY ) { } inline void SceneNavigatorDisplayGL::keyPressed( unsigned char inKey, int inX, int inY ) { if( inKey == 'y' || inKey == 'Y' ) { // turn view downwards mRotationOffset->mX = M_PI/20; } else if( inKey == 'h' || inKey == 'H' ) { // turn view upwards mRotationOffset->mX = -M_PI/20; } if( inKey == 's' || inKey == 'S' ) { // turn to the left mRotationOffset->mY = M_PI/20; } else if( inKey == 'f' || inKey == 'F' ) { // turn to the right mRotationOffset->mY = -M_PI/20; } if( inKey == 'i' || inKey == 'I' ) { // move upwards mYMovement = 1; } else if( inKey == 'k' || inKey == 'K' ) { // move downwards mYMovement = -1; } if( inKey == 'j' || inKey == 'J' ) { // strafe to the left mMoveDirection->mX = 1; } else if( inKey == 'l' || inKey == 'L' ) { // strafe to the right mMoveDirection->mX = -1; } if( inKey == 'e' || inKey == 'E' ) { // move forward mMoveDirection->mZ = 1; } else if( inKey == 'd' || inKey == 'D' ) { // move backward mMoveDirection->mZ = -1; } else if( inKey == 'q' || inKey == 'Q' ) { // quit ::exit( 0 ); } } inline void SceneNavigatorDisplayGL::keyReleased( unsigned char inKey, int inX, int inY ) { if( inKey == 'e' || inKey == 'E' ) { mMoveDirection->mZ = 0; } else if( inKey == 'd' || inKey == 'D' ) { mMoveDirection->mZ = 0; } if( inKey == 'j' || inKey == 'J' ) { mMoveDirection->mX = 0; } else if( inKey == 'l' || inKey == 'L' ) { mMoveDirection->mX = 0; } if( inKey == 'i' || inKey == 'I' ) { mYMovement = 0; } else if( inKey == 'k' || inKey == 'K' ) { mYMovement = 0; } if( inKey == 's' || inKey == 'S' ) { mRotationOffset->mY = 0; } else if( inKey == 'f' || inKey == 'F' ) { mRotationOffset->mY = 0; } if( inKey == 'y' || inKey == 'Y' ) { mRotationOffset->mX = 0; } else if( inKey == 'h' || inKey == 'H' ) { mRotationOffset->mX = 0; } } inline void SceneNavigatorDisplayGL::specialKeyPressed( int inKey, int inX, int inY ) { } inline void SceneNavigatorDisplayGL::specialKeyReleased( int inKey, int inX, int inY ) { } inline void SceneNavigatorDisplayGL::fireRedraw() { unsigned long currentTimeS; unsigned long currentTimeMS; Time::getCurrentTime( ¤tTimeS, ¤tTimeMS ); unsigned long deltaMS = Time::getMillisecondsSince( mTimeLastRedrawS, mTimeLastRedrawMS ); mTimeLastRedrawS = currentTimeS; mTimeLastRedrawMS = currentTimeMS; // compute the number of units to move to maintain // a constant speed of mUnitsPerSecond double unitsToMove = mMoveUnitsPerSecond * ( deltaMS / 1000.0 ); double radiansToRotate = mRotateRadiansPerSecond * ( deltaMS / 1000.0 ); // scale our rotation offset Angle3D *scaledRotationOffset = new Angle3D( mRotationOffset ); scaledRotationOffset->scale( radiansToRotate ); // rotate the view in the super class rotateView( scaledRotationOffset ); delete scaledRotationOffset; Vector3D *move = new Vector3D( mMoveDirection ); // scale our move direction move->scale( unitsToMove ); Angle3D *rotation = new Angle3D( getViewOrientation() ); // only rotate movement direction about y axis // we don't want view y direction to affect the direction of movement rotation->mX = 0; rotation->mZ = 0; move->rotate( rotation ); delete rotation; // now add in y movement, scaled move->mY = mYMovement * unitsToMove; // move the view in the superclass moveView( move ); delete move; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/SingleTextureGL.h0000640000175000017500000000622410206430337024051 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2005-February-21 Jason Rohrer * Added comments about channel ordering. * Created a cpp file for static init and longer function definitions. */ #ifndef SINGLE_TEXTURE_GL_INCLUDED #define SINGLE_TEXTURE_GL_INCLUDED #include #include #include #include "minorGems/graphics/Image.h" #include "minorGems/graphics/RGBAImage.h" /** * A single-layer OpenGL 32-bit texture map. * * This is essentially a simplified version of TextureGL. * * @author Jason Rohrer */ class SingleTextureGL { public: /** * Constructs a texture map, specifying texture data. * * Note that this constructor can only be called * after a GL context has been setup. * * @param inImage the image to use as a source * for texture data. The image should have * 4 channels. Any extra channels are ignored, * and black is set into additional texture * channels if inImage has too few channels. * Must be destroyed by caller, and can be * destroyed as soon as this constructor returns. * Channels are ordered as RGBA. * Must be destroyed by caller. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. */ SingleTextureGL( Image *inImage ); /** * The OpenGL texture is deleted when the SingleTextureGL object is * destroyed. */ ~SingleTextureGL(); /** * Sets the data for this texture. * * @param inImage the image to use as a source * for texture data. The image should have * 4 channels. Any extra channels are ignored, * and black is set into additional texture * channels if inImage has too few channels. * Must be destroyed by caller, and can be * destroyed as soon as this constructor returns. * Channels are ordered as RGBA. * Must be destroyed by caller. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. */ void setTextureData( Image *inImage ); /** * Enables this texture. */ void enable(); /** * Disables this texture and all of its layers. */ void disable(); private: unsigned int mTextureID; }; inline SingleTextureGL::SingleTextureGL( Image *inImage ) { glGenTextures( 1, &mTextureID ); setTextureData( inImage ); } inline SingleTextureGL::~SingleTextureGL() { unsigned int *textureIDs = new unsigned int[1]; textureIDs[0] = mTextureID; glDeleteTextures( 1, textureIDs ); delete [] textureIDs; } inline void SingleTextureGL::enable() { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, mTextureID ); int error = glGetError(); if( error != GL_NO_ERROR ) { // error printf( "Error binding texture id %d, error = %d\n", mTextureID, error ); } } inline void SingleTextureGL::disable() { glDisable( GL_TEXTURE_2D ); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/TestSceneHandlerGL.h0000640000175000017500000000275007343452732024455 0ustar pabspabs/* * Modification History * * 2001-August-29 Jason Rohrer * Created. * * 2001-August-30 Jason Rohrer * Added random triangels for testing. */ #ifndef TEST_SCENE_HANDLER_GL_INCLUDED #define TEST_SCENE_HANDLER_GL_INCLUDED #include #include "SceneHandlerGL.h" #include "minorGems/util/random/StdRandomSource.h" /** * A test scene implementation. * * @author Jason Rohrer */ class TestSceneHandlerGL : public SceneHandlerGL { public: // implements the SceneHandlerGL interface virtual void drawScene(); }; void TestSceneHandlerGL::drawScene() { glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); StdRandomSource source( 0 ); glBegin( GL_TRIANGLES ); { for( int i=0; i<10; i++ ) { glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); glColor4f( source.getRandomFloat(), source.getRandomFloat(), source.getRandomFloat(), 1.0 ); glVertex3f( source.getRandomFloat() * 100, source.getRandomFloat() * 100, source.getRandomFloat() * 100 ); } } glEnd(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/ObjectGL.h0000640000175000017500000001172507253777626022506 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Added some missing includes. * * 2001-January-16 Jason Rohrer * Changed to use a Transform3D instead of Vectors, Angles, and scales for * each primitive in the object. Changed draw() function to take a Transform3D. * Fixed a memory leak in the draw() function. * * 2001-March-14 Jason Rohrer * Added support for paramatization and temporal animations. */ #ifndef OBJECT_GL_INCLUDED #define OBJECT_GL_INCLUDED #include #include "PrimitiveGL.h" #include "LightingGL.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/graphics/3d/Object3D.h" /** * OpenGL object. * * Comprised of a collection of primitives. * * @author Jason Rohrer */ class ObjectGL { public: /** * Constructs an Object. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * object is destroyed. * * @param inObject the Object3D to base this GL version on. */ ObjectGL( Object3D *inObject ); ~ObjectGL(); /** * Draws the object. * * @param inTransform the transform to apply to the object before * drawing it. * @param inLighting the lighting to use when drawing the object. */ void draw( Transform3D *inTransform, LightingGL *inLighting ); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /** * Sets a parameter for this object. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this object. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this object forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this object. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this object. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); private: Object3D *mObject; // GL versions of the primitives PrimitiveGL **mPrimitives; }; inline ObjectGL::ObjectGL( Object3D *inObject ) : mObject( inObject ) { mPrimitives = new PrimitiveGL*[ mObject->mNumPrimitives ]; for( int i=0; imNumPrimitives; i++ ) { mPrimitives[i] = new PrimitiveGL( mObject->mPrimitives[i] ); } } inline ObjectGL::~ObjectGL() { for( int i=0; imNumPrimitives; i++ ) { delete mPrimitives[i]; } delete [] mPrimitives; delete mObject; } inline void ObjectGL::draw( Transform3D *inTransform, LightingGL *inLighting ) { for( int i=0; imNumPrimitives; i++ ) { // need to transform the position of primitive within object // copy the transform for this primitive Transform3D *primTransform = new Transform3D( mObject->mTransform[i] ); // add the object transform to the end of it primTransform->transform( inTransform ); mPrimitives[i]->draw( primTransform, inLighting ); delete primTransform; } } // these just pass the function call on to the underlying Object3D inline int ObjectGL::getNumParameters() { return mObject->getNumParameters(); } inline int ObjectGL::getNumAnimations() { return mObject->getNumAnimations(); } inline void ObjectGL::setParameter( int inParameterIndex, double inValue ) { mObject->setParameter( inParameterIndex, inValue ); } inline double ObjectGL::getParameter( int inParameterIndex ) { return mObject->getParameter( inParameterIndex ); } inline void ObjectGL::step( double inStepSize ) { mObject->step( inStepSize ); } inline void ObjectGL::startAnimation( int inAnimationIndex ) { mObject->startAnimation( inAnimationIndex ); } inline void ObjectGL::stopAnimation( int inAnimationIndex ) { mObject->stopAnimation( inAnimationIndex ); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/testNavigatorGLcompile0000750000175000017500000000030207343255360025226 0ustar pabspabsg++ -g -o testNavigatorGL -I/usr/X11R6/include -I../../.. -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 -lXi -lXext -lXmu ScreenGL.cpp testNavigatorGL.cpp ../../../minorGems/io/linux/TypeIOLinux.cpp transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/MultiLightingGL.h0000640000175000017500000000441507220445427024037 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef MULTI_LIGHTING_GL_INCLUDED #define MULTI_LIGHTING_GL_INCLUDED #include #include "LightingGL.h" #include "minorGems/util/SimpleVector.h" /** * A LightingGL implementation that contains a collection of * LightingGLs. * * @author Jason Rohrer */ class MultiLightingGL : public LightingGL { public: /** * Constructs a MultiLighting. */ MultiLightingGL(); ~MultiLightingGL(); /** * Adds a lighting to this MultiLighting. * * @param inLighting lighting to add. Is not * destroyed when the MultiLighting is destroyed. */ void addLighting( LightingGL *inLighting ); /** * Removes a lighting to this MultiLighting. * * @param inLighting lighting to remove. */ void removeLighting( LightingGL *inLighting ); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: SimpleVector *mLightingVector; }; inline MultiLightingGL::MultiLightingGL() : mLightingVector( new SimpleVector() ) { } inline MultiLightingGL::~MultiLightingGL() { delete mLightingVector; } inline void MultiLightingGL::addLighting( LightingGL *inLighting ) { mLightingVector->push_back( inLighting ); } inline void MultiLightingGL::removeLighting( LightingGL *inLighting ) { mLightingVector->deleteElement( inLighting ); } inline void MultiLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { int numLightings = mLightingVector->size(); outColor->r = 0; outColor->g = 0; outColor->b = 0; // sum lighting contributions from each lighting for( int i=0; igetElement( i ) ); thisLighting->getLighting( inPoint, inNormal, tempColor ); outColor->r += tempColor->r; outColor->g += tempColor->g; outColor->b += tempColor->b; delete tempColor; } // clip color components: if( outColor->r > 1.0 ) { outColor->r = 1.0; } if( outColor->g > 1.0 ) { outColor->g = 1.0; } if( outColor->b > 1.0 ) { outColor->b = 1.0; } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/DirectionLightingGL.h0000640000175000017500000000331007220445427024656 0ustar pabspabs/* * Modification History * * 2000-December-20 Jason Rohrer * Created. */ #ifndef DIRECTION_LIGHTING_GL_INCLUDED #define DIRECTION_LIGHTING_GL_INCLUDED #include #include "LightingGL.h" /** * A LightingGL implementation with a single point light source * at distance infinity. * * @author Jason Rohrer */ class DirectionLightingGL : public LightingGL { public: /** * Constructs a DirectionLighting. * * @param inColor color of lighting for a surface * that is facing directly into the light source. * Is not copied, so cannot be accessed again by caller. * @param inDirection incoming direction of light source. * Is not copied, so cannot be accessed again by caller. */ DirectionLightingGL( Color *inColor, Vector3D *inDirection ); ~DirectionLightingGL(); // implements LightingGL interface void getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ); private: Color *mColor; // direction pointing *towards* light source Vector3D *mDirection; }; inline DirectionLightingGL::DirectionLightingGL( Color *inColor, Vector3D *inDirection ) : mColor( inColor ), mDirection( inDirection ) { // reverse the passed-in direction mDirection->scale( -1 ); } inline DirectionLightingGL::~DirectionLightingGL() { delete mColor; delete mDirection; } inline void DirectionLightingGL::getLighting( Vector3D *inPoint, Vector3D *inNormal, Color *outColor ) { double dot = inNormal->dot( mDirection ); if( dot > 0 ) { outColor->r = mColor->r * dot; outColor->g = mColor->g * dot; outColor->b = mColor->b * dot; } else { outColor->r = 0; outColor->g = 0; outColor->b = 0; } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/KeyboardHandlerGL.h0000640000175000017500000000476107367364150024326 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Added key release functions. * * 2001-October-29 Jason Rohrer * Added fuction for querying a handler's focus. */ #ifndef KEYBOARD_HANDLER_GL_INCLUDED #define KEYBOARD_HANDLER_GL_INCLUDED /** * Interface for an object that can field OpenGL keystrokes. * * @author Jason Rohrer */ class KeyboardHandlerGL { public: /** * Gets whether this handler is focused (in other words, * whether this handler wants to reserve keyboard * events for itself). * * If no registered handler is focused, then all * registered handlers receive keyboard events. However, * if some handlers are focused, then only focused handlers * receive keyboard events. * * Note that in general, handlers should be unfocused. * A default implementation is included in this interface, * so handlers that do not care about focus can ignore * this function. * * @return true iff this handler is focused. */ virtual char isFocused(); /** * Callback function for when an ASCII-representable key is pressed. * * @param inKey ASCII representation of the pressed key. * @param inX x position of mouse when key was pressed. * @param inY y position of mouse when key was pressed. */ virtual void keyPressed( unsigned char inKey, int inX, int inY ) = 0; /** * Callback function for when an ASCII-representable key is released. * * @param inKey ASCII representation of the released key. * @param inX x position of mouse when key was released. * @param inY y position of mouse when key was released. */ virtual void keyReleased( unsigned char inKey, int inX, int inY ) = 0; /** * Callback function for when an special key is pressed. * * @param inKey integer constant representation of the pressed key. * @param inX x position of mouse when key was pressed. * @param inY y position of mouse when key was pressed. */ virtual void specialKeyPressed( int inKey, int inX, int inY ) = 0; /** * Callback function for when an special key is released. * * @param inKey integer constant representation of the released key. * @param inX x position of mouse when key was released. * @param inY y position of mouse when key was released. */ virtual void specialKeyReleased( int inKey, int inX, int inY ) = 0; }; inline char KeyboardHandlerGL::isFocused() { return false; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/testScreenGL.cpp0000640000175000017500000000113607236561234023730 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-February-2 Jason Rohrer * Fixed the random seed for testing. */ #include "TestHandlerGL.h" #include "ScreenGL.h" #include "minorGems/util/random/StdRandomSource.h" // simple test function int main() { StdRandomSource *randSource = new StdRandomSource( 2 ); TestHandlerGL *handler = new TestHandlerGL( randSource, 30 ); char *name = "test window"; ScreenGL *screen = new ScreenGL( 200, 200, false, name, handler, handler, handler ); handler->setupPrimitives(); screen->start(); } transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/0000750000175000017500000000000010305077063021435 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/glGUITest.cpp0000640000175000017500000000755407351520325023765 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Added support for testing ButtonGL. * * 2001-September-16 Jason Rohrer * Added support for testing StickyButtonGL. * Added support for testing MultiButtonGL. * Added support for testing MouseCursorRegionGL. * * 2001-September-16 Jason Rohrer * Added support for testing SliderGL. */ #include "SliderGL.h" #include "MouseCursorRegionGL.h" #include "MultiButtonGL.h" #include "GUIPanelGL.h" #include "GUIContainerGL.h" #include "GUITranslatorGL.h" #include "ButtonGL.h" #include "StickyButtonGL.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/ScreenGL.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" // prototype /** * Loads an image from a BMP file. * * @param inFileName the name of the file to load from. * * @return the loaded image, or NULL if reading from the file fails. */ Image *loadBMPImage( char *inFileName ); // a test program for gl-based guis int main() { Image *buttonUpImage = loadBMPImage( "test.bmp" ); if( buttonUpImage == NULL ) { printf( "image loading failed.\n" ); return 1; } Image *buttonDownImage = loadBMPImage( "test2.bmp" ); if( buttonUpImage == NULL ) { printf( "image loading failed.\n" ); return 1; } Image *sliderImage = loadBMPImage( "test3.bmp" ); if( sliderImage == NULL ) { printf( "image loading failed.\n" ); return 1; } // construct the screen first so that other GL calls work ScreenGL *screen = new ScreenGL( 400, 300, false, "Test GL GUI" ); // construct a button with these images ButtonGL *button = new ButtonGL( 0.45, 0.45, 0.1, 0.1, buttonUpImage, buttonDownImage ); StickyButtonGL *stickyButton = new StickyButtonGL( 0.45, 0.56, 0.1, 0.1, buttonUpImage->copy(), buttonDownImage->copy() ); int numMultiButton = 4; Image **pressedImages = new Image*[numMultiButton]; Image **unpressedImages = new Image*[numMultiButton]; for( int i=0; icopy(); unpressedImages[i] = buttonUpImage->copy(); } double gutterFraction = 0.125; MultiButtonGL *multiButton = new MultiButtonGL( 0.4, 0.67, 0.2, 0.2, numMultiButton, unpressedImages, pressedImages, gutterFraction ); SliderGL *slider = new SliderGL( 0.35, 0.34, 0.3, 0.1, sliderImage, 0.33333, new Color( 0, 0, 0), new Color( 1.0f, 0, 0 ), new Color( 0.5f, 0.5f, 0.5f ), new Color( 0.6f, 0.6f, 0.6f ) ); GUIContainerGL *container = new GUIContainerGL( 0, 0, 1, 1 ); GUITranslatorGL *translator = new GUITranslatorGL( container, screen ); screen->addRedrawListener( translator ); screen->addMouseHandler( translator ); GUIPanelGL *panel = new GUIPanelGL( 0.25, 0.25, 0.5, 0.7, new Color( 0.25f, 0.25f, 0.25f ) ); panel->add( button ); panel->add( stickyButton ); panel->add( multiButton ); panel->add( slider ); container->add( panel ); MouseCursorRegionGL *cursor = new MouseCursorRegionGL( 0, 0, 1.0, 1.0, 0.125, new Color( 0, 0, 1.0f ), new Color( 0, 0, .75f ) ); container->add( cursor ); GUIPanelGL *panel2 = new GUIPanelGL( 0, 0, 0.25, 1, new Color( 0, 1.0f, 0 ) ); container->add( panel2 ); container->add( cursor ); screen->start(); delete translator; // this deletes container too delete screen; return 0; } Image *loadBMPImage( char *inFileName ) { // load images for the button File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileInputStream *imageStream = new FileInputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); Image *returnImage = converter->deformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/LabelGL.h0000640000175000017500000000456607367364030023073 0ustar pabspabs/* * Modification History * * 2001-October-13 Jason Rohrer * Created. * * 2001-October-29 Jason Rohrer * Added a missing destructor. */ #ifndef LABEL_GL_INCLUDED #define LABEL_GL_INCLUDED #include "GUIComponentGL.h" #include "TextGL.h" /** * A text label for OpenGL-based GUIs. * * @author Jason Rohrer */ class LabelGL : public GUIComponentGL { public: /** * Constructs a label. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inString the text to display in this label. * Is copied internally, so must be destroyed * by caller if not const. * @param inText the text object to use when drawing * this label. Must be destroyed by caller after * this class is destroyed. */ LabelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText ); ~LabelGL(); /** * Sets the text displayed by this label. * * @param inString the text to display in this label. * Is copied internally, so must be destroyed * by caller if not const. */ void setText( char *inString ); /** * Gets the text displayed by this label. * * @return the text to display in this label. * Must not be destroyed or modified by caller. */ char *getText(); // override fireRedraw in GUIComponentGL virtual void fireRedraw(); protected: TextGL *mText; char *mString; }; inline LabelGL::LabelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mText( inText ), mString( NULL ) { setText( inString ); } inline LabelGL::~LabelGL() { if( mString != NULL ) { delete [] mString; } } inline void LabelGL::setText( char *inString ) { if( mString != NULL ) { delete [] mString; } int length = strlen( inString ) + 1; mString = new char[ length ]; memcpy( mString, inString, length ); } inline char *LabelGL::getText() { return mString; } inline void LabelGL::fireRedraw() { mText->drawText( mString, mAnchorX, mAnchorY, mWidth, mHeight ); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/GUIComponentGL.h0000640000175000017500000001032607367425325024357 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Changed to use normalized floating point coordinates. * Fixed some compile bugs. * Changed to extend the GUIComponent class. * * 2001-October-29 Jason Rohrer * Added support for focus. */ #ifndef GUI_COMPONENT_GL_INCLUDED #define GUI_COMPONENT_GL_INCLUDED #include "minorGems/graphics/openGL/RedrawListenerGL.h" #include "minorGems/ui/GUIComponent.h" /** * A base class for all OpenGL GUI components. * * Note that all coordinates are specified in a normalized * [0,1.0] space, where the screen is spanned by this [0,1.0] range. * * @author Jason Rohrer */ class GUIComponentGL : public GUIComponent, public RedrawListenerGL { public: /** * Tests whether a point is inside this component. * * @param inX the x value to check. * @param inY the y value to check. * * @return true iff the point is insided this component. */ virtual char isInside( double inX, double inY ); /** * Sets this component's focus status. * * Note that this component may ignore this function call * and configure its own focus (for example, some * components are never focused). * * The default implementation ignores this call * and is never focused. * * @param inFocus true iff this component should be focused. */ virtual void setFocus( char inFocus ); // the implementations below do nothing, but they allow // subclasses to pick which input they care about (and which // functions they want to override) // implements a normalized (to [0,1.0] version // of a MouseHandlerGL-like interface virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); // implements a normalized (to [0,1.0] version // of a KeyboardHandlerGL-like interface virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); // implements the RedrawListenerGL interface virtual void fireRedraw(); protected: /** * Constructs a component. * * Should only be called by subclass constructors. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ GUIComponentGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); double mAnchorX; double mAnchorY; double mWidth; double mHeight; }; inline GUIComponentGL::GUIComponentGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : mAnchorX( inAnchorX ), mAnchorY( inAnchorY ), mWidth( inWidth ), mHeight( inHeight ) { } inline char GUIComponentGL::isInside( double inX, double inY ) { if( inX >= mAnchorX && inX < mAnchorX + mWidth && inY >= mAnchorY && inY < mAnchorY + mHeight ) { return true; } else { return false; } } inline void GUIComponentGL::mouseMoved( double inX, double inY ) { } inline void GUIComponentGL::mouseDragged( double inX, double inY ) { } inline void GUIComponentGL::mousePressed( double inX, double inY ) { } inline void GUIComponentGL::mouseReleased( double inX, double inY ) { } inline void GUIComponentGL::setFocus( char inFocus ) { // default implementation ignores this call } inline char GUIComponentGL::isFocused() { return false; } inline void GUIComponentGL::keyPressed( unsigned char inKey, double inX, double inY ) { } inline void GUIComponentGL::specialKeyPressed( int inKey, double inX, double inY ) { } inline void GUIComponentGL::keyReleased( unsigned char inKey, double inX, double inY ) { } inline void GUIComponentGL::specialKeyReleased( int inKey, double inX, double inY ) { } inline void GUIComponentGL::fireRedraw() { } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/GUIPanelGL.h0000640000175000017500000000351707351043625023450 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. */ #ifndef GUI_PANEL_GL_INCLUDED #define GUI_PANEL_GL_INCLUDED #include "GUIComponentGL.h" #include "GUIContainerGL.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/graphics/Color.h" #include /** * A container with a background color that is drawn * behind the components. * * @author Jason Rohrer */ class GUIPanelGL : public GUIContainerGL { public: /** * Constructs a panel. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inColor the background color for this panel. * Will be destroyed when this class is destroyed. */ GUIPanelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ); ~GUIPanelGL(); // override fireRedraw() in GUIComponentGL virtual void fireRedraw(); protected: Color *mColor; }; inline GUIPanelGL::GUIPanelGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Color *inColor ) : GUIContainerGL( inAnchorX, inAnchorY, inWidth, inHeight ), mColor( inColor ) { } inline GUIPanelGL::~GUIPanelGL() { delete mColor; } inline void GUIPanelGL::fireRedraw() { // draw our background color as a rectangle glColor3f( mColor->r, mColor->g, mColor->b ); glBegin( GL_QUADS ); { glVertex2d( mAnchorX, mAnchorY ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } glEnd(); // call the supercalss redraw GUIContainerGL::fireRedraw(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/test3.bmp0000640000175000017500000003006607352744545023223 0ustar pabspabsBM606(@@0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?BB>BB>BB?BB?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?AA>EDED?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?AA=GG:QP6][3fd3fd6][:QP=GG?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>ED9RQ2ig)‚$“$’Ž*|3ec:QP>ED?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB;ML3ec'‰…°«ÉÂÇÁ«¦*€}6][BB@@@@@@@@@@@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BBBB@@@@@@@@@?@@?@@?@@?@@?@@?@@>@@>@@>@@>AA=AA=@@=@@>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>BB:NN/om£ž ÖÏñéöííäÏÈ!›—2ig;LL?BB@@@@@@@@@?@@?@@>@@>@@>@@=@@<@@;CC;GG;II:II:HG;DD@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>CC9RQ-wt®¨ áÚùñúñìäÉÃ#–’3fd@@<@@;AA:DD9FF8IH7LK5RP2YX3_]2_^3YX6PO9HG@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=BB8TS,wt°« àØöíøïíäËÄ"—“2fe=GG@@@@@@?@@?BB;BB9FF7LK5RQ3XW1^\/ec*ro&‚%‰†'„*ur/ca4SR8HG;AA=@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@>@@@@>AA=ED@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@?@@>@@=BB9PO.ol¢ ÙÒôëùðóê ÝÕ°ª'|4ZY:GG9KK6WV2db*rp&|y#…‚ŽŠ˜“£ž°«Áº ÒËÙÒ ÌÅ­¨#ˆ„,ig3RP8EE;@@=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@>@@@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@>@@>@@>@@=@@=@@=@@=@@=@@=@@=@@;AA8II2^]$‡ƒ¾¸çßöíöíêáÌŦ¡#†ƒ'{x$†ƒ¢Á» ÎÇ ÒË ÐÉ ÐÈÓÌØÐÝÖãÛèàæÞ×й³!‘,lj4QP8DD;@@<@@=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@>@@=@@=@@=@@=@@<@@<@@;@@;@@<@@<@@<@@;@@9HG4XW(yw«¦ ÙÒîæôëêâ Ó̵°žš›—ª¥ÆÀáÙêâéáåÝáÙÞÖÞÖß×àØàÙÚÓÉ«¥&„/ba6LK9CC;@@<@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@=@@<@@;@@:@@9@@:@@9@@9@@:@@9@@:@@9AA7GG4TT*pn˜ËÅæÞïçêâÙÒý´¯¶°Å¿ÜÕíåðèïçêâãÛÛÔÖÎ ÑÊ ÍÆ ÊÄÁ»®©"‘,pn5TS9EE:AA<@@<@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@=@@;@@:@@8AA6CC6BC5CC5CC6AB7@B7AB8DD6JJ3WV*om™•žáÙìäêâߨ ÑÊ ÇÀ ËÅ ØÑèàòéòêëãáÙ ÖÎ Êþ¸´®®¨§¡œ—%‰…,qo4YW9GF;@@;@@<@@=@@=@@=@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@<@@:A@8DD4HH2LM1NO0NP0MO1IL3GJ4GJ5JL3RR0`_%zx¢ÆÀÞ×ëãìäåÝÜÕÖÏÛÔåÝïæóêîåáÚ ÒËÀº®©œ— Š$ƒ€'{x,qo1ca7UT;HG@@>@@>@@>@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@<@@8FE4ON/XX,`a*df*ch)_f*Yb,U].T[.V[-``)pnŒ‰¯ª ÍÇáÚêãìåêãåÞäÜçßíäòéñèèßÚÒ Ä½¬§’Ž%zw,ge1[Z4UT7ON:JIAA>@@>@@=@@=@@=@@>@@>@@>@@>@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@;CC6NM0^\)on#{| €…ˆy‡ s„"l}$iw&kt&ux!‡†£Ÿ¾¹ÔÍâÜëäîèíçëåëäïçóêõìòéèß×ÐÀº¥ "‰…,ml3XW9IHBB?BB?@@@@@?@@?@@>@@>@@>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:GG3XW+qo!Šˆœž¡¨¬•¬Ž©ˆ¤†ž‡—˜¡¢·´ ËÆÛÕåßëæíéîêîéïéòëõí÷îóêêâÜÔ É°ª–’'|z/hf4WU;JJ>DD?AA@@@@@@@@@?@@>@@>@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>@@=@@:II2_^&~¤¢»½ ÁËºÐ°Ñ ¦Ï ¡Ì ¡Ä¤»­¸»½ ÊÇØÒàÛäáèæêéëêìëíìñíõîøðõíïçåÞÖÏÆÀµ¯£ž!‘(|y1kh6[Z;NM=FF?BB?AA>AA=@@>@@>@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:II1a`%‰…µ²ÑÓÕâÌæ¾èµè²èµâ¼ÙÅÒÐÓØÖÞÚàßâããçäéåëæìèííîôð÷ñöïõíðççàÞ× ÕÎÍÆ»±« œ—(ƒ€0nl5^]8TS;NM;II;GG@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@;HH4[Z'ƒ€²¯ÕÕÛçÑîÅñ½ñ¼ðÄðÍé×ãßáâáàáßãÞæÞèÝêÝëÞíáíæïíðóòöð÷ðöîóëðèîæìäéá àØÐʺ´¢%Š+|y/pn3cb8WV;ML>ED?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>DD9RQ/rožšÇÆÙßÔêÈîÃðÅðÍñÙîâêèéåçßåÛæØçÖéÔêÓëÔìÖíÝïåðïñöñùòúñúñúñúñúñúòöîñéæÞ Öϼ±¬¢$‘,yv5`^;NM>CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB;KK7XW1ed)vv#†‡™®³ ÄÇÕÙàäáìÙëÑêÊëÄëÁë¾ì¾ì¾ìÀíÊîØðäñíñôñöñøñùñúñùðöîòêòêöíøïùñúñùñóëÒË"™”2ig;NM>BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?BB=GG@@8JK+dh†‘¢¶³Ò½äÁìÂìÃíÄíÆîÉîÍîÑîÖîÜïâðçðéïéïåíâëÝç×áÍÕ ¼À§¥‹Ї–’ ›¦¡ ž™'І/pn7ZXAA>BB@@=CC;JJ:NM8RQ4VV&lq‰˜¡º°Ó¹ä½ìÁîÅïÉðÏðÕðÛðáðåðéðìðíðéïâîÚëÐçÅÞ¹Ó«À ˜§Šhl&TT-ML0NN5QP8NMDD?AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>AA;GG5VU0ec,qo*yw(yx‡Šœ¨ ®Ã¹Ø¿åÂìÄîÆïÊðÐñØñàñæñëñîñððððéïâî×ëÊæ¾Û°Í¡¸ Ÿv‚_d&LM.CC3BB8BB;BB=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=AA:IH2]\)vu"Ž¡Ÿ­«°®°² ¸¾ÀÎÆÜÉæÉìÇîÇïÈñÎòÕòàòêñðñòñõñòðìïãîØêÉ弨­Èœ²‡™o|Y_%GJ,@@3@@9@@<@@=@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@:ED3YY'yx˜™±² ÃÅ ÐÑ×ÖÓÒÒÔÔÛÔáÓèÐìÊîÅðÃòÄóÐôÜóçòðòôñ÷ñöðïïçîÜêÏäÁױƞ±‡—oyW]$FH+@@2@@:@@<@@=@@>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@=@@@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@<@@8FF.[Z"} ¦ ¸ÄÆÙÍäÓêÚìàêäèäåáâÛâÔãÉæ¿ì¶ò»öÈôÚôçóññôîöíóëóëóîêêàåÕÛÅ̳¹ ¢„‡kl TT(DD0@@7@@<@@=@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:BB5LM*de„‰¢®µÉ¾ÛÄæÉëÐí×éÝåÞàÛÛÔ×ÊÕÁ×¹àºèÃïÏðÛðçïíëîçîåèàéáðèïêëèâáÓÓÃà °°šš‚ig$SR-DD6@@;@@=@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:DD3PQ(gj‚‰œ«­Å´×¹â½åÂäÉàÍØÌÐ ÇÆ ¾¾³·ª¸ «Ã¶ÓÅàÒçßëæêéæäÞÛÔÔÍÔÍâÚìåíæìåáÜÔÏÅÀ ²®œ™„!ki*SR2ED:@@<@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:DD2QQ(ei}…’¢ ¢»©Ì­Ö¯Ø²Õ´Ì ´À°´¦¥™˜Œ†• ª¸ÀÍÒÝÞäãäáß×Ó Éø³´®ü ÜÔéáíæèßàÙÕÎÇÁ ¶° ›…'ge0NN9CC<@@>@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@:CC3NO*`c!s{†”•« œ» Ã Ã Ÿ½œ°•¡Œ‘ &qp*fe)hiz}š  ¶¼ÎÓÛÞÞàÚÙÍË º¶¦¡š–Ÿ›º´ ÔÌãÛâÛáÙÜÔÓÌÇÀ¶°›–$xu/XW9GG<@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@=@@;@@5II-XZ%hnyƒ…–£ª¨‰ ‘ w€'jn,^_0TT2QQ.VW'jlŒ«¯ÅËÓÚØÞÔØÇȳ²œ™Ї#ƒ ’Ž®¨ÈÁ ÐÉÖÏ×ÐÔÍÍÇ Àº©¤#„€/_]:II>AA?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@<@@7DD1OO+\`%jptz‰}xˆ q$gq*\a0QT3JJ4FF4HH0OP)bc€ƒ ¨¼ÈËÛÐàÐÛÅͳ¶›š!)pn*ro"†ƒŸš²­¿¹ ÆÀ É ƿ¼¶§¢%‚1^];II>AA?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:@@5EE0PP,Z](ag&em&fo'ck*[b.RV2IK6DE8AA7@A6CD1LN)]a w”¤ ±ÉÁÞÈåÍäÈ× ¹À¡¢ ‚+ig0_],ec&xuŒˆœ—¨£®¨®©¨¢”*sp6VU=GG?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>@@<@@:@@6EE3KK1PR/SV/RV0OS2JK6DE7@A:@@9@@8@@7AC2IM*X_!o}Š¢ ¦Ë¶ãÀíÉíÍäÅÏ­¯ŠŠ+ih2VU2RQ0[Y)ig%zw"…Œˆމ#ˆ„)wt1`^9ML>CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@>@@=@@:@@9BB8DD7EE7EE8CC9AA:@@;@@<@@;@@9@@7AB3GK,T]#gyž ›Ê¬è¶ñÃôÒïÒÞ½¿••)nm2TS5II5JI2RQ.\Z,fd*mj+nk-hf1[Z7NM@@=@@=@@<@@<@@=@@=@@=@@=@@=@@<@@:@@8@A4EI.OX&`rw•‘Á¥ä°òÁöÕóàê ËÌžœ*sq4UT8FF8CC6FE5KJ4PO3SR4TR6ON8II@@>@@=@@<@@:@@7BE2JR+Wf"k…ƒ« œÏ®åÆðÜñäë ÏÏ +sp5UT9FF:AA9@@9CB9ED9FE:EE:CC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@=@@<@@9@C6EJ1NY*\n#q‹Šª§ÆÇÝßéääÉÄ –’.lj7QP:DD;@@<@@<@@<@@<@@=@@=@@=@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@=@@<@@:AD7FL3NX.[i&rŒ›¯·ÆÈŪ¦(4][:KJ@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@>@@>@@=@@<@@;AB:DG8LP3W],mr&„†#“’#“*~2ec9QP=ED>@@>@@?@@?@@?@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@>@@>@@=@@<@@AA>@@>@@?@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?@@?@@?@@?@@>AA>EDED?AA@@@?@@?@@?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?@@?BB>BB>BB?BB?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/GUIContainerGL.h0000640000175000017500000001606407370706607024343 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * Changed to use normalized floating point coordinates. * Fixed some compile bugs. * Changed so that mouse released events are passed to all components. * * 2001-October-29 Jason Rohrer * Added support for focus, including mouse-release to focus. * * 2001-November-2 Jason Rohrer * Changed to send keyboard events only to focused components, * regardless of mouse position. */ #ifndef GUI_CONTAINER_GL_INCLUDED #define GUI_CONTAINER_GL_INCLUDED #include "GUIComponentGL.h" #include "minorGems/util/SimpleVector.h" /** * A container full of gui components that delegates * redraw and ui events to the contained components. * * @author Jason Rohrer */ class GUIContainerGL : public GUIComponentGL { public: /** * Constructs a container. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. */ GUIContainerGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ); ~GUIContainerGL(); /** * Adds a component to this container. * * Note that the added component should lie within the bounds * of this container (or else event delegation may not function * properly). * * @param inComponent the component to add. Is destroyed * when this container is destroyed. */ virtual void add( GUIComponentGL *inComponent ); /** * Removes a component from this container. * * @param inComponent the component to remove. Must be * destroyed by the caller. * * @return true iff the component was removed successfully. */ virtual char remove( GUIComponentGL *inComponent ); // the implementations below delegate events to appropriate // contained components // override functions in GUIComponentGL virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); virtual void fireRedraw(); protected: SimpleVector *mComponentVector; }; inline GUIContainerGL::GUIContainerGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mComponentVector( new SimpleVector() ) { } inline GUIContainerGL::~GUIContainerGL() { // delete each contained component for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); delete component; } delete mComponentVector; } inline void GUIContainerGL::add( GUIComponentGL *inComponent ) { mComponentVector->push_back( inComponent ); } inline char GUIContainerGL::remove( GUIComponentGL *inComponent ) { return mComponentVector->deleteElement( inComponent ); } inline void GUIContainerGL::mouseMoved( double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( component->isInside( inX, inY ) ) { component->mouseMoved( inX, inY ); } } } inline void GUIContainerGL::mouseDragged( double inX, double inY ) { // released events should be passed to all components // so that a pressed component can react if the mouse // is dragged elsewhere for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->mouseDragged( inX, inY ); } } inline void GUIContainerGL::mousePressed( double inX, double inY ) { // only pass pressed events to components // that contain the coordinate pressed for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( component->isInside( inX, inY ) ) { component->mousePressed( inX, inY ); } } } inline void GUIContainerGL::mouseReleased( double inX, double inY ) { // first, unfocus all components for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->setFocus( false ); } // released events should be passed to all components // so that a pressed component can react if the mouse // is released elsewhere for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->mouseReleased( inX, inY ); // focus any components hit by the release directly if( component->isInside( inX, inY ) ) { component->setFocus( true ); } } } inline char GUIContainerGL::isFocused() { // we are focused if any of our sub-components are focused for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); if( component->isFocused() ) { return true; } } // else, none focused return false; } inline void GUIContainerGL::keyPressed( unsigned char inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->keyPressed( inKey, inX, inY ); } } } inline void GUIContainerGL::specialKeyPressed( int inKey, double inX, double inY ) { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->specialKeyPressed( inKey, inX, inY ); } } } inline void GUIContainerGL::keyReleased( unsigned char inKey, double inX, double inY ) { char someComponentFocused = isFocused(); for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->keyReleased( inKey, inX, inY ); } } } inline void GUIContainerGL::specialKeyReleased( int inKey, double inX, double inY ) { char someComponentFocused = isFocused(); for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); // only send events to focused components if( component->isFocused() ) { component->specialKeyReleased( inKey, inX, inY ); } } } inline void GUIContainerGL::fireRedraw() { for( int i=0; isize(); i++ ) { GUIComponentGL *component = *( mComponentVector->getElement( i ) ); component->fireRedraw(); } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/test.bmp0000640000175000017500000001406607352744545023142 0ustar pabspabsBM66(@ ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ôôôÿÿÿÿÿÿôôôÚÚÚÉÉÉ«««™™™ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ½½½àààúúúÿÿÿÿÿÿýýýïïïÔÔÔ±±±———‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽ«««ÒÒÒïïïÿÿÿÿÿÿÿÿÿÿÿÿàààÄÄÄšššŽŽŽ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡½½½éééÿÿÿÿÿÿøøøæææÎÎη··žžžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÔÔÔººº‰‰‰‡‡‡‡‡‡‡‡‡ŒŒŒ°°°éééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûçççÁÁÁ   ‹‹‹‡‡‡‡‡‡‡‡‡“““ÈÈÈùùùÿÿÿÿÿÿõõõïïïïïïïïïþþþþþþðððÉÉÉ›››‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ºººÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿÿÿÿÿÿÿÿÿùùùáááÏÏÏÏÏÏÔÔÔãããûûû‡‡‡‡‡‡‹‹‹±±±íííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõççççççéééõõõÿÿÿûûûèèèÁÁÁ•••‡‡‡ŠŠŠ¿¿¿úúúÿÿÿûûûÕÕÕ­­­§§§§§§§§§¿¿¿÷÷÷ÿÿÿúúúÚÚÚ¡¡¡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŒŒŒñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéééÓÓÓÏÏÏÏÏÏÿÿÿÿÿÿùùùÎÎΕ•••••ˆˆˆ‡‡‡‡‡‡åååÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØØØ—————————ŸŸŸ½½½ìììÿÿÿùùùÑÑÑŒŒŒöööÿÿÿÿÿÿýýý¿¿¿‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¹¹¹ÿÿÿÿÿÿüüüÒÒÒ•••‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿààদ¦ÿÿÿÿÿÿêêê¡¡¡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉɇ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŒŒŒ´´´ää䨨ØöööÿÿÿÿÿÿÿÿÿØØØ™™™‡‡‡‡‡‡‡‡‡‡‡‡”””ÿÿÿÿÿÿÿÿÿõõõµµµ‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿººº‡‡‡‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿííí¨¨¨‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘ÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿóóó›››‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡˜˜˜«««ŠŠŠŠŠŠ¾¾¾öööÿÿÿüüüØØØ›››‡‡‡‡‡‡‡‡‡‡‡‡§§§ÿÿÿÿÿÿÿÿÿÿÿÿÊÊʇ‡‡‡‡‡‡‡‡ŒŒŒñññÿÿÿÿÿÿÿÿÿÿÿÿÀÀÀŠŠŠ‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿõõõ½½½‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡•••ÞÞÞÿÿÿÿÿÿÿÿÿÿÿÿÖÖÖ“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘°°°¾¾¾¹¹¹   ‹‹‹‡‡‡‡‡‡‡‡‡ßßßÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓ•••‡‡‡‡‡‡‡‡‡‹‹‹ÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÒÒÒ•••‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿýýýÒÒÒ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡———âââÿÿÿÿÿÿÿÿÿÿÿÿÇÇLJ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡¢¢¢ÜÜÜüüüÿÿÿÿÿÿÿÿÿÿÿÿÍÍÍ‘‘‘‡‡‡‡‡‡‡‡‡‰‰‰¾¾¾ÿÿÿÿÿÿÿÿÿÿÿÿååå   ‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÜÜÜ‘‘‘‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡–––àààÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ–––‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ———ÇÇÇøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóó²²²‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ©©©ÿÿÿÿÿÿÿÿÿÿÿÿòòò¨¨¨‡‡‡‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿçç磣£‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡’’’ÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿçççÒÒÒÏÏÏÏÏÏËËË···³³³§§§™™™‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡³³³çççýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÍÍÍ•••‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡———úúúÿÿÿÿÿÿÿÿÿÿÿÿ¶¶¶‰‰‰‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿïïï±±±‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ºººÿÿÿÿÿÿÿÿÿÿÿÿùùùêêêôôôþþþÿÿÿÿÿÿûûûïïïáááÊÊʤ¤¤‹‹‹‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘¿¿¿òòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõÉÉɘ˜˜‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““èèèÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆ‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿøøøÀÀÀ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡§§§÷÷÷ÿÿÿÿÿÿÿÿÿýýý«««›››   ÃÃÃæææÿÿÿÿÿÿÿÿÿûûûðððÐÐБ‘‘‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽ¿¿¿÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòò²²²‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿØØØ–––‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏŠŠŠ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““ÏÏÏÿÿÿÿÿÿÿÿÿÿÿÿÅÅÅŒŒŒ‡‡‡‡‡‡‰‰‰¥¥¥àààúúúÿÿÿÿÿÿÿÿÿäää“““‡‡‡‡‡‡‡‡‡¨¨¨òòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÊÊʇ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹½½½ÿÿÿÿÿÿÿÿÿÿÿÿéé采‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÜÜÜššš‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰®®®ðððÿÿÿÿÿÿÿÿÿëëë–––‡‡‡‡‡‡‡‡‡‡‡‡šššÔÔÔúúúÿÿÿÿÿÿÿÿÿ¼¼¼‡‡‡‡‡‡‡‡‡°°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿããã°°°‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ¨¨¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¥¥‡‡‡‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿçç禦¦‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽÁÁÁ÷÷÷ÿÿÿÿÿÿÿÿÿ¼¼¼ˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡   âââÿÿÿÿÿÿÿÿÿððð‡‡‡‡‡‡‡‡‡ªªªõõõÿÿÿÿÿÿÿÿÿßßß   ‡‡‡‡‡‡‡‡‡‰‰‰³³³‰‰‰‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡žžžóóóÿÿÿÿÿÿÿÿÿÿÿÿ···‹‹‹‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿííí­­­‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡”””ÏÏÏúúúÿÿÿÿÿÿõõõ‡‡‡‡‡‡‡‡‡‡‡‡‰‰‰ÍÍÍÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡‡‡‡‡‡‡ŸŸŸãããÿÿÿÿÿÿÿÿÿªªªˆˆˆ‡‡‡‡‡‡‡‡‡–––ÓÓÓõõõèèè®®®‰‰‰‡‡‡‡‡‡‡‡‡‡‡‡™™™åååÿÿÿÿÿÿÿÿÿÿÿÿÃÃLJ‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ———————————————•••‹‹‹‡‡‡‡‡‡‡‡‡ŒŒŒÁÁÁóóóþþþÿÿÿïï¢‹‹‹‡‡‡‡‡‡“““ÖÖÖÿÿÿÿÿÿÿÿÿõõõ‡‡‡‡‡‡‡‡‡ŠŠŠ´´´ñññÿÿÿÿÿÿ¶¶¶ŠŠŠ‡‡‡‡‡‡‹‹‹ºººÿÿÿÿÿÿÿÿÿåå凇‡‡‡‡žžž£££ÖÖÖÿÿÿÿÿÿÿÿÿÿÿÿåå墢¢———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòòòçççççççççççççççßßß   ‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ºººçççýýýþþþïïïÀÀÀªªª«««ËËËôôôÿÿÿÿÿÿÿÿÿººº‡‡‡‡‡‡‡‡‡‡‡‡‘‘‘¿¿¿òòòÿÿÿììì¹¹¹¢¢¢———¥¥¥çççÿÿÿÿÿÿÿÿÿââ✜œ‡‡‡‡‡‡²²²üüüîîîóóóÿÿÿÿÿÿÿÿÿÿÿÿøøøéééçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ§§§‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠ¡¡¡ÂÂÂîîîÿÿÿüüüòòòóóóüüüÿÿÿÿÿÿøøøÕÕÕ‘‘‘‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡®®®ßßßûûûúúúêêêßßßåååÿÿÿÿÿÿÿÿÿñññ»»»ŽŽŽ‡‡‡‡‡‡™™™ÌÌÌòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××ÿÿÿÿÿÿÿÿÿÿÿÿîî¸ŸŸŸŸŸŸŸŸŸŸŸŸ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‹‹‹ššš¸¸¸ØØØðððÿÿÿÿÿÿòòòÛÛÛ²²²“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ˆˆˆ”””­­­ÏÏÏßßßÿÿÿÿÿÿÿÿÿóóóÐÐЩ©©ŒŒŒ‡‡‡‡‡‡‡‡‡‡‡‡ŽŽŽŸŸŸÄÄÄòòòÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ¡¡¡ŠŠŠ½½½úúúÿÿÿÿÿÿÿÿÿ···‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡¥¥¥èèèÿÿÿÿÿÿÿÿÿÝÝ݇‡‡’’’×××ÿÿÿÿÿÿÿÿÿÀÀÀŒŒŒ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ŠŠŠºººõõõÿÿÿÿÿÿëë뇇‡ˆˆˆ¥¥¥éééÿÿÿÿÿÿÓÓÓ–––‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡“““ÒÒÒüüüÿÿÿÿÿÿ˜˜˜‡‡‡‡‡‡»»»øøøÿÿÿãããžžž‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡¥¥¥èèèÿÿÿÿÿÿ°°°‡‡‡‡‡‡‡‡‡ŽŽŽ   §§§   ‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡œœœ§§§§§§“““‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/MultiButtonGL.h0000640000175000017500000001603707371145520024332 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. * * 2001-November-3 Jason Rohrer * Fixed a gutter size bug that occurred when there was * only one row or column. * Changed so that rows are filled before adding new columns. * Fixed a bug in setSelectedIndex. */ #ifndef MULTI_BUTTON_GL_INCLUDED #define MULTI_BUTTON_GL_INCLUDED #include "GUIContainerGL.h" #include "StickyButtonGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/ui/event/ActionListenerList.h" #include "minorGems/ui/event/ActionListener.h" #include "minorGems/ui/GUIComponent.h" #include #include /** * A set of buttons that allows only one to be depressed * at a time. * * @author Jason Rohrer */ class MultiButtonGL : public GUIContainerGL, public ActionListener, public ActionListenerList { public: /** * Constructs a set of buttons. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inNumButtons the number of buttons in the set. * @param inUnpressedImages the images to display * when each button is unpressed. Images must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImages the images to display * when each button is pressed. Images must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inGutterFraction the fraction of each row * and column that should be taken up by the gutters * (the spaces between buttons). In [0, 1.0]. */ MultiButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, int inNumButtons, Image **inUnpressedImages, Image **inPressedImages, double inGutterFraction ); ~MultiButtonGL(); /** * Sets the currently depressed button. * * Note that if this function causes a change * in the state of the set, an action will * be fired to all registered listeners. * * @param inButtonIndex the index of the button to depress. */ void setSelectedButton( int inButtonIndex ); /** * Gets the index of the currently depressed button. * * Note that if this function causes a change * in the state of the set, an action will * be fired to all registered listeners. * * @return the index of the button that is depressed. */ int getSelectedButton(); // we don't need to override any mouse // or redraw functions, since the container // and the buttons should handle these correctly // implements the ActionListener interface virtual void actionPerformed( GUIComponent *inTarget ); protected: int mNumButtons; StickyButtonGL **mButtons; int mSelectedIndex; char mIgnoreEvents; /** * Maps a button in mButtons to its index in the * mButtons array. * * @param inButton the button to get an index for. * * @return the index of inButton in the mButtons array. */ int buttonToIndex( StickyButtonGL *inButton ); }; inline MultiButtonGL::MultiButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, int inNumButtons, Image **inUnpressedImages, Image **inPressedImages, double inGutterFraction ) : GUIContainerGL( inAnchorX, inAnchorY, inWidth, inHeight ), mNumButtons( inNumButtons ), mButtons( new StickyButtonGL*[inNumButtons] ), mSelectedIndex( 0 ), mIgnoreEvents( false ) { int numColumns = (int)( sqrt( mNumButtons ) ); int numRows = mNumButtons / numColumns; while( numRows * numColumns < mNumButtons ) { numRows++; } // determine gutter and button sizes double gutterSizeX; double gutterSizeY; if( numRows == 1 ) { gutterSizeY = 0; } else { gutterSizeY = ( inHeight * inGutterFraction ) / ( numRows - 1 ); } if( numColumns == 1 ) { gutterSizeX = 0; } else { gutterSizeX = ( inWidth * inGutterFraction ) / ( numColumns - 1 ); } double buttonSizeX = ( inWidth * ( 1 - inGutterFraction ) ) / ( numColumns ); double buttonSizeY = ( inHeight * ( 1 - inGutterFraction ) ) / ( numRows ); // setup each button int buttonIndex = 0; for( int r=0; r= mNumButtons ) { // jump out of our loop if we run out of buttons c = numColumns; r = numRows; } } } // now we've added all of our buttons // press one of them before we add any listeners mButtons[ mSelectedIndex ]->setPressed( true ); // now add ourselves as an action listener to each button for( int i=0; iaddActionListener( this ); } // delete the arrays pointing to the images, since // they are no longer needed delete [] inUnpressedImages; delete [] inPressedImages; } inline MultiButtonGL::~MultiButtonGL() { // note that we don't need to delete the buttons // themselves, since they will be deleted by GUIContainer // destructor delete [] mButtons; } inline void MultiButtonGL::setSelectedButton( int inButtonIndex ) { // simply press the appropriate button, and let the // action handlers do the rest mButtons[ inButtonIndex ]->setPressed( true ); } inline int MultiButtonGL::getSelectedButton() { return mSelectedIndex; } inline void MultiButtonGL::actionPerformed( GUIComponent *inTarget ) { if( !mIgnoreEvents ) { int buttonIndex = buttonToIndex( (StickyButtonGL*)inTarget ); // if another button is being pressed if( buttonIndex != mSelectedIndex && mButtons[ buttonIndex ]->isPressed() ) { // unpress the old button, ignoring the resulting event mIgnoreEvents = true; mButtons[ mSelectedIndex ]->setPressed( false ); mIgnoreEvents = false; mSelectedIndex = buttonIndex; fireActionPerformed( this ); } // else if our selected button is being unpressed else if( buttonIndex == mSelectedIndex && !( mButtons[ buttonIndex ]->isPressed() ) ) { // don't allow it to become unpressed // re-press it, ignoring the resulting event mIgnoreEvents = true; mButtons[ mSelectedIndex ]->setPressed( true ); mIgnoreEvents = false; // don't fire an action, since our state // has not changed } } } inline int MultiButtonGL::buttonToIndex( StickyButtonGL *inButton ) { for( int i=0; i /** * A textured button for GL-based GUIs. * * @author Jason Rohrer */ class ButtonGL : public GUIComponentGL, public ActionListenerList { public: /** * Constructs a button. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inUnpressedImage the image to display * when this button is unpressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImage the image to display * when this button is pressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. */ ButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ); ~ButtonGL(); // override funxtions in GUIComponentGL virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: Image *mUnpressedImage; Image *mPressedImage; SingleTextureGL *mUnpressedTexture; SingleTextureGL *mPressedTexture; SingleTextureGL *mCurrentTexture; }; inline ButtonGL::ButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mUnpressedImage( inUnpressedImage ), mPressedImage( inPressedImage ) { mUnpressedTexture = new SingleTextureGL( mUnpressedImage ); mPressedTexture = new SingleTextureGL( mPressedImage ); mCurrentTexture = mUnpressedTexture; } inline ButtonGL::~ButtonGL() { delete mUnpressedImage; delete mPressedImage; delete mUnpressedTexture; delete mPressedTexture; } inline void ButtonGL::mouseDragged( double inX, double inY ) { if( mCurrentTexture == mPressedTexture && !isInside( inX, inY ) ) { // the mouse has been dragged outside of us, so unpress mCurrentTexture = mUnpressedTexture; } } inline void ButtonGL::mousePressed( double inX, double inY ) { // we'll only get pressed events if the mouse is pressed on us mCurrentTexture = mPressedTexture; } inline void ButtonGL::mouseReleased( double inX, double inY ) { // always unpress on a release mCurrentTexture = mUnpressedTexture; if( isInside( inX, inY ) ) { // fire an event fireActionPerformed( this ); } } inline void ButtonGL::fireRedraw() { // set our texture mCurrentTexture->enable(); glColor3f( 1.0f, 1.0f, 1.0f ); glDisable( GL_BLEND ); glBegin( GL_QUADS ); { glTexCoord2f( 0, 1.0f ); glVertex2d( mAnchorX, mAnchorY ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( mAnchorX + mWidth, mAnchorY ); glTexCoord2f( 1.0f, 0 ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glTexCoord2f( 0, 0 ); glVertex2d( mAnchorX, mAnchorY + mHeight ); } glEnd(); mCurrentTexture->disable(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/TextFieldGL.h0000640000175000017500000002042407370710377023736 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2001-November-2 Jason Rohrer * Added support for cursor position. * Added support for clicking to reposition the cursor. * Fixed an array out-of-bounds bug in the keyReleased function. */ #ifndef TEXT_FIELD_GL_INCLUDED #define TEXT_FIELD_GL_INCLUDED #include "GUIComponentGL.h" #include "TextGL.h" #include "minorGems/graphics/Color.h" /** * A text field for OpenGL-based GUIs. * * @author Jason Rohrer */ class TextFieldGL : public GUIComponentGL { public: /** * Constructs a text field. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inString the text to display in this text field. * Is copied internally, so must be destroyed * by caller if not const. * @param inText the text object to use when drawing * this textField. Must be destroyed by caller after * this class is destroyed. * @param inBorderColor the color of this field's border. * Will be destroyed when this class is destroyed. * @param inFocusedBorderColor the color of this field's border * when the field is focused. * Will be destroyed when this class is destroyed. * @param inBackgroundColor the color of this field's background. * Will be destroyed when this class is destroyed. * @param inCursorColor the color of this field's cursor line. * Will be destroyed when this class is destroyed. */ TextFieldGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText, Color *inBorderColor, Color *inFocusedBorderColor, Color *inBackgroundColor, Color *inCursorColor ); ~TextFieldGL(); /** * Sets the text displayed by this text field. * * @param inString the text to display in this text field. * Is copied internally, so must be destroyed * by caller if not const. */ void setText( char *inString ); /** * Gets the text displayed by this text field. * * @return the text to display in this textField. * Must not be destroyed or modified by caller. */ char *getText(); // override functions in GUIComponentGL virtual void setFocus( char inFocus ); virtual char isFocused(); virtual void keyPressed( unsigned char inKey, double inX, double inY ); virtual void specialKeyPressed( int inKey, double inX, double inY ); virtual void keyReleased( unsigned char inKey, double inX, double inY ); virtual void specialKeyReleased( int inKey, double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: /** * Adjusts the cursor position to bring it in bounds of the * current string. */ void fixCursorPosition(); TextGL *mText; char *mString; Color *mBorderColor; Color *mFocusedBorderColor; Color *mBackgroundColor; Color *mCursorColor; Color *mCurrentBorderColor; char mFocused; // cursor position, in number of characters int mCursorPosition; }; inline TextFieldGL::TextFieldGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, char *inString, TextGL *inText, Color *inBorderColor, Color *inFocusedBorderColor, Color *inBackgroundColor, Color *inCursorColor ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mText( inText ), mString( NULL ), mBorderColor( inBorderColor ), mFocusedBorderColor( inFocusedBorderColor ), mBackgroundColor( inBackgroundColor ), mCursorColor( inCursorColor ), mFocused( false ), mCurrentBorderColor( inBorderColor ), mCursorPosition( 0 ) { setText( inString ); } inline TextFieldGL::~TextFieldGL() { if( mString != NULL ) { delete [] mString; } delete mBorderColor; delete mFocusedBorderColor; delete mBackgroundColor; delete mCursorColor; } inline void TextFieldGL::setText( char *inString ) { if( mString != NULL ) { delete [] mString; } int length = strlen( inString ) + 1; mString = new char[ length ]; memcpy( mString, inString, length ); } inline char *TextFieldGL::getText() { return mString; } inline void TextFieldGL::setFocus( char inFocus ) { mFocused = inFocus; if( mFocused ) { mCurrentBorderColor = mFocusedBorderColor; } else { mCurrentBorderColor = mBorderColor; } } inline char TextFieldGL::isFocused() { return mFocused; } inline void TextFieldGL::keyPressed( unsigned char inKey, double inX, double inY ) { } inline void TextFieldGL::specialKeyPressed( int inKey, double inX, double inY ) { } inline void TextFieldGL::keyReleased( unsigned char inKey, double inX, double inY ) { //printf( "key press: %d\n", inKey ); // backspace and delete if( inKey == 127 || inKey == 8 ) { if( mCursorPosition != 0 ) { int length = strlen( mString ); if( length != 0 ) { char *newString = new char[ length ]; // copy mString up to the last char before the deleted char memcpy( newString, mString, mCursorPosition-1 ); // copy the portion of mString after the deleted char // this will include the trailing \0 memcpy( &( newString[mCursorPosition-1] ), &( mString[mCursorPosition] ), length - mCursorPosition + 1 ); setText( newString ); delete [] newString; mCursorPosition--; } } } // allowable character key, from space up to tilde else if( inKey >= 32 && inKey <= 126 ) { // add a character to our string int oldStringLength = strlen( mString ) + 2; int length = oldStringLength + 2; char *newString = new char[ length ]; if( mCursorPosition != 0 ) { // copy chars up to cursor position memcpy( newString, mString, mCursorPosition ); } // copy chars after cursor position, including trailing \0 memcpy( &( newString[mCursorPosition+1] ), &( mString[mCursorPosition] ), oldStringLength - mCursorPosition + 1 ); // now stick in the inserted char newString[ mCursorPosition ] = inKey; setText( newString ); delete [] newString; mCursorPosition++; } // else a non-valid key hit } inline void TextFieldGL::specialKeyReleased( int inKey, double inX, double inY ) { switch( inKey ) { case GLUT_KEY_RIGHT: mCursorPosition++; break; case GLUT_KEY_LEFT: mCursorPosition--; default: break; } fixCursorPosition(); } inline void TextFieldGL::mouseReleased( double inX, double inY ) { if( isInside( inX, inY ) ) { mCursorPosition = (int)( 0.5 + strlen( mString ) * ( inX - mAnchorX ) / mWidth ); } } inline void TextFieldGL::fixCursorPosition() { if( mCursorPosition < 0 ) { mCursorPosition = 0; } else { int stringLength = strlen( mString ); if( mCursorPosition > stringLength ) { mCursorPosition = stringLength; } } } inline void TextFieldGL::fireRedraw() { // draw the background glColor3f( mBackgroundColor->r, mBackgroundColor->g, mBackgroundColor->b ); glBegin( GL_QUADS ); { glVertex2d( mAnchorX, mAnchorY ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); // draw the text mText->drawText( mString, mAnchorX, mAnchorY, mWidth, mHeight ); if( mFocused ) { // draw the cursor glColor3f( mCursorColor->r, mCursorColor->g, mCursorColor->b ); glBegin( GL_LINES ); { // here we assume that fonts are fixed-width double cursorViewX; int stringLength = strlen( mString ); if( stringLength != 0 ) { cursorViewX = mAnchorX + mWidth * ( mCursorPosition / (double)( stringLength ) ); } else { cursorViewX = mAnchorX; } glVertex2d( cursorViewX, mAnchorY ); glVertex2d( cursorViewX, mAnchorY + mHeight ); } glEnd(); } // draw the border on top of the text and cursor glColor3f( mCurrentBorderColor->r, mCurrentBorderColor->g, mCurrentBorderColor->b ); glBegin( GL_LINE_LOOP ); { glVertex2d( mAnchorX, mAnchorY ); glVertex2d( mAnchorX, mAnchorY + mHeight ); glVertex2d( mAnchorX + mWidth, mAnchorY + mHeight ); glVertex2d( mAnchorX + mWidth, mAnchorY ); } glEnd(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/SliderGL.h0000640000175000017500000001760607353535634023302 0ustar pabspabs/* * Modification History * * 2001-September-17 Jason Rohrer * Created. * * 2001-September-21 Jason Rohrer * Fixed a bug in thumb positioning in response to mouse position. * * 2001-September-23 Jason Rohrer * Fixed a bug in icon color clearing. */ #ifndef SLIDER_GL_INCLUDED #define SLIDER_GL_INCLUDED #include "GUIComponentGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" #include "minorGems/ui/event/ActionListenerList.h" #include /** * A textured slider for GL-based GUIs. * * @author Jason Rohrer */ class SliderGL : public GUIComponentGL, public ActionListenerList { public: /** * Constructs a slider. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inIconImage the image to display * to the left of this slider. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * If set to NULL, then no icon will be drawn. * @param inIconWidthFraction the fraction of the slider's * width that should be taken up by the icon. * @param inBarStartColor the color on the left end of * the slider bar. * Will be destroyed when this class is destroyed. * @param inBarEndColor the color on the right end of * the slider bar. * Will be destroyed when this class is destroyed. * @param inThumbColor the color of the slider thumb. * Will be destroyed when this class is destroyed. * @param inBorderColor the color of the slider border. * Will be destroyed when this class is destroyed. */ SliderGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inIconImage, double inIconWidthFraction, Color *inBarStartColor, Color *inBarEndColor, Color *inThumbColor, Color *inBorderColor ); ~SliderGL(); /** * Gets the position of the slider thumb. * * @return the thumb position, in [0,1.0]. */ double getThumbPosition(); /** * Sets the position of the slider thumb. * * Note that if the slider thumb position changes * as a result of this call, then an action * will be fired to all registered listeners. * * @param inPosition the thumb position, in [0,1.0]. */ void setThumbPosition( double inPosition ); // override funxtions in GUIComponentGL virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: SingleTextureGL *mIconTexture; double mIconWidthFraction; Color *mBarStartColor; Color *mBarEndColor; Color *mThumbColor; Color *mBorderColor; double mThumbPosition; // true iff the slider is currently being dragged char mDragging; }; inline SliderGL::SliderGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inIconImage, double inIconWidthFraction, Color *inBarStartColor, Color *inBarEndColor, Color *inThumbColor, Color *inBorderColor ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mIconWidthFraction( inIconWidthFraction ), mBarStartColor( inBarStartColor ), mBarEndColor( inBarEndColor ), mThumbColor( inThumbColor ), mBorderColor( inBorderColor ), mThumbPosition( 0.5 ), mDragging( false ) { if( inIconImage != NULL ) { mIconTexture = new SingleTextureGL( inIconImage ); // the image is no longer needed delete inIconImage; } else { mIconTexture = NULL; } } inline SliderGL::~SliderGL() { if( mIconTexture != NULL ) { delete mIconTexture; } delete mBarStartColor; delete mBarEndColor; delete mThumbColor; delete mBorderColor; } inline double SliderGL::getThumbPosition() { return mThumbPosition; } inline void SliderGL::setThumbPosition( double inPosition ) { if( mThumbPosition != inPosition ) { mThumbPosition = inPosition; fireActionPerformed( this ); } } inline void SliderGL::mouseDragged( double inX, double inY ) { if( mDragging ) { double barWidth = mWidth * ( 1 - mIconWidthFraction ); double iconEndX = mAnchorX + mWidth - barWidth; double thumbWidth = 0.1 * barWidth; // we want the mouse centered over the thumb double thumbUsableBarWidth = barWidth - thumbWidth; mThumbPosition = ( inX - ( iconEndX + 0.5 * thumbWidth ) ) / thumbUsableBarWidth; if( mThumbPosition > 1 ) { mThumbPosition = 1; } else if( mThumbPosition < 0 ) { mThumbPosition = 0; } // fire to listeners fireActionPerformed( this ); } } inline void SliderGL::mousePressed( double inX, double inY ) { mDragging = true; } inline void SliderGL::mouseReleased( double inX, double inY ) { // always stop dragging on a release mDragging = false; } inline void SliderGL::fireRedraw() { // these values will change below if there is an icon double barStartX = mAnchorX; double barWidth = mWidth; if( mIconTexture != NULL ){ // first, draw the icon // set our texture mIconTexture->enable(); double textureStartX = mAnchorX; double textureStartY = mAnchorY; double textureEndX = mAnchorX + mIconWidthFraction * mWidth; double textureEndY = mAnchorY + mHeight; glBegin( GL_QUADS ); { // make sure our color is set to white for our texture // (to avoid leftover colors) glColor3f( 1.0, 1.0, 1.0 ); glTexCoord2f( 0, 1.0f ); glVertex2d( textureStartX, textureStartY ); glTexCoord2f( 1.0f, 1.0f ); glVertex2d( textureEndX, textureStartY ); glTexCoord2f( 1.0f, 0 ); glVertex2d( textureEndX, textureEndY ); glTexCoord2f( 0, 0 ); glVertex2d( textureStartX, textureEndY ); } glEnd(); mIconTexture->disable(); barWidth = mWidth * ( 1 - mIconWidthFraction ); barStartX = textureEndX; } // end check for a non-NULL icon texture // now, draw the slider bar double barHeight = 0.5 * mHeight; // center the bar vertically double barStartY = mAnchorY + ( mHeight - barHeight ) * 0.5; // draw its gradient-filled center glBegin( GL_QUADS ); { // start of bar glColor3f( mBarStartColor->r, mBarStartColor->g, mBarStartColor->b ); glVertex2d( barStartX, barStartY ); glVertex2d( barStartX, barStartY + barHeight ); // end of bar glColor3f( mBarEndColor->r, mBarEndColor->g, mBarEndColor->b ); glVertex2d( barStartX + barWidth, barStartY + barHeight ); glVertex2d( barStartX + barWidth, barStartY ); } glEnd(); // draw it's border glColor3f( mBorderColor->r, mBorderColor->g, mBorderColor->b ); glBegin( GL_LINE_LOOP ); { glVertex2d( barStartX, barStartY ); glVertex2d( barStartX, barStartY + barHeight ); glVertex2d( barStartX + barWidth, barStartY + barHeight ); glVertex2d( barStartX + barWidth, barStartY ); } glEnd(); // now draw the thumb // draw a thumb that is 1/10th as wide as the bar double thumbWidth = 0.1 * barWidth; // we don't want the thumb going off the ends of the bar double thumbUsableBarWidth = barWidth - thumbWidth; double thumbStartX = mThumbPosition * thumbUsableBarWidth + barStartX; double thumbEndX = thumbStartX + thumbWidth; glColor3f( mThumbColor->r, mThumbColor->g, mThumbColor->b ); glBegin( GL_QUADS ); { glVertex2d( thumbStartX, mAnchorY ); glVertex2d( thumbStartX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY ); } glEnd(); // draw it's border glColor3f( mBorderColor->r, mBorderColor->g, mBorderColor->b ); glBegin( GL_LINE_LOOP ); { glVertex2d( thumbStartX, mAnchorY ); glVertex2d( thumbStartX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY + mHeight ); glVertex2d( thumbEndX, mAnchorY ); } glEnd(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/TextGL.h0000640000175000017500000001706507371205122022765 0ustar pabspabs/* * Modification History * * 2001-October-13 Jason Rohrer * Created. * Changed to support alpha-blended fonts, even for 3-channel font textures. * Fixed a bug in the font coloring. * * 2001-November-3 Jason Rohrer * Fixed some missing includes. */ #ifndef TEXT_GL_INCLUDED #define TEXT_GL_INCLUDED #include #include #include #include "minorGems/graphics/Color.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * A class for drawing text strings to an OpenGL context. * Fonts are texture maps. * * @author Jason Rohrer */ class TextGL { public: /** * Constructs a gl text object. * * Note that this function can only be called * after an OpenGL drawing context has been created. * * @param inImage the image to use as a font texture. * Must be a square image with dimensions that are a power of 2. * Fonts in the image are assumed to be in row-major ASCII order, * with 16 characters per row. * Must be destroyed by caller. Can be destroyed as soon as * this constructor returns. * @param inUseAlpha true iff alpha blending should * be used when rendering the font textures. If alpha * blending is enabled and the image has only 3 channels, * only the red channel (channel 0) * of the image is used: it is interpreted as the alpha * channel for a white fontset. * */ TextGL( Image *inImage, char inUseAlpha = false ); virtual ~TextGL(); /** * Sets the main color of the font. * * If the gradient color is non-NULL, this color will be at the * top of the fonts. Otherwise, the entire extent * of the font will be this color. * * @param inColor the main color of the font. * Will be destroyed when this class is destroyed. */ void setFontColor( Color *inColor ); /** * Gets the main color of the font. * * If the gradient color is non-NULL, this color will be at the * top of the fonts. Otherwise, the entire extent * of the font will be this color. * * @return the main color of the font. * Will be destroyed when this class is destroyed. */ Color *getFontColor(); /** * Sets the gradient color of the font. * * If non-NULL, this color will be at the * bottom of the fonts. * * @param inColor the gradient color of the font, or * NULL to disable the gradient. * Will be destroyed when this class is destroyed. */ void setGradientColor( Color *inColor ); /** * Gets the gradient color of the font. * * If non-NULL, this color will be at the * bottom of the fonts. * * @return the gradient color of the font, or * NULL to disable the gradient. * Will be destroyed when this class is destroyed. */ Color *getGradientColor(); /** * Draws a text string into a specified region of the * context. Text is squeezed to fit into the region. * * Uses the currently active OpenGL projection settings. * * @param inString the \0-delimited string to draw. * Must be destroyed by caller if not const. * @param inX the x coordinate of the region's lower-left * corner. * @param inY the y coordinate of the region's lower-left * corner. * @param inWidth the width of the region. * @param inHeight the height of the region. */ void drawText( char *inString, double inX, double inY, double inWidth, double inHeight ); protected: Color *mFontColor; Color *mGradientColor; SingleTextureGL *mFontTexture; char mUseAlpha; /** * Draws a character into a specified region of the * context. The character is squeezed to fit into the region. * * Uses the currently active OpenGL projection settings. * * @param inCharacter the character to draw. * @param inX the x coordinate of the region's lower-left * corner. * @param inY the y coordinate of the region's lower-left * corner. * @param inWidth the width of the region. * @param inHeight the height of the region. */ void drawCharacter( char inCharacter, double inX, double inY, double inWidth, double inHeight ); }; inline TextGL::TextGL( Image *inImage, char inUseAlpha ) : mFontColor( new Color( 1.0f, 1.0f, 1.0f, 1.0f ) ), mGradientColor( NULL ), mUseAlpha( inUseAlpha ) { if( inUseAlpha && inImage->getNumChannels() != 4 ) { Image *fontImage = new Image( inImage->getWidth(), inImage->getHeight(), 4 ); int numPixels = inImage->getWidth() * inImage->getHeight(); // copy the red channel of inImage into our alpha memcpy( fontImage->getChannel(3), inImage->getChannel(0), numPixels * sizeof( double ) ); // set other channels to white for( int c=0; c<3; c++ ) { double *channel = fontImage->getChannel( c ); for( int p=0; penable(); glBegin( GL_QUADS ); { if( mGradientColor != NULL ) { glColor4f( mGradientColor->r, mGradientColor->g, mGradientColor->b, mGradientColor->a ); } else { glColor4f( mFontColor->r, mFontColor->g, mFontColor->b, mFontColor->a ); } glTexCoord2f( textureStartX, textureStartY ); glVertex2d( charStartX, charStartY ); glTexCoord2f( textureEndX, textureStartY ); glVertex2d( charEndX, charStartY ); glColor4f( mFontColor->r, mFontColor->g, mFontColor->b, mFontColor->a ); glTexCoord2f( textureEndX, textureEndY ); glVertex2d( charEndX, charEndY ); glTexCoord2f( textureStartX, textureEndY ); glVertex2d( charStartX, charEndY ); } glEnd(); mFontTexture->disable(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/test2.bmp0000640000175000017500000001406607352744545023224 0ustar pabspabsBM66(@ QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ’’’™™™™™™’’’‚‚‚xxxggg\\\VVVQQQQQQQQQQQQQQQQQQSSS^^^qqq………–––™™™™™™———jjjZZZSSSQQQQQQQQQQQQQQQQQQUUUfff~~~™™™™™™™™™™™™†††uuu\\\UUUQQQQQQQQQQQQQQQQQQQQQQQQUUUqqq‹‹‹™™™™™™”””‰‰‰{{{mmm^^^™™™™™™™™™™™™™™™™™™™™™oooRRRQQQQQQQQQTTTiii‹‹‹™™™™™™™™™™™™™™™™™™™™™™™™–––ŠŠŠsss___SSSQQQQQQQQQXXXwww•••™™™™™™“““˜˜˜˜˜˜xxx]]]QQQQQQQQQQQQQQQQQQRRRooo™™™™™™™™™™™™™™™™™™™™™™™™”””™™™™™™™™™•••†††||||||ˆˆˆ–––UUUQQQQQQSSSjjjŽŽŽ™™™™™™™™™™™™™™™“““ŠŠŠŠŠŠ‹‹‹’’’™™™–––‹‹‹sssYYYQQQSSSrrr–––™™™–––€€€hhhdddddddddrrr”””™™™–––‚‚‚```QQQQQQQQQQQQQQQTTT™™™™™™™™™™™™™™™‹‹‹~~~||||||™™™™™™•••{{{YYYUUUUUUVVVYYY^^^QQQQQQQQQ^^^‰‰‰™™™™™™™™™™™™™™™^^^ZZZZZZ[[[___qqq™™™•••}}}TTTVVV“““™™™™™™˜˜˜sssQQQQQQQQQQQQRRRooo™™™™™™———~~~YYYQQQQQQQQQQQQUUU™™™™™™™™™™™™™™™†††cccVVVUUUUUU™™™™™™ŒŒŒ```QQQQQQQQQQQQQQQQQQQQQQQQSSSmmm™™™™™™™™™™™™™™™xxxTTTQQQQQQQQQQQQQQQQQQTTTlll‰‰‰TTTVVV“““™™™™™™™™™[[[QQQQQQQQQQQQYYY™™™™™™™™™’’’lllQQQQQQQQQQQQUUU™™™™™™™™™™™™™™™oooQQQQQQQQQQQQ™™™™™™ŽŽŽdddQQQQQQQQQQQQQQQQQQQQQQQQWWW{{{™™™™™™™™™™™™’’’]]]QQQQQQQQQQQQQQQQQQQQQQQQQQQ[[[fffSSSSSSqqq“““™™™———\\\QQQQQQQQQQQQddd™™™™™™™™™™™™yyyVVVQQQQQQQQQTTT™™™™™™™™™™™™sssSSSQQQQQQQQQ™™™™™™’’’pppQQQQQQQQQQQQQQQQQQQQQQQQYYY………™™™™™™™™™™™™€€€XXXQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQWWWiiirrrnnn```SSSQQQQQQQQQVVV………™™™™™™™™™™™™~~~YYYQQQQQQQQQSSS™™™™™™™™™™™™}}}YYYQQQQQQQQQ™™™™™™˜˜˜~~~QQQQQQQQQQQQQQQQQQQQQQQQZZZ‡‡‡™™™™™™™™™™™™wwwVVVQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRQQQQQQQQQQQQQQQaaaƒƒƒ———™™™™™™™™™™™™{{{WWWQQQQQQQQQRRRrrr™™™™™™™™™™™™‰‰‰```QQQQQQQQQ™™™™™™™™™ƒƒƒVVVQQQQQQQQQQQQQQQQQQQQQZZZ†††™™™™™™™™™™™™xxxZZZUUUUUUUUUQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRZZZwww•••™™™™™™™™™™™™™™™‘‘‘jjjQQQQQQQQQQQQQQQeee™™™™™™™™™™™™‘‘‘eeeQQQQQQQQQ™™™™™™™™™ŠŠŠbbbQQQQQQQQQQQQQQQQQQQQQWWW}}}™™™™™™™™™™™™ŠŠŠ~~~||||||zzzmmmkkkddd[[[SSSQQQQQQQQQQQQQQQQQQQQQQQQQQQUUUkkkŠŠŠ———™™™™™™™™™™™™™™™•••{{{YYYQQQQQQQQQQQQQQQZZZ–––™™™™™™™™™™™™mmmRRRQQQQQQ™™™™™™™™™jjjQQQQQQQQQQQQQQQQQQQQQSSSooo™™™™™™™™™™™™•••ŒŒŒ’’’˜˜˜™™™™™™–––‡‡‡xxxbbbSSSQQQQQQQQQQQQQQQQQQWWWsss‘‘‘™™™™™™™™™™™™™™™™™™“““xxx[[[QQQQQQQQQQQQQQQQQQXXX‹‹‹™™™™™™™™™™™™wwwVVVQQQQQQ™™™™™™™™™”””sssQQQQQQQQQQQQQQQQQQQQQQQQddd”””™™™™™™™™™———fff]]]```uuuŠŠŠ™™™™™™™™™———}}}WWWQQQQQQQQQQQQUUUrrr”””™™™™™™™™™™™™™™™™™™‘‘‘kkkUUUQQQQQQQQQQQQQQQQQQQQQUUU{{{™™™™™™™™™™™™ZZZQQQQQQ™™™™™™™™™™™™|||SSSQQQQQQQQQQQQQQQQQQQQQXXX|||™™™™™™™™™™™™vvvTTTQQQQQQRRRbbb†††–––™™™™™™™™™‰‰‰XXXQQQQQQQQQeee‘‘‘™™™™™™™™™™™™™™™’’’xxxUUUQQQQQQQQQQQQQQQQQQQQQQQQQQQSSSqqq™™™™™™™™™™™™ŒŒŒ^^^QQQQQQ™™™™™™™™™™™™„„„\\\QQQQQQQQQQQQQQQQQQQQQRRRhhh™™™™™™™™™ZZZQQQQQQQQQQQQ\\\–––™™™™™™™™™pppQQQQQQQQQiii™™™™™™™™™™™™™™™ˆˆˆiiiQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQddd™™™™™™™™™™™™™™™cccQQQQQQ™™™™™™™™™™™™ŠŠŠcccQQQQQQQQQQQQQQQQQQQQQQQQUUUsss”””™™™™™™™™™pppQQQQQQQQQQQQQQQ```‡‡‡™™™™™™™™™QQQQQQQQQfff“““™™™™™™™™™†††```QQQQQQQQQRRR^^^kkk]]]RRRQQQQQQQQQQQQQQQ___’’’™™™™™™™™™™™™nnnSSSQQQ™™™™™™™™™™™™ŽŽŽhhhQQQQQQQQQQQQQQQQQQQQQQQQQQQXXX{{{–––™™™™™™“““]]]QQQQQQQQQQQQRRR{{{™™™™™™™™™™™™QQQQQQQQQ___‡‡‡™™™™™™™™™fffQQQQQQQQQQQQZZZ~~~“““‹‹‹gggRRRQQQQQQQQQQQQ\\\‰‰‰™™™™™™™™™™™™uuuUUUQQQ™™™™™™™™™™™™™™™xxxZZZZZZZZZZZZZZZYYYSSSQQQQQQQQQTTTsss‘‘‘˜˜˜™™™aaaSSSQQQQQQXXX€€€™™™™™™™™™’’’QQQQQQQQQRRRkkk™™™™™™mmmSSSQQQQQQSSSooo™™™™™™™™™‰‰‰^^^QQQQQQVVV^^^aaa€€€™™™™™™™™™™™™‰‰‰aaaZZZ™™™™™™™™™™™™™™™‘‘‘ŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠ†††___QQQQQQQQQQQQSSSooo‹‹‹———˜˜˜sssffffffyyy’’’™™™™™™™™™oooQQQQQQQQQQQQWWWsss‘‘‘™™™nnn```ZZZcccŠŠŠ™™™™™™™™™‡‡‡]]]QQQQQQkkk———ŽŽŽ’’’™™™™™™™™™™™™”””ŒŒŒŠŠŠ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™“““dddQQQQQQQQQQQQQQQSSS```tttŽŽŽ™™™———‘‘‘‘‘‘———™™™™™™”””€€€WWWQQQQQQQQQQQQQQQUUUhhh………–––•••ŒŒŒ………‰‰‰™™™™™™™™™‘‘‘pppUUUQQQQQQ[[[zzz‘‘‘™™™™™™™™™™™™™™™™™™™™™™™™€€€™™™™™™™™™™™™nnn____________^^^TTTQQQQQQQQQQQQQQQQQQQQQSSS\\\nnn™™™™™™‘‘‘‚‚‚jjjXXXQQQQQQQQQQQQQQQQQQQQQQQQXXXggg{{{………™™™™™™™™™‘‘‘}}}eeeTTTQQQQQQQQQQQQUUU___uuu™™™™™™™™™™™™yyyaaaSSSqqq–––™™™™™™™™™mmmQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQccc‹‹‹™™™™™™™™™………UUUQQQWWW€€€™™™™™™™™™sssTTTQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRooo“““™™™™™™UUUQQQRRRbbb‹‹‹™™™™™™~~~YYYQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQXXX}}}———™™™™™™[[[QQQQQQTTTppp”””™™™ˆˆˆ___QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQccc‹‹‹™™™™™™jjjQQQQQQQQQUUU```ddd___TTTQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ]]]ddddddWWWQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQtranscend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/StickyButtonGL.h0000640000175000017500000000663707351162241024510 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. */ #ifndef STICKY_BUTTON_GL_INCLUDED #define STICKY_BUTTON_GL_INCLUDED #include "ButtonGL.h" #include "minorGems/graphics/Image.h" #include "minorGems/graphics/openGL/SingleTextureGL.h" /** * A button that can be toggled between a pressed * and unpressed state. * * @author Jason Rohrer */ class StickyButtonGL : public ButtonGL { public: /** * Constructs a button. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inUnpressedImage the image to display * when this button is unpressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. * @param inPressedImage the image to display * when this button is pressed. Must have dimensions * that are powers of 2. * Will be destroyed when this class is destroyed. */ StickyButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ); /** * Gets whether this button is toggled to a pressed state. * * @return true iff this button is pressed. */ virtual char isPressed(); /** * Sets this button's state. * * Note that if this function call causes * this button's state to change, then all * registered ActionListeners are notified. * * @param inPressed true iff this button should be * pressed. */ virtual void setPressed( char inPressed ); // override funxtions in GUIComponentGL virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); }; inline StickyButtonGL::StickyButtonGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, Image *inUnpressedImage, Image *inPressedImage ) : ButtonGL( inAnchorX, inAnchorY, inWidth, inHeight, inUnpressedImage, inPressedImage ) { } inline char StickyButtonGL::isPressed() { if( mCurrentTexture == mPressedTexture ) { return true; } else { return false; } } inline void StickyButtonGL::setPressed( char inPressed ) { SingleTextureGL *lastTexture = mCurrentTexture; if( inPressed ) { mCurrentTexture = mPressedTexture; } else { mCurrentTexture = mUnpressedTexture; } // check for state change if( lastTexture != mCurrentTexture ) { fireActionPerformed( this ); } } inline void StickyButtonGL::mouseDragged( double inX, double inY ) { // do nothing until the release } inline void StickyButtonGL::mousePressed( double inX, double inY ) { // do nothing until the release } inline void StickyButtonGL::mouseReleased( double inX, double inY ) { // Note the following situation: // If the mouse is pressed on another button and then // released on this button, this button will toggle... // This is non-standard behavior, but the situation occurs // rarely. By ignoring this situation, we save a bit of coding, // but this may need to be fixed later. if( isInside( inX, inY ) ) { // toggle our state to the opposite of it's current setting setPressed( !isPressed() ); // this will fire an action automatically } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/GUITranslatorGL.h0000640000175000017500000001675207371110172024541 0ustar pabspabs/* * Modification History * * 2001-September-15 Jason Rohrer * Created. * * 2001-September-16 Jason Rohrer * Changed so that translation preserves the aspect * ration of GUI components. * Fixed a bug with inverted y components of mouse coordinates. * * 2001-October-29 Jason Rohrer * Added support for focus. * * 2001-November-3 Jason Rohrer * Changed translation methods to be public. */ #ifndef GUI_TRANSLATOR_GL_INCLUDED #define GUI_TRANSLATOR_GL_INCLUDED #include "GUIComponentGL.h" #include #include "minorGems/graphics/openGL/MouseHandlerGL.h" #include "minorGems/graphics/openGL/KeyboardHandlerGL.h" #include "minorGems/graphics/openGL/RedrawListenerGL.h" #include "minorGems/graphics/openGL/ScreenGL.h" /** * A class that translates coordinates for a gui component. * * Notes on intended use: * This class is best used as a wrapper for the entire * GL-based gui. In this case, the component passed to the * constructor would be a GUIContainerGL containing the * entire GUI. This class can be thought of as a wrapper * for interfacing size-independent GUI components to the * screen. * * @author Jason Rohrer */ class GUITranslatorGL : public MouseHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL { public: /** * Constructs a translator. * * @param inComponent the component for which events * will be translated and delegated. All events will * be delegated to inComponent, regardless of whether the * events pass the inComponent->isInside() test. * Will be destroyed when this class is destroyed. * @param inScreen the screen whose size is used during * event translation. Must be destroyed by caller after * this class is destroyed. */ GUITranslatorGL( GUIComponentGL *inComponent, ScreenGL *inScreen ); ~GUITranslatorGL(); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * @param inY the y value of the screen coordinate. * @param outX a pointer to an value in which the gui x * component will be returned. * @param outY a pointer to an value in which the gui y * component will be returned. */ void translate( int inX, int inY, double *outX, double *outY ); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * * @return the gui x component will be returned. */ double translateX( int inX ); /** * Translates screen coordinates to gui coordinates. * * @param inX the x value of the screen coordinate. * * @return the gui x component will be returned. */ double translateY( int inY ); // implements the MouseHandlerGL interface virtual void mouseMoved( int inX, int inY ); virtual void mouseDragged( int inX, int inY ); virtual void mousePressed( int inX, int inY ); virtual void mouseReleased( int inX, int inY ); // implements the KeyboardHandlerGL interface virtual char isFocused(); virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); protected: GUIComponentGL *mComponent; ScreenGL *mScreen; /** * Gets the factor by which screen coordinates need * to be multiplied to get gui coordinates. * * @return the translation factor to multiply by. */ double getTranslationFactor(); }; inline GUITranslatorGL::GUITranslatorGL( GUIComponentGL *inComponent, ScreenGL *inScreen ) : mComponent( inComponent ), mScreen( inScreen ) { } inline GUITranslatorGL::~GUITranslatorGL() { delete mComponent; } inline void GUITranslatorGL::translate( int inX, int inY, double *outX, double *outY ) { *outX = translateX( inX ); *outY = translateY( inY ); } inline double GUITranslatorGL::translateX( int inX ) { return inX * getTranslationFactor(); } inline double GUITranslatorGL::translateY( int inY ) { int height = mScreen->getHeight(); return ( height - inY ) * getTranslationFactor(); } inline double GUITranslatorGL::getTranslationFactor() { double width = (double)( mScreen->getWidth() ); double height = (double)( mScreen->getHeight() ); if( width <= height ) { return 1.0 / width; } else { return 1.0 / height; } } inline void GUITranslatorGL::mouseMoved( int inX, int inY ) { mComponent->mouseMoved( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mouseDragged( int inX, int inY ) { mComponent->mouseDragged( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mousePressed( int inX, int inY ) { mComponent->mousePressed( translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::mouseReleased( int inX, int inY ) { mComponent->mouseReleased( translateX( inX ), translateY( inY ) ); } inline char GUITranslatorGL::isFocused() { return mComponent->isFocused(); } inline void GUITranslatorGL::keyPressed( unsigned char inKey, int inX, int inY ) { mComponent->keyPressed( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::specialKeyPressed( int inKey, int inX, int inY ) { mComponent->specialKeyPressed( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::keyReleased( unsigned char inKey, int inX, int inY ) { mComponent->keyReleased( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::specialKeyReleased( int inKey, int inX, int inY ) { mComponent->specialKeyReleased( inKey, translateX( inX ), translateY( inY ) ); } inline void GUITranslatorGL::fireRedraw() { // setup orthographic projection matrices before // telling our component to draw itself // (thus, the component can draw in [0.0, 1.0] space // with no regard to screen size) // Ack: // some of this code was adapted from NeHe's tutorial #17, // which can be found at http://nehe.gamedev.net glMatrixMode( GL_PROJECTION ); // Select The Projection Matrix glPushMatrix(); // Store The Projection Matrix glLoadIdentity(); // Reset The Projection Matrix // setup an orthographic projection for our 2d gui // the shorter screen dimension gets mapped to the range [0,1], // and the longer dimension extends beyond 1.0 double translationFactor = getTranslationFactor(); double guiScreenWidth = mScreen->getWidth() * translationFactor; double guiScreenHeight = mScreen->getHeight() * translationFactor; // we don't care about z for 2d gui's, so just select // the region around 0 double zStart = -1; double zEnd = 1; // set the projection matrix to an orthographic projection glOrtho( 0, guiScreenWidth, 0, guiScreenHeight, zStart, zEnd ); glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix glPushMatrix(); // Store The Modelview Matrix // set the model view matrix to the identity glLoadIdentity(); glDisable( GL_DEPTH_TEST ); glDisable( GL_CULL_FACE ); // draw our gui mComponent->fireRedraw(); glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); // restore the matrices glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix glPopMatrix(); // Restore The Old Modelview Matrix glMatrixMode( GL_PROJECTION ); // Select The Projection Matrix glPopMatrix(); // Restore The Old Projection Matrix } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/glGUITestCompile0000750000175000017500000000042507351044134024504 0ustar pabspabsg++ -g -o glGUITest -I/usr/X11R6/include -I../../../.. -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 -lXi -lXext -lXmu glGUITest.cpp ../../../../minorGems/graphics/openGL/ScreenGL.cpp ../../../../minorGems/io/linux/TypeIOLinux.cpp ../../../../minorGems/io/file/linux/PathLinux.cpp transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/gui/MouseCursorRegionGL.h0000640000175000017500000000750007351276771025504 0ustar pabspabs/* * Modification History * * 2001-September-16 Jason Rohrer * Created. */ #ifndef MOUSE_CURSOR_REGION_GL_INCLUDED #define MOUSE_CURSOR_REGION_GL_INCLUDED #include "minorGems/graphics/openGL/gui/GUIComponentGL.h" #include "minorGems/graphics/Color.h" #include /** * A region in a GL-based gui where a mouse cursor * will be displayed. * * @author Jason Rohrer */ class MouseCursorRegionGL : public GUIComponentGL { public: /** * Constructs a mouse cursor region. * * @param inAnchorX the x position of the upper left corner * of this component. * @param inAnchorY the y position of the upper left corner * of this component. * @param inWidth the width of this component. * @param inHeight the height of this component. * @param inCursorFraction the fraction of the * region to be filled by the cursor. * @param inUnpressedColor the color to draw the * cursor with when the mouse is unpressed. * Will be destroyed when this class is destroyed. * @param inPressedColor the color to draw the * cursor with when the mouse is pressed. * Will be destroyed when this class is destroyed. */ MouseCursorRegionGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inCursorFraction, Color *inUnpressedColor, Color *inPressedColor ); ~MouseCursorRegionGL(); // override functions in GUIComponentGL virtual void mouseMoved( double inX, double inY ); virtual void mouseDragged( double inX, double inY ); virtual void mousePressed( double inX, double inY ); virtual void mouseReleased( double inX, double inY ); virtual void fireRedraw(); protected: double mCursorFraction; double mMouseLocationX; double mMouseLocationY; Color *mUnpressedColor; Color *mPressedColor; Color *mCurrentColor; }; inline MouseCursorRegionGL::MouseCursorRegionGL( double inAnchorX, double inAnchorY, double inWidth, double inHeight, double inCursorFraction, Color *inUnpressedColor, Color *inPressedColor ) : GUIComponentGL( inAnchorX, inAnchorY, inWidth, inHeight ), mCursorFraction( inCursorFraction ), mUnpressedColor( inUnpressedColor ), mPressedColor( inPressedColor ), mMouseLocationX( -1 ), mMouseLocationY( -1 ) { mCurrentColor = mUnpressedColor; } inline MouseCursorRegionGL::~MouseCursorRegionGL() { delete mUnpressedColor; delete mPressedColor; } inline void MouseCursorRegionGL::mouseMoved( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mUnpressedColor; } inline void MouseCursorRegionGL::mouseReleased( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mUnpressedColor; } inline void MouseCursorRegionGL::mouseDragged( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mPressedColor; } inline void MouseCursorRegionGL::mousePressed( double inX, double inY ) { mMouseLocationX = inX; mMouseLocationY = inY; mCurrentColor = mPressedColor; } inline void MouseCursorRegionGL::fireRedraw() { if( isInside( mMouseLocationX, mMouseLocationY ) ) { // set our color glColor3f( mCurrentColor->r, mCurrentColor->g, mCurrentColor->b ); double cursorLineLengthX = mCursorFraction * mWidth; double cursorLineLengthY = mCursorFraction * mHeight; // horizontal line glBegin( GL_LINES ); { glVertex2d( mMouseLocationX - 0.5 * cursorLineLengthX, mMouseLocationY ); glVertex2d( mMouseLocationX + 0.5 * cursorLineLengthX, mMouseLocationY ); } glEnd(); // vertical line glBegin( GL_LINES ); { glVertex2d( mMouseLocationX, mMouseLocationY - 0.5 * cursorLineLengthY ); glVertex2d( mMouseLocationX, mMouseLocationY + 0.5 * cursorLineLengthY ); } glEnd(); } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/testScreenGLcompile0000750000175000017500000000021407220267517024516 0ustar pabspabsg++ -g -o testScreenGL -I../../.. -lGL -lglut -lGLU -L/usr/X11R6/lib ScreenGL.cpp testScreenGL.cpp ../../../minorGems/util/random/Noise.cpp transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/RadiosGLView.ini0000640000175000017500000000013207205611501023642 0ustar pabspabsFULLSCREEN 1 DITHER 1 SCREENWIDE 640 SCREENHIGH 480 MODELFILE radiosity.out INVERTMOUSE 0 transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/RedrawListenerGL.h0000640000175000017500000000103707237417170024210 0ustar pabspabs/* * Modification History * * 2001-February-4 Jason Rohrer * Created. */ #ifndef REDRAW_LISTENER_GL_INCLUDED #define REDRAW_LISTENER_GL_INCLUDED /** * Interface for an object that will be called at the beginning of * every openGL redraw. * * @author Jason Rohrer */ class RedrawListenerGL { public: /** * Tells the redraw listener that a redraw is occuring. * * Note that the redraw operation waits for this call to return * before proceeding. */ virtual void fireRedraw() = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/SceneHandlerGL.h0000640000175000017500000000062007220162371023576 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. */ #ifndef SCENE_HANDLER_GL_INCLUDED #define SCENE_HANDLER_GL_INCLUDED /** * Interface for an object that can draw a scene onto a ScreenGL. * * @author Jason Rohrer */ class SceneHandlerGL { public: /** * Draws a scene of objects into a ScreenGL. */ virtual void drawScene() = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/ScreenGL.cpp0000640000175000017500000002437610222511003023015 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-31 Jason Rohrer * Fixed a bug in the fullscreen code. * * 2001-February-4 Jason Rohrer * Added support for keyboard up functions. * Added support for redraw listeners. * Added missing initialization. * * 2001-August-29 Jason Rohrer * Added support for vectors of mouse, keyboard, and scene handlers. * * 2001-September-15 Jason Rohrer * Fixed a bug in passing redraw events to the redraw listeners. * * 2001-October-13 Jason Rohrer * Added a function for applying the view matrix transformation. * Removed unneeded code from the glutResize function. * * 2001-October-29 Jason Rohrer * Added support for focusable keyboard handlers. * * 2001-October-29 Jason Rohrer * Added support for focusable keyboard handlers. * * 2004-August-30 Jason Rohrer * Fixed distortion issues when screen aspect ratio is not 1:1. * * 2005-February-7 Jason Rohrer * Fixed bug of incorrect GL matrix stack usage. Now fog works correctly. * * 2005-February-21 Jason Rohrer * Removed incorrect call to glPopMatrix. * * 2005-March-4 Jason Rohrer * Changed to call redraw listeners before applying view transform. */ #include "ScreenGL.h" #include /* ScreenGL to be accessed by callback functions. * * Note that this is a bit of a hack, but the callbacks * require a C-function (not a C++ member) and have fixed signatures, * so there's no way to pass the current screen into the functions. * * This hack prevents multiple instances of the ScreenGL class from * being used simultaneously. */ ScreenGL *currentScreenGL; // prototypes /* void glutResize( int inW, int inH ); void glutKeyboard( unsigned char inKey, int inX, int inY ); void glutMotion( int inX, int inY ); void glutPassiveMotion( int inX, int inY ); void glutMouse( int inButton, int inState, int inX, int inY ); void glutDisplay(); void glutIdle(); */ ScreenGL::ScreenGL( int inWide, int inHigh, char inFullScreen, char *inWindowName, KeyboardHandlerGL *inKeyHandler, MouseHandlerGL *inMouseHandler, SceneHandlerGL *inSceneHandler ) : mWide( inWide ), mHigh( inHigh ), mFullScreen( inFullScreen ), mViewPosition( new Vector3D( 0, 0, 0 ) ), mViewOrientation( new Angle3D( 0, 0, 0 ) ), mMouseHandlerVector( new SimpleVector() ), mKeyboardHandlerVector( new SimpleVector() ), mSceneHandlerVector( new SimpleVector() ), mRedrawListenerVector( new SimpleVector() ) { // add handlers if NULL (the default) was not passed in for them if( inMouseHandler != NULL ) { addMouseHandler( inMouseHandler ); } if( inKeyHandler != NULL ) { addKeyboardHandler( inKeyHandler ); } if( inSceneHandler != NULL ) { addSceneHandler( inSceneHandler ); } if( mFullScreen ) { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); //| GLUT_FULLSCREEN ); } else { glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); } glutInitWindowSize( mWide, mHigh ); glutCreateWindow( inWindowName ); if( mFullScreen ) { glutFullScreen(); } glutKeyboardFunc( glutKeyboard ); glutKeyboardUpFunc( glutKeyboardUp ); glutSpecialFunc( glutSpecialKeyboard ); glutSpecialUpFunc( glutSpecialKeyboardUp ); glutReshapeFunc( glutResize ); glutMotionFunc( glutMotion ); glutMouseFunc( glutMouse ); glutPassiveMotionFunc( glutPassiveMotion ); glutDisplayFunc( glutDisplay ); glutIdleFunc( glutIdle ); glEnable( GL_DEPTH_TEST ); glEnable( GL_CULL_FACE ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glCullFace( GL_BACK ); glFrontFace( GL_CCW ); } void ScreenGL::start() { currentScreenGL = this; glutMainLoop(); } void ScreenGL::applyViewTransform() { // compute view angle // default angle is 90, but we want to force a 1:1 aspect ratio to avoid // distortion. // If our screen's width is different than its height, we need to decrease // the view angle so that the angle coresponds to the smaller dimension. // This keeps the zoom factor constant in the smaller dimension. // Of course, because of the way perspective works, only one Z-slice // will have a constant zoom factor // The zSlice variable sets the distance of this slice from the picture // plane double zSlice = .31; double maxDimension = mWide; if( mHigh > mWide ) { maxDimension = mHigh; } double aspectDifference = fabs( mWide / 2 - mHigh / 2 ) / maxDimension; // default angle is 90 degrees... half the angle is PI/4 double angle = atan( tan( M_PI / 4 ) + aspectDifference / zSlice ); // double it to get the full angle angle *= 2; // convert to degrees angle = 360 * angle / ( 2 * M_PI ); // set up the projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); //gluPerspective( 90, mWide / mHigh, 1, 9999 ); gluPerspective( angle, 1, 1, 9999 ); // set up the model view matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // create default view and up vectors, // then rotate them by orientation angle Vector3D *viewDirection = new Vector3D( 0, 0, 1 ); Vector3D *upDirection = new Vector3D( 0, 1, 0 ); viewDirection->rotate( mViewOrientation ); upDirection->rotate( mViewOrientation ); // get a point out in front of us in the view direction viewDirection->add( mViewPosition ); // look at takes a viewer position, // a point to look at, and an up direction gluLookAt( mViewPosition->mX, mViewPosition->mY, mViewPosition->mZ, viewDirection->mX, viewDirection->mY, viewDirection->mZ, upDirection->mX, upDirection->mY, upDirection->mZ ); delete viewDirection; delete upDirection; } void glutResize( int inW, int inH ) { ScreenGL *s = currentScreenGL; s->mWide = inW; s->mHigh = inH; // viewport is square and matches largest dimension // (to ensure a 1:1 aspect ratio) if( s->mWide >= s->mHigh ) { int excess = s->mWide - s->mHigh; glViewport( 0, -excess / 2, s->mWide, s->mWide ); } else { int excess = s->mHigh - s->mWide; glViewport( -excess / 2, 0, s->mHigh, s->mHigh ); } glutPostRedisplay(); } void glutKeyboard( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyPressed( inKey, inX, inY ); } } } void glutKeyboardUp( unsigned char inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->keyReleased( inKey, inX, inY ); } } } void glutSpecialKeyboard( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyPressed( inKey, inX, inY ); } } } void glutSpecialKeyboardUp( int inKey, int inX, int inY ) { char someFocused = currentScreenGL->isKeyboardHandlerFocused(); // fire to all handlers for( int h=0; hmKeyboardHandlerVector->size(); h++ ) { KeyboardHandlerGL *handler = *( currentScreenGL->mKeyboardHandlerVector->getElement( h ) ); // if some are focused, only fire to this handler if it is one // of the focused handlers if( !someFocused || handler->isFocused() ) { handler->specialKeyReleased( inKey, inX, inY ); } } } void glutMotion( int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseDragged( inX, inY ); } } void glutPassiveMotion( int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseMoved( inX, inY ); } } void glutMouse( int inButton, int inState, int inX, int inY ) { // fire to all handlers for( int h=0; hmMouseHandlerVector->size(); h++ ) { MouseHandlerGL *handler = *( currentScreenGL->mMouseHandlerVector->getElement( h ) ); handler->mouseMoved( inX, inY ); if( inState == GLUT_DOWN ) { handler->mousePressed( inX, inY ); } else if( inState == GLUT_UP ) { handler->mouseReleased( inX, inY ); } else { printf( "Error: Unknown mouse state received from OpenGL\n" ); } } } void glutDisplay() { ScreenGL *s = currentScreenGL; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // fire to all redraw listeners // do this first so that they can update our view transform // this makes control much more responsive for( int r=0; rmRedrawListenerVector->size(); r++ ) { RedrawListenerGL *listener = *( s->mRedrawListenerVector->getElement( r ) ); listener->fireRedraw(); } // apply our view transform s->applyViewTransform(); // fire to all handlers for( int h=0; hmSceneHandlerVector->size(); h++ ) { SceneHandlerGL *handler = *( currentScreenGL->mSceneHandlerVector->getElement( h ) ); handler->drawScene(); } glutSwapBuffers(); } void glutIdle() { glutPostRedisplay(); } transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/TestHandlerGL.h0000640000175000017500000002707207343227173023502 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-9 Jason Rohrer * Changed to use new Primitive3D implementations. * * 2001-January-16 Jason Rohrer * Changed to use new Translate3D class for drawing primitives. * * 2001-January-30 Jason Rohrer * Updated to comply with new Primitive3D interface. * * 2001-January-31 Jason Rohrer * Added definition of M_PI if not automatically defined. * Added a quit key handler. * Added multitexturing to central quad to test multitexture implementations. * Had to recommit because of lost log message. * * 2001-February-2 Jason Rohrer * Fixed a bug in the way textures were generated. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2001-August-29 Jason Rohrer * Fixed to use new KeyboardHandler interface. */ #ifndef TEST_HANDLER_GL_INCLUDED #define TEST_HANDLER_GL_INCLUDED #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "ScreenGL.h" #include "MouseHandlerGL.h" #include "KeyboardHandlerGL.h" #include "SceneHandlerGL.h" #include "PrimitiveGL.h" #include "ObjectGL.h" #include "minorGems/graphics/3d/LathePrimitive3D.h" #include "minorGems/graphics/3d/LandscapePrimitive3D.h" #include "TextureGL.h" #include "LightingGL.h" #include "NoLightingGL.h" #include "DirectionLightingGL.h" #include "MultiLightingGL.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/graphics/RGBAImage.h" #include "minorGems/graphics/filters/BoxBlurFilter.h" #include "minorGems/graphics/filters/ThresholdFilter.h" #include "minorGems/graphics/filters/InvertFilter.h" #include "minorGems/util/random/RandomSource.h" #include "minorGems/util/random/Noise.h" #include "minorGems/graphics/Image.h" /** * Test class for ScreenGL implementation. * * Draws a simple polygon. * * @author Jason Rohrer */ class TestHandlerGL : public MouseHandlerGL, public KeyboardHandlerGL, public SceneHandlerGL { public: TestHandlerGL( RandomSource *inRandSource, int inNumTriangles ); ~TestHandlerGL(); /** * Sets up primitives. Must be called *after* OpenGL context is * constructed. */ void setupPrimitives(); // implement the MouseHandlerGL interface void mouseMoved( int inX, int inY ); void mouseDragged( int inX, int inY ); void mousePressed( int inX, int inY ); void mouseReleased( int inX, int inY ); // implement the KeyboardHandlerGL interface void keyPressed( unsigned char inKey, int inX, int inY ); void specialKeyPressed( int inKey, int inX, int inY ); void keyReleased( unsigned char inKey, int inX, int inY ); void specialKeyReleased( int inKey, int inX, int inY ); // implement the SceneHandlerGL interface void drawScene(); private: RandomSource *mRandSource; int mNumTriangles; Vector3D ***mTriangles; Color ***mColors; PrimitiveGL *mPrimitive; PrimitiveGL *mPrimitive2; PrimitiveGL *mPrimitive3; LightingGL *mLightingA; LightingGL *mLightingB; MultiLightingGL *mLighting; double mCurrentAngle; double mAngleStep; }; TestHandlerGL::TestHandlerGL( RandomSource *inRandSource, int inNumTriangles ) : mRandSource( inRandSource ), mNumTriangles( inNumTriangles ), mTriangles( new Vector3D**[inNumTriangles] ), mColors( new Color**[inNumTriangles] ) { mLightingA = new DirectionLightingGL( new Color( 1.0, 1.0, 1.0 ), new Vector3D( 0, 0, 1 ) ); mLightingB = new DirectionLightingGL( new Color( 0, 0, 1.0 ), new Vector3D( 1, 1, 0 ) ); mLighting = new MultiLightingGL(); mLighting->addLighting( mLightingA ); mLighting->addLighting( mLightingB ); for( int i=0; igetRandomDouble() * 10, mRandSource->getRandomDouble() * 10 - 5, mRandSource->getRandomDouble() * 10 ); mColors[i][j] = new Color( mRandSource->getRandomFloat(), mRandSource->getRandomFloat(), mRandSource->getRandomFloat(), mRandSource->getRandomFloat() ); } } } TestHandlerGL::~TestHandlerGL() { for( int i=0; igetChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); Image *selection = new Image( textSize, textSize, 1 ); genFractalNoise2d( selection->getChannel(0), textSize, textSize, fPower, true, mRandSource ); BoxBlurFilter *blur = new BoxBlurFilter( 10 ); ThresholdFilter *threshold = new ThresholdFilter( 0.5 ); InvertFilter *invert = new InvertFilter(); selection->filter( threshold ); noiseImage->setSelection( selection ); //noiseImage->filter(invert); delete blur; delete threshold; delete invert; noiseImage->clearSelection(); delete selection; /*for( int t=0; tcopy(); //double *alpha = imageArray[1]->getChannel(3); //int numPixels = imageArray[1]->getWidth() * imageArray[1]->getHeight(); //for( int p=0; psetTransparent( true ); // all passed in args will be destroyed when primitive is destroyed // setup second primitive Vector3D **latheCurve = new Vector3D*[4]; latheCurve[0] = new Vector3D( 0.5, 1, 0 ); latheCurve[1] = new Vector3D( 1, 0.5, 0 ); latheCurve[2] = new Vector3D( 1, -0.5, 0 ); latheCurve[3] = new Vector3D( 0.5, -1, 0 ); noiseImage = new RGBAImage( textSize, textSize ); genFractalNoise2d( noiseImage->getChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); Primitive3D *lathePrimitive3D = new LathePrimitive3D( 4, latheCurve, 15, 2 * M_PI, noiseImage ); lathePrimitive3D->setTransparent( false ); mPrimitive2 = new PrimitiveGL( lathePrimitive3D ); //mPrimitive2->setBackVisible( true ); // all passed in args will be destroyed when primitive is destroyed noiseImage = new RGBAImage( textSize, textSize ); genFractalNoise2d( noiseImage->getChannel(0), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(1), textSize, textSize, fPower, true, mRandSource ); genFractalNoise2d( noiseImage->getChannel(2), textSize, textSize, fPower, true, mRandSource ); int landSize = 20; unsigned long *intHeights = new unsigned long[ landSize * landSize ]; genFractalNoise2d( intHeights, landSize, landSize ); double *heights = new double[ landSize * landSize ]; for( int l=0; lr, mColors[p][v]->g, mColors[p][v]->b, mColors[p][v]->a ); glVertex3f( mTriangles[p][v]->mX, mTriangles[p][v]->mY, mTriangles[p][v]->mZ ); } } glEnd(); */ Vector3D *pos = new Vector3D( 0, 0, 10 ); Angle3D *rot = new Angle3D( mCurrentAngle, mCurrentAngle, 0 ); //Angle3D *rot = new Angle3D( 0, 0, 0 ); Transform3D *trans = new Transform3D(); trans->scale( 5 ); trans->rotate( rot ); trans->translate( pos ); //mPrimitive->setBackVisible( false ); mPrimitive->draw( trans, mLighting ); delete rot; delete pos; delete trans; pos = new Vector3D( 0, -10, 10 ); rot = new Angle3D( 0, mCurrentAngle, 0 ); trans = new Transform3D(); trans->scale( 20 ); trans->rotate( rot ); trans->translate( pos ); mPrimitive3->draw( trans, mLighting ); delete rot; delete pos; delete trans; pos = new Vector3D( 0, 5, 0 ); rot = new Angle3D( M_PI, mCurrentAngle, 0 ); trans = new Transform3D(); trans->scale( 20 ); trans->rotate( rot ); trans->translate( pos ); //mPrimitive3->setBackVisible( false ); //mPrimitive3->draw( trans, mLighting ); delete rot; delete pos; delete trans; mCurrentAngle += mAngleStep; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/openGL/TextureGL.h0000640000175000017500000002335507236121004022710 0ustar pabspabs/* * Modification History * * 2000-December-19 Jason Rohrer * Created. * * 2001-January-19 Jason Rohrer * Added support for multi-texturing. Now a TextureGL no longer has * an overall width and height (each layer can have a different size). * Also, a TextureGL no longer has an overall ID. * Got rid of an old, commented-out interface in the process. * Changed how multi-textures are blended. Settled on using MODULATE * for all layers of the texture, since nothing else produces results * that are compatible with vertex lighting. * * 2001-January-30 Jason Rohrer * Changed to check for GL version 1.2 before using 1.2 features. * Now we assume multi-texture is defined (because of the availability * of glext), but we check that it is supported at runtime. * Moved enable() below ARB function name definitions. * * 2001-January-31 Jason Rohrer * Fixed even more compile bugs. There's no need to do a multitexture * bind when setting texture data. * Removed extra checks for unneeded extensions, and fixed more compile bugs. * Fixed a bug in initMultitexture(). * Fixed a bug in disable(). */ #ifndef TEXTURE_GL_INCLUDED #define TEXTURE_GL_INCLUDED #include #include #include // try and include extensions if multitexture isn't // built-in to gl.h #ifndef GL_ARB_multitexture #include #endif /** * OpenGL 32-bit multi-layered texture map. * * @author Jason Rohrer */ class TextureGL { public: /** * Constructs a texture map. * * @param inNumLayers the number of multi-texture layers * in this texture. Currently supports a value in range [1:8]. */ TextureGL( int inNumLayers ); /** * The OpenGL texture is deleted when the TextureGL object is * destroyed. */ ~TextureGL(); /** * Sets the data for a layer of this texture. * * Blocks of 4 characters specify each pixel, and each * block must be ordered as RGBA. * * Note that width and height must each be powers of 2 and * not larger than 256 (for 3DFX compatability). Additionally, * for 3DFX compatability, the aspect ratio should not exceed 8:1. * * @param inLayer the multi-texture layer to set the data to. * @param inData a character array containing the color and alpha * components for this texture. All data is copied internally. * @param inWide width of the map in 4-byte blocks. * @param inHigh height of the map. */ void setTextureData( int inLayer, unsigned char *inData, int inWide, int inHigh ); /** * Gets the number of layers in this multi-texture. * * @return the number of layers in this multi-texture. */ int getNumLayers(); /** * Enables this texture and all of its layers. * * Note that this function only makes sense when * isMultitexturingSupported() returns true. * Otherwise, it is equivalent * to calling enable( 0 ). */ void enable(); /** * Enables a particular texture layer. * * @param inLayerNumber the layer to enable. */ void enable( int inLayerNumber ); /** * Disables this texture and all of its layers. */ void disable(); /** * Gets whether multitexturing is supported by the loaded * GL implementation. * * @return true if multitexturing is supported. */ static char isMultiTexturingSupported(); static GLenum sMultiTextureEnum[8]; private: static int sNextFreeTextureID; int mNumLayers; unsigned int *mTextureID; static const int MAX_NUM_LAYERS = 8; static char sMultiTexturingSupported; // a static initializer function static void staticInit(); static char sBeenInited; }; // initialize static members int TextureGL::sNextFreeTextureID = 13; char TextureGL::sBeenInited = false; char TextureGL::sMultiTexturingSupported = false; GLenum TextureGL::sMultiTextureEnum[MAX_NUM_LAYERS] = { GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB, GL_TEXTURE3_ARB, GL_TEXTURE4_ARB, GL_TEXTURE5_ARB, GL_TEXTURE6_ARB, GL_TEXTURE7_ARB }; inline TextureGL::TextureGL( int inNumLayers ) : mNumLayers( inNumLayers ), mTextureID( new unsigned int[inNumLayers] ) { if( !sBeenInited ) { // call static initializer function TextureGL::staticInit(); } if( inNumLayers > MAX_NUM_LAYERS ) { printf( "TextureGL only supports 8 multi-texture layers.\n" ); printf( "The following number of layers is out of range: %d\n", inNumLayers ); } for( int i=0; i1) && string[i-1]=='\n')) { // New Extension Begins Here! other=&string[i]; pos=0; // Begin New Search while (string[i]!='\n') { // Search Whole Extension-String if (string[i]==search[pos]) pos++; // Next Position if ((pos>maxpos) && string[i+1]=='\n') return true; // We Have A Winner! i++; } } } return false; // Sorry, Not Found! } // isMultitextureSupported() Checks At Run-Time If Multitexturing Is Supported inline char initMultitexture(void) { char *extString = (char *)glGetString( GL_EXTENSIONS ); // Fetch Extension String int len = strlen( extString ); // allow for end of string character char *extensions = new char[ len + 1 ]; strcpy( extensions, extString ); for (int i=0; i #include "Color.h" class IconMap { public: IconMap( int w, int h); // construct a map of a certain width and height IconMap( int w, int h, int *offset ); // pass in precomputed y offsets into image map ~IconMap(); int wide; int high; int numPixels; int *yOffset; unsigned long *imageMap; // draw a solid IconMap into this IconMap void drawIconMap( IconMap *icon, int xPos, int yPos ); // draw a transparent IconMap into this IconMap void drawIconMapAlpha( IconMap *icon, int xPos, int yPos ); void copy( IconMap *icon ); // copy contents of another icon map into this one // does nothing if icon maps aren't the same size private: char yOffsetExternal; // is the yOffset ptr external? float invChannelMax; Color utilColor; }; inline IconMap::IconMap( int w, int h ) { invChannelMax = 1 / 255.0; wide = w; high = h; numPixels = wide * high; imageMap = new unsigned long[ wide * high ]; yOffset = new int[high]; for( int y=0; yimageMap; int *imageYOffset = icon->yOffset; int imageWide = icon->wide; int imageHigh = icon->high; // watch for buffer bounds int minY = yPos; if( minY < 0 ) { minY = 0; } int maxY = yPos + imageHigh; if( maxY > high ) { maxY = high; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + imageWide; if( maxX > wide ) { maxX = wide; } for( int y=minY; yimageMap; int *imageYOffset = icon->yOffset; int imageWide = icon->wide; int imageHigh = icon->high; // watch for buffer bounds int minY = yPos; if( minY < 0 ) { minY = 0; } int maxY = yPos + imageHigh; if( maxY > high ) { maxY = high; } int minX = xPos; if( minX < 0 ) { minX = 0; } int maxX = xPos + imageWide; if( maxX > wide ) { maxX = wide; } for( int y=minY; y> 24) * invChannelMax; float oneMinusAlpha = 1-alpha; unsigned long buffARGB = imageMap[ buffYContrib + x ]; argb = utilColor.getWeightedComposite( argb, alpha ); buffARGB = utilColor.getWeightedComposite( buffARGB, oneMinusAlpha ); unsigned long sum = utilColor.sumComposite( argb, buffARGB ); imageMap[ buffYContrib + x ] = sum; } } } inline void IconMap::copy( IconMap *icon ) { // make sure they are the same size if( numPixels != icon->numPixels ) { return; } // void * memcpy (void * dst, const void * src, size_t len); // each pixel is 4 bytes, so shift numPixels by 2 memcpy( (void *)(imageMap), (void *)(icon->imageMap), numPixels << 2 ); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/3d/0000750000175000017500000000000010305077062017772 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/3d/LandscapePrimitive3D.h0000640000175000017500000000776707253033271024141 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from LandscapePrimitiveGL, which this class will replace * * 2001-January-15 Jason Rohrer * Fixed a bug in the constructor. * * 2001-January-17 Jason Rohrer * Fliped how x and y in height map correspond to x and z in the terrain. * * 2001-January-19 Jason Rohrer * Changed to support multi-texturing. * * 2001-January-30 Jason Rohrer * Fixed a bug that occurs when the class defaults to no detail texture. * * 2001-March-11 Jason Rohrer * Fixed a bug in the texture map anchor points. */ #ifndef LANDSCAPE_PRIMITIVE_3D_INCLUDED #define LANDSCAPE_PRIMITIVE_3D_INCLUDED #include "Primitive3D.h" /** * Primitive 3D lanscape object. * * Made from a height map. Mesh generated spans both x and z between -1 and * 1, and can vary in height (y) from 0 to 1. * * @author Jason Rohrer */ class LandscapePrimitive3D : public Primitive3D { public: /** * Constructs a LandscapePrimitive with a multi-layer texture. * * @param inWide width of mesh in number of vertices. * Must be even and at least 2. * @param inHigh height of mesh in number of vertices. * @param inHeights array of heights for each vertex, each in * [0,1]. Must be destroyed by caller. * @param inTexture the texture to map onto the lanscape. * Texture is anchored by its corners to the corners of the * lanscape. inTexture is destroyed when primitive is destroyed. * @param inDetailTexture the second layer in the multi-texture. * The alpha channel of inDetailTexture will be used as * the blending factor to mix the detail with the global texture. * Set to NULL to use only a single layer. * @param inDetailScale the scale factor of the detailed texture. * Setting to 1.0 will anchor the detail at the corners of the * mesh (just like inTexture). Setting to 0.25 will cause * the detail texture to cycle 4 times across the surface of the * mesh. */ LandscapePrimitive3D( int inWide, int inHigh, double *inHeights, RGBAImage *inTexture, RGBAImage *inDetailTexture = NULL, double inDetailScale = 1.0 ); }; inline LandscapePrimitive3D::LandscapePrimitive3D( int inWide, int inHigh, double *inHeights, RGBAImage *inTexture, RGBAImage *inDetailTexture, double inDetailScale ) { // first, set Primitve3D members mHigh = inHigh; mWide = inWide; mNumVertices = mHigh * mWide; if( inDetailTexture == NULL ) { mNumTextures = 1; mTexture = new RGBAImage*[1]; mTexture[0] = inTexture; mAnchorX = new double*[1]; mAnchorY = new double*[1]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; } else { mNumTextures = 2; mTexture = new RGBAImage*[2]; mTexture[0] = inTexture; mTexture[1] = inDetailTexture; mAnchorX = new double*[2]; mAnchorY = new double*[2]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; mAnchorX[1] = new double[mNumVertices]; mAnchorY[1] = new double[mNumVertices]; } mVertices = new Vector3D*[mNumVertices]; // anchor at texture corners, and step linearly through texture double anchorYStep = 1.0 / ( mHigh - 1 ); double anchorXStep = 1.0 / ( mWide - 1 ); double detailAnchorYStep = 1.0 / ( ( mHigh - 1 ) * inDetailScale ); double detailAnchorXStep = 1.0 / ( ( mWide - 1 ) * inDetailScale ); for( int y=0; y #include "minorGems/graphics/RGBAImage.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/io/Serializable.h" /** * 3D primitive object. * * Comprised of a triangle mesh, texture map, and anchor points. * * @author Jason Rohrer */ class Primitive3D : public Serializable { public: /** * Constructs a Primitive. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inWide width of mesh in number of vertices. * Must be even and at least 2. * @param inHigh height of mesh in number of vertices. * @param inVertices vertices in row-major order. * @param inNumTextures number of multi-texture layers for * this primitive. * @param inTexture array of textures to map onto mesh. * Note that no RGBAImage in this array should have a * selection in it. * @param inAnchorX x texture anchors for each texture and * each vertex (indexed as inAnchorX[textNum][vertNum]), * in range [0,1] (outside this range, texture will wrap). * @param inAnchorY y texture anchors for each texture and * each vertex (indexed as inAnchorY[textNum][vertNum]), * in range [0,1] (outside this range, texture will wrap). */ Primitive3D( long inWide, long inHigh, Vector3D **inVertices, long inNumTextures, RGBAImage **inTexture, double **inAnchorX, double **inAnchorY ); // construct without initializing any members // for use by subclasses and for deserialization. Primitive3D(); char mMembersAllocated; ~Primitive3D(); /** * Sets whether this primitive is transparent or not. Default * is not transparent. * * @param inTransparent true if this primitive is transparent. */ void setTransparent( char inTransparent ); /** * Gets whether this primitive is transparent or not. * * @return true iff this primitive is transparent. */ char isTransparent(); /** * Sets whether this primitive's back face is visible. Default * is not visible. * * @param inIsVisible true if the back face is visible. */ void setBackVisible( char inIsVisible ); /** * Gets whether this primitive's back face is visible or not. * * @return true iff the back face is visible. */ char isBackVisible(); /** * Gets a new instance of the derived class type. * * Should be equivalent to calling the default constructor * for the derived class type. * * Should be overridden by all derived classes that * have data members beyond those provided by Primitive3D. * Note that if the extra data members require copying, * then the copy() function should be overridden instead * of simply overriding this function. * * Primitive3D::copy() will use this function to produce * a class instance before doing a copy. * * Note that this functionality SHOULD be provided by * the built-in RTTI, but it doesn't seem to be. * * @return an instance of the derived class. */ virtual Primitive3D *getNewInstance(); /** * Copies this primitive. * * @return a copy of this primitive, which must be destroyed * by the caller. */ virtual Primitive3D *copy(); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /* * Note that the default implementations for all the parameter * and temporal animation functions do nothing. */ /** * Sets a parameter for this primative. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this primative. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this primitive forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this primitive. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this primitive. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); long mHigh, mWide; long mNumVertices; Vector3D **mVertices; Vector3D **mNormals; long mNumTextures; // Note that no RGBAImage in this array should have a // selection in it. RGBAImage **mTexture; double **mAnchorX; double **mAnchorY; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: /** * Generates standard normals from the vertices. * * If subclass is not generating normals, this * must be called before primitive is drawn, but after * the vertices have been initialized. */ void generateNormals(); char mTransparent; char mBackVisible; }; inline Primitive3D::Primitive3D( long inWide, long inHigh, Vector3D **inVertices, long inNumTextures, RGBAImage **inTexture, double **inAnchorX, double **inAnchorY ) : mHigh( inHigh ), mWide( inWide ), mNumVertices( inHigh * inWide ), mNumTextures( inNumTextures ), mVertices( inVertices ), mTexture( inTexture ), mAnchorX( inAnchorX ), mAnchorY( inAnchorY ), mTransparent( false ), mBackVisible( false ), mMembersAllocated( true ) { generateNormals(); } inline Primitive3D::Primitive3D() : mTransparent( false ), mBackVisible( false ), mMembersAllocated( false ) { } inline Primitive3D::~Primitive3D() { if( mMembersAllocated ) { int i; for( i=0; isubtract( mVertices[ index ] ); } } // now cross and add into sum for( e=0; e<4; e++ ) { if( edges[e] != NULL && edges[ (e+1) % 4 ] != NULL ) { // not that this order of crossing works // because our cross product is right handed Vector3D *normal = edges[e]->cross( edges[ (e+1) % 4 ] ); normal->normalize(); // add this normal to our sum normalSum->add( normal ); delete normal; } } // now delete edges for( e=0; e<4; e++ ) { if( edges[e] != NULL ) { delete edges[e]; } } delete [] edges; // save summed normal as normal for this point normalSum->normalize(); mNormals[index] = normalSum; } } } inline void Primitive3D::setTransparent( char inTransparent ) { mTransparent = inTransparent; } inline char Primitive3D::isTransparent() { return mTransparent; } inline void Primitive3D::setBackVisible( char inIsVisible ) { mBackVisible = inIsVisible; } inline char Primitive3D::isBackVisible() { return mBackVisible; } inline Primitive3D *Primitive3D::getNewInstance() { return new Primitive3D(); } inline Primitive3D *Primitive3D::copy() { // get an instance of the derived class, if they've // overridden getNewInstance() Primitive3D *primCopy = getNewInstance(); primCopy->mHigh = mHigh; primCopy->mWide = mWide; primCopy->mNumVertices = mNumVertices; primCopy->mVertices = new Vector3D*[mNumVertices]; primCopy->mNormals = new Vector3D*[mNumVertices]; int i; for( i=0; imVertices[i] = new Vector3D( mVertices[i] ); primCopy->mNormals[i] = new Vector3D( mNormals[i] ); } primCopy->mNumTextures = mNumTextures; primCopy->mTexture = new RGBAImage*[mNumTextures]; primCopy->mAnchorX = new double*[mNumTextures]; primCopy->mAnchorY = new double*[mNumTextures]; for( i=0; imTexture[i] = mTexture[i]->copy(); primCopy->mAnchorX[i] = new double[mNumVertices]; primCopy->mAnchorY[i] = new double[mNumVertices]; memcpy( primCopy->mAnchorX[i], mAnchorX[i], sizeof( double ) * mNumVertices ); memcpy( primCopy->mAnchorY[i], mAnchorY[i], sizeof( double ) * mNumVertices ); } primCopy->mMembersAllocated = true; primCopy->setBackVisible( isBackVisible() ); primCopy->setTransparent( isTransparent() ); return primCopy; } inline int Primitive3D::getNumParameters() { return 0; } inline int Primitive3D::getNumAnimations() { return 0; } // the base class versions of these functions do nothing inline void Primitive3D::setParameter( int inParameterIndex, double inValue ) { } inline double Primitive3D::getParameter( int inParameterIndex ) { return 0; } inline void Primitive3D::step( double inStepSize ) { } inline void Primitive3D::startAnimation( int inAnimationIndex ) { } inline void Primitive3D::stopAnimation( int inAnimationIndex ) { } inline int Primitive3D::serialize( OutputStream *inOutputStream ) { int numBytes = 0; numBytes += inOutputStream->writeLong( mWide ); numBytes += inOutputStream->writeLong( mHigh ); int i; // output vertices and normals for( i=0; iserialize( inOutputStream ); } for( i=0; iserialize( inOutputStream ); } numBytes += inOutputStream->writeLong( mNumTextures ); // output textures for( i=0; iserialize( inOutputStream ); } // output anchor arrays for( i=0; iwriteDouble( mAnchorX[i][p] ); } } for( i=0; iwriteDouble( mAnchorY[i][p] ); } } numBytes += inOutputStream->write( (unsigned char *)&mTransparent, 1 ); numBytes += inOutputStream->write( (unsigned char *)&mBackVisible, 1 ); return numBytes; } inline int Primitive3D::deserialize( InputStream *inInputStream ) { int numBytes = 0; int i; if( mMembersAllocated ) { // delete the old vertices, normals, and anchors for( i=0; ireadLong( &mWide ); numBytes += inInputStream->readLong( &mHigh ); mNumVertices = mHigh * mWide; mVertices = new Vector3D*[mNumVertices]; mNormals = new Vector3D*[mNumVertices]; // input vertices and normals for( i=0; ideserialize( inInputStream ); } for( i=0; ideserialize( inInputStream ); } // input number of textures numBytes += inInputStream->readLong( &mNumTextures ); mAnchorX = new double*[mNumTextures]; mAnchorY = new double*[mNumTextures]; mTexture = new RGBAImage*[mNumTextures]; // input textures for( i=0; ideserialize( inInputStream ); } // input anchor arrays for( i=0; ireadDouble( &( mAnchorX[i][p] ) ); } } for( i=0; ireadDouble( &( mAnchorY[i][p] ) ); } } numBytes += inInputStream->read( (unsigned char *)&mTransparent, 1 ); numBytes += inInputStream->read( (unsigned char *)&mBackVisible, 1 ); mMembersAllocated = true; return numBytesRead; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/3d/Primitive3DFactory.h0000640000175000017500000000646207261725075023656 0ustar pabspabs/* * Modification History * * 2001-March-14 Jason Rohrer * Created. * * 2001-April-1 Jason Rohrer * Added subreal entity vane class to factory. */ #ifndef PRIMITIVE_3D_FACTORY_INCLUDED #define PRIMITIVE_3D_FACTORY_INCLUDED #include "Primitive3D.h" // Run-time type identification interface (RTTI) #include // include these primitives only if we are part of subreal #ifdef SUBREAL #include "subreal/entity/EntityBodyPrimitive3D.h" #define FACTORY_ENTITY_BODY_PRIMITIVE_FLAG 1 #include "subreal/entity/EntityVanePrimitive3D.h" #define FACTORY_ENTITY_VANE_PRIMITIVE_FLAG 2 #endif // the default flag #define FACTORY_DEFAULT_PRIMITIVE_FLAG 0 /** * Class that maps Primitive3D integer subclass type flags to Primitive3D * subclass instances. * * Motivation for this class: * We want to extend Object3D to support subclass typed serialization * and deserialization without placing too much of a burden on * future Object3D subclasses. For instance, we don't want subclasses * to have to write their own de/serialization functions so that * particular Primitive3D subclasses are serialized correctly. * We can avoid this burden by writing the base Object3D serialization * code so that it uses this factory to transmit subclasses with * type informaton. * * @author Jason Rohrer */ class Primitive3DFactory { public: /** * Finds an integer subclass type flag for a primitive instance. * * @param inPrimitive the primitive to determine a flag for. Must * be destroyed by the caller. * * @return a type flag for inObject. 0 (the default Object3D * baseclass flag) will be returned if subclass determination fails. */ static int primitive3DToInt( Primitive3D *inPrimitive ); /** * Constructs a new, unitialized Primitive3D (in other words, * an Primitive3D ready for deserialization) of a subclass * type matching inTypeFlag. * * @param inTypeFlag the type flag specifying the class * of the returned primitive. * * @return an (unitialized) Primitive3D class instance with a * subclass type corresponding to inTypeFlag. If no * matching class is found, a default Primitive3D baseclass * instance is returned. Must be destroyed by the caller. */ static Primitive3D *intToPrimitive3D( int inTypeFlag ); }; inline int Primitive3DFactory::primitive3DToInt( Primitive3D *inPrimitive ) { // use RTTI to determine type of inPrimitive #ifdef SUBREAL if( typeid( *inPrimitive ) == typeid( EntityBodyPrimitive3D ) ) { return FACTORY_ENTITY_BODY_PRIMITIVE_FLAG; } else if( typeid( *inPrimitive ) == typeid( EntityVanePrimitive3D ) ) { return FACTORY_ENTITY_VANE_PRIMITIVE_FLAG; } #endif // else return the default flag return FACTORY_DEFAULT_PRIMITIVE_FLAG; } inline Primitive3D *Primitive3DFactory::intToPrimitive3D( int inTypeFlag ) { switch( inTypeFlag ) { case FACTORY_DEFAULT_PRIMITIVE_FLAG: return new Primitive3D(); break; /* these primitives are only defined if * we are part of subreal */ #ifdef SUBREAL case FACTORY_ENTITY_BODY_PRIMITIVE_FLAG: return new EntityBodyPrimitive3D(); break; case FACTORY_ENTITY_VANE_PRIMITIVE_FLAG: return new EntityVanePrimitive3D(); break; #endif default: // unknown primitive flag type return new Primitive3D(); break; } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/3d/Object3DFactory.h0000640000175000017500000000645207261725007023106 0ustar pabspabs/* * Modification History * * 2001-March-14 Jason Rohrer * Created. * * 2001-March-17 Jason Rohrer * Finished implementation. * * 2001-April-1 Jason Rohrer * Fixed flag name. */ #ifndef OBJECT_3D_FACTORY_INCLUDED #define OBJECT_3D_FACTORY_INCLUDED #include "Object3D.h" // Run-time type identification interface (RTTI) #include // include these objects only if we are part of subreal #ifdef SUBREAL #include "subreal/entity/EntityObject3D.h" #define FACTORY_ENTITY_OBJECT_FLAG 1 #endif // the default flag #define FACTORY_DEFAULT_OBJECT_FLAG 0 /** * Class that maps Object3D integer subclass type flags to Object3D * subclass instances. * * Motivation for this class: * We have Object3D instances that exist simultaneously on opposite * sides of the network (at both the client and server ends). Initially, * we just let the objects get sent to the server via the base class * Object3D serialization function. At that time, subclasses of Object3D * did nothing more than init an Object3D in a particular way. This * worked fine when we didn't have animations associated with various * Object3D subclasses, but doesn't work now that we have animation * code in various subclasses. During serialization (if the object * is sent as a generic Object3D), the animation code is lost on * the client end. We need a way for the client to pick the correct * subclass to construct for deserialization. We can encode the * various subtypes by integers, and then this factory class can * be used to construct a class of appropriate subtype before * deserialization. * * @author Jason Rohrer */ class Object3DFactory { public: /** * Finds an integer subclass type flag for an object instance. * * @param inObject the object to determine a flag for. Must * be destroyed by the caller. * * @return a type flag for inObject. 0 (the defautl Object3D * baseclass flag) will be returned if subclass determination fails. */ static int object3DToInt( Object3D *inObject ); /** * Constructs a new, unitialized Object3D (in other words, * an Object3D ready for deserialization) of a subclass * type matching inTypeFlag. * * @param inTypeFlag the type flag specifying the class * of the returned object. * * @return an (unitialized) Object3D class instance with a * subclass type corresponding to inTypeFlag. If no * matching class is found, a default Object3D baseclass * instance is returned. Must be destroyed by the caller. */ static Object3D *intToObject3D( int inTypeFlag ); }; inline int Object3DFactory::object3DToInt( Object3D *inObject ) { // use RTTI to determine type of inObject #ifdef SUBREAL if( typeid( *inObject ) == typeid( EntityObject3D ) ) { return FACTORY_ENTITY_OBJECT_FLAG; } #endif // else return the default flag return FACTORY_DEFAULT_OBJECT_FLAG; } inline Object3D *Object3DFactory::intToObject3D( int inTypeFlag ) { switch( inTypeFlag ) { case FACTORY_DEFAULT_OBJECT_FLAG: return new Object3D(); break; /* these objects are only defined if * we are part of subreal */ #ifdef SUBREAL case FACTORY_ENTITY_OBJECT_FLAG: return new EntityObject3D(); break; #endif default: // unknown object flag type return new Object3D(); break; } } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/3d/Object3D.h0000640000175000017500000001700607254677305021564 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from ObjectGL. * * 2001-January-10 Jason Rohrer * Made class serializable. Added a parameterless constructor * to facilitate deserialization. * * 2001-January-15 Jason Rohrer * Fixed several bugs in the deserialize() function, as well as in the * destructor. * * 2001-January-16 Jason Rohrer * Changed to use a Transform3D instead of Vectors, Angles, and scales for * each primitive in the object. * * 2001-January-24 Jason Rohrer * Added a copy() function. * Fixed a bug in deserialize(). * Made mMembersAllocated public for copy function. * * 2001-January-26 Jason Rohrer * Fixed a bug in copy(). * * 2001-January-31 Jason Rohrer * Fixed bugs in serialize() and deserialize(). * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-March-11 Jason Rohrer * Added support for paramatization and temporal animations. * * 2001-March-13 Jason Rohrer * Added interface for getting the number of parameters and animations. * * 2001-March-14 Jason Rohrer * Added use of Primitive3DFactory for typed subclass primitive * de/serialization. */ #ifndef OBJECT_3D_INCLUDED #define OBJECT_3D_INCLUDED #include "Primitive3D.h" #include "minorGems/math/geometry/Transform3D.h" #include "minorGems/io/Serializable.h" #include "Primitive3DFactory.h" /** * 3D object. * * Comprised of a collection of primitives. * * @author Jason Rohrer */ class Object3D : public Serializable { public: /** * Constructs an Object. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * object is destroyed. * * @param inNumPrimitives the number of primitives in this object. * @param inPrimitives the primitives comprising this object. * @param inTransform a transform for each object. */ Object3D( long inNumPrimitives, Primitive3D **inPrimitives, Transform3D **inTransform ); Object3D(); ~Object3D(); /** * Copies this object. * * @return a copy of this object, which must be destroyed * by the caller. */ Object3D *copy(); /** * Gets the number of parameters associated with this object. * * @return the number of parameters. */ virtual int getNumParameters(); /** * Gets the number of animations associated with this object. * * @return the number of animations. */ virtual int getNumAnimations(); /* * Note that the default implementations for all the parameter * and temporal animation functions do nothing. */ /** * Sets a parameter for this object. * * @param inParameterIndex the index of the parameter to set. * If an index for a non-existing parameter is specified, * this call has no effect. * @param inValue the value to set the parameter to, in [-1, 1]. * The default value for all parameters is 0. */ virtual void setParameter( int inParameterIndex, double inValue ); /** * Gets a parameter for this object. * * @param inParameterIndex the index of the parameter to get. * If an index for a non-existing parameter is specified, * 0 is returned. * * @return the value of the parameter, in [-1, 1]. * The default value for all parameters is 0. */ virtual double getParameter( int inParameterIndex ); /** * Steps this object forward in time. * * @param inStepSize the size of the timestep to take. */ virtual void step( double inStepSize ); /** * Starts a temporal animation of this object. * The animation progresses as step() is called repeatedly. * If called for a non-existent animation or for one that is * already running, this function has no effect. */ virtual void startAnimation( int inAnimationIndex ); /** * Stops a temporal animation of this object. If called * for a non-existent animation or for one that is not currently * running, this function has no effect. */ virtual void stopAnimation( int inAnimationIndex ); long mNumPrimitives; Primitive3D **mPrimitives; Transform3D **mTransform; // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); char mMembersAllocated; }; inline Object3D::Object3D( long inNumPrimitives, Primitive3D **inPrimitives, Transform3D **inTransform ) : mNumPrimitives( inNumPrimitives ), mPrimitives( inPrimitives ), mTransform( inTransform ), mMembersAllocated( true ) { } inline Object3D::Object3D() : mMembersAllocated( false ) { } inline Object3D::~Object3D() { if( mMembersAllocated ) { for( int i=0; imNumPrimitives = mNumPrimitives; objCopy->mPrimitives = new Primitive3D*[ mNumPrimitives ]; objCopy->mTransform = new Transform3D*[ mNumPrimitives ]; int i; for( i=0; imPrimitives[i] = mPrimitives[i]->copy(); objCopy->mTransform[i] = new Transform3D( mTransform[i] ); } objCopy->mMembersAllocated = true; return objCopy; } inline int Object3D::getNumParameters() { return 0; } inline int Object3D::getNumAnimations() { return 0; } // the base class versions of these functions do nothing inline void Object3D::setParameter( int inParameterIndex, double inValue ) { } inline double Object3D::getParameter( int inParameterIndex ) { return 0; } inline void Object3D::step( double inStepSize ) { } inline void Object3D::startAnimation( int inAnimationIndex ) { } inline void Object3D::stopAnimation( int inAnimationIndex ) { } inline int Object3D::serialize( OutputStream *inOutputStream ) { int i; int numBytes = 0; numBytes += inOutputStream->writeLong( mNumPrimitives ); // write each primitive for( i=0; iwriteLong( typeFlag ); // write the primitive numBytes += mPrimitives[i]->serialize( inOutputStream ); } // write each primitive's transform for( i=0; iserialize( inOutputStream ); } return numBytes; } inline int Object3D::deserialize( InputStream *inInputStream ) { if( mMembersAllocated ) { // first, delete current contents of object for( int i=0; ireadLong( &mNumPrimitives ); printf( "receiving %d primitives\n", mNumPrimitives ); mPrimitives = new Primitive3D*[mNumPrimitives]; for( i=0; ireadLong( &typeFlag ); // construct a new object based on the type flag mPrimitives[i] = Primitive3DFactory::intToPrimitive3D( typeFlag ); // deserialize it numBytes += mPrimitives[i]->deserialize( inInputStream ); } mTransform = new Transform3D*[mNumPrimitives]; for( i=0; ideserialize( inInputStream ); } mMembersAllocated = true; return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/3d/LathePrimitive3D.h0000640000175000017500000000650607252736762023310 0ustar pabspabs/* * Modification History * * 2001-January-9 Jason Rohrer * Created. Copied from LathePrimitiveGL, which this class will replace. * * 2001-January-19 Jason Rohrer * Changed to support multi-texturing internally, though this primitive * type doesn't use it. * * 2001-January-21 Jason Rohrer * Fixed a bug in the constructor. * * 2001-January-31 Jason Rohrer * Got rid of an unused variable. * * 2001-March-11 Jason Rohrer * Fixed a bug in the texture map anchor points. */ #ifndef LATHE_PRIMITIVE_3D_INCLUDED #define LATHE_PRIMITIVE_3D_INCLUDED #include "Primitive3D.h" /** * Primitive 3D lathe object. * * Made of a curve rotated around an axis. * * @author Jason Rohrer */ class LathePrimitive3D : public Primitive3D { public: /** * Constructs a LathePrimitive. The lathe curve is rotated * about the y axis. * * No parameters are copied, so they should not be destroyed * or re-accessed by caller. All are destroyed when the * primitive is destroyed. * * @param inNumCurvePoints number of points in curve to be lathed. * @param inCurvePoints points to be lathed. All z components * are set to 0 before lathing, so only x and y values matter. * @param inNumLatheSteps the number of quad segments around * the circumference of the lathed object. For example, * setting to 4 (with a lathe angle of 2pi) * will produce an extruded square object. * @param inNetLatheAngle total angle to sweep during lathing, * in (0,2pi]. * @param inTexture the texture to map onto the lathed object. * Texture is anchored by its corners to the ends of the lath * curve at the beginning and end of the lathe sweep */ LathePrimitive3D( int inNumCurvePoints, Vector3D **inCurvePoints, int inNumLatheSteps, double inNetLatheAngle, RGBAImage *inTexture ); }; inline LathePrimitive3D::LathePrimitive3D( int inNumCurvePoints, Vector3D **inCurvePoints, int inNumLatheSteps, double inNetLatheAngle, RGBAImage *inTexture ) { // first, set Primitve3D members mHigh = inNumCurvePoints; mWide = inNumLatheSteps + 1; mNumVertices = mHigh * mWide; mNumTextures = 1; mTexture = new RGBAImage*[1]; mTexture[0] = inTexture; double stepAngle = inNetLatheAngle / inNumLatheSteps; int i; // first, set all z values for control points to 0 for( i=0; imZ = 0; } mVertices = new Vector3D*[mNumVertices]; mAnchorX = new double*[1]; mAnchorY = new double*[1]; mAnchorX[0] = new double[mNumVertices]; mAnchorY[0] = new double[mNumVertices]; // anchor at texture corners, and step linearly through texture double anchorYStep = 1.0 / ( mHigh - 1 ); double anchorXStep = 1.0 / ( mWide - 1 ); for( int y=0; yreverseRotate( latheRotation ); delete latheRotation; } // cleanup as we go along delete inCurvePoints[y]; } delete [] inCurvePoints; generateNormals(); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/loadFile.cpp0000640000175000017500000000335007205611501021706 0ustar pabspabs#include #include #include "loadFile.h" // loads any file into the dest ptr. void loadFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr) { FILE *f; int numBytesRead; f = fopen( fileName, "rb"); // open the file // don't load if file failed to open if( f == NULL ) { return; } numBytesRead = fread( (void*)byteDestPtr, sizeof(char), sizeInBytes, f); fclose(f); } // loads a photoshop RAW image file, 32-bit // NOTE: // This function exists because photoshop swaps the red and blue channels when // it saves a file as raw. Thus, the file stream doesn't match the way a video // card deals with 32-bit color. // This function loads the file and then swaps the appropriate bytes void loadRawFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr) { //unsigned char tempChannel; long byteCount; FILE *f; int numBytesRead; f = fopen( fileName, "rb"); // open the file // don't load if file failed to open if( f == NULL ) { return; } numBytesRead = fread( (void*)byteDestPtr, sizeof(char), sizeInBytes, f); // now that file read, swap the red and blue channels: for( byteCount = 0; byteCount< sizeInBytes; byteCount=byteCount+4) { unsigned char alpha = byteDestPtr[byteCount+3]; byteDestPtr[byteCount+3] = byteDestPtr[byteCount+2]; byteDestPtr[byteCount+2] = byteDestPtr[byteCount+1]; byteDestPtr[byteCount+1] = byteDestPtr[byteCount]; byteDestPtr[byteCount] = alpha; /* tempChannel = byteDestPtr[byteCount]; // currently in red position byteDestPtr[byteCount] = byteDestPtr[byteCount+3]; // currently set to what's in alpha position byteDestPtr[byteCount+2] = tempChannel; */ } fclose(f); }transcend-0.3+dfsg2.orig/minorGems/graphics/converters/0000750000175000017500000000000010305077062021656 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/converters/bmpformat.txt0000640000175000017500000000611307244265544024424 0ustar pabspabs Size Description Header 14 bytes Windows Structure: BITMAPFILEHEADER Signature 2 bytes 'BM' FileSize 4 bytes File size in bytes reserved 4 bytes unused (=0) DataOffset 4 bytes File offset to Raster Data InfoHeader 40 bytes Windows Structure: BITMAPINFOHEADER Size 4 bytes Size of InfoHeader =40 Width 4 bytes Bitmap Width Height 4 bytes Bitmap Height Planes 2 bytes Number of Planes (=1) BitCount 2 bytes Bits per Pixel 1 = monochrome palette. NumColors = 1 4 = 4bit palletized. NumColors = 16 8 = 8bit palletized. NumColors = 256 16 = 16bit RGB. NumColors = 65536 (?) 24 = 24bit RGB. NumColors = 16M Compression 4 bytes Type of Compression 0 = BI_RGB no compression 1 = BI_RLE8 8bit RLE encoding 2 = BI_RLE4 4bit RLE encoding ImageSize 4 bytes (compressed) Size of Image It is valid to set this =0 if Compression = 0 XpixelsPerM 4 bytes horizontal resolution: Pixels/meter YpixelsPerM 4 bytes vertical resolution: Pixels/meter ColorsUsed 4 bytes Number of actually used colors ColorsImportant 4 bytes Number of important colors 0 = all ColorTable 4 * NumColors bytes present only if Info.BitsPerPixel <= 8 colors should be ordered by importance Red 1 byte Red intensity Green 1 byte Green intensity Blue 1 byte Blue intensity reserved 1 byte unused (=0) repeated NumColors times Raster Data Info.ImageSize bytes The pixel data transcend-0.3+dfsg2.orig/minorGems/graphics/converters/LittleEndianImageConverter.h0000640000175000017500000000521207353214427027246 0ustar pabspabs/* * Modification History * * 2001-September-22 Jason Rohrer * Created. */ #ifndef LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED #define LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED #include "minorGems/graphics/ImageConverter.h" /** * A base class for converters that have little endian file formats. * Basically includes little endian reading and writing functions. * * @author Jason Rohrer */ class LittleEndianImageConverter : public ImageConverter { public: // does not implement the ImageConverter interface, // which makes this class abstract. protected: /** * Writes a long value in little endian format. * * @param inLong the long value to write. * @param inStream the stream to write inLong to. */ void writeLittleEndianLong( long inLong, OutputStream *inStream ); /** * Writes a short value in little endian format. * * @param inShort the short value to write. * @param inStream the stream to write inShort to. */ void writeLittleEndianShort( short inShort, OutputStream *inStream ); /** * Reads a long value in little endian format. * * @param inStream the stream to read the long value from. * * @return the long value. */ long readLittleEndianLong( InputStream *inStream ); /** * Reads a short value in little endian format. * * @param inStream the stream to read the short value from. * * @return the short value. */ short readLittleEndianShort( InputStream *inStream ); }; inline void LittleEndianImageConverter::writeLittleEndianLong( long inLong, OutputStream *inStream ) { unsigned char buffer[4]; buffer[0] = (unsigned char)( inLong & 0xFF ); buffer[1] = (unsigned char)( ( inLong >> 8 ) & 0xFF ); buffer[2] = (unsigned char)( ( inLong >> 16 ) & 0xFF ); buffer[3] = (unsigned char)( ( inLong >> 24 ) & 0xFF ); inStream->write( buffer, 4 ); } inline long LittleEndianImageConverter::readLittleEndianLong( InputStream *inStream ) { unsigned char buffer[4]; inStream->read( buffer, 4 ); long outLong = buffer[0] | ( buffer[1] << 8 ) | ( buffer[2] << 16 ) | ( buffer[3] << 24 ); return outLong; } inline void LittleEndianImageConverter::writeLittleEndianShort( short inShort, OutputStream *inStream ) { unsigned char buffer[2]; buffer[0] = (unsigned char)( inShort & 0xFF ); buffer[1] = (unsigned char)( ( inShort >> 8 ) & 0xFF ); inStream->write( buffer, 2 ); } inline short LittleEndianImageConverter::readLittleEndianShort( InputStream *inStream ) { unsigned char buffer[2]; inStream->read( buffer, 2 ); long outShort = buffer[0] | ( buffer[1] << 8 ); return outShort; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/converters/bmpConverterTest.cpp0000640000175000017500000000376707265377107025724 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2001-April-12 Jason Rohrer * Changed to comply with new FileInput/OutputStream interface * (underlying File must be destroyed explicitly). */ #include #include "minorGems/graphics/Image.h" #include "BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/io/file/FileInputStream.h" // test function for the BMPImageConverter class int main( char inNumArgs, char**inArgs ) { if( inNumArgs != 2 ) { printf( "must pass in a file name to write to\n" ); return 1; } int length = 0; while( inArgs[1][length] != '\0' ) { length++; } File *file = new File( NULL, inArgs[1], length ); // read image in FileInputStream *stream = new FileInputStream( file ); BMPImageConverter *converter = new BMPImageConverter(); Image *image = converter->deformatImage( stream ); if( image != NULL ) { // write image back out File *fileOut = new File( NULL, "testOut.bmp", 11 ); FileOutputStream *outStream = new FileOutputStream( fileOut ); converter->formatImage( image, outStream ); delete outStream; delete image; } delete stream; delete file; delete converter; /* FileOutputStream *stream = new FileOutputStream( file ); BMPImageConverter *converter = new BMPImageConverter(); Image *image = new Image( 256, 256, 3 ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); for( int y=0; ygetHeight(); y++ ) { for( int x=0; xgetWidth(); x++ ) { long index = y * image->getWidth() + x; red[index] = (double)y / (double)( image->getHeight() ); green[index] = (double)x / (double)( image->getWidth() ); //red[index] = 1.0; } } converter->formatImage( image, stream ); delete stream; // delete file explicitly delete file; delete converter; delete image; */ return 0; } transcend-0.3+dfsg2.orig/minorGems/graphics/converters/unix/0000750000175000017500000000000010305077062022641 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp0000640000175000017500000004047007273016312027600 0ustar pabspabs/* * Modification History * * 2001-April-27 Jason Rohrer * Created. * * 2001-April-29 Jason Rohrer * Finished implementation. * Added an optimization to formatImage, but it did not improve * performance, so it has been commented out. */ /** * Unix-specific JPEGImageConverter implementation * * Code for compression and decompression modeled after IJG's * libjpeg example code. * * For now, it use libjpeg to write converted data out to * file, and then reads it back in. */ #include "minorGems/graphics/converters/JPEGImageConverter.h" #include "minorGems/io/file/File.h" #include #include #include // include the jpeg library as a C file. // (yuk... spent way too much time trying to figure this one out!) extern "C" { #include } /* * is used for the decompression * error recovery mechanism. */ #include void JPEGImageConverter::formatImage( Image *inImage, OutputStream *inStream ) { if( inImage->getNumChannels() != 3 ) { printf( "JPEGImageConverter only works on 3-channel images.\n" ); return; } // most of this code was copied without modification from // IJG's example.c // This struct contains the JPEG compression parameters and pointers to // working space (which is allocated as needed by the JPEG library). // It is possible to have several such structures, representing multiple // compression/decompression processes, in existence at once. We refer // to any one struct (and its associated working data) as a "JPEG object". struct jpeg_compress_struct cinfo; // This struct represents a JPEG error handler. It is declared separately // because applications often want to supply a specialized error handler // (see the second half of this file for an example). But here we just // take the easy way out and use the standard error handler, which will // print a message on stderr and call exit() if compression fails. // Note that this struct must live as long as the main JPEG parameter // struct, to avoid dangling-pointer problems. struct jpeg_error_mgr jerr; // More stuff FILE * outfile; // target file JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] int row_stride; // physical row width in image buffer // Step 1: allocate and initialize JPEG compression object // We have to set up the error handler first, in case the initialization // step fails. (Unlikely, but it could happen if you are out of memory.) // This routine fills in the contents of struct jerr, and returns jerr's // address which we place into the link field in cinfo. cinfo.err = jpeg_std_error( &jerr ); // Now we can initialize the JPEG compression object. jpeg_create_compress( &cinfo ); // Step 2: specify data destination (eg, a file) // Note: steps 2 and 3 can be done in either order. // use a temp file with a random name to make this more // thread-safe char *fileName = new char[99]; sprintf( fileName, "temp%d.dat", rand() ); // Here we use the library-supplied code to send compressed data to a // stdio stream. You can also write your own code to do something else. // VERY IMPORTANT: use "b" option to fopen() if you are on a machine that // requires it in order to write binary files. if( ( outfile = fopen( fileName, "wb" ) ) == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return; } jpeg_stdio_dest( &cinfo, outfile ); // Step 3: set parameters for compression // First we supply a description of the input image. // Four fields of the cinfo struct must be filled in: // image width and height, in pixels cinfo.image_width = inImage->getWidth(); cinfo.image_height = inImage->getHeight(); cinfo.input_components = 3; // # of color components per pixel cinfo.in_color_space = JCS_RGB; // colorspace of input image // Now use the library's routine to set default compression parameters. // (You must set at least cinfo.in_color_space before calling this, // since the defaults depend on the source color space.) jpeg_set_defaults( &cinfo ); // Now you can set any non-default parameters you wish to. // Here we just illustrate the use of // quality (quantization table) scaling: jpeg_set_quality( &cinfo, mQuality, TRUE ); // limit to baseline-JPEG values // Step 4: Start compressor // TRUE ensures that we will write a complete interchange-JPEG file. // Pass TRUE unless you are very sure of what you're doing. jpeg_start_compress( &cinfo, TRUE ); // Step 5: while (scan lines remain to be written) // jpeg_write_scanlines(...); // Here we use the library's state variable cinfo.next_scanline as the // loop counter, so that we don't have to keep track ourselves. // To keep things simple, we pass one scanline per call; you can pass // more if you wish, though. // JSAMPLEs per row in image_buffer row_stride = cinfo.image_width * 3; // channels of inImage, which we will need to pull pixel values out of double *redChannel = inImage->getChannel(0); double *greenChannel = inImage->getChannel(1); double *blueChannel = inImage->getChannel(2); // array that we will copy inImage pixels into // one scanline at a time row_pointer[0] = new JSAMPLE[ row_stride ]; //int rowNumber = 0; while( cinfo.next_scanline < cinfo.image_height ) { // jpeg_write_scanlines expects an array of pointers to scanlines. // Here the array is only one element long, but you could pass // more than one scanline at a time if that's more convenient. // make a scanline int yOffset = cinfo.next_scanline * cinfo.image_width; // for each pixel in the row for( int p=0; pgetLength(); unsigned char *fileBuffer = new unsigned char[ fileLength ]; fread( fileBuffer, 1, fileLength, inFile ); // now write the entire buffer to our output stream inStream->write( fileBuffer, fileLength ); delete [] fileBuffer; delete file; fclose( inFile ); // delete this temporary file remove( fileName ); delete [] fileName; // And we're done! } // copied this directly from IJG's example.c //extern "C" { /* * ERROR HANDLING: * * The JPEG library's standard error handler (jerror.c) is divided into * several "methods" which you can override individually. This lets you * adjust the behavior without duplicating a lot of code, which you might * have to update with each future release. * * Our example here shows how to override the "error_exit" method so that * control is returned to the library's caller when a fatal error occurs, * rather than calling exit() as the standard error_exit method does. * * We use C's setjmp/longjmp facility to return * control. This means that the * routine which calls the JPEG library must * first execute a setjmp() call to * establish the return point. We want the replacement error_exit to do a * longjmp(). But we need to make the setjmp buffer accessible to the * error_exit routine. To do this, we make a private extension of the * standard JPEG error handler object. (If we were using C++, we'd say we * were making a subclass of the regular error handler.) * * Here's the extended error handler struct: */ struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; /* * Here's the routine that will replace the standard error_exit method: */ METHODDEF(void) my_error_exit( j_common_ptr cinfo ) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr)( cinfo->err ); /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ (*cinfo->err->output_message)( cinfo ); /* Return control to the setjmp point */ longjmp( myerr->setjmp_buffer, 1 ); } // } Image *JPEGImageConverter::deformatImage( InputStream *inStream ) { // use a temp file with a random name to make this more // thread-safe char *fileName = new char[99]; sprintf( fileName, "temp%d.dat", rand() ); FILE *tempFile = fopen( fileName, "wb" ); if( tempFile == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return NULL; } // buffer for dumping stream to temp file unsigned char *tempBuffer = new unsigned char[1]; unsigned char previousByte = 0; // dump the JPEG stream from the input stream into tempFile // so that we can pass this file to libjpeg /* // optimization: use a buffer to prevent too many fwrite calls int bufferLength = 5000; unsigned char *fileBuffer = new unsigned char[ bufferLength ]; int currentBufferPosition = 0; while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) { previousByte = tempBuffer[0]; inStream->read( tempBuffer, 1 ); fileBuffer[currentBufferPosition] = tempBuffer[0]; if( currentBufferPosition == bufferLength - 1 ) { // at the end of the file buffer fwrite( fileBuffer, 1, bufferLength, tempFile ); currentBufferPosition = 0; } else { // keep filling the fileBuffer currentBufferPosition++; } } // now write remaining fileBuffer data to file fwrite( fileBuffer, 1, currentBufferPosition + 1, tempFile ); delete [] fileBuffer; */ // write until EOI sequence seen (0xFFD9) while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) { previousByte = tempBuffer[0]; inStream->read( tempBuffer, 1 ); fwrite( tempBuffer, 1, 1, tempFile ); } // end of jpeg stream reached. fclose( tempFile ); delete [] tempBuffer; // the remainder of this method was mostly copied from // IJG's example.c /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct my_error_mgr jerr; /* More stuff */ FILE * infile; /* source file */ JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ /* In this example we want to open the input * file before doing anything else, * so that the setjmp() error recovery below can assume the file is open. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to read binary files. */ if( ( infile = fopen( fileName, "rb" ) ) == NULL ) { printf( "can't open jpeg conversion temp file %s\n", fileName ); return NULL; } /* Step 1: allocate and initialize JPEG decompression object */ /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if( setjmp( jerr.setjmp_buffer ) ) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, * close the input file, and return. */ jpeg_destroy_decompress( &cinfo ); fclose( infile ); printf( "error in decompressing jpeg from stream.\n" ); return NULL; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress( &cinfo ); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src( &cinfo, infile ); /* Step 3: read file parameters with jpeg_read_header() */ (void) jpeg_read_header( &cinfo, TRUE ); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ (void) jpeg_start_decompress( &cinfo ); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ int imageWidth = cinfo.output_width; int imageHeight = cinfo.output_height; // the return image with 3 channels Image *returnImage = new Image( imageWidth, imageHeight, 3 ); // channels of returnImage, // which we will need to put pixel values into of double *redChannel = returnImage->getChannel(0); double *greenChannel = returnImage->getChannel(1); double *blueChannel = returnImage->getChannel(2); int currentIndex = 0; row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that * will go away when done with image */ buffer = ( *cinfo.mem->alloc_sarray ) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1 ); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ int rowNumber = 0; double inv255 = 1.0 / 255.0; while( cinfo.output_scanline < cinfo.output_height ) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines( &cinfo, buffer, 1 ); // write the scanline into returnImage int yOffset = rowNumber * cinfo.output_width; // for each pixel in the row // copy it into the return image channels for( int p=0; pgetNumChannels(); // make sure the image is in the right format if( numChannels != 3 && numChannels != 4 ) { printf( "Only 3- and 4-channel images can be converted to " ); printf( "the TGA format.\n" ); return; } long width = inImage->getWidth(); long height = inImage->getHeight(); long numPixels = width * height; // a buffer for writing single bytes unsigned char *byteBuffer = new unsigned char[1]; // write the identification field size // (an empty identification field) byteBuffer[0] = 0; inStream->write( byteBuffer, 1 ); // write the color map type // (no color map) byteBuffer[0] = 0; inStream->write( byteBuffer, 1 ); // write the image type code // (type 2: unmapped RGB image) byteBuffer[0] = 2; inStream->write( byteBuffer, 1 ); // no color map spec // (set to 0, though it will be ignored) unsigned char *colorMapSpec = new unsigned char[5]; int i; for( i=0; i<5; i++ ) { colorMapSpec[i] = 0; } inStream->write( colorMapSpec, 5 ); delete [] colorMapSpec; // now for the image specification // x origin coordinate writeLittleEndianShort( 0, inStream ); // y origin coordinate writeLittleEndianShort( 0, inStream ); writeLittleEndianShort( width, inStream ); writeLittleEndianShort( height, inStream ); // number of bits in pixels if( numChannels == 3 ) { byteBuffer[0] = 24; } else { byteBuffer[0] = 32; } inStream->write( byteBuffer, 1 ); // image descriptor byte if( numChannels == 3 ) { // setting to 0 specifies: // -- no attributes per pixel (for 24-bit) // -- screen origin in lower left corner // -- non-interleaved data storage byteBuffer[0] = 0; } else { // setting to 8 specifies: // -- 8 attributes per pixel (for 32-bit) (attributes are alpha bits) // -- screen origin in lower left corner // -- non-interleaved data storage byteBuffer[0] = 8; } // set bit 5 to 1 to specify screen origin in upper left corner byteBuffer[0] = byteBuffer[0] | ( 1 << 5 ); inStream->write( byteBuffer, 1 ); // We skip the image identification field, // since we set its length to 0 above. // We also skip the color map data, // since we have none (as specified above). // now we write the pixels, in BGR(A) order unsigned char *raster = new unsigned char[ numPixels * numChannels ]; double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); long rasterIndex = 0; if( numChannels == 3 ) { for( int i=0; iwrite( raster, numPixels * 3 ); } else { // numChannels == 4 double *alpha = inImage->getChannel( 3 ); for( int i=0; iwrite( raster, numPixels * numChannels ); delete [] raster; delete [] byteBuffer; } inline Image *TGAImageConverter::deformatImage( InputStream *inStream ) { // a buffer for reading single bytes unsigned char *byteBuffer = new unsigned char[1]; // read the identification field size inStream->read( byteBuffer, 1 ); int identificationFieldSize = byteBuffer[0]; // read the color map type // (only 0, or no color map, is supported) inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 0 ) { printf( "Only TGA files without colormaps can be read.\n" ); delete [] byteBuffer; return NULL; } // read the image type code // (only type 2, unmapped RGB image, is supported) inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 2 ) { printf( "Only TGA files containing unmapped RGB images can be read.\n" ); delete [] byteBuffer; return NULL; } // ignore color map spec // (skip all 5 bytes of it) unsigned char *colorMapSpec = new unsigned char[5]; inStream->read( colorMapSpec, 5 ); delete [] colorMapSpec; // now for the image specification // x origin coordinate int xOriginCoord = readLittleEndianShort( inStream ); // y origin coordinate int yOrigineCoord = readLittleEndianShort( inStream ); long width = readLittleEndianShort( inStream ); long height = readLittleEndianShort( inStream ); long numPixels = width * height; // number of bits in pixels // only 24 bits per pixel supported inStream->read( byteBuffer, 1 ); if( byteBuffer[0] != 24 && byteBuffer[0] != 32 ) { printf( "Only 24- and 32-bit TGA files can be read.\n" ); delete [] byteBuffer; return NULL; } int numChannels = 0; if( byteBuffer[0] == 24 ) { numChannels = 3; } else { numChannels = 4; } // image descriptor byte // setting to 0 specifies: // -- no attributes per pixel (for 24-bit) // -- screen origin in lower left corner // -- non-interleaved data storage // set bit 5 to 1 to specify screen origin in upper left corner inStream->read( byteBuffer, 1 ); char originAtTop = byteBuffer[0] & ( 1 << 5 ); if( identificationFieldSize > 0 ) { // We skip the image identification field unsigned char *identificationField = new unsigned char[ identificationFieldSize ]; inStream->read( identificationField, identificationFieldSize ); delete [] identificationField; } // We also skip the color map data, // since we have none (as specified above). // now we read the pixels, in BGR(A) order unsigned char *raster = new unsigned char[ numPixels * numChannels ]; inStream->read( raster, numPixels * numChannels ); Image *image = new Image( width, height, numChannels ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); double *blue = image->getChannel( 2 ); long rasterIndex = 0; double inv255 = 1.0 / 255.0; if( numChannels == 3 ) { if( originAtTop ) { for( int i=0; i=0; y-- ) { for( int x=0; xgetChannel( 3 ); if( originAtTop ) { for( int i=0; i=0; y-- ) { for( int x=0; x 100 || mQuality < 0 ) { printf( "JPEG quality must be in [0,100]\n" ); mQuality = 50; } } inline void JPEGImageConverter::setQuality( int inQuality ) { mQuality = inQuality; if( mQuality > 100 || mQuality < 0 ) { printf( "JPEG quality must be in [0,100]\n" ); mQuality = 50; } } inline int JPEGImageConverter::getQuality() { return mQuality; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/converters/jpegConverterTestCompile0000750000175000017500000000020307272310031026560 0ustar pabspabsg++ -I../../.. -ljpeg -o jpegConverterTest jpegConverterTest.cpp unix/JPEGImageConverterUnix.cpp ../../io/file/linux/PathLinux.cpp transcend-0.3+dfsg2.orig/minorGems/graphics/converters/jpegConverterTest.cpp0000640000175000017500000000366307273016172026055 0ustar pabspabs/* * Modification History * * 2001-April-27 Jason Rohrer * Created. * * 2001-April-29 Jason Rohrer * Completed initial version and used to test JPEGImageConverter * successfully. */ #include #include "minorGems/graphics/Image.h" #include "JPEGImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileOutputStream.h" #include "minorGems/io/file/FileInputStream.h" // test function for the BMPImageConverter class int main( char inNumArgs, char**inArgs ) { if( inNumArgs != 2 ) { printf( "must pass in a file name to write to\n" ); return 1; } int length = 0; while( inArgs[1][length] != '\0' ) { length++; } File *file = new File( NULL, inArgs[1], length ); // read image in FileInputStream *stream = new FileInputStream( file ); JPEGImageConverter *converter = new JPEGImageConverter( 50 ); Image *image = converter->deformatImage( stream ); if( image != NULL ) { // write image back out File *fileOut = new File( NULL, "testOut.jpg", 11 ); FileOutputStream *outStream = new FileOutputStream( fileOut ); converter->formatImage( image, outStream ); delete outStream; delete fileOut; delete image; } delete stream; delete converter; delete file; /* FileOutputStream *stream = new FileOutputStream( file ); JPEGImageConverter *converter = new JPEGImageConverteonverter( 50 ); Image *image = new Image( 256, 256, 3 ); double *red = image->getChannel( 0 ); double *green = image->getChannel( 1 ); for( int y=0; ygetHeight(); y++ ) { for( int x=0; xgetWidth(); x++ ) { long index = y * image->getWidth() + x; red[index] = (double)y / (double)( image->getHeight() ); green[index] = (double)x / (double)( image->getWidth() ); //red[index] = 1.0; } } converter->formatImage( image, stream ); delete stream; // delete file explicitly delete file; delete converter; delete image; */ return 0; } transcend-0.3+dfsg2.orig/minorGems/graphics/converters/BMPImageConverter.h0000640000175000017500000001374207353214427025317 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-February-19 Jason Rohrer * Fixed some bugs in the raster formatting code. * * 2001-May-19 Jason Rohrer * Fixed a bug in zero padding for each line. * * 2001-May-21 Jason Rohrer * Fixed another bug in zero padding for each line. * * 2001-September-22 Jason Rohrer * Changed to subclass LittleEndianImageConverter. */ #ifndef BMP_IMAGE_CONVERTER_INCLUDED #define BMP_IMAGE_CONVERTER_INCLUDED #include "LittleEndianImageConverter.h" /** * Windows BMP implementation of the image conversion interface. * * Note that it only supports 24-bit BMP files (and thus only 3-channel * Images). * * BMP format information taken from: * http://www.daubnet.com/formats/BMP.html * Some information from this site is incorrect: * 24-bit pixels are _not_ stored in 32-bit blocks. * * @author Jason Rohrer */ class BMPImageConverter : public LittleEndianImageConverter { public: // implement the ImageConverter interface virtual void formatImage( Image *inImage, OutputStream *inStream ); virtual Image *deformatImage( InputStream *inStream ); }; inline void BMPImageConverter::formatImage( Image *inImage, OutputStream *inStream ) { // make sure the image is in the right format if( inImage->getNumChannels() != 3 ) { printf( "Only 3-channel images can be converted to the BMP format.\n" ); return; } long width = inImage->getWidth(); long height = inImage->getHeight(); long numPixels = width * height; // each line should be padded with zeros to // end on a 4-byte boundary int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4; short bitCount = 24; long rasterSize = numPixels * 3; // zero padding bytes for each row rasterSize += numZeroPaddingBytes * height; // offset past two headers long offsetToRaster = 14 + 40; long fileSize = offsetToRaster + rasterSize; long compressionType = 0; long pixelsPerMeter = 2834; // both are 0 since we have no color map long colorsUsed = 0; long colorsImportant = 0; // write the header unsigned char *signature = new unsigned char[2]; signature[0] = 'B'; signature[1] = 'M'; inStream->write( signature, 2 ); delete [] signature; writeLittleEndianLong( fileSize, inStream ); writeLittleEndianLong( 0, inStream ); writeLittleEndianLong( offsetToRaster, inStream ); // write the info header // header size writeLittleEndianLong( 40, inStream ); writeLittleEndianLong( width, inStream ); writeLittleEndianLong( height, inStream ); // numPlanes writeLittleEndianShort( 1, inStream ); writeLittleEndianShort( bitCount, inStream ); writeLittleEndianLong( compressionType, inStream ); writeLittleEndianLong( rasterSize, inStream ); writeLittleEndianLong( pixelsPerMeter, inStream ); writeLittleEndianLong( pixelsPerMeter, inStream ); writeLittleEndianLong( colorsUsed, inStream ); writeLittleEndianLong( colorsImportant, inStream ); // no color table... // now write the raster unsigned char *raster = new unsigned char[ rasterSize ]; double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); // pixels are stored bottom-up, left to right // (row major order) long rasterIndex = 0; for( int y=height-1; y>=0; y-- ) { for( int x=0; xwrite( raster, rasterSize ); delete [] raster; } inline Image *BMPImageConverter::deformatImage( InputStream *inStream ) { // temp buffer used to skip data in the stream unsigned char *temp = new unsigned char[ 100 ]; // skip signature inStream->read( temp, 2 ); long fileSize = readLittleEndianLong( inStream ); // skip unused inStream->read( temp, 4 ); long rasterOffset = readLittleEndianLong( inStream ); long rasterSize = fileSize - rasterOffset; // skip size of header inStream->read( temp, 4 ); long width = readLittleEndianLong( inStream ); long height = readLittleEndianLong( inStream ); // skip planes inStream->read( temp, 2 ); short bitCount = readLittleEndianShort( inStream ); char failing = false; if( bitCount != 24 ) { printf( "Only 24-bit BMP file formats supported.\n" ); failing = true; } long compression = readLittleEndianLong( inStream ); if( compression != 0 ) { printf( "Only uncompressed BMP file formats supported.\n" ); failing = true; } // skip imageSize, resolution, and color usage information inStream->read( temp, 20 ); // now we're at the raster. // each line should be padded with zeros to // end on a 4-byte boundary int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4; unsigned char *raster = new unsigned char[ rasterSize ]; inStream->read( raster, rasterSize ); Image *returnImage; if( failing ) { return NULL; } else { returnImage = new Image( width, height, 3 ); double *red = returnImage->getChannel( 0 ); double *green = returnImage->getChannel( 1 ); double *blue = returnImage->getChannel( 2 ); // pixels are stored bottom-up, left to right // (row major order) long rasterIndex = 0; for( int y=height-1; y>=0; y-- ) { for( int x=0; x #include class Color { public : /** * Constructs a color. * * @param red, green, blue the components of the color, each in [0,1]. * @param alpha the alpha value in [0,1]. Defaults to 0. * @param inBuildComposite set to true to build the composite * upon construction, or false to skip building composite (faster). * Defaults to true. */ Color(float red, float green, float blue, float alpha=0, char inBuildComposite=false ); Color(); /** * Constructs an rgb color from HSV components. * * @param inHue, inSaturation, inValue the HSV components of the * color, each in [0,1]. * @param alpha the alpha value in [0,1]. Defaults to 0. * @param inBuildComposite set to true to build the composite * upon construction, or false to skip building composite (faster). * Defaults to true. * * @return an RGBA color equivalent to the HSV color. * Must be destroyed by caller. */ static Color *makeColorFromHSV( float inHue, float inSaturation, float inValue, float inAlpha=0, char inBuildComposite=false ); float r, g, b, a; char mCompositeBuilt; unsigned long composite; // 32-bit composite color Color *copy(); // make a copy of this color /** * Sets the RGBA values of this color. * * @param red, green, blue, alpha the values to set. * Alpha defaults to 0. */ void setValues( float red, float green, float blue, float alpha=0 ); /** * Sets the RGBA values of this color using the values from * another color. * * @param inOtherColor the color to copy values from. * Must be destroyed by caller. */ void setValues( Color *inOtherColor ); /** * Tests whether this color is equal to another color. * * @param inOtherColor the other color. * Must be destroyed by caller. * * @return true if they are equal, or false otherwise. */ char equals( Color *inOtherColor ); void print(); /** * Computes the linear weighted sum of two colors. * * @param inFirst the first color. * @param inSecond the second color. * @param inFirstWeight the weight given to the first color in the * sum. The second color is weighted (1-inFirstWeight). * * @return the sum color. Must be destroyed by caller. */ static Color *linearSum( Color *inFirst, Color *inSecond, float inFirstWeight ); // after adjusting the r, g, b, a values exterally // call this to remake the composite unsigned long unsigned long rebuildComposite(); // get largest component of R,G,B float getMax(); // alter color data by multiplying by weight void weightColor( float weight ); /** * Alters color data by multiplying by a weight color. * * @param inWeightColor the color to multiply this color by. * Must be destroyed by caller. */ void weightColor( Color *inWeightColor ); /** * Inverts this color. * * Ignores alpha channel. */ void invert(); /** * Saturates this color, ensuring that at most 2 components are * non-zero. * * Ignores alpha channel. */ void saturate(); // get component by component weighted 32-bit composite // (returns alpha unweighted) unsigned long getWeightedComposite( float weight ); // from this color unsigned long getWeightedComposite(unsigned long c1, float weight ); // from composite unsigned long sumComposite(unsigned long c1, unsigned long c2); // access this color as a three vector float &operator[](int rgbIndex); }; inline Color::Color() { r = 0; g = 0; b = 0; a = 0; mCompositeBuilt = true; composite = 0; } inline Color::Color(float red, float green, float blue, float alpha, char inBuildComposite ) { r = red; g = green; b = blue; a = alpha; if( inBuildComposite ) { rebuildComposite(); } else { composite = 0; mCompositeBuilt = false; } } inline Color *Color::makeColorFromHSV( float inHue, float inSaturation, float inValue, float inAlpha, char inBuildComposite ) { // based on pseudocode from http://www.easyrgb.com/math.php float r, g, b; if ( inSaturation == 0 ) { r = inValue; g = inValue; b = inValue; } else { float var_h = inHue * 6; float var_i = int( var_h ); // Or var_i = floor( var_h ) float var_1 = inValue * ( 1 - inSaturation ); float var_2 = inValue * ( 1 - inSaturation * ( var_h - var_i ) ); float var_3 = inValue * ( 1 - inSaturation * ( 1 - ( var_h - var_i ) ) ); if( var_i == 0 ) { r = inValue; g = var_3; b = var_1; } else if( var_i == 1 ) { r = var_2; g = inValue; b = var_1; } else if( var_i == 2 ) { r = var_1; g = inValue; b = var_3; } else if( var_i == 3 ) { r = var_1; g = var_2; b = inValue; } else if( var_i == 4 ) { r = var_3; g = var_1; b = inValue; } else { r = inValue; g = var_1; b = var_2; } } return new Color( r, g, b, inAlpha, inBuildComposite ); } inline Color *Color::copy() { Color *copyColor = new Color(r,g,b,a, mCompositeBuilt ); return copyColor; } inline void Color::setValues( float red, float green, float blue, float alpha ) { r = red; g = green; b = blue; a = alpha; if( mCompositeBuilt ) { rebuildComposite(); } } inline void Color::setValues( Color *inOtherColor ) { setValues( inOtherColor->r, inOtherColor->g, inOtherColor->b, inOtherColor->a ); } inline char Color::equals( Color *inOtherColor ) { if( r == inOtherColor->r && g == inOtherColor->g && b == inOtherColor->b && a == inOtherColor->a ) { return true; } else { return false; } } inline void Color::print() { printf( "(%f, %f, %f)", r, g, b ); } inline Color *Color::linearSum( Color *inFirst, Color *inSecond, float inFirstWeight ) { float secondWeight = 1 - inFirstWeight; float r = inFirstWeight * inFirst->r + secondWeight * inSecond->r; float g = inFirstWeight * inFirst->g + secondWeight * inSecond->g; float b = inFirstWeight * inFirst->b + secondWeight * inSecond->b; float a = inFirstWeight * inFirst->a + secondWeight * inSecond->a; return new Color( r, g, b, a, inFirst->mCompositeBuilt || inSecond->mCompositeBuilt ); } inline unsigned long Color::rebuildComposite() { composite = ((int)(b*255)) | ((int)(g*255)) << 8 | ((int)(r*255)) << 16 | ((int)(a*255)) << 24; mCompositeBuilt = true; return composite; } inline float Color::getMax() { float max = -FLT_MAX; if( r > max ) max = r; if( g > max ) max = g; if( b > max ) max = b; return max; } inline void Color::weightColor( float weight ) { r = r * weight; g = g * weight; b = b * weight; // for now, don't touch alpha if( mCompositeBuilt ) { rebuildComposite(); } } inline void Color::invert() { r = 1 - r; g = 1 - g; b = 1 - b; } inline void Color::saturate() { if( r < g && r < b ) { r = 0; } else if( g < r && g < b ) { g = 0; } else if( b < r && b < g ) { b = 0; } else if( r != 0 ) { // they are all equal, but non-zero // default to dropping red r = 0; } //else // they are all 0 // leave as black } inline void Color::weightColor( Color *inWeightColor ) { r *= inWeightColor->r; g *= inWeightColor->g; b *= inWeightColor->b; a *= inWeightColor->a; if( mCompositeBuilt ) { rebuildComposite(); } } inline float &Color::operator[](int rgbIndex) { if( rgbIndex == 0) return r; else if( rgbIndex == 1) return g; else if( rgbIndex == 2) return b; else if( rgbIndex == 3) return a; return r; // default, return r reference } inline unsigned long Color::getWeightedComposite( float weight ) { return ((int)(b*255*weight)) | ((int)(g*255*weight)) << 8 | ((int)(r*255*weight)) << 16 | ((int)(a*255)) << 24; } inline unsigned long Color::getWeightedComposite( unsigned long c1, float weight ) { int b = c1 & 0xFF; int g = (c1 >> 8) & 0xFF; int r = (c1 >> 16) & 0xFF; int a = c1 >> 24; return ((int)(b*weight)) | (((int)(g*weight)) << 8) | (((int)(r*weight)) << 16) | (((int)(a*weight)) << 24); } inline unsigned long Color::sumComposite(unsigned long c1, unsigned long c2) { int b = (c1 & 0xFF) + (c2 & 0xFF); if( b > 255) b = 255; int g = ((c1 >> 8) & 0xFF) + ((c2 >> 8) & 0xFF); if( g > 255) g = 255; int r = ((c1 >> 16) & 0xFF) + ((c2 >> 16) & 0xFF); if( r > 255) r = 255; int a = (c1 >> 24) + (c2 >> 24); if( a > 255) a = 255; return b | (g << 8) | (r << 16) | (a << 24); } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/ImageConverter.h0000640000175000017500000000234007273534231022555 0ustar pabspabs/* * Modification History * * 2001-February-19 Jason Rohrer * Created. * * 2001-April-28 Jason Rohrer * Updated comments to deal with a failed deformatting operation. */ #ifndef IMAGE_CONVERTER_INCLUDED #define IMAGE_CONVERTER_INCLUDED #include "Image.h" #include "minorGems/io/InputStream.h" #include "minorGems/io/OutputStream.h" /** * Interface for a class that can convert Images to and from * various image formats. * * @author Jason Rohrer */ class ImageConverter { public: /** * Sends an image out to a stream as a particular * format. * * None of the parameters are destroyed by this call. * * @param inImage the image to convert. * @param inStream the stream to write the formatted image to. */ virtual void formatImage( Image *inImage, OutputStream *inStream ) = 0; /** * Reads an image in from a stream as a particular * format. * * None of the parameters are destroyed by this call. * * @param inStream the stream to read the formatted image. * * @return the deformatted image, or NULL if the deformatting * operation fails. Must be destroyed by caller. */ virtual Image *deformatImage( InputStream *inStream ) = 0; }; #endif transcend-0.3+dfsg2.orig/minorGems/graphics/getMouse.h0000640000175000017500000000034207205611501021422 0ustar pabspabs// Jason Rohrer // getMouse.h /** * * general interface for getting current mouse position * Implemented by a graphix framework on a particular platform * * * Created 3-21-2000 * Mods: * */ void getMouse( int *x, int *y );transcend-0.3+dfsg2.orig/minorGems/graphics/keyCodes.h0000640000175000017500000000104407205611501021400 0ustar pabspabs// Jason Rohrer // keyCodes.h /** * * Header for defining key codes for various platforms * * * Created 12-15-99 * Mods: * Jason Rohrer 3-23-2000 Added more key codes * */ #ifdef WINDOWS_KEY_CODES #define M_KEY 0x4D #define N_KEY 0x4E #define S_KEY 0x53 #define Q_KEY 0x51 #define L_KEY 0x4C #define R_KEY 0x52 #define T_KEY 0x54 #endif #ifdef MAC_KEY_CODES #define M_KEY 0x2E #define N_KEY 0x2D #define S_KEY 0x01 #define Q_KEY 0x0C #define L_KEY 0x25 #define R_KEY 0x0F #define T_KEY 0x11 #endiftranscend-0.3+dfsg2.orig/minorGems/graphics/linux/0000750000175000017500000000000010305077062020623 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/linux/SDLTest.cpp0000640000175000017500000001173007274131037022620 0ustar pabspabs //#include #include "minorGems/graphics/ScreenGraphics.h" #include "minorGems/graphics/GraphicBuffer.h" #include "minorGems/graphics/Color.h" #include "minorGems/ui/Mouse.h" #include #include #include int numIcons = 200; int currentStep = 0; int numSteps = 1000; void catch_int(int sig_num) { printf( "Quiting..." ); currentStep = numSteps; signal( SIGINT, catch_int); } int main() { int i, j; // let catch_int handle interrupt (^c) signal( SIGINT, catch_int); ScreenGraphics *graphics = new ScreenGraphics( 640, 480 ); Mouse *mouse = new Mouse(2); unsigned long *pixelBuff = new unsigned long[ 640 * 480 ]; GraphicBuffer *buffer = new GraphicBuffer( pixelBuff, 640, 480 ); IconMap **maps = new IconMap*[numIcons]; IconMap *mouseMap; IconMap *mouseMap1 = new IconMap( 10, 10 ); Color mouseColor1( 1.0, 0.0, 0.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap1->imageMap[ mouseMap1->yOffset[y] + x ] = mouseColor1.composite; } } IconMap *mouseMap2 = new IconMap( 10, 10 ); Color mouseColor2( 0.0, 1.0, 0.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap2->imageMap[ mouseMap2->yOffset[y] + x ] = mouseColor2.composite; } } IconMap *mouseMap3 = new IconMap( 10, 10 ); Color mouseColor3( 0.0, 0.0, 1.0, 1.0 ); for( int y=0; y<10; y++ ) { for( int x=0; x<10; x++ ) { mouseMap3->imageMap[ mouseMap3->yOffset[y] + x ] = mouseColor3.composite; } } mouseMap = mouseMap1; Color c( 0.0f, 0.0f, 0.0f, 1.0f ); buffer->fill( c ); float *xPos = new float[numIcons]; float *yPos = new float[numIcons]; float *xDelta = new float[numIcons]; float *yDelta = new float[numIcons]; for( i=0; iimageMap[ maps[i]->yOffset[y] + x ] = randColor.composite; } } } int mouseX = 0; int mouseY = 0; char buttonDown1 = false; char buttonDown2 = false; for( currentStep=0; currentSteperaseIconMap( mouseMap, mouseX, mouseY, c ); mouse->getLocation( &mouseX, &mouseY ); buffer->drawIconMap( mouseMap, mouseX, mouseY ); if( !buttonDown1 && mouse->isButtonDown(0) ) { buttonDown1 = true; mouseMap = mouseMap2; } else if( buttonDown1 && !mouse->isButtonDown(0) ) { buttonDown1 = false; mouseMap = mouseMap1; } else if( !buttonDown2 && mouse->isButtonDown(1) ) { buttonDown2 = true; mouseMap = mouseMap3; } else if( buttonDown2 && !mouse->isButtonDown(1) ) { buttonDown2 = false; mouseMap = mouseMap1; } for( i=0; ieraseIconMap( maps[i], (int)( xPos[i] - 5 ), (int)( yPos[i] - 5 ), c ); if( xPos[i] > 640 || xPos[i] < 0 ) { xDelta[i] = -xDelta[i]; } xPos[i] += xDelta[i]; if( yPos[i] > 480 || yPos[i] < 0 ) { yDelta[i] = -yDelta[i]; } yPos[i] += yDelta[i]; buffer->drawIconMap( maps[i], (int)( xPos[i] - 5 ), (int)( yPos[i] - 5 ) ); } graphics->swapBuffers( buffer ); } /* for( int i=0; i<100; i++ ) { if( i%2 == 0 ) { Color c( 1.0f, 0.0f, 0.0f, 1.0f ); buffer->fill( c ); } else { Color c( 0.0f, 1.0f, 0.0f, 1.0f ); buffer->fill( c ); } graphics->swapBuffers( buffer ); } */ printf( "Done.\n" ); return 0; /* if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); exit(1); } //atexit( SDL_Quit ); SDL_Surface *screen = SDL_SetVideoMode( 640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF );//| SDL_FULLSCREEN ); if ( screen == NULL ) { printf( "Couldn't set 640x480x32 video mode: %s\n", SDL_GetError() ); exit(1); } for( int i=0; i< 100; i++ ) { if ( SDL_MUSTLOCK(screen) ) { if ( SDL_LockSurface(screen) < 0 ) { printf( "Couldn't lock screen: %s\n", SDL_GetError()); exit(1); } } Uint32 value; if( i%2 == 0 ) { value = 0x0; } else { value = 0xFFFFFFFF; } Uint32 *buffer = (Uint32 *)( screen->pixels ); for ( int y=0; yh; y++ ) { for ( int x=0; xw; x++ ) { int r = ( ( ( x * 255 ) / screen->w ) + i ) % 255; int g = ( ( ( y * 255 ) / screen->h ) + i ) % 255; int b = 0; int a = 255; //buffer[ y * screen->w + x ] = (a << 24) | (r << 16) | (g << 8) | b; buffer[ y * screen->w + x ] = value; } } if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } //SDL_UpdateRect( screen, 0, 0, screen->w, screen->h ); SDL_Flip( screen ); } SDL_Quit(); SDL_Delay(2000); exit(0); */ } transcend-0.3+dfsg2.orig/minorGems/graphics/linux/ScreenGraphicsLinux.cpp0000640000175000017500000000412707274131037025260 0ustar pabspabs/* * Modification History * * 2000-November-18 Jason Rohrer * Created. * * 2000-November-19 Jason Rohrer * Change so that it doesn't use SDL's interrupt parachute, i.e., * so it will quit on ^c. * * 2001-May-1 Jason Rohrer * Changed to use more standard SDL include location. */ #include "minorGems/graphics/ScreenGraphics.h" #include /** * Note: Linux implementation uses windowed mode. */ ScreenGraphics::ScreenGraphics( int inWidth, int inHeight ) : mWidth( inWidth ), mHeight( inHeight ) { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); return; } SDL_Surface *screen = SDL_SetVideoMode( mWidth, mHeight, 32, SDL_SWSURFACE ); if ( screen == NULL ) { printf( "Couldn't set %dx%dx32 video mode: %s\n", mWidth, mHeight, SDL_GetError() ); return; } mNativeObjectPointer = (void*)screen; } ScreenGraphics::~ScreenGraphics() { SDL_Quit(); } char ScreenGraphics::isResolutionAvailable( int inWidth, int inHeight ) { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) { printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); return false; } // ask SDL if this screen size is supported char videoModeOk = (char)SDL_VideoModeOK( inWidth, inHeight, 32, SDL_SWSURFACE ); SDL_Quit(); return videoModeOk; } void ScreenGraphics::swapBuffers( GraphicBuffer *inOutBuffer ) { if( inOutBuffer->getHeight() != mHeight || inOutBuffer->getWidth() != mWidth ) { printf( "Buffer of incorrect size passed to screen.\n" ); return; } SDL_Surface *screen = (SDL_Surface*)mNativeObjectPointer; // check if we need to lock the screen if( SDL_MUSTLOCK( screen ) ) { if( SDL_LockSurface( screen ) < 0 ) { printf( "Couldn't lock screen: %s\n", SDL_GetError() ); return; } } Uint32 *buffer = (Uint32 *)( screen->pixels ); memcpy( (void*)buffer, (void*)( inOutBuffer->getBuffer() ), mWidth * mHeight * 4 ); // unlock the screen if necessary if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } SDL_Flip( screen ); } transcend-0.3+dfsg2.orig/minorGems/graphics/linux/graphixCommonDefs.h0000640000175000017500000000013007205611501024401 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define LINUX_KEY_CODES transcend-0.3+dfsg2.orig/minorGems/graphics/Image.h0000640000175000017500000003224410206405732020664 0ustar pabspabs/* * Modification History * * 2000-December-21 Jason Rohrer * Created. * * 2001-January-6 Jason Rohrer * Fixed a bug in filter( ChannelFilter * ). * Set mSelection to NULL by default. * * 2001-January-10 Jason Rohrer * Made class serializable. * * 2001-January-15 Jason Rohrer * Made copy() not-virtual, so it can be overridden by subclasses * while allowing pointer type to determine which function * implementation is invoked. * * 2001-January-31 Jason Rohrer * Fixed a bug in copy(). * * 2001-February-3 Jason Rohrer * Updated serialization code to use new interfaces. * * 2001-February-4 Jason Rohrer * Rewrote the serialization code to send the image across as a byte * array with one byte per channel. This will reduce the transfer size by * a factor of 8. Keeping images in double format is convennient for image * creation, but the added quality never affects the end user anyway, so * there's no point in sending the extra data to a stream. * Removed an unused array allocation. * * 2005-February-21 Jason Rohrer * Made destructor virtual to avoid compiler warnings. */ #ifndef IMAGE_INCLUDED #define IMAGE_INCLUDED #include #include #include "ChannelFilter.h" #include "minorGems/io/Serializable.h" /** * A multi-channel, double-valued image. * * Is Serializable. Note that a serialized image doesn't have a selection. * * @author Jason Rohrer */ class Image : public Serializable { public: /** * Constructs an image. All channels are initialized to 0. * * @param inWidth width of image in pixels. * @param inHeight height of image in pixels. * @param inNumChannels number of channels in image. */ Image( int inWidth, int inHeight, int inNumChannels ); virtual ~Image(); // gets the dimensions of this image. virtual long getWidth(); virtual long getHeight(); virtual long getNumChannels(); /** * Gets the values of a particular channel. * * Values are not copied. * * @param inChannel the channel to get. * * @return the values of the specified channel in row-major order. */ virtual double *getChannel( int inChannel ); /** * Selects a region of the image. Default is a clear selection, * which means all regions of image are affected by an applied * filter. * * @param inSelection the image to use as the selection mask. * Values of 0 indicate pixels that are not selection, and 1 * indicate pixels that are selected, with selection amount * varying linearly between 0 and 1. * If inSelection is a single channel, then that channel is * used as a selection mask for all channels in this image. * If inSelection contains the same number of channels as this * image, then the corresponding channels of inSelection are * are used to mask each channel of this image. * If inSelection contains a number of channels different * from the number in this image, the first channel of inSelection * is used to mask each channel in this image. * * Note that inSelection is not copied or destroyed by this class. * Thus, modifying inSelection after calling setSelection will * modify the selection in this image. */ virtual void setSelection( Image *inSelection ); /** * Gets the selection for this image. * * @return the selection for this image. Returns NULL * if there is no selection. Must not be destroyed * by caller before calling clearSelection. */ virtual Image *getSelection(); /** * Clears the selection. Effectively selects the entire image. */ virtual void clearSelection(); /** * Applies a filter to the selected region of * a specified channel of this image. * * @param inFilter the filter to apply. * @param inChannel the channel to filter. */ virtual void filter( ChannelFilter *inFilter, int inChannel ); /** * Applies a filter to the selected region of * all channels of this image. * * @param inFilter the filter to apply. */ virtual void filter( ChannelFilter *inFilter ); /** * Copies the selected region of this image. Not virtual, * so can be overridden by subclasses while allowing pointer * type to determine which function implementation is invoked. * * @return a new image with the same number of channels * as this image, each containing the selected region * from each corresponding channel of this image. Unselected * regions are set to black. Returned image has no selection. */ Image *copy(); /** * Pastes the selected region from another image into * the selected region of this image. * * @param inImage the image to paste. Let c be the number * of channels in this image, and cp be the number * of channels in the image being pasted. * If ccp, then only the first cp channels * of this image are pasted into. */ virtual void paste( Image *inImage ); /** * Copies the data from the selected region of a channel. * * @param inChannel the channel to copy. * * @return a copy of the channel data. Must be destroyed * by the caller. */ virtual double *copyChannel( int inChannel ); /** * Pastes channel data into the selected region of a specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, int inChannel ); /** * Gets the mask for a specified channel. * * @param inChannel the channel to get a mask for. * * @return the mask data for the specified channel. * If selection has the same number of channels as this image * then a different mask is returned for each channel. Otherwise, * the first channel from the selection is returned as the * mask for every channel. Returns NULL if there is no selection. */ virtual double *getChannelSelection( int inChannel ); // implement the Serializable interface virtual int serialize( OutputStream *inOutputStream ); virtual int deserialize( InputStream *inInputStream ); protected: long mWide, mHigh, mNumPixels, mNumChannels; double **mChannels; // NULL if nothing selected. Image *mSelection; /** * Pastes masked channel data into the selected region of a * specified channel. * * @param inChannelData an array containing the channel * data to be pasted. Must be destroyed by caller. * @param inMask the selection mask to use for passed-in channel. * Set to NULL for no mask. * @param inChannel the channel to paste into. */ virtual void pasteChannel( double *inChannelData, double *inMask, int inChannel ); }; inline Image::Image( int inWidth, int inHeight, int inNumChannels ) : mWide( inWidth ), mHigh( inHeight ), mNumPixels( inWidth * inHeight ), mNumChannels( inNumChannels ), mChannels( new double*[inNumChannels] ), mSelection( NULL ) { // initialize all channels for( int i=0; iapply( mChannels[ inChannel ], mWide, mHigh ); } else { // part of image selected // turn selection off and filter channel entirely Image *tempSelection = mSelection; mSelection = NULL; // filter a copy of the channel double *filteredChannel = copyChannel( inChannel ); inFilter->apply( filteredChannel, mWide, mHigh ); // now paste filtered channel back into selected region mSelection = tempSelection; pasteChannel( filteredChannel, inChannel ); } } inline void Image::filter( ChannelFilter *inFilter ) { for( int i=0; ipaste( this ); return copiedImage; } inline void Image::paste( Image *inImage ) { // copy paste in the min number of channels only int numChannelsToPaste = mNumChannels; if( numChannelsToPaste > inImage->getNumChannels() ) { numChannelsToPaste = inImage->getNumChannels(); } for( int i=0; igetChannel(i), inImage->getChannelSelection(i), i ); } } inline double *Image::copyChannel( int inChannel ) { // first, copy the channel double *copiedChannel = new double[mNumPixels]; memcpy( copiedChannel, mChannels[inChannel], sizeof( double ) * mNumPixels ); if( mSelection != NULL ) { // apply selection to copied channel double *selection = getChannelSelection( inChannel ); // scale copied channel with selection for( int i=0; igetNumChannels() == mNumChannels ) { // use separate selection for each channel return mSelection->getChannel( inChannel ); } else { // use first channel of selection for all channels return mSelection->getChannel( 0 ); } } } inline void Image::pasteChannel( double *inChannelData, int inChannel ) { pasteChannel( inChannelData, NULL, inChannel ); } // We've abstracted away the complexity in the other fuctions, // but it all seemed to filter down into this function, which // is very messy. inline void Image::pasteChannel( double *inChannelData, double *inMask, int inChannel ) { double *thisChannel = mChannels[inChannel]; if( mSelection != NULL ) { // scale incoming data with this selection double *selection = getChannelSelection(inChannel); if( inMask != NULL ) { // scale incoming data with both masks for( int i=0; iwriteLong( mWide ); numBytes += inOutputStream->writeLong( mHigh ); // then output number of channels numBytes += inOutputStream->writeLong( mNumChannels ); // now output each channel for( int i=0; iwriteDouble( mChannels[i][p] ); byteArray[p] = (unsigned char)( mChannels[i][p] * 255 ); } numBytes += inOutputStream->write( byteArray, mNumPixels ); delete [] byteArray; } return numBytes; } inline int Image::deserialize( InputStream *inInputStream ) { int i; // first delete old image channels for( i=0; ireadLong( &mWide ); numBytes += inInputStream->readLong( &mHigh ); mNumPixels = mWide * mHigh; // then input number of channels numBytes += inInputStream->readLong( &mNumChannels ); mChannels = new double*[mNumChannels]; // now input each channel for( i=0; iread( byteArray, mNumPixels ); // convert each byte to an 8-bit double pixel for( int p=0; preadDouble( &( mChannels[i][p] ) ); mChannels[i][p] = (double)( byteArray[p] ) / 255.0; } delete [] byteArray; } return numBytes; } #endif transcend-0.3+dfsg2.orig/minorGems/graphics/ImageColorConverter.h0000640000175000017500000003377607353715726023606 0ustar pabspabs/* * Modification History * * 2001-February-26 Jason Rohrer * Created. * Added functions for converting to and from gray byte arrays. * * 2001-September-19 Jason Rohrer * Added an RGB->HSB conversion function, which was copied * from minorGems/ai/robotics/ImageStatistics. * Added RGB<->YIQ functions. * * 2001-September-20 Jason Rohrer * Fixed a bug in the YIQ conversion. * Got rid of this bug fix, as it distorts the YIQ space, * and there is no way to prevent colors from going out of the * [0,1] range all of the time anyway. * * 2001-September-24 Jason Rohrer * Added RGB<->YCbCr functions. * Abstracted out a common coefficient multiplying function. */ #ifndef IMAGE_COLOR_CONVERTER_INCLUDED #define IMAGE_COLOR_CONVERTER_INCLUDED #include "Image.h" /** * A container class for static functions that convert * images between various color spaces. * * @author Jason Rohrer */ class ImageColorConverter { public: /** * Converts a 3-channel RGB image to a 1-channel grayscale * image using an NTSC luminosity standard. * * @param inImage the RGB image to convert. Must be destroyed * by caller. * * @return a new, grayscale version of inImage. Must be * destroyed by the caller. Returns NULL if inImage * is not a 3-channel image. */ static Image *RGBToGrayscale( Image *inImage ); /** * Converts a 1-channel grayscae image to a 3-channel RGB * image. * * @param inImage the grayscale image to convert. Must be destroyed * by caller. * * @return a new, RGB version of inImage. Must be * destroyed by the caller. Returns NULL if inImage * is not a 1-channel image. */ static Image *grayscaleToRGB( Image *inImage ); /** * Converts a 1-channel grayscae image to a 1-channel byte array. * * @param inImage the grayscale image to convert. Must be destroyed * by caller. * @param inChannelNumber the channel number to use as the * gray channel. Defaults to 0; * * @return a new byte array with one byte per image pixel, * and image [0,1] values mapped to [0,255]. */ static unsigned char *grayscaleToByteArray( Image *inImage, int inChannelNumber = 0 ); /** * Converts a byte array to a 1-channel grayscale * image. * * @param inBytes the byte array to convert. Must be destroyed * by caller. * @param inWidth the width of the image contained in the byte array. * @param inHeight the height of the image contained in the byte array. * * @return a new, grayscale image version of the byte array. Must be * destroyed by the caller. */ static Image *byteArrayToGrayscale( unsigned char *inBytes, int inWidth, int inHeight ); /** * Converts an RGB image to HSB. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the HSB conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToHSB( Image *inRGBImage ); /** * Converts an RGB image to YIQ. * * Note that color values in the resulting YIQ * image may lie outside of the range [0,1]. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the YIQ conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToYIQ( Image *inRGBImage ); /** * Converts a YIQ image to RGB. * * * Note that color values in the resulting RGB * image may lie outside of the range [0,1]. * * @param inYIQImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the RGB conversion of the * YIQ image. Must be destroyed by caller. */ static Image *YIQToRGB( Image *inYIQImage ); /** * Converts an RGB image to YCbCr. * * Note that in the YCbCr standard, Y is in the range * [0,1], while Cb and Cr are both in the range [-0.5, 0.5]. * This function returns Cb and Cr components shifted * into the range [0,1]. * * @param inRGBImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the YCbCr conversion of the * RGB image. Must be destroyed by caller. */ static Image *RGBToYCbCr( Image *inRGBImage ); /** * Converts a YCbCr image to RGB. * * * Note that in the YCbCr standard, Y is in the range * [0,1], while Cb and Cr are both in the range [-0.5, 0.5]. * This function expects input Cb and Cr components to be shifted * into the range [0,1]. * * @param inYCbCrImage the rgb image to convert. * Must be destroyed by caller. * * @return a new image that is the RGB conversion of the * YCbCr image. Must be destroyed by caller. */ static Image *YCbCrToRGB( Image *inYCbCrImage ); protected: /** * Converts an 3-channel image to another 3-channel * image using a matrix of conversion coefficients. * * The following formulae are used; * outChan0 = inC00 * inChan0 + inC01 * inChan1 + inC02 * inChan2 * outChan1 = inC10 * inChan0 + inC11 * inChan1 + inC12 * inChan2 * outChan2 = inC20 * inChan0 + inC21 * inChan1 + inC22 * inChan2 * * @param inImage the image to convert. * Must be destroyed by caller. * * @return a new image that is inImage converted, or NULL if * conversion failed (usually because inImage does not * contain 3 channels). * Must be destroyed by caller. */ static Image *coefficientConvert( Image *inImage, double inC00, double inC01, double inC02, double inC10, double inC11, double inC12, double inC20, double inC21, double inC22 ); }; inline Image *ImageColorConverter::RGBToGrayscale( Image *inImage ) { int w = inImage->getWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *grayImage = new Image( w, h, 1 ); double *red = inImage->getChannel( 0 ); double *green = inImage->getChannel( 1 ); double *blue = inImage->getChannel( 2 ); double *gray = grayImage->getChannel( 0 ); for( int i=0; igetWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *rgbImage = new Image( w, h, 3 ); double *red = rgbImage->getChannel( 0 ); double *green = rgbImage->getChannel( 1 ); double *blue = rgbImage->getChannel( 2 ); double *gray = inImage->getChannel( 0 ); for( int i=0; igetWidth(); int h = inImage->getHeight(); int numPixels = w * h; unsigned char *bytes = new unsigned char[ numPixels ]; double *gray = inImage->getChannel( inChannelNumber ); for( int i=0; igetChannel( 0 ); for( int i=0; igetNumChannels() != 3 ) { printf( "RGBtoHSB requires a 3-channel image as input.\n" ); return NULL; } int w = inRGBImage->getWidth(); int h = inRGBImage->getHeight(); int numPixels = w * h; Image *hsbImage = new Image( w, h, 3 ); double *redChannel = inRGBImage->getChannel( 0 ); double *greenChannel = inRGBImage->getChannel( 1 ); double *blueChannel = inRGBImage->getChannel( 2 ); double *hueChannel = hsbImage->getChannel( 0 ); double *satChannel = hsbImage->getChannel( 1 ); double *brightChannel = hsbImage->getChannel( 2 ); for( int i=0; i g) ? r : g; if (b > cmax) { cmax = b; } int cmin = (r < g) ? r : g; if (b < cmin) { cmin = b; } bright = ( (double)cmax ) / 255.0; if( cmax != 0 ) { sat = ( (double)( cmax - cmin ) ) / ( (double) cmax ); } else { sat = 0; } if( sat == 0 ) { hue = 0; } else { double redc = ( (double)( cmax - r ) ) / ( (double)( cmax - cmin ) ); double greenc = ( (double) ( cmax - g ) ) / ( (double)( cmax - cmin ) ); double bluec = ( (double)( cmax - b ) ) / ( (double)( cmax - cmin ) ); if( r == cmax ) { hue = bluec - greenc; } else if( g == cmax ) { hue = 2.0 + redc - bluec; } else { hue = 4.0 + greenc - redc; } hue = hue / 6.0; if( hue < 0 ) { hue = hue + 1.0; } } hueChannel[i] = hue; satChannel[i] = sat; brightChannel[i] = bright; } return hsbImage; } inline Image *ImageColorConverter::RGBToYIQ( Image *inRGBImage ) { if( inRGBImage->getNumChannels() != 3 ) { printf( "RGBtoYIQ requires a 3-channel image as input.\n" ); return NULL; } Image *yiqImage = coefficientConvert( inRGBImage, 0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311 ); return yiqImage; } inline Image *ImageColorConverter::YIQToRGB( Image *inYIQImage ) { if( inYIQImage->getNumChannels() != 3 ) { printf( "YIQtoRGB requires a 3-channel image as input.\n" ); return NULL; } Image *rgbImage = coefficientConvert( inYIQImage, 1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702 ); return rgbImage; } inline Image *ImageColorConverter::RGBToYCbCr( Image *inRGBImage ) { if( inRGBImage->getNumChannels() != 3 ) { printf( "RGBtoYCbCr requires a 3-channel image as input.\n" ); return NULL; } // coefficients taken from the color space faq /* RGB -> YCbCr (with Rec 601-1 specs) Y = 0.2989 * Red + 0.5866 * Green + 0.1145 * Blue Cb = -0.1687 * Red - 0.3312 * Green + 0.5000 * Blue Cr = 0.5000 * Red - 0.4183 * Green - 0.0816 * Blue YCbCr (with Rec 601-1 specs) -> RGB Red = Y + 0.0000 * Cb + 1.4022 * Cr Green = Y - 0.3456 * Cb - 0.7145 * Cr Blue = Y + 1.7710 * Cb + 0.0000 * Cr */ Image *ycbcrImage = coefficientConvert( inRGBImage, 0.2989, 0.5866, 0.1145, -0.1687, -0.3312, 0.5000, 0.5000, -0.4183, -0.0816 ); // adjust the Cb and Cr channels so they are in the range [0,1] int numPixels = ycbcrImage->getWidth() * ycbcrImage->getHeight(); double *cbChannel = ycbcrImage->getChannel( 1 ); double *crChannel = ycbcrImage->getChannel( 2 ); for( int i=0; igetNumChannels() != 3 ) { printf( "YCbCrtoRGB requires a 3-channel image as input.\n" ); return NULL; } // adjust the normalized Cb and Cr channels // so they are in the range [-0.5,0.5] int numPixels = inYCbCrImage->getWidth() * inYCbCrImage->getHeight(); double *cbChannel = inYCbCrImage->getChannel( 1 ); double *crChannel = inYCbCrImage->getChannel( 2 ); for( int i=0; i YCbCr (with Rec 601-1 specs) Y = 0.2989 * Red + 0.5866 * Green + 0.1145 * Blue Cb = -0.1687 * Red - 0.3312 * Green + 0.5000 * Blue Cr = 0.5000 * Red - 0.4183 * Green - 0.0816 * Blue YCbCr (with Rec 601-1 specs) -> RGB Red = Y + 0.0000 * Cb + 1.4022 * Cr Green = Y - 0.3456 * Cb - 0.7145 * Cr Blue = Y + 1.7710 * Cb + 0.0000 * Cr */ Image *rgbImage = coefficientConvert( inYCbCrImage, 1.0, 0.0000, 1.4022, 1.0, -0.3456, -0.7145, 1.0, 1.7710, 0.0000 ); // clip r, g, and b channels to the range [0,1], since // some YCbCr pixel values might map out of this range // (in other words, some YCbCr values map outside of rgb space) for( int c=0; c<3; c++ ) { double *channel = rgbImage->getChannel( c ); for( int p=0; p 1 ) { channel[p] = 1.0; } } } return rgbImage; } inline Image *ImageColorConverter::coefficientConvert( Image *inImage, double inC00, double inC01, double inC02, double inC10, double inC11, double inC12, double inC20, double inC21, double inC22 ) { if( inImage->getNumChannels() != 3 ) { return NULL; } int w = inImage->getWidth(); int h = inImage->getHeight(); int numPixels = w * h; Image *outImage = new Image( w, h, 3 ); double *outChannel0 = outImage->getChannel( 0 ); double *outChannel1 = outImage->getChannel( 1 ); double *outChannel2 = outImage->getChannel( 2 ); double *inChannel0 = inImage->getChannel( 0 ); double *inChannel1 = inImage->getChannel( 1 ); double *inChannel2 = inImage->getChannel( 2 ); for( int i=0; iScreenGraphics. * Adjusts screen resolution and fills screen with black. * * @param inWidth width of desired full screen. * @param inHeight height of desired full screen. */ ScreenGraphics( int inWidth, int inHeight ); /** * Desctructor restores screen to original state. */ ~ScreenGraphics(); /** * Swaps a buffer to the screen. * The next buffer to be filled is returned in inOutBuffer. * No guarentee is made about what will be in the next buffer returned. * (On systems that support on-card double buffering, it may even * be a buffer pointing directly into video memory.) * * @param inOutBuffer pointer to the buffer to swap to the screen, * and pointer where the next buffer will be stored. */ void swapBuffers( GraphicBuffer *inOutBuffer ); /** * Determines whether a particular screen setting is available. * * NOTE: Do not call after constructing a ScreenGraphics instance. * * @param inWidth width of desired full screen. * @param inHeight height of desired full screen. * * @return true iff screen setting is available. */ static char isResolutionAvailable( int inWidth, int inHeight ); private: int mWidth; int mHeight; void *mNativeObjectPointer; }; #endif transcend-0.3+dfsg2.orig/minorGems/graphics/mac/0000750000175000017500000000000010305077062020224 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/mac/graphixCommonDefs.h0000640000175000017500000000012507205611502024007 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define MAC_KEY_CODEStranscend-0.3+dfsg2.orig/minorGems/graphics/mac/graphixFramework.cpp0000640000175000017500000001727507205611502024263 0ustar pabspabs/** 32 bit DrawSprockets pluggable framework * * Jason Rohrer, 4-28-99 * * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * Jason Rohrer 12-15-99 Added support for keyboard querying * Jason Rohrer 3-21-2000 Added support for mouse querying * */ #include #include #include // for keyboard events #include #include #include "swapBuffers.h" // interface for swapping buffers #include "getKey.h" // interface for getting key states #include "getMouse.h" // interface for getting mouse state //#include "ALifeGuiRunner.h" #include "GoGUIRunner.h" #include "GraphicBuffer.h" // Constants #define BallWidth 20 #define BallHeight 20 #define BobSize 8 /* Size of text in each ball */ //MT Tutorial #define rWindow 128 // resource ID number for window // Globals Rect windRect; WindowPtr w; DSpContextReference theContext; unsigned long *double_buffer = NULL; // the double buffer unsigned long *screen_buffer = NULL; GraphicBuffer *graphicDoubleBuffer; short bufferHigh = 480; short bufferWide = 640; // prototypes void Initialize(void); void graphixPicker(void); // our mother function, picks effect order, etc. void initVideo(DSpContextReference *theContext); void MyInitAttributes(DSpContextAttributes*); void CleanUp(DSpContextReference theContext); int main() { Initialize(); // setup a window MaxApplZone(); initVideo( &theContext ); // set up and switch video graphixPicker(); CleanUp( theContext); } void graphixPicker() { // our "mother function" // selects which effect to run next // graphix plug-in format: // myGraphix( bufferPtr, bufferHigh, bufferWide, colorBits, numFrames) // function can cll "swapBuffer32" internally whenever it needs the back buffer // sent to the screen. // jcrTorus( double_buffer, 480, 640, 32, 100); // jcrVoxels( double_buffer, 480, 640, 32, 230); // jcrTorus( double_buffer, 480, 640, 32, 50); //jcrBloom( double_buffer, 480, 640, 32, 100); // GraphicBuffer passed in contains all needed info about screen // pass in number of frames to run for //ALifeGuiRunner( *graphicDoubleBuffer, 1000 ); GoGUIRunner( *graphicDoubleBuffer, 0 ); } // Initialize the draw sprockets // set up the video, fade out display, and switch video modes void initVideo(DSpContextReference *theContext) { CGrafPtr backBuffer; PixMapHandle bufferMap; DSpContextAttributes theDesiredAttributes; OSStatus theError; MyInitAttributes(&theDesiredAttributes); theDesiredAttributes.displayWidth = 640; theDesiredAttributes.displayHeight = 480; theDesiredAttributes.colorNeeds = kDSpColorNeeds_Require; theDesiredAttributes.backBufferDepthMask = kDSpDepthMask_32; theDesiredAttributes.displayDepthMask = kDSpDepthMask_32; theDesiredAttributes.backBufferBestDepth = 32; theDesiredAttributes.displayBestDepth = 32; theError = DSpFindBestContext(&theDesiredAttributes, theContext); // add page flipping by video card to request theDesiredAttributes.contextOptions |= kDSpContextOption_PageFlip; //theDesiredAttributes.contextOptions |= kDSpContextOption_DontSyncVBL; // Reserver a display theError = DSpContext_Reserve(*theContext, &theDesiredAttributes); // fade out theError = DSpContext_FadeGammaOut(NULL, NULL); theError = DSpContext_SetState(*theContext, kDSpContextState_Active); ShowCursor(); HideCursor(); // setup global pointers to screen and back buffer theError = DSpContext_GetBackBuffer(*theContext, kDSpBufferKind_Normal, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); double_buffer = (unsigned long*)(**bufferMap).baseAddr; // create GraphicBuffer object graphicDoubleBuffer = new GraphicBuffer( double_buffer, 640, 480 ); theError = DSpContext_GetFrontBuffer(*theContext, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); screen_buffer = (unsigned long*)(**bufferMap).baseAddr; theError = DSpContext_FadeGammaIn(NULL, NULL); } // initialize a set of Display attributes to zero void MyInitAttributes (DSpContextAttributes *inAttributes) { if (NULL == inAttributes) DebugStr("\pStimpy! You Idiot!"); inAttributes->frequency = 0; inAttributes->displayWidth = 0; inAttributes->displayHeight = 0; inAttributes->reserved1 = 0; inAttributes->reserved2 = 0; inAttributes->colorNeeds = 0; inAttributes->colorTable = NULL; inAttributes->contextOptions = 0; inAttributes->backBufferDepthMask = 0; inAttributes->displayDepthMask = 0; inAttributes->backBufferBestDepth = 0; inAttributes->displayBestDepth = 0; inAttributes->pageCount = 0; inAttributes->gameMustConfirmSwitch = false; inAttributes->reserved3[0] = 0; inAttributes->reserved3[1] = 0; inAttributes->reserved3[2] = 0; inAttributes->reserved3[3] = 0; } void CleanUp(DSpContextReference theContext) { OSStatus theError; theError = DSpContext_FadeGammaOut(NULL, NULL); /* put the context into the inactive state */ theError = DSpContext_SetState(theContext, kDSpContextState_Inactive); /* fade back in */ theError = DSpContext_FadeGammaIn(NULL, NULL); /* release the context */ theError = DSpContext_Release(theContext); ShowCursor(); } // swap bufferB to the screen (32 bit version) void swapBuffers32( GraphicBuffer &bufferB ) { OSStatus theError; // swap buffers theError = DSpContext_SwapBuffers(theContext, NULL, 0); // get pointer to new back buffer and return it CGrafPtr backBuffer; PixMapHandle bufferMap; theError = DSpContext_GetBackBuffer(theContext, kDSpBufferKind_Normal, &backBuffer); bufferMap = (PixMapHandle)GetGWorldPixMap(backBuffer); // replace the buffer in bufferB with the new double buffer bufferB.setBuffer( (unsigned long*)(**bufferMap).baseAddr ); // memcpy(screen_buffer, bufferPtrB, 4*bufferWide*bufferHigh); } // end of swapBuffers // returns true if key represented by given key code is down char getKeyDown( int vKeyCode ) { unsigned char keyArray [16]; GetKeys( (long *)keyArray ); return ((keyArray[vKeyCode>>3] >> (vKeyCode & 7)) & 1); } // returns true if key is up char getKeyUp( int vKeyCode ) { unsigned char keyArray [16]; GetKeys( (long *)keyArray ); return !((keyArray[vKeyCode>>3] >> (vKeyCode & 7)) & 1); } void getMouse( int *x, int *y ) { Point mousePoint; DSpGetMouse( &mousePoint ); *x = mousePoint.h; *y = mousePoint.v; } void Initialize(void) { WindowPtr mainPtr; OSErr error; SysEnvRec theWorld; // // Test the computer to be sure we can do color. // If not we would crash, which would be bad. // If we can¹t run, just beep and exit. // error = SysEnvirons(1, &theWorld); if (theWorld.hasColorQD == false) { // SysBeep(50); ExitToShell(); // If no color QD, we must leave. } // Initialize all the needed managers. InitGraf(&qd.thePort); // InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs(nil); InitCursor(); // // To make the Random sequences truly random, we need to make the seed start // at a different number. An easy way to do this is to put the current time // and date into the seed. Since it is always incrementing the starting seed // will always be different. Don¹t for each call of Random, or the sequence // will no longer be random. Only needed once, here in the init. // GetDateTime((unsigned long*) &qd.randSeed); // // Make a new window for drawing in, and it must be a color window. // The window is full screen size, made smaller to make it more visible. // mainPtr = GetNewCWindow(rWindow, nil, (WindowPtr) -1); // MW Tutorial windRect = mainPtr->portRect; SetPort(mainPtr); // set window to current graf port TextSize(BobSize); // smaller font for drawing. } transcend-0.3+dfsg2.orig/minorGems/graphics/win32/0000750000175000017500000000000010305077063020427 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/win32/graphixCommonDefs.h0000640000175000017500000000013107205611501024205 0ustar pabspabs// ALifeGUICommonDefs.h // common definitions for ALifeGui #define WINDOWS_KEY_CODEStranscend-0.3+dfsg2.orig/minorGems/graphics/win32/graphixFramework.cpp0000640000175000017500000002651507205611501024461 0ustar pabspabs// 32 bit DDraw pluggable framework // Jason Rohrer, 4-28-99 /** * Mods: * Jason Rohrer 11-8-99 Changed to use GraphicBuffer object as screen buffer * Jason Rohrer 3-21-2000 Added support for mouse querying */ // based on the work of: /* * water ripples effect * -------------------- * anthony greene :: emit * april 1999 */ #define WIN32_LEAN_AND_MEAN // make sure certain headers are included correctly /* includes */ #include // include the standard windows stuff //#include // include the 32 bit stuff //#include // include the multi media stuff // need winmm.lib also #include // include direct draw components #include #include "swapBuffers.h" // interface for swapping buffers #include "getKey.h" // interface for handling keyboard input #include "getMouse.h" // interface for handling mouse input #include "GraphicBuffer.h" //#include "ALifeGuiRunner.h" #include "GoGUIRunner.h" /* defines */ #define WINDOW_CLASS_NAME "WINDOW_CLASS" // this is the name of the window class #define SCREEN_WIDTH 640 // the width of the viewing surface #define SCREEN_HEIGHT 480 // the height of the viewing surface #define SCREEN_BPP 32 // the bits per pixel #define MAX_COLORS 256 // the maximum number of colors /* types */ typedef unsigned char BYTE; typedef unsigned char UCHAR; typedef unsigned short WORD; /* macros */ // these query the keyboard in real-time #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) /* prototypes */ int DD_Init(HWND hwnd); int DD_Shutdown(void); void graphixPicker(void); // our mother function, picks effect order, etc. /* directdraw globals */ LPDIRECTDRAW lpdd = NULL; // dd object LPDIRECTDRAWSURFACE lpddsprimary = NULL; // dd primary surface LPDIRECTDRAWSURFACE lpddsback = NULL; // dd back surface LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette PALETTEENTRY color_palette[256]; // holds the shadow palette entries DDSURFACEDESC ddsd; // a direct draw surface description struct DDSCAPS ddscaps; // a direct draw surface capabilities struct HRESULT ddrval; // result back from dd calls HWND main_window_handle = NULL; // used to store the window handle unsigned long *double_buffer = NULL; // the double buffer unsigned long *screen_buffer = NULL; GraphicBuffer *graphicDoubleBuffer; /* globals */ int Height[2][640*480]; int old; int nu; int running = 0; int cycle; /* direct x functions */ int DD_Init(HWND hwnd) { // this function is responsible for initializing direct draw, // it creates a primary surface int index; // looping index // now that the windows portion is complete, start up direct draw if (DirectDrawCreate(NULL,&lpdd,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now set the coop level to exclusive and set for full screen and mode x if (lpdd->SetCooperativeLevel(hwnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now set the display mode if (lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // Create the primary surface memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if (lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // end if // allocate memory for the double buffer if ((double_buffer = (unsigned long *)malloc(SCREEN_WIDTH*SCREEN_HEIGHT*4))==NULL) return(0); // create GraphicBuffer object graphicDoubleBuffer = new GraphicBuffer( double_buffer, 640, 480 ); // create the palette and attach it to the primary surface // clear all the palette entries to RGB 0,0,0 memset(color_palette,0,256*sizeof(PALETTEENTRY)); // set all of the flags to the correct value int c=0; for (index=0; index<256; index++) { // create the GRAY/RED/GREEN/BLUE palette, 64 shades of each if (index < 64) { color_palette[index].peRed = index; color_palette[index].peGreen = 0; color_palette[index].peBlue = 0; } else if (index < 128) { color_palette[index].peRed = index; color_palette[index].peGreen = 0; color_palette[index].peBlue = 0; } else if (index < 192) { color_palette[index].peRed = index; color_palette[index].peGreen = c; color_palette[index].peBlue = c; c++; } else if (index < 256) { color_palette[index].peRed = index; color_palette[index].peGreen = c; color_palette[index].peBlue = c; c++; } // set the no collapse flag color_palette[index].peFlags = PC_NOCOLLAPSE; } // now create the palette object, note that it is a member of the dd object itself if (lpdd->CreatePalette((DDPCAPS_8BIT | DDPCAPS_INITIALIZE),color_palette,&lpddpal,NULL)!=DD_OK) { // shutdown any other dd objects and kill window DD_Shutdown(); return(0); } // now attach the palette to the primary surface lpddsprimary->SetPalette(lpddpal); // return success if we got this far return(1); } int DD_Shutdown(void) { // this function tests for dd components that have been created // and releases them back to the operating system // test if the dd object exists if (lpdd) { // test if there is a primary surface if(lpddsprimary) { // release the memory and set pointer to NULL lpddsprimary->Release(); lpddsprimary = NULL; } // now release the dd object itself lpdd->Release(); lpdd = NULL; // free double buffer if (double_buffer!=NULL) free(double_buffer); // return success return(1); } else return(0); } /* windows callback fucntion */ LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system HDC hdc; // handle to graphics context PAINTSTRUCT ps; // used to hold the paint info // what is the message.. switch(msg) { case WM_CREATE: { // do windows inits here return(0); } break; case WM_PAINT: { // this message occurs when your window needs repainting hdc = BeginPaint(hwnd,&ps); EndPaint((struct HWND__ *)hdc,&ps); return(0); } break; case WM_DESTROY: { // this message is sent when your window is destroyed PostQuitMessage(0); return(0); } break; case WM_MOUSEMOVE: { // extract x,y /* int mouse_x = (int)LOWORD(lparam); int mouse_y = (int)HIWORD(lparam); Height[old][mouse_y*640+mouse_x] = 300; */ return(0); } break; default:break; } // let windows process any messages that we didn't take care of return (DefWindowProc(hwnd, msg, wparam, lparam)); } int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX winclass; // this holds the windows class info HWND hwnd; // this holds the handle of our new window MSG msg; // this holds a generic message // first fill in the window class stucture winclass.cbSize = sizeof(WNDCLASSEX); winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WindowProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (struct HBRUSH__ *)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = WINDOW_CLASS_NAME; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(WS_EX_TOPMOST, WINDOW_CLASS_NAME, // class "water", // title WS_VISIBLE | WS_POPUP, 0,0, // x,y GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, // parent NULL, // menu hinstance, // instance NULL))) // creation parms return(0); // hide the mouse cursor ShowCursor(0); // save the window handle main_window_handle = hwnd; // initialize direct draw if (!DD_Init(hwnd)) { DestroyWindow(hwnd); return(0); } // peek at the message once just to make them happy PeekMessage(&msg,NULL,0,0,PM_REMOVE); // do it once, then leave graphixPicker(); // picks which effects to run. // afterwards, end!!! // shut down direct draw DD_Shutdown(); // return to Windows return(msg.wParam); } void graphixPicker() { // our "mother function" // selects which effect to run next // graphix plug-in format: // myGraphix( bufferPtr, bufferHigh, bufferWide, colorBits, numFrames) // function can cll "swapBuffer32" internally whenever it needs the back buffer // sent to the screen. // jcrTorus( double_buffer, 480, 640, 32, 100); // jcrVoxels( double_buffer, 480, 640, 32, 230); // jcrTorus( double_buffer, 480, 640, 32, 50); // jcrBloom( double_buffer, 480, 640, 32, 100); //ALifeGuiRunner( *graphicDoubleBuffer, 200 ); GoGUIRunner( *graphicDoubleBuffer, 0 ); } char getKeyDown( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? true : false); } char getKeyUp( int vKeyCode ) { return ((GetAsyncKeyState(vKeyCode) & 0x8000) ? false : true); } void getMouse( int *x, int *y ) { POINT p; GetCursorPos( &p ); *x = p.x; *y = p.y; } /// CODE FOR SWAPPING BUFFERES BELOW THIS POINT....... // swap bufferB to the screen (32 bit version) void swapBuffers32( GraphicBuffer &bufferB ) { unsigned long *primary_buffer = NULL, // used to draw *dest_ptr = NULL, // used in line by line copy *src_ptr = NULL; // " " short bufferHigh = 480; short bufferWide = 640; // copy the double buffer into the primary buffer memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); // lock the primary surface lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // get video pointer to primary surfce primary_buffer = (unsigned long *)ddsd.lpSurface; // test if memory is linear if (ddsd.lPitch == 640*4) { // copy memory from double buffer to primary buffer memcpy(primary_buffer, double_buffer, 640*480*4); } else { // non-linear // make copy of source and destination addresses dest_ptr = primary_buffer; src_ptr = double_buffer; // memory is non-linear, copy line by line for (int y=0; yUnlock(primary_buffer); } // end of swapBuffers transcend-0.3+dfsg2.orig/minorGems/graphics/loadfile.h0000640000175000017500000000025307205611501021412 0ustar pabspabsvoid loadFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr); void loadRawFile( const char* fileName, long sizeInBytes, unsigned char* byteDestPtr);transcend-0.3+dfsg2.orig/minorGems/graphics/test/0000750000175000017500000000000010305077063020444 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/graphics/test/tgaConverter.cpp0000640000175000017500000001002007353716210023610 0ustar pabspabs/* * Modification History * * 2001-September-24 Jason Rohrer * Created. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/TGAImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include #include void usage( char *inAppName ); Image *loadTGAImage( char *inFileName ); void writeTGAImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts a tga image between // parameterizable color formats int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 5 ) { usage( inArgs[0] ); } Image *inImage = loadTGAImage( inArgs[1] ); Image *outImage; // convert inImage to outImage based on the color space strings if( !strcmp( inArgs[3], "rgb" ) ) { if( !strcmp( inArgs[4], "yiq" ) ) { outImage = ImageColorConverter::RGBToYIQ( inImage ); } if( !strcmp( inArgs[4], "ycbcr" ) ) { outImage = ImageColorConverter::RGBToYCbCr( inImage ); } } if( !strcmp( inArgs[3], "yiq" ) ) { if( !strcmp( inArgs[4], "rgb" ) ) { outImage = ImageColorConverter::YIQToRGB( inImage ); } if( !strcmp( inArgs[4], "ycbcr" ) ) { Image *tempImage = ImageColorConverter::YIQToRGB( inImage ); outImage = ImageColorConverter::RGBToYCbCr( tempImage ); delete tempImage; } } if( !strcmp( inArgs[3], "ycbcr" ) ) { if( !strcmp( inArgs[4], "rgb" ) ) { outImage = ImageColorConverter::YCbCrToRGB( inImage ); } if( !strcmp( inArgs[4], "yiq" ) ) { Image *tempImage = ImageColorConverter::YCbCrToRGB( inImage ); outImage = ImageColorConverter::RGBToYIQ( tempImage ); delete tempImage; } } writeTGAImage( inArgs[2], outImage ); delete inImage; delete outImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s in_image_tga out_image_tga in_format out_format\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testRGB.tga testYIQ.bmp rgb yiq\n", inAppName ); printf( "The following color formats are supported:\n" ); printf( "\trgb, yiq, ycbcr\n" ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeTGAImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); TGAImageConverter *converter = new TGAImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } transcend-0.3+dfsg2.orig/minorGems/graphics/test/yiq2rgb.cpp0000640000175000017500000000622207353474230022540 0ustar pabspabs/* * Modification History * * 2001-September-20 Jason Rohrer * Created. * Changed so that images are normalized to [0,1] before * being output. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include void usage( char *inAppName ); Image *loadBMPImage( char *inFileName ); void writeBMPImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts an rgb BMP to a yiq BMP int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 3 ) { usage( inArgs[0] ); } Image *yiqImage = loadBMPImage( inArgs[1] ); if( yiqImage == NULL ) { printf( "Reading image from file %s failed\n", inArgs[1] ); usage( inArgs[0] ); } Image *rgbImage = ImageColorConverter::YIQToRGB( yiqImage ); normalizeImage( rgbImage ); writeBMPImage( inArgs[2], rgbImage ); delete rgbImage; delete yiqImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s yiq_image_bmp rgb_image_bmp\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testYIQ.bmp testRGB.bmp\n", inAppName ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeBMPImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } transcend-0.3+dfsg2.orig/minorGems/graphics/test/rgb2yiq.cpp0000640000175000017500000000615707353474230022547 0ustar pabspabs/* * Modification History * * 2001-September-19 Jason Rohrer * Created. * * 2001-September-20 Jason Rohrer * Finished initial implementation. * Changed so that images are normalized to [0,1] before * being output. */ #include "minorGems/graphics/Image.h" #include "minorGems/graphics/ImageColorConverter.h" #include "minorGems/graphics/converters/BMPImageConverter.h" #include "minorGems/io/file/File.h" #include "minorGems/io/file/FileInputStream.h" #include "minorGems/io/file/FileOutputStream.h" #include #include void usage( char *inAppName ); Image *loadBMPImage( char *inFileName ); void writeBMPImage( char *inFileName, Image *inImage ); // normalizes an image into the pixel range [0,1] if some // pixels are out of range void normalizeImage( Image *inImage ); // a test program that converts an rgb BMP to a yiq BMP int main( char inNumArgs, char **inArgs ) { if( inNumArgs != 3 ) { usage( inArgs[0] ); } Image *rgbImage = loadBMPImage( inArgs[1] ); Image *yiqImage = ImageColorConverter::RGBToYIQ( rgbImage ); normalizeImage( yiqImage ); writeBMPImage( inArgs[2], yiqImage ); delete rgbImage; delete yiqImage; return 0; } void usage( char *inAppName ) { printf( "Usage:\n" ); printf( "\t%s rgb_image_bmp yiq_image_bmp\n", inAppName ); printf( "Example:\n" ); printf( "\t%s testRGB.bmp testYIQ.bmp\n", inAppName ); exit( 1 ); } void normalizeImage( Image *inImage ) { // now normalize the image so that pixels are in the range [0,1] int numPixels = inImage->getWidth() * inImage->getHeight(); int numChannels = inImage->getNumChannels(); double minValue = 1000.0; double maxValue = -1000.0; int c; // search for min and max values for( c=0; cgetChannel( c ); for( int p=0; p maxValue ) { maxValue = channel[p]; } } } double diff = maxValue - minValue; if( minValue >= 0 && diff + minValue <= 1 ) { // no need to normalize, as values are already // in range return; } double scale = 1.0 / diff; // now normalize all pixels for( c=0; cgetChannel( c ); for( int p=0; pdeformatImage( imageStream ); delete imageFile; delete imageStream; delete converter; return returnImage; } void writeBMPImage( char *inFileName, Image *inImage ) { File *imageFile = new File( NULL, inFileName, strlen( inFileName ) ); FileOutputStream *imageStream = new FileOutputStream( imageFile ); BMPImageConverter *converter = new BMPImageConverter(); converter->formatImage( inImage, imageStream ); delete imageFile; delete imageStream; delete converter; } transcend-0.3+dfsg2.orig/minorGems/graphics/test/tgaConverterCompile0000750000175000017500000000014107353716210024345 0ustar pabspabsg++ -g -o tgaConverter -I../../.. tgaConverter.cpp ../../../minorGems/io/file/linux/PathLinux.cpptranscend-0.3+dfsg2.orig/minorGems/graphics/test/rgb2yiqCompile0000750000175000017500000000012407352467255023275 0ustar pabspabsg++ -o rgb2yiq -I../../.. rgb2yiq.cpp ../../../minorGems/io/file/linux/PathLinux.cpptranscend-0.3+dfsg2.orig/minorGems/graphics/test/yiq2rgbCompile0000750000175000017500000000012707352505010023256 0ustar pabspabsg++ -g -o yiq2rgb -I../../.. yiq2rgb.cpp ../../../minorGems/io/file/linux/PathLinux.cpptranscend-0.3+dfsg2.orig/minorGems/numtest.cpp0000640000175000017500000000135507237350067020105 0ustar pabspabs/* * Modification History * * 2001-February-3 Jason Rohrer * Created. * * 2001-February-4 Jason Rohrer * Fixed a bug that made this test not compatible with TypeIO. * Fixed comment. */ #include // function for testing how doubles are stored /* * The output from a big-endian linux is as follows: * * size of double = 8 * byte 0 = 63 * byte 1 = 255 * byte 2 = 189 * byte 3 = 137 * byte 4 = 176 * byte 5 = 126 * byte 6 = 158 * byte 7 = 168 * */ int main() { printf( "size of double = %d\n", sizeof( double ) ); double x = 1.983773889; unsigned char *doubleBuffer = (unsigned char*)( &x ); for( int i=0; i<8; i++ ) { printf( "byte %d = %d\n", i, doubleBuffer[i] ); } return 0; } transcend-0.3+dfsg2.orig/minorGems/system/0000750000175000017500000000000010305077065017213 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/system/semaphoreTest.cpp0000640000175000017500000000366407234645702022562 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Made printing in threads thread-safe. */ #include "BinarySemaphore.h" #include "Semaphore.h" #include "Thread.h" #include "ThreadSafePrinter.h" #include /** * Thread that waits on a semaphore. * * @author Jason Rohrer */ class WaitingThread : public Thread { public: WaitingThread( int inID, Semaphore *inSemaphore ); // override the run method from PThread void run(); private: Semaphore *mSemaphore; int mID; }; inline WaitingThread::WaitingThread( int inID, Semaphore *inSemaphore ) : mID( inID ), mSemaphore( inSemaphore ) { } inline void WaitingThread::run() { for( int i=0; i<10; i++ ) { ThreadSafePrinter::printf( "%d waiting for signal %d...\n", mID, i ); mSemaphore->wait(); ThreadSafePrinter::printf( "%d received signal %d.\n", mID, i ); } } /** * Thread that signals on a semaphore. * * @author Jason Rohrer */ class SignalingThread : public Thread { public: SignalingThread( Semaphore *inSemaphore ); // override the run method from PThread void run(); private: Semaphore *mSemaphore; }; inline SignalingThread::SignalingThread( Semaphore *inSemaphore ) : mSemaphore( inSemaphore ) { } inline void SignalingThread::run() { for( int i=0; i<5; i++ ) { sleep( 5000 ); ThreadSafePrinter::printf( "Signaling 20 times\n" ); for( int j=0; j<20; j++ ) { mSemaphore->signal(); } } } int main() { int i; Semaphore *semph = new Semaphore(); SignalingThread *threadS = new SignalingThread( semph ); WaitingThread **threadW = new WaitingThread*[10]; for( i=0; i<10; i++ ) { threadW[i] = new WaitingThread( i, semph ); threadW[i]->start(); } threadS->start(); for( i=0; i<10; i++ ) { threadW[i]->join(); delete threadW[i]; } threadS->join(); delete semph; delete threadS; delete [] threadW; return 0; } transcend-0.3+dfsg2.orig/minorGems/system/unix/0000750000175000017500000000000010305077065020176 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/system/unix/LauncherUnix.cpp0000640000175000017500000000077307607633527023333 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. */ #include "minorGems/system/Launcher.h" #include #include void Launcher::launchCommand( char *inCommandName, char **inArguments ) { int forkValue = fork(); if( forkValue == 0 ) { // we're in child process, so exec command execvp( inCommandName, inArguments ); // we'll never return from this call } } transcend-0.3+dfsg2.orig/minorGems/system/unix/TimeUnix.cpp0000640000175000017500000000123407447774050022460 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2002-March-13 Jason Rohrer * Added include of time.h so that FreeBSD compile will work. * Changed to use newer gettimeofday that should work on all unix platforms. * Fixed a conversion bug. */ #include "minorGems/system/Time.h" #include #include #include #include void Time::getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ) { struct timeval currentTime; gettimeofday( ¤tTime, NULL ); *outMilliseconds = currentTime.tv_usec / 1000; *outSeconds = currentTime.tv_sec; } transcend-0.3+dfsg2.orig/minorGems/system/endian.h0000640000175000017500000000276110000551321020611 0ustar pabspabs/* * Modification History * * 2002-May-25 Jason Rohrer * Created. * * 2004-January-12 Jason Rohrer * Added support for metrowerks win32 compiler. */ #include "minorGems/common.h" /** * Include this file to define __BYTE_ORDER * * After this has been included, __BYTE_ORDER will be either * __LITTLE_ENDIAN or * --BIG_ENDIAN */ #ifdef __FreeBSD__ #include #elif defined(__NetBSD__) #include // default BSD case #elif defined(BSD) #include #elif defined(SOLARIS) // Code for Solaris defs adapted from: // MD5 message-digest algorithm. // by Colin Plumb in 1993, no copyright is claimed. //each solaris is different -- this won't work on 2.6 or 2.7 # include #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #ifdef _LITTLE_ENDIAN #define __BYTE_ORDER __LITTLE_ENDIAN #else // default to big endian #define __BYTE_ORDER __BIG_ENDIAN #endif // end solaris case #elif defined(WIN_32) || \ ( defined(__MWERKS__) && defined(__INTEL__) ) // windows case #define __LITTLE_ENDIAN 1234 #define __BYTE_ORDER __LITTLE_ENDIAN // end windows case #else // linux case #include // end linux case #endif // end of all system-specific cases // BSD calls it BYTE_ORDER, linux calls it __BYTE_ORDER #ifndef __BYTE_ORDER #define __BYTE_ORDER BYTE_ORDER #endif #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN LITTLE_ENDIAN #endif #ifndef __BIG_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif transcend-0.3+dfsg2.orig/minorGems/system/BinarySemaphore.h0000640000175000017500000000331307722702203022453 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Fixed a bug in the precompiler directives. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. */ #include "minorGems/common.h" #ifndef BINARY_SEMAPHORE_CLASS_INCLUDED #define BINARY_SEMAPHORE_CLASS_INCLUDED #include "MutexLock.h" /** * Binary semaphore class. Semaphore starts out with a value of 0. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class BinarySemaphore { public: /** * Constructs a binary semaphore. */ BinarySemaphore(); ~BinarySemaphore(); /** * Blocks on this semaphore until signal() is called by another thread. * Note that if signal() has already been called before wait() is * called, then this call will return immediately, though the semaphore * is reset to 0 by this call. * * @param inTimeoutInMilliseconds the maximum time to wait in * milliseconds, or -1 to wait forever. Defaults to -1. * * @return 1 if the semaphore was signaled, or 0 if it timed out. */ int wait( int inTimeoutInMilliseconds = -1 ); /** * Signals the semaphore, allowing a waiting thread to return from * its call to wait(). (The semaphore is set to 1 by this call if * no thread is waiting on the semaphore currently.) */ void signal(); private: // starts at 0 int mSemaphoreValue; /** * Used by platform-specific implementations. */ void *mNativeObjectPointerA; void *mNativeObjectPointerB; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/Launcher.h0000640000175000017500000000163007607633465021143 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. */ #include "minorGems/common.h" #ifndef LAUNCHER_INCLUDED #define LAUNCHER_INCLUDED /** * Interface for launching processes. * * @author Jason Rohrer */ class Launcher { public: /** * Launches a command in a new process. * * @param inCommandName the name of the command to launch. * Must be destroyed by caller if non-const. * @param inArguments an array of argument strings for the command. * This array must be terminated by a NULL pointer. * Note that by convention, the first argument should be the * command name. * Must be destroyed by caller. */ static void launchCommand( char *inCommandName, char **inArguments ); }; #endif transcend-0.3+dfsg2.orig/minorGems/system/Time.h0000640000175000017500000000444310202731646020266 0ustar pabspabs/* * Modification History * * 2001-October-29 Jason Rohrer * Created. * * 2004-October-14 Jason Rohrer * Fixed sign bug. * * 2005-February-10 Jason Rohrer * Added function to get time in floating point format. */ #include "minorGems/common.h" #ifndef TIME_INCLUDED #define TIME_INCLUDED /** * Interface for platform-independent, high-resolution time access. * * @author Jason Rohrer */ class Time { public: /** * Gets the current time in seconds and milliseconds. * * No guarentee about when absolute 0 of this time * scale is for particular systems. * * @param outSeconds pointer to where the time in seconds * will be returned. * @param outMilliseconds pointer to where the extra * milliseconds will be returned. Value returned is in [0,999]. */ static void getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ); /** * Gets the current time in fractional (double) seconds. * * @return the current time in seconds. */ static double getCurrentTime(); /** * Gets the number of milliseconds that have passed * since a time in seconds and milliseconds. * * @param inSeconds the start time, in seconds. * @param inMilliseconds the start time's additional milliseconds. * * @return the number of milliseconds that have passed * since inSeconds:inMilliseconds. May overflow if * more than 49 days have passed (assuming 32-bit longs). */ static unsigned long getMillisecondsSince( unsigned long inSeconds, unsigned long inMilliseconds ); }; inline double Time::getCurrentTime() { unsigned long currentTimeS; unsigned long currentTimeMS; getCurrentTime( ¤tTimeS, ¤tTimeMS ); return currentTimeS + currentTimeMS / 1000.0; } inline unsigned long Time::getMillisecondsSince( unsigned long inSeconds, unsigned long inMilliseconds ) { unsigned long currentTimeS; unsigned long currentTimeMS; getCurrentTime( ¤tTimeS, ¤tTimeMS ); unsigned long deltaS = ( currentTimeS - inSeconds ); long deltaMS = ( (long)currentTimeMS - (long)inMilliseconds ); // carry, if needed if( deltaMS < 0 ) { deltaS--; deltaMS += 1000; } return 1000 * deltaS + deltaMS; } #endif transcend-0.3+dfsg2.orig/minorGems/system/Semaphore.h0000640000175000017500000001023307777611355021326 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2001-January-11 Jason Rohrer * Added a willBlock() function. * * 2001-February-24 Jason Rohrer * Fixed incorrect delete usage. * * 2002-February-11 Jason Rohrer * Fixed a mistake in the signal() comment. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. * * 2003-December-28 Jason Rohrer * Fixed a bug in semaphore value when we timeout on wait. * * 2004-January-9 Jason Rohrer * Fixed a preprocessor error. */ #include "minorGems/common.h" #ifndef SEMAPHORE_CLASS_INCLUDED #define SEMAPHORE_CLASS_INCLUDED #include "MutexLock.h" #include "BinarySemaphore.h" /** * General semaphore with an unbounded value. * * This class uses BinarySemaphores to implement general semaphores, * so it relies on platform-specific BinarySemaphore implementations, * but this class itself is platform-independent. * * @author Jason Rohrer */ class Semaphore { public: /** * Constructs a semaphore. * * @param inStartingValue the starting value for this semaphore. * Defaults to 0 if unspecified. */ Semaphore( int inStartingValue = 0 ); ~Semaphore(); /** * If this semaphore's current value is 0, then this call blocks * on this semaphore until signal() is called by another thread. * If this semaphore's value is >0, then it is decremented by this * call. * * @param inTimeoutInMilliseconds the maximum time to wait in * milliseconds, or -1 to wait forever. Defaults to -1. * * @return 1 if the semaphore was signaled, or 0 if it timed out. */ int wait( int inTimeoutInMilliseconds = -1 ); /** * If a thread is waiting on this semaphore, then the thread * becomes unblocked. * If no thread is waiting, then the semaphore is incremented. */ void signal(); /** * Returns true if a call to wait would have blocked. */ char willBlock(); private: // starts at 0 int mSemaphoreValue; // mutex semaphore starts at 1 BinarySemaphore *mMutexSemaphore; // blocking semaphore starts at 0 BinarySemaphore *mBlockingSemaphore; }; inline Semaphore::Semaphore( int inStartingValue ) : mSemaphoreValue( inStartingValue ), mMutexSemaphore( new BinarySemaphore() ), mBlockingSemaphore( new BinarySemaphore() ) { // increment the mutex semaphore to 1 mMutexSemaphore->signal(); } inline Semaphore::~Semaphore() { delete mMutexSemaphore; delete mBlockingSemaphore; } inline int Semaphore::wait( int inTimeoutInMilliseconds ) { int returnValue; // this implementation copied from _Operating System Concepts_, p. 172 // lock the mutex mMutexSemaphore->wait(); // decrement the semaphore mSemaphoreValue--; if( mSemaphoreValue < 0 ) { // we should block // release the mutex mMutexSemaphore->signal(); // block returnValue = mBlockingSemaphore->wait( inTimeoutInMilliseconds ); if( returnValue != 1 ) { // timed out // increment the semaphore, since we never got signaled // lock the mutex mMutexSemaphore->wait(); mSemaphoreValue++; // we will unlock the mutex below } } else { returnValue = 1; } // release the mutex // ( if we were signaled, then the signaller left the mutex locked ) // ( if we timed out, then we re-locked the mutex above ) mMutexSemaphore->signal(); return returnValue; } inline char Semaphore::willBlock() { char returnValue = false; // lock the mutex mMutexSemaphore->wait(); // check if we will block if( mSemaphoreValue <= 0 ) { returnValue = true; } // release the mutex mMutexSemaphore->signal(); return returnValue; } inline void Semaphore::signal() { // lock the mutex mMutexSemaphore->wait(); // increment the semaphore mSemaphoreValue++; if( mSemaphoreValue <= 0 ) { // we need to wake up a waiting thread mBlockingSemaphore->signal(); // let the waiting thread unlock the mutex } else { // no threads are waiting, so we need to unlock the mutex mMutexSemaphore->signal(); } } #endif transcend-0.3+dfsg2.orig/minorGems/system/MutexLock.h0000640000175000017500000000207307554113530021302 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. */ #ifndef MUTEX_LOCK_CLASS_INCLUDED #define MUTEX_LOCK_CLASS_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Mutex lock class. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class MutexLock { public: /** * Constructs a mutex lock; */ MutexLock(); ~MutexLock(); /** * Locks the mutex. Blocks until mutex available if it's * already locked by another thread. */ void lock(); /** * Unlocks the mutex. */ void unlock(); private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/FinishedSignalThread.cpp0000640000175000017500000000203410033036772023734 0ustar pabspabs/* * Modification History * * 2002-March-9 Jason Rohrer * Created. * * 2002-March-11 Jason Rohrer * Changed so that destructor joins thread. * * 2002-April-4 Jason Rohrer * Changed name of lock to avoid confusion with subclass-provided locks. * * 2002-August-5 Jason Rohrer * Fixed member initialization order to match declaration order. * * 2004-April-1 Jason Rohrer * Moved from konspire2b into minorGems. * Changed so that destructor does not join the thread. */ #include "FinishedSignalThread.h" #include FinishedSignalThread::FinishedSignalThread() : mFinishedLock( new MutexLock() ), mFinished( false ) { } FinishedSignalThread::~FinishedSignalThread() { delete mFinishedLock; } char FinishedSignalThread::isFinished() { mFinishedLock->lock(); char finished = mFinished; mFinishedLock->unlock(); return finished; } void FinishedSignalThread::setFinished() { mFinishedLock->lock(); mFinished = true; mFinishedLock->unlock(); } transcend-0.3+dfsg2.orig/minorGems/system/FinishedSignalThreadManager.cpp0000640000175000017500000000461510170260026025227 0ustar pabspabs/* * Modification History * * 2004-November-9 Jason Rohrer * Created. * Modified from MUTE's ChannelReceivingThreadManager. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to allow sleep to be interrupted. */ #include "minorGems/system/FinishedSignalThreadManager.h" FinishedSignalThreadManager::FinishedSignalThreadManager() : mLock( new MutexLock() ), mThreadVector( new SimpleVector() ), mStopSignal( false ), mSleepSemaphore( new BinarySemaphore() ) { this->start(); } FinishedSignalThreadManager::~FinishedSignalThreadManager() { mLock->lock(); mStopSignal = true; mLock->unlock(); // signal the sleeping semaphore to wake up the thread mSleepSemaphore->signal(); this->join(); mLock->lock(); // destroy all remaining threads int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); } delete mThreadVector; mLock->unlock(); delete mLock; delete mSleepSemaphore; } void FinishedSignalThreadManager::addThread( FinishedSignalThread *inThread ) { mLock->lock(); mThreadVector->push_back( inThread ); mLock->unlock(); } void FinishedSignalThreadManager::run() { char stopped; mLock->lock(); stopped = mStopSignal; mLock->unlock(); while( !stopped ) { // wait for 10 seconds int wasSignaled = mSleepSemaphore->wait( 10000 ); if( wasSignaled == 1 ) { // signaled... we should stop return; } char foundFinished = true; while( foundFinished ) { foundFinished = false; mLock->lock(); int numThreads = mThreadVector->size(); for( int i=0; igetElement( i ) ); if( currentThread->isFinished() ) { delete currentThread; mThreadVector->deleteElement( i ); foundFinished = true; } } mLock->unlock(); } mLock->lock(); stopped = mStopSignal; mLock->unlock(); } } transcend-0.3+dfsg2.orig/minorGems/system/TestThread.h0000640000175000017500000000252107565075331021444 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Switched to a ThreadSafePrinter in attempt to get it to work on Win32. * Changed print call to printf. * * 2002-November-14 Jason Rohrer * Added missing destructor. */ #include "minorGems/common.h" #ifndef TEST_THREAD_CLASS_INCLUDED #define TEST_THREAD_CLASS_INCLUDED #include "Thread.h" #include "ThreadSafePrinter.h" #include /** * Test subclass of Thread class. Useful for testing if platform-specific * thread implmentations are working. * * @author Jason Rohrer */ class TestThread : public Thread { public: /** * Constructs a test thread and tells it how high to count to. * * @param inID id number thread will print along with count. * @param inNumToCount thread will count from 0 to this number. */ TestThread( int inID, int inNumToCount ); ~TestThread(); // override the run method from PThread void run(); private: int mID; int mNumToCount; }; inline TestThread::TestThread( int inID, int inNumToCount ) : mID( inID ), mNumToCount( inNumToCount ) { } inline TestThread::~TestThread() { } inline void TestThread::run() { for( int i=0; i<=mNumToCount; i++ ) { ThreadSafePrinter::printf( "Thread %d counting %d.\n", mID, i ); } } #endif transcend-0.3+dfsg2.orig/minorGems/system/StopSignalThread.cpp0000640000175000017500000000231510170267060023130 0ustar pabspabs/* * Modification History * * 2002-April-4 Jason Rohrer * Created. * Changed to reflect the fact that the base class * destructor is called *after* the derived class destructor. * * 2002-August-5 Jason Rohrer * Fixed member initialization order to match declaration order. * * 2003-September-5 Jason Rohrer * Moved into minorGems. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to make sleep interruptable by stop. */ #include "StopSignalThread.h" StopSignalThread::StopSignalThread() : mStopLock( new MutexLock() ), mStopped( false ), mSleepSemaphore( new BinarySemaphore() ) { } StopSignalThread::~StopSignalThread() { delete mStopLock; delete mSleepSemaphore; } void StopSignalThread::sleep( unsigned long inTimeInMilliseconds ) { mSleepSemaphore->wait( inTimeInMilliseconds ); } char StopSignalThread::isStopped() { mStopLock->lock(); char stoped = mStopped; mStopLock->unlock(); return stoped; } void StopSignalThread::stop() { mStopLock->lock(); mStopped = true; mStopLock->unlock(); // signal the semaphore to wake up the thread, if it is sleeping mSleepSemaphore->signal(); } transcend-0.3+dfsg2.orig/minorGems/system/linux/0000750000175000017500000000000010305077065020352 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/system/linux/ThreadLinux.cpp0000640000175000017500000001335410174541132023307 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-January-11 Jason Rohrer * Added missing sleep() implementation. * * 2002-March-27 Jason Rohrer * Added support for gprof-friendly thread wrappers. * Fixed a compile bug when gprof threads disabled. * * 2002-August-5 Jason Rohrer * Removed an unused variable. * * 2003-February-3 Jason Rohrer * Fixed sleep to be thread safe (signals were interrupting thread sleeps). * * 2004-March-31 Jason Rohrer * Added support for detatched mode. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include #include #include #include #include /** * Linux-specific implementation of the Thread class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread * If thread profiling is desired for gprof on linux, compile * with -DUSE_GPROF_THREADS (otherwise, only main thread is profiled). */ #ifdef USE_GPROF_THREADS // prototype int gprof_pthread_create( pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg ); #endif // prototype /** * A wrapper for the run method, since pthreads won't take * a class's member function. Takes a pointer to the Thread to run, * cast as a void*; */ void *linuxThreadFunction( void * ); Thread::Thread() { // allocate a pthread structure on the heap mNativeObjectPointer = (void *)( new pthread_t[1] ); } Thread::~Thread() { // de-allocate the pthread structure from the heap pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; delete [] threadPointer; } void Thread::start( char inDetach ) { mIsDetached = inDetach; // get a pointer to the pthread pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; // create the pthread, which also sets it running #ifdef USE_GPROF_THREADS gprof_pthread_create( &( threadPointer[0] ), NULL, linuxThreadFunction, (void*)this ); #else pthread_create( &( threadPointer[0] ), NULL, linuxThreadFunction, (void*)this ); #endif if( mIsDetached ) { pthread_detach( threadPointer[0] ); } } void Thread::join() { void *joinStat; pthread_t *threadPointer = (pthread_t *)mNativeObjectPointer; pthread_join( threadPointer[0], &joinStat ); } void Thread::staticSleep( unsigned long inTimeInMilliseconds ) { unsigned long seconds = inTimeInMilliseconds / 1000; unsigned long milliseconds = inTimeInMilliseconds % 1000; struct timespec remainingSleepTimeStruct; remainingSleepTimeStruct.tv_sec = seconds; remainingSleepTimeStruct.tv_nsec = milliseconds * 1000000; struct timespec timeToSleepStruct; // sleep repeatedly, ignoring signals, untill we use up all of the time int sleepReturn = -1; while( sleepReturn == -1 ) { timeToSleepStruct.tv_sec = remainingSleepTimeStruct.tv_sec; timeToSleepStruct.tv_nsec = remainingSleepTimeStruct.tv_nsec; sleepReturn = nanosleep( &timeToSleepStruct, &remainingSleepTimeStruct ); } } // takes a pointer to a Thread object as the data value void *linuxThreadFunction( void *inPtrToThread ) { Thread *threadToRun = (Thread *)inPtrToThread; threadToRun->run(); if( threadToRun->isDetatched() ) { // thread detached, so we must destroy it delete threadToRun; } return inPtrToThread; } #ifdef USE_GPROF_THREADS // found at http://sam.zoy.org/doc/programming/gprof.html #include /* * pthread_create wrapper for gprof compatibility * * needed headers: * */ typedef struct wrapper_s { void * (*start_routine)(void *); void * arg; pthread_mutex_t lock; pthread_cond_t wait; struct itimerval itimer; } wrapper_t; static void * wrapper_routine(void *); /** * Same prototype as pthread_create; use some #define magic to * transparently replace it in other files */ int gprof_pthread_create( pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg ) { wrapper_t wrapper_data; int i_return; /* Initialize the wrapper structure */ wrapper_data.start_routine = start_routine; wrapper_data.arg = arg; getitimer(ITIMER_PROF, &wrapper_data.itimer); pthread_cond_init(&wrapper_data.wait, NULL); pthread_mutex_init(&wrapper_data.lock, NULL); pthread_mutex_lock(&wrapper_data.lock); /* The real pthread_create call */ i_return = pthread_create(thread, attr, &wrapper_routine, &wrapper_data); /* If the thread was successfully spawned, wait for the data * to be released */ if( i_return == 0 ) { pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock); } pthread_mutex_unlock(&wrapper_data.lock); pthread_mutex_destroy(&wrapper_data.lock); pthread_cond_destroy(&wrapper_data.wait); return i_return; } /** * The wrapper function in charge for setting the itimer value */ static void * wrapper_routine( void * data ) { /* Put user data in thread-local variables */ void * (*start_routine)(void *) = ((wrapper_t*)data)->start_routine; void * arg = ((wrapper_t*)data)->arg; /* Set the profile timer value */ setitimer(ITIMER_PROF, &((wrapper_t*)data)->itimer, NULL); /* Tell the calling thread that we don't need its data anymore */ pthread_mutex_lock(&((wrapper_t*)data)->lock); pthread_cond_signal(&((wrapper_t*)data)->wait); pthread_mutex_unlock(&((wrapper_t*)data)->lock); /* Call the real function */ return start_routine(arg); } #endif transcend-0.3+dfsg2.orig/minorGems/system/linux/MutexLockLinux.cpp0000640000175000017500000000311307554122211024004 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. * Changed to use malloc instead of new internally to work with debugMemory. * Made use of mNativeObjectPointer a bit cleaner. */ #include "minorGems/common.h" #include #include #include /** * Linux-specific implementation of the MutexLock class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread */ MutexLock::MutexLock() { // allocate a mutex structure on the heap mNativeObjectPointer = malloc( sizeof( pthread_mutex_t ) ); // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; // init the mutex pthread_mutex_init( mutexPointer, NULL ); } MutexLock::~MutexLock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; // destroy the mutex pthread_mutex_destroy( mutexPointer ); // de-allocate the mutex structure from the heap free( mNativeObjectPointer ); } void MutexLock::lock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; pthread_mutex_lock( mutexPointer ); } void MutexLock::unlock() { // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointer; pthread_mutex_unlock( mutexPointer ); } transcend-0.3+dfsg2.orig/minorGems/system/linux/BinarySemaphoreLinux.cpp0000640000175000017500000000742607722702212025176 0ustar pabspabs/* * Modification History * * 2001-January-11 Jason Rohrer * Created. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. */ #include #include /** * Linux-specific implementation of the BinarySemaphore class member functions. * * May also be compatible with other POSIX-like systems. * * To compile: * g++ -lpthread */ /** * Native object pointer A is the condition variable. * Pointer B is the mutex that must go along with it. */ BinarySemaphore::BinarySemaphore() : mSemaphoreValue( 0 ) { // allocate a condition variable structure on the heap mNativeObjectPointerA = (void *)( new pthread_cond_t[1] ); // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // init the cond pthread_cond_init( &( condPointer[0] ), NULL ); // allocate a mutex structure on the heap mNativeObjectPointerB = (void *)( new pthread_mutex_t[1] ); // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // init the mutex pthread_mutex_init( &( mutexPointer[0] ), NULL ); } BinarySemaphore::~BinarySemaphore() { // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // destroy the cond pthread_cond_destroy( &( condPointer[0] ) ); // de-allocate the cond structure from the heap delete [] condPointer; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // destroy the mutex pthread_mutex_destroy( &( mutexPointer[0] ) ); // de-allocate the mutex structure from the heap delete [] mutexPointer; } int BinarySemaphore::wait( int inTimeoutInMilliseconds ) { int returnValue = 1; // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // lock the mutex pthread_mutex_lock( &( mutexPointer[0] ) ); if( mSemaphoreValue == 0 ) { // wait on condition variable, which automatically unlocks // the passed-in mutex if( inTimeoutInMilliseconds == -1 ) { // no timeout pthread_cond_wait( &( condPointer[0] ), &( mutexPointer[0] ) ); } else { // use timeout version long currentSec = time( NULL ); long timeoutSec = inTimeoutInMilliseconds / 1000; long extraMS = inTimeoutInMilliseconds % 1000; long extraNS = extraMS * 1000000; struct timespec abstime; abstime.tv_sec = currentSec + timeoutSec; abstime.tv_nsec = extraNS; int result = pthread_cond_timedwait( &( condPointer[0] ), &( mutexPointer[0] ), &abstime ); if( result != 0 ) { // timed out returnValue = 0; } } // mutex is apparently re-locked when we return from cond_wait } // decrement the semaphore value mSemaphoreValue = 0; // unlock the mutex again pthread_mutex_unlock( &( mutexPointer[0] ) ); return returnValue; } void BinarySemaphore::signal() { // get a pointer to the cond pthread_cond_t *condPointer = (pthread_cond_t *)mNativeObjectPointerA; // get a pointer to the mutex pthread_mutex_t *mutexPointer = (pthread_mutex_t *)mNativeObjectPointerB; // lock the mutex pthread_mutex_lock( &( mutexPointer[0] ) ); // increment the semaphore value mSemaphoreValue = 1; pthread_cond_signal( &( condPointer[0] ) ); // unlock the mutex pthread_mutex_unlock( &( mutexPointer[0] ) ); } transcend-0.3+dfsg2.orig/minorGems/system/FinishedSignalThread.h0000640000175000017500000000326510147345462023415 0ustar pabspabs/* * Modification History * * 2002-March-9 Jason Rohrer * Created. * * 2002-March-10 Jason Rohrer * Made destructor public. * * 2002-March-11 Jason Rohrer * Changed so that destructor joins thread. * * 2002-April-4 Jason Rohrer * Changed name of lock to avoid confusion with subclass-provided locks. * * 2004-April-1 Jason Rohrer * Moved from konspire2b into minorGems. * Changed so that destructor does not join the thread. * * 2004-November-19 Jason Rohrer * Changed to virtual inheritance from Thread class. */ #ifndef FINISHED_SIGNAL_THREAD_INCLUDED #define FINISHED_SIGNAL_THREAD_INCLUDED #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" /** * Abstract subclass if thread that has a * synchronized finished signal. * * @author Jason Rohrer */ class FinishedSignalThread : public virtual Thread { public: /** * Only destroys this thread. * Does not join. */ virtual ~FinishedSignalThread(); /** * Gets whether this thread is finished and * ready to be destroyed. * * @return true iff this thread is finished. */ char isFinished(); protected: FinishedSignalThread(); /** * Sets that this thread is finished and * ready to be destroyed. * * For this class to work properly, the subclass * MUST call this function at the end of its run method. */ void setFinished(); private: MutexLock *mFinishedLock; char mFinished; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/FinishedSignalThreadManager.h0000640000175000017500000000273610170257606024710 0ustar pabspabs/* * Modification History * * 2004-November-9 Jason Rohrer * Created. * Modified from MUTE's ChannelReceivingThreadManager. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to allow sleep to be interrupted. */ #ifndef FINISHED_SIGNAL_THREAD_MANAGER_INCLUDED #define FINISHED_SIGNAL_THREAD_MANAGER_INCLUDED #include "minorGems/system/FinishedSignalThread.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/BinarySemaphore.h" /** * A thread that manages the destruction of FinishedSignalThreads. * * @author Jason Rohrer. */ class FinishedSignalThreadManager : public Thread { public: /** * Constructs and starts this manager. */ FinishedSignalThreadManager(); /** * Stops and destroys this manager. */ ~FinishedSignalThreadManager(); /** * Adds a thread to this manager. * * @param inThread the thread to add. * Will be destroyed by this class. */ void addThread( FinishedSignalThread *inThread ); // implements the Thread interface void run(); protected: MutexLock *mLock; SimpleVector *mThreadVector; char mStopSignal; BinarySemaphore *mSleepSemaphore; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/ThreadSafePrinter.h0000640000175000017500000000262710032526077022745 0ustar pabspabs/* * Modification History * * 2000-October-14 Jason Rohrer * Created. * * 2001-January-27 Jason Rohrer * Converted to use MutexLock and added to minorGems source tree. * Changed tprintf to be static (the mutexes don't work otherwise). * Now we're closing the argument list. * Fixed so that it works with any number of arguments. * Changed name of print function to printf. * * 2004-March-31 Jason Rohrer * Fixed static memory leak. */ #include "minorGems/common.h" #ifndef THREAD_SAFE_PRINTER_INCLUDED #define THREAD_SAFE_PRINTER_INCLUDED #include "MutexLock.h" #include // for variable argument lists #include /** * Thread safe printf function. Note that printf is actually thread safe * anyway, so this is just to demonstrate and test locks. It seems as * though printf _isn't_ thread safe on certain platforms, so this class * may be useful. * * @author Jason Rohrer */ class ThreadSafePrinter { public: static int printf( const char* inFormatString, ... ); private: static MutexLock sLock; }; // initialize static members MutexLock ThreadSafePrinter::sLock; inline int ThreadSafePrinter::printf( const char*inFormatString, ... ) { va_list argList; va_start( argList, inFormatString ); sLock.lock(); int returnVal = vprintf( inFormatString, argList ); fflush( stdout ); sLock.unlock(); va_end( argList ); return returnVal; } #endif transcend-0.3+dfsg2.orig/minorGems/system/Thread.h0000640000175000017500000000560710174541121020575 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Made sleep() static so it can be called by non-Thread classes. * * 2001-May-12 Jason Rohrer * Added comments about joining before destroying. * * 2002-March-29 Jason Rohrer * Added Fortify inclusion. * * 2002-August-5 Jason Rohrer * Made destructor virtual. * * 2004-March-31 Jason Rohrer * Added support for detatched mode. * * 2005-January-9 Jason Rohrer * Made sleep function virtual to allow overrides. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include "minorGems/common.h" #ifndef THREAD_CLASS_INCLUDED #define THREAD_CLASS_INCLUDED #ifdef FORTIFY #include "minorGems/util/development/fortify/fortify.h" #endif /** * Base class to be subclassed by all threads. * * Note: Implementation for the functions defined here is provided * separately for each platform (in the mac/ linux/ and win32/ * subdirectories). * * @author Jason Rohrer */ class Thread { public: Thread(); virtual ~Thread(); /** * Starts this Thread. * * Note that after starting a non-detached thread, it _must_ be * joined before being destroyed to avoid memory leaks. * * Threads running in detatched mode handle their own destruction * as they terminate and do not need to be joined at all. * * @param inDetach true if this thread should run in detatched mode, * or false to run in non-detached mode. Defaults to false. */ void start( char inDetach = false ); /** * To be overriden by subclasses. * This method will be run by the Thread after start() has been called. */ virtual void run() = 0; /** * Blocks until this thread finishes executing its run() method. * * Must be called before destroying this thread, if this thread * has been started. */ void join(); /** * Puts the current thread to sleep for a specified amount of time. * * Note that given a thread instance threadA, calling threadA.sleep() * will put the calling thread to sleep. * * @param inTimeInMilliseconds the number of milliseconds to sleep. */ virtual void sleep( unsigned long inTimeInMilliseconds ) { staticSleep( inTimeInMilliseconds ); } /** * Same as sleep, but can be called without constructing a thread. */ static void staticSleep( unsigned long inTimeInMilliseconds ); /** * Gets whether this thread is detached. * * @return true if this thread is detached. */ char isDetatched() { return mIsDetached; } private: /** * Used by platform-specific implementations. */ void *mNativeObjectPointer; char mIsDetached; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/TestThread.cpp0000640000175000017500000000231710032526430021762 0ustar pabspabs/* * Modification History * * 2000-December-13 Jason Rohrer * Created. * * 2002-November-14 Jason Rohrer * Added more verbose printouts. * * 2004-March-31 Jason Rohrer * Added test of detached threads. */ #include "TestThread.h" int numToCount = 1000; /** * Main method that spawns two TestThreads. * * @author Jason Rohrer */ int main() { TestThread *thread1 = new TestThread( 1, numToCount ); TestThread *thread2 = new TestThread( 2, numToCount ); TestThread *thread3 = new TestThread( 3, numToCount ); ThreadSafePrinter::printf( "Starting thread 1\n" ); thread1->start(); ThreadSafePrinter::printf( "Starting thread 2\n" ); thread2->start(); ThreadSafePrinter::printf( "Starting thread 3 in detached mode\n" ); thread3->start( true ); Thread::sleep( 5000 ); ThreadSafePrinter::printf( "Joining thread 1\n" ); thread1->join(); ThreadSafePrinter::printf( "Joining thread 2\n" ); thread2->join(); ThreadSafePrinter::printf( "Destroying thread 1\n" ); delete thread1; ThreadSafePrinter::printf( "Destroying thread 2\n" ); delete thread2; ThreadSafePrinter::printf( "Thread 3 should handle its own destruction.\n" ); return 0; } transcend-0.3+dfsg2.orig/minorGems/system/StopSignalThread.h0000640000175000017500000000361610170267060022602 0ustar pabspabs/* * Modification History * * 2002-April-4 Jason Rohrer * Created. * Changed to reflect the fact that the base class * destructor is called *after* the derived class destructor. * * 2003-September-5 Jason Rohrer * Moved into minorGems. * * 2004-November-19 Jason Rohrer * Changed to virtual inheritance from Thread class. * * 2005-January-9 Jason Rohrer * Changed to sleep on a semaphore to make sleep interruptable by stop. */ #ifndef STOP_SIGNAL_THREAD_INCLUDED #define STOP_SIGNAL_THREAD_INCLUDED #include "minorGems/system/Thread.h" #include "minorGems/system/MutexLock.h" #include "minorGems/system/BinarySemaphore.h" /** * Abstract subclass of thread that has a stop signal. * * Note that subclasses MUST check the isStopped() function * periodically in their run() function for this class to work * properly. * * @author Jason Rohrer */ class StopSignalThread : public virtual Thread { public: /** * Only destroys this thread. * Does not stop or join. */ virtual ~StopSignalThread(); protected: StopSignalThread(); // overrides Thread::sleep to make it interruptable by our stop call virtual void sleep( unsigned long inTimeInMilliseconds ); /** * Signals this thread to stop, interrupting it if it is sleeping. * * Thread safe. * * Thread must be joined after this call returns. */ void stop(); /** * Gets whether this thread has been signaled to stop. * * Thread safe. * * @return true if this thread should stop. */ char isStopped(); private: MutexLock *mStopLock; char mStopped; BinarySemaphore *mSleepSemaphore; }; #endif transcend-0.3+dfsg2.orig/minorGems/system/win32/0000750000175000017500000000000010305077065020155 5ustar pabspabstranscend-0.3+dfsg2.orig/minorGems/system/win32/ThreadWin32.cpp0000640000175000017500000000456010174541132022714 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2004-March-31 Jason Rohrer * Added missing call to CloseHandle in destructor. * Added support for detatched mode. * * 2004-April-1 Jason Rohrer * Fixed a bug in CloseHandle call pointed out by Mycroftxxx. * * 2005-January-22 Jason Rohrer * Added a static sleep function. */ #include "minorGems/system/Thread.h" #include /** * Win32-specific implementation of the Thread class member functions. * */ // prototype /** * A wrapper for the run method, since windows thread (perhaps) won't take * a class's member function. Takes a pointer to the Thread to run, * cast as a void*; */ DWORD WINAPI win32ThreadFunction( void * ); Thread::Thread() { // allocate a handle on the heap mNativeObjectPointer = (void *)( new HANDLE[1] ); } Thread::~Thread() { // get a pointer to the allocated handle HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; // close the handle to ensure that the thread resources are freed CloseHandle( threadPointer[0] ); // de-allocate the thread handle from the heap delete [] threadPointer; } void Thread::start( char inDetach ) { mIsDetached = inDetach; // get a pointer to the allocated handle HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; DWORD threadID; threadPointer[0] = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, // no attributes (DWORD)0, // default stack size win32ThreadFunction, // function (LPVOID)this, // function arg (DWORD)0, // no creation flags (start thread immediately) &threadID ); } void Thread::join() { HANDLE *threadPointer = (HANDLE *)mNativeObjectPointer; WaitForSingleObject( threadPointer[0], INFINITE ); } void Thread::staticSleep( unsigned long inTimeInMilliseconds ) { Sleep( inTimeInMilliseconds ); } // takes a pointer to a Thread object as the data value DWORD WINAPI win32ThreadFunction( void *inPtrToThread ) { Thread *threadToRun = (Thread *)inPtrToThread; threadToRun->run(); if( threadToRun->isDetatched() ) { // thread detached, so we must destroy it delete threadToRun; } return 0; } transcend-0.3+dfsg2.orig/minorGems/system/win32/BinarySemaphoreWin32.cpp0000640000175000017500000000401707722702212024575 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2003-August-26 Jason Rohrer * Added support for timeouts on wait. */ #include "minorGems/system/BinarySemaphore.h" #include /** * Win32-specific implementation of the BinarySemaphore class member functions. */ /** * Native object pointer A is the semaphore handle. * Pointer B is not used. */ BinarySemaphore::BinarySemaphore() : mSemaphoreValue( 0 ) { // allocate a handle on the heap mNativeObjectPointerA = (void *)( new HANDLE[1] ); // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; semaphorePointer[0] = CreateSemaphore( (LPSECURITY_ATTRIBUTES) NULL, // no attributes 0, // initial count 1, // maximum count (LPCTSTR) NULL ); // no name } BinarySemaphore::~BinarySemaphore() { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; // destroy the semaphore CloseHandle( semaphorePointer[0] ); // de-allocate the handle from the heap delete [] semaphorePointer; } int BinarySemaphore::wait( int inTimeoutInMilliseconds ) { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; if( inTimeoutInMilliseconds == -1 ) { WaitForSingleObject( semaphorePointer[0], INFINITE ); return 1; } else { // timeout int result = WaitForSingleObject( semaphorePointer[0], inTimeoutInMilliseconds ); if( result == WAIT_TIMEOUT ) { return 0; } else { return 1; } } } void BinarySemaphore::signal() { // retrieve handle from the heap HANDLE *semaphorePointer = (HANDLE *)mNativeObjectPointerA; ReleaseSemaphore( semaphorePointer[0], 1, (LPLONG) NULL ); } transcend-0.3+dfsg2.orig/minorGems/system/win32/LauncherWin32.cpp0000640000175000017500000000065007637725606023266 0ustar pabspabs/* * Modification History * * 2003-January-10 Jason Rohrer * Created. * * 2003-March-24 Jason Rohrer * Fixed a syntax typo. */ #include "minorGems/system/Launcher.h" #include #include void Launcher::launchCommand( char *inCommandName, char **inArguments ) { _spawnvp( _P_NOWAIT, inCommandName, inArguments ); } transcend-0.3+dfsg2.orig/minorGems/system/win32/MutexLockWin32.cpp0000640000175000017500000000341107554370760023431 0ustar pabspabs/* * Modification History * * 2001-January-27 Jason Rohrer * Created. * * 2001-March-4 Jason Rohrer * Replaced include of and with * to fix compile bugs encountered with newer windows compilers. * * 2002-October-18 Jason Rohrer * Moved common include out of header and into platform-specific cpp files, * since MemoryTrack uses a mutex lock. * * 2002-October-19 Jason Rohrer * Changed to use malloc instead of new internally to work with debugMemory. * Made use of mNativeObjectPointer a bit cleaner. * Fixed a few bugs with new use of mNativeObjectPointer. */ #include "minorGems/common.h" #include "minorGems/system/MutexLock.h" #include #include /** * Win32-specific implementation of the MutexLock class member functions. */ MutexLock::MutexLock() { // allocate a handle on the heap mNativeObjectPointer = malloc( sizeof( HANDLE ) ); // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; // create the mutex *mutexPointer = CreateMutex( (LPSECURITY_ATTRIBUTES) NULL, // no attributes (BOOL) false, // not initially locked (LPCTSTR) NULL ); // no name } MutexLock::~MutexLock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; // destroy the mutex CloseHandle( *mutexPointer ); // de-allocate the mutex structure from the heap free( mutexPointer ); } void MutexLock::lock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; WaitForSingleObject( *mutexPointer, INFINITE ); } void MutexLock::unlock() { // retrieve handle from the heap HANDLE *mutexPointer = (HANDLE *)mNativeObjectPointer; ReleaseMutex( *mutexPointer ); } transcend-0.3+dfsg2.orig/minorGems/system/win32/TimeWin32.cpp0000640000175000017500000000447010133647061022406 0ustar pabspabs/* * Modification History * * 2001-November-7 Jason Rohrer * Created. * * 2002-April-11 Jason Rohrer * Added missing include, and fixed a bug. * * 2004-January-29 Jason Rohrer * Fixed so that 0-point of time is the same as on other platforms. * * 2004-October-14 Jason Rohrer * Fixed bug in second/millisecond callibration. * Fixed bug in win32 time to ANSI time translation. * Fixed daylight savings time bug. */ #include "minorGems/system/Time.h" #include #include #include #include /** * Windows implementation of Time.h. * * The 0-point should match the ANSI standard. */ void Time::getCurrentTime( unsigned long *outSeconds, unsigned long *outMilliseconds ) { // convert from win32 broken-down time (which has msec resolution) // to an ANSI time struct and then convert to an absolute time in // seconds // This procedure ensures that the 0-point matches the ANSI standard. // note: // we cannot simply call ANSI time() to get the seconds and then rely // on GetLocalTime to get the milliseconds, since the seconds value // used by GetLocalTime is (strangely enough) not calibrated to the seconds // value of time(). // In other words, it is possible for the time() seconds to advance // at a different clock cycle than the GetLocalTime seconds. // get time using a win32 call SYSTEMTIME win32TimeStruct; GetLocalTime( &win32TimeStruct ); // convert this win32 structure to the ANSI standard structure struct tm ansiTimeStruct; ansiTimeStruct.tm_sec = win32TimeStruct.wSecond; ansiTimeStruct.tm_min = win32TimeStruct.wMinute; ansiTimeStruct.tm_hour = win32TimeStruct.wHour; ansiTimeStruct.tm_mday = win32TimeStruct.wDay; // ANSI time struct has month in range [0..11] ansiTimeStruct.tm_mon = win32TimeStruct.wMonth - 1; // ANSI time struct has year that is an offset from 1900 ansiTimeStruct.tm_year = win32TimeStruct.wYear - 1900; // unknown daylight savings time (dst) status // if we fail to init this value, we can get inconsistent results ansiTimeStruct.tm_isdst = -1; unsigned long secondsSinceEpoch = mktime( &ansiTimeStruct ); *outSeconds = secondsSinceEpoch; *outMilliseconds = (unsigned long)( win32TimeStruct.wMilliseconds ); } transcend-0.3+dfsg2.orig/runToBuild0000750000175000017500000000072310305075353015744 0ustar pabspabs#!/bin/bash # # Modification History # # 2004-January-15 Jason Rohrer # Added bash path. # Fixed string comparison operator. # cd Transcend chmod u+x ./configure ./configure echo "Building portaudio..." cd portaudio chmod u+x ./configure ./configure make cd .. echo "Building Transcend..." cd game make cd .. cd .. cp Transcend/game/Transcend ./TranscendApp cp -r Transcend/levels . cp Transcend/doc/how_to_*.txt . echo "Run TranscendApp to play." transcend-0.3+dfsg2.orig/Transcend/0000750000175000017500000000000011476353073015656 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/Makefile.FreeBSDX860000640000175000017500000000130610062106434021021 0ustar pabspabs# # Modification History # # 2002-September-9 Jason Rohrer # Created. # # 2003-November-2 Jason Rohrer # Moved minorGems platform prefixes into platform-specific Makefile templates. # ## # The common FreeBSD portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DBSD # pthread library linked differently for BSD PLATFORM_LINK_FLAGS = -pthread # All platforms but OSX support g++ and need no linker hacks GXX=g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix transcend-0.3+dfsg2.orig/Transcend/game/0000750000175000017500000000000010305077143016556 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/game/game.cpp0000640000175000017500000023431710304640217020203 0ustar pabspabs/* * Modification History * * 2004-June-10 Jason Rohrer * Created. * * 2004-June-11 Jason Rohrer * Added zoom that adjusts with speed. Disabled depth testing. * * 2004-June-12 Jason Rohrer * Changed to test control points. * * 2004-June-14 Jason Rohrer * Added call to glutInit to follow GLUT spec (particularly on Mac OS X). * * 2004-June-15 Jason Rohrer * Changed so that ship movement/rotation rates don't vary with framerate. * * 2004-June-18 Jason Rohrer * Disabled wrapping to increase framerate. * Made accellerations framerate-independent. * * 2004-June-20 Jason Rohrer * Added support for enemy/explosion scaling. * * 2004-June-21 Jason Rohrer * Added support for ship being impacted by enemy bullets. * Added umbilical cord. Changed order of grid drawing. * Removed old code and random object code. * Fixed a memory leak. * Added support for changing working directory on Mac platform. * * 2004-June-29 Jason Rohrer * Added a pause function. * * 2004-July-3 Jason Rohrer * Zoomed out farther. Improved ship bullet physics. * Added ship rotation accelleration. * * 2004-July-5 Jason Rohrer * Fixed memory leaks. * * 2004-August-6 Jason Rohrer * Added enemy shot sounds. * * 2004-August-9 Jason Rohrer * Added test of parameterized sound space. * * 2004-August-17 Jason Rohrer * Fixed a bug that caused ship to bounce back and forth when jarred to center. * * 2004-August-19 Jason Rohrer * Fixed bug in way sculpture piece moves with ship near boundaries. * * 2004-August-22 Jason Rohrer * Added support for music. * * 2004-August-22 Jason Rohrer * Added support for music. * * 2004-August-23 Jason Rohrer * Added current music position cursor. * * 2004-August-24 Jason Rohrer * Added color to music cursor. * Inverted music cursor. * Fixed a memory leak. * * 2004-August-25 Jason Rohrer * Changed so that sculpture piece parameter controls an aspect of MusicPart. * Parameterized grid and background colors. * Added support for loading levels and fading between them. * * 2004-August-26 Jason Rohrer * Fixed bug in setting mac working directory. * * 2004-August-30 Jason Rohrer * Added bullet scaling parameters. * Fixed a bug when boss bullets hit ship while we are carrying a piece. * Added code to set music loudness based on number of sculpture pieces. * * 2004-August-31 Jason Rohrer * Added support for specifying level number on command line. * Improved draw order. * Added code for reading reverb config and umbilical colors from file. * Fixed memory leaks. * Improved pause behavior. * * 2004-September-3 Jason Rohrer * Improved grid coloring. * * 2004-October-13 Jason Rohrer * Added zoom in/out during fade in/out. * Added code to eliminate large position jumps during slow frames. * Parameterized grid size per level. * Added strafe keys, and switched pick-up/drop key. * Added limit for number of ship bullets on screen. * * 2004-October-14 Jason Rohrer * Got portals working. * Fixed key release bug that caused stuck keys. * Fixed zoom effects and added a zoom-in effect at level end. * Switched back to zoom-out effect at level end. * Removed an error print message. * Added a cheat for judges to skip to next level. * * 2005-August-21 Jason Rohrer * Fixed zoom behavior for strafing. * * 2005-August-22 Jason Rohrer * Doubled pick-up radius to make piece pick up easier. * Added magnet mode to smooth piece pick-up and drop. * Started work on boss damage graphics. * * 2005-August-28 Jason Rohrer * Fixed time error message. * * 2005-August-29 Jason Rohrer * Disabled the skip-level cheat. */ #include #include #include #include #include #include #include "minorGems/graphics/openGL/ScreenGL.h" #include "minorGems/graphics/openGL/SceneHandlerGL.h" #include "minorGems/util/random/StdRandomSource.h" #include "minorGems/system/Time.h" #include "minorGems/system/Thread.h" #include "minorGems/io/file/File.h" #include "DrawableObject.h" #include "ObjectParameterSpaceControlPoint.h" #include "LevelDirectoryManager.h" #include "ParameterizedObject.h" #include "ShipBullet.h" #include "BulletSound.h" #include "ShipBulletManager.h" #include "EnemyManager.h" #include "SculptureManager.h" #include "BossManager.h" #include "PortalManager.h" #include "SoundPlayer.h" #include "ReverbSoundFilter.h" #include "ParameterizedStereoSound.h" #include "MusicPart.h" #include "MusicNoteWaveTable.h" #include "MusicPlayer.h" class GameSceneHandler : public SceneHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL { public: /** * Constructs a sceen handler. * * @param inStartingLevel the level to start on. * Defaults to 0. */ GameSceneHandler( int inStartingLevel = 1 ); virtual ~GameSceneHandler(); ScreenGL *mScreen; // implements the SceneHandlerGL interface virtual void drawScene(); // implements the KeyboardHandlerGL interface virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); /** * Loads the next level. */ void loadNextLevel(); /** * Destroys the currently loaded level. */ void destroyLevel(); protected: int mLevelNumber; double mFadeLevel; double mFadeTime; // true if ship is in portal to move on to next level char mShipInPortal; double mMaxXPosition; double mMinXPosition; double mMaxYPosition; double mMinYPosition; double mGridSpacing; // the time that the last frame was drawn unsigned long mLastFrameSeconds; unsigned long mLastFrameMilliseconds; // our current frame rate unsigned long mFrameMillisecondDelta; // tracking our current state of movement double mForwardBackwardMoveRate; double mRightLeftMoveRate; double mMaxMoveRate; double mShipAccelleration; double mShipFriction; double mRotationRate; double mBaseRotationRate; double mMaxRotationRate; double mShipRotationAccelleration; double mShipScale; double mCurrentShipRadius; char mMovingUp; char mMovingDown; char mMovingLeft; char mMovingRight; char mZoomingIn; char mZoomingOut; char mRotatingClockwise; char mRotatingCounterClockwise; Vector3D *mCurrentShipVelocityVector; char mPaused; StdRandomSource *mRandSource; ParameterizedObject *mShipParameterSpace; ShipBulletManager *mShipBulletManager; double mShipBulletRange; double mShipBulletBaseVelocity; int mMaxNumShipBullets; ShipBulletManager *mEnemyBulletManager; double mEnemyBulletRange; double mEnemyBulletBaseVelocity; double mEnemyBulletsPerSecond; double mEnemyBulletShipJarPower; double mEnemyBulletSculptureJarPower; double mBossBulletShipJarPower; double mBossBulletSculptureJarPower; double mSculptureFriction; double mCurrentShipJarForce; ShipBulletManager *mBossBulletManager; ShipBulletManager *mBossDamageManager; EnemyManager *mEnemyManager; int mNumEnemies; SculptureManager *mSculptureManager; BossManager *mBossManager; PortalManager *mPortalManager; MusicNoteWaveTable *mWaveTable; MusicPlayer *mMusicPlayer; int mCurrentPieceCarried; double mPiecePickupRadius; double mMaxFrameRate; char mPrintFrameRate; unsigned long mNumFrames; unsigned long mFrameBatchSize; unsigned long mFrameBatchStartTimeSeconds; unsigned long mFrameBatchStartTimeMilliseconds; Color *mBackgroundColor; Color *mNearBossGridColor; Color *mFarBossGridColor; Color *mWeakUmbilicalColor; Color *mStrongUmbilicalColor; int mSampleRate; double mMusicLoudness; int mMaxSimultaneousSounds; SoundPlayer *mSoundPlayer; void addRandomEnemy(); }; GameSceneHandler *sceneHandler; ScreenGL *screen; //double baseViewZ = -30; double baseViewZ = -50; // function that destroys object when exit is called. // exit is the only way to stop the GLUT-based ScreenGL void cleanUpAtExit() { printf( "exiting\n" ); delete sceneHandler; delete screen; } int main( int inNumArgs, char **inArgs ) { int startingLevel = 1; if( inNumArgs > 1 ) { int numRead = sscanf( inArgs[1], "%d", &startingLevel ); if( numRead == 1 ) { if( startingLevel < 1 ) { startingLevel = 1; } } } sceneHandler = new GameSceneHandler( startingLevel ); // must pass args to GLUT before constructing the screen glutInit( &inNumArgs, inArgs ); screen = new ScreenGL( 300, 300, false, "Transcend", sceneHandler, NULL, sceneHandler ); sceneHandler->mScreen = screen; screen->addRedrawListener( sceneHandler ); Vector3D *move = new Vector3D( 0, 0, baseViewZ ); screen->moveView( move ); delete move; // do this mac check after constructing scene handler and screen, // since these cause various Mac frameworks to be loaded (which can // change the current working directory out from under us) #ifdef __mac__ // make sure working directory is the same as the directory // that the app resides in // this is especially important on the mac platform, which // doesn't set a proper working directory for double-clicked // app bundles // arg 0 is the path to the app executable char *appDirectoryPath = stringDuplicate( inArgs[0] ); char *appNamePointer = strstr( appDirectoryPath, "Transcend.app" ); // terminate full app path to get parent directory appNamePointer[0] = '\0'; chdir( appDirectoryPath ); delete [] appDirectoryPath; #endif sceneHandler->loadNextLevel(); // register cleanup function, since screen->start() will never return atexit( cleanUpAtExit ); screen->start(); return 0; } GameSceneHandler::GameSceneHandler( int inStartingLevel ) : mLevelNumber( inStartingLevel - 1 ), mFadeLevel( 0 ), // start fully faded out, mFadeTime( 3 ), // 3 seconds mShipInPortal( false ), mMaxXPosition( 100 ), mMinXPosition( -100 ), mMaxYPosition( 100 ), mMinYPosition( -100 ), mGridSpacing( 10 ), mFrameMillisecondDelta( 0 ), mForwardBackwardMoveRate( 0 ), mRightLeftMoveRate( 0 ), mRotationRate( 0 ), mMovingUp( false ), mMovingDown( false ), mMovingLeft( false ), mMovingRight( false ), mZoomingIn( false ), mZoomingOut( false ), mRotatingClockwise( false ), mRotatingCounterClockwise( false ), mPaused( false ), mRandSource( new StdRandomSource() ), mMaxFrameRate( 400 ), // don't limit frame rate mPrintFrameRate( false ), mNumFrames( 0 ), mFrameBatchSize( 100 ), mFrameBatchStartTimeSeconds( time( NULL ) ), mFrameBatchStartTimeMilliseconds( 0 ), mMusicLoudness( 0.1 ), mMaxSimultaneousSounds( 2 ) { Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); mSampleRate = 11025; mSoundPlayer = new SoundPlayer( mSampleRate, mMaxSimultaneousSounds, NULL, mMusicLoudness ); } void GameSceneHandler::loadNextLevel() { mCurrentShipVelocityVector = new Vector3D( 0, 0, 0 ); // reset ship position and angle Vector3D *viewPosition = new Vector3D( 0, 0, baseViewZ ); mScreen->setViewPosition( viewPosition ); delete viewPosition; // reset view orientation back to the zero vector Angle3D *viewOrientation = mScreen->getViewOrientation(); Angle3D *viewRotation = new Angle3D( viewOrientation ); viewRotation->scale( -1 ); mScreen->rotateView( viewRotation ); delete viewRotation; int i; mLevelNumber++; char *levelString = new char[4]; // zero pad sprintf( levelString, "%03d", mLevelNumber ); File *levelsDirectory = new File( NULL, "levels" ); File *currentLevelDirectory = levelsDirectory->getChildFile( levelString ); if( !( currentLevelDirectory->exists() ) ) { // we have run out of levels... back to level 1 mLevelNumber = 1; delete currentLevelDirectory; currentLevelDirectory = levelsDirectory->getChildFile( "001" ); } LevelDirectoryManager::setLevelDirectory( currentLevelDirectory->copy() ); delete [] levelString; // read grid size for level char error = false; int xGridSize = LevelDirectoryManager::readIntFileContents( "gridSizeX", &error, true ); if( error ) { xGridSize = 200; } error = false; int yGridSize = LevelDirectoryManager::readIntFileContents( "gridSizeY", &error, true ); if( error ) { yGridSize = 200; } error = false; mMinXPosition = - xGridSize / 2; mMinYPosition = - yGridSize / 2; mMaxXPosition = xGridSize / 2; mMaxYPosition = yGridSize / 2; // clear existing filters mSoundPlayer->removeAllFilters(); // load information about reverb filters FILE *reverbFILE = LevelDirectoryManager::getStdStream( "reverbFilters", true ); while( !error ) { double time; double loudness; error = true; int numRead = fscanf( reverbFILE, "%lf", &time ); if( numRead == 1 ) { numRead = fscanf( reverbFILE, "%lf", &loudness ); if( numRead == 1 ) { error = false; mSoundPlayer->addFilter( new ReverbSoundFilter( (unsigned long)( mSampleRate * time ), loudness ) ); } } } FILE *backgroundColorFILE = LevelDirectoryManager::getStdStream( "backgroundColor", true ); if( backgroundColorFILE != NULL ) { mBackgroundColor = ObjectParameterSpaceControlPoint::readColorFromFile( backgroundColorFILE ); fclose( backgroundColorFILE ); } else { mBackgroundColor = new Color( 0, 0, 0, 1 ); } FILE *nearBossGridColorFILE = LevelDirectoryManager::getStdStream( "nearBossGridColor", true ); if( nearBossGridColorFILE != NULL ) { mNearBossGridColor = ObjectParameterSpaceControlPoint::readColorFromFile( nearBossGridColorFILE ); fclose( nearBossGridColorFILE ); } else { mNearBossGridColor = new Color( 1, 0, 0, 0.5 ); } FILE *farBossGridColorFILE = LevelDirectoryManager::getStdStream( "farBossGridColor", true ); if( farBossGridColorFILE != NULL ) { mFarBossGridColor = ObjectParameterSpaceControlPoint::readColorFromFile( farBossGridColorFILE ); fclose( farBossGridColorFILE ); } else { mFarBossGridColor = new Color( 0, 0, 1, 0.5 ); } FILE *weakUmbilicalColorFILE = LevelDirectoryManager::getStdStream( "weakUmbilicalColor", true ); if( weakUmbilicalColorFILE != NULL ) { mWeakUmbilicalColor = ObjectParameterSpaceControlPoint::readColorFromFile( weakUmbilicalColorFILE ); fclose( weakUmbilicalColorFILE ); } else { mWeakUmbilicalColor = new Color( 1, 0, 0, 1 ); } FILE *strongUmbilicalColorFILE = LevelDirectoryManager::getStdStream( "strongUmbilicalColor", true ); if( strongUmbilicalColorFILE != NULL ) { mStrongUmbilicalColor = ObjectParameterSpaceControlPoint::readColorFromFile( strongUmbilicalColorFILE ); fclose( strongUmbilicalColorFILE ); } else { mStrongUmbilicalColor = new Color( 1, 1, 0, 1 ); } mWaveTable = new MusicNoteWaveTable( mSampleRate ); FILE *shipFILE = LevelDirectoryManager::getStdStream( "ship", true ); mShipParameterSpace = new ParameterizedObject( shipFILE, &error ); if( error ) { printf( "Error reading control points from ship file\n" ); } if( shipFILE != NULL ) { fclose( shipFILE ); } FILE *shipBulletFILE = LevelDirectoryManager::getStdStream( "shipBullet", true ); error = false; ShipBullet *shipBulletTemplate = new ShipBullet( shipBulletFILE, &error ); if( error ) { printf( "Error reading from shipBullet file\n" ); } if( shipBulletFILE != NULL ) { fclose( shipBulletFILE ); } error = false; double shipBulletScale = LevelDirectoryManager::readDoubleFileContents( "shipBulletScale", &error, true ); if( error ) { // default shipBulletScale = 1; } FILE *shipBulletSoundFILE = LevelDirectoryManager::getStdStream( "shipBulletSound", true ); error = false; BulletSound *shipBulletSoundTemplate = new BulletSound( shipBulletSoundFILE, &error ); if( error ) { printf( "Error reading from shipBulletSound file\n" ); } if( shipBulletSoundFILE != NULL ) { fclose( shipBulletSoundFILE ); } mShipBulletManager = new ShipBulletManager( shipBulletTemplate, shipBulletScale, mSoundPlayer, shipBulletSoundTemplate, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); error = false; mShipBulletRange = LevelDirectoryManager::readDoubleFileContents( "shipBulletRange", &error, true ); if( error ) { // default mShipBulletRange = 30; } error = false; mShipBulletBaseVelocity = LevelDirectoryManager::readDoubleFileContents( "shipBulletBaseVelocity", &error, true ); if( error ) { // default mShipBulletBaseVelocity = 20; } error = false; mMaxNumShipBullets = LevelDirectoryManager::readIntFileContents( "maxShipBulletsOnScreen", &error, true ); if( error ) { // default mMaxNumShipBullets = 3; } FILE *enemyBulletFILE = LevelDirectoryManager::getStdStream( "enemyBullet", true ); error = false; ShipBullet *enemyBulletTemplate = new ShipBullet( enemyBulletFILE, &error ); if( error ) { printf( "Error reading from enemyBullet file\n" ); } if( enemyBulletFILE != NULL ) { fclose( enemyBulletFILE ); } error = false; double enemyBulletScale = LevelDirectoryManager::readDoubleFileContents( "enemyBulletScale", &error, true ); if( error ) { // default enemyBulletScale = 1; } FILE *enemyBulletSoundFILE = LevelDirectoryManager::getStdStream( "enemyBulletSound", true ); error = false; BulletSound *enemyBulletSoundTemplate = new BulletSound( enemyBulletSoundFILE, &error ); if( error ) { printf( "Error reading from enemyBulletSound file\n" ); } if( enemyBulletSoundFILE != NULL ) { fclose( enemyBulletSoundFILE ); } mEnemyBulletManager = new ShipBulletManager( enemyBulletTemplate, enemyBulletScale, mSoundPlayer, enemyBulletSoundTemplate, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); error = false; mEnemyBulletShipJarPower = LevelDirectoryManager::readDoubleFileContents( "enemyBulletShipJarPower", &error, true ); if( error ) { // default mEnemyBulletShipJarPower = .5; } error = false; mEnemyBulletSculptureJarPower = LevelDirectoryManager::readDoubleFileContents( "enemyBulletSculptureJarPower", &error, true ); if( error ) { // default mEnemyBulletSculptureJarPower = .5; } error = false; mMaxMoveRate = LevelDirectoryManager::readDoubleFileContents( "shipMaxVelocity", &error, true ); if( error ) { // default mMaxMoveRate = 40; } error = false; mShipAccelleration = LevelDirectoryManager::readDoubleFileContents( "shipAccelleration", &error, true ); if( error ) { // default mShipAccelleration = 10; } error = false; mShipFriction = LevelDirectoryManager::readDoubleFileContents( "shipFriction", &error, true ); if( error ) { // default mShipFriction = 10; } error = false; mBaseRotationRate = LevelDirectoryManager::readDoubleFileContents( "shipBaseRotationRate", &error, true ); if( error ) { // default mBaseRotationRate = 1.6; } error = false; mMaxRotationRate = LevelDirectoryManager::readDoubleFileContents( "shipMaxRotationRate", &error, true ); if( error ) { // default mMaxRotationRate = 3.2; } error = false; mShipRotationAccelleration = LevelDirectoryManager::readDoubleFileContents( "shipRotationAccelleration", &error, true ); if( error ) { // default mShipRotationAccelleration = 1; } error = false; mShipScale = LevelDirectoryManager::readDoubleFileContents( "shipScale", &error, true ); if( error ) { // default mShipScale = 1; } FILE *bossBulletFILE = LevelDirectoryManager::getStdStream( "bossBullet", true ); error = false; ShipBullet *bossBulletTemplate = new ShipBullet( bossBulletFILE, &error ); if( error ) { printf( "Error reading from bossBullet file\n" ); } if( bossBulletFILE != NULL ) { fclose( bossBulletFILE ); } error = false; double bossBulletScale = LevelDirectoryManager::readDoubleFileContents( "bossBulletScale", &error, true ); if( error ) { // default bossBulletScale = 1; } FILE *bossBulletSoundFILE = LevelDirectoryManager::getStdStream( "bossBulletSound", true ); error = false; BulletSound *bossBulletSoundTemplate = new BulletSound( bossBulletSoundFILE, &error ); if( error ) { printf( "Error reading from bossBulletSound file\n" ); } if( bossBulletSoundFILE != NULL ) { fclose( bossBulletSoundFILE ); } mBossBulletManager = new ShipBulletManager( bossBulletTemplate, bossBulletScale, mSoundPlayer, bossBulletSoundTemplate, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); error = false; mBossBulletShipJarPower = LevelDirectoryManager::readDoubleFileContents( "bossBulletShipJarPower", &error, true ); if( error ) { // default mBossBulletShipJarPower = .5; } error = false; mBossBulletSculptureJarPower = LevelDirectoryManager::readDoubleFileContents( "bossBulletSculptureJarPower", &error, true ); if( error ) { // default mBossBulletSculptureJarPower = .5; } error = false; mSculptureFriction = LevelDirectoryManager::readDoubleFileContents( "sculptureFriction", &error, true ); if( error ) { // default mSculptureFriction = 1; } FILE *firstSculpturePieceFILE = LevelDirectoryManager::getStdStream( "firstSculpturePiece", true ); error = false; ParameterizedObject *firstPieceSpace = new ParameterizedObject( firstSculpturePieceFILE, &error ); if( error ) { printf( "Error reading control points from firstSculpturePiece file\n" ); } if( firstSculpturePieceFILE != NULL ) { fclose( firstSculpturePieceFILE ); } FILE *secondSculpturePieceFILE = LevelDirectoryManager::getStdStream( "secondSculpturePiece", true ); error = false; ParameterizedObject *secondPieceSpace = new ParameterizedObject( secondSculpturePieceFILE, &error ); if( error ) { printf( "Error reading control points from secondSculpturePiece file\n" ); } if( secondSculpturePieceFILE != NULL ) { fclose( secondSculpturePieceFILE ); } error = false; int numPieces = LevelDirectoryManager::readIntFileContents( "numberOfSculpturePieces", &error, true ); if( error ) { // default numPieces = 10; } double *pieceParameters = new double[ numPieces ]; Vector3D **piecePositions = new Vector3D*[ numPieces ]; Angle3D **pieceRotations = new Angle3D*[ numPieces ]; MusicPart **pieceMusicParts = new MusicPart*[ numPieces ]; for( i=0; igetRandomDouble(); double x, y; x = mRandSource->getRandomDouble(); y = mRandSource->getRandomDouble(); // round one param at random to push all pieces to edges of world int coin = mRandSource->getRandomBoundedInt( 0, 1 ); if( coin == 0 ) { x = rint( x ); } else { y = rint( y ); } x = x * ( mMaxXPosition - mMinXPosition ) + mMinXPosition; y = y * ( mMaxYPosition - mMinYPosition ) + mMinYPosition; piecePositions[i] = new Vector3D( x, y, 0 ); pieceRotations[i] = new Angle3D( 0, 0, mRandSource->getRandomDouble() * 2 * M_PI ); pieceMusicParts[i] = new MusicPart( mWaveTable, mRandSource, pieceParameters[i] ); } error = false; double sculptureScale = LevelDirectoryManager::readDoubleFileContents( "sculptureScale", &error, true ); if( error ) { // default sculptureScale = 1; } error = false; double maxDistanceToBePartOfSculpture = LevelDirectoryManager::readDoubleFileContents( "maxSculptureSeparation", &error, true ); if( error ) { // default maxDistanceToBePartOfSculpture = 10; } error = false; double sculptureAnimationTime = LevelDirectoryManager::readDoubleFileContents( "sculptureAnimationTime", &error, true ); if( error ) { // default sculptureAnimationTime = 4; } FILE *sculpturePiecePowerupFILE = LevelDirectoryManager::getStdStream( "sculpturePiecePowerupSpace", true ); error = false; mSculptureManager = new SculptureManager( firstPieceSpace, secondPieceSpace, sculptureScale, maxDistanceToBePartOfSculpture, sculptureAnimationTime, numPieces, pieceParameters, piecePositions, pieceRotations, pieceMusicParts, sculpturePiecePowerupFILE, &error, mEnemyBulletManager, mEnemyBulletSculptureJarPower, mBossBulletManager, mBossBulletSculptureJarPower, mSculptureFriction, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); if( error ) { printf( "Error reading from sculpturePiecePowerupSpace file\n" ); } if( sculpturePiecePowerupFILE != NULL ) { fclose( sculpturePiecePowerupFILE ); } mMusicPlayer = new MusicPlayer( mSampleRate, mSculptureManager, mWaveTable, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition, mGridSpacing ); mSoundPlayer->setMusicPlayer( mMusicPlayer ); // avoid clipping mMusicLoudness = ( 1 - 0.1 * mMaxSimultaneousSounds ) / numPieces; mCurrentPieceCarried = -1; error = false; mPiecePickupRadius = LevelDirectoryManager::readDoubleFileContents( "piecePickupRadius", &error, true ); if( error ) { // default mPiecePickupRadius = 10; } error = false; mEnemyBulletRange = LevelDirectoryManager::readDoubleFileContents( "enemyBulletRange", &error, true ); if( error ) { // default mEnemyBulletRange = 30; } error = false; mEnemyBulletBaseVelocity = LevelDirectoryManager::readDoubleFileContents( "enemyBulletBaseVelocity", &error, true ); if( error ) { // default mEnemyBulletBaseVelocity = 20; } error = false; mEnemyBulletsPerSecond = LevelDirectoryManager::readDoubleFileContents( "enemyBulletsPerSecond", &error, true ); if( error ) { // default mEnemyBulletsPerSecond = .5; } mCurrentShipJarForce = 0; FILE *enemyFILE = LevelDirectoryManager::getStdStream( "enemy", true ); error = false; Enemy *enemyTemplate = new Enemy( enemyFILE, &error ); if( error ) { printf( "Error reading from enemy file\n" ); } if( enemyFILE != NULL ) { fclose( enemyFILE ); } error = false; double enemyScale = LevelDirectoryManager::readDoubleFileContents( "enemyScale", &error, true ); if( error ) { // default enemyScale = 1; } error = false; double enemyExplosionScale = LevelDirectoryManager::readDoubleFileContents( "enemyExplosionScale", &error, true ); if( error ) { // default enemyExplosionScale = 1; } error = false; double enemyVelocity = LevelDirectoryManager::readDoubleFileContents( "enemyVelocity", &error, true ); if( error ) { // default enemyVelocity = 3; } FILE *enemyExplosionSoundFILE = LevelDirectoryManager::getStdStream( "enemyExplosionSound", true ); error = false; BulletSound *enemyExplosionSoundTemplate = new BulletSound( enemyExplosionSoundFILE, &error ); if( error ) { printf( "Error reading from enemyExplosionSound file\n" ); } if( enemyExplosionSoundFILE != NULL ) { fclose( enemyExplosionSoundFILE ); } mEnemyManager = new EnemyManager( enemyTemplate, enemyScale, enemyExplosionScale, enemyVelocity, mSculptureManager, mShipBulletManager, mEnemyBulletManager, mEnemyBulletRange, mEnemyBulletBaseVelocity, mEnemyBulletsPerSecond, mSoundPlayer, enemyExplosionSoundTemplate, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); error = false; mNumEnemies = LevelDirectoryManager::readIntFileContents( "numberOfEnemies", &error, true ); if( error ) { // default mNumEnemies = 10; } FILE *bossFILE = LevelDirectoryManager::getStdStream( "boss", true ); error = false; Enemy *bossTemplate = new Enemy( bossFILE, &error ); if( error ) { printf( "Error reading from boss file\n" ); } if( bossFILE != NULL ) { fclose( bossFILE ); } error = false; double bossScale = LevelDirectoryManager::readDoubleFileContents( "bossScale", &error, true ); if( error ) { // default bossScale = 3; } error = false; double bossExplosionScale = LevelDirectoryManager::readDoubleFileContents( "bossExplosionScale", &error, true ); if( error ) { // default bossExplosionScale = 3; } error = false; double bossMinVelocity = LevelDirectoryManager::readDoubleFileContents( "bossMinVelocity", &error, true ); if( error ) { // default bossMinVelocity = 3; } error = false; double bossMaxVelocity = LevelDirectoryManager::readDoubleFileContents( "bossMaxVelocity", &error, true ); if( error ) { // default bossMaxVelocity = 30; } error = false; double bossBulletRange = LevelDirectoryManager::readDoubleFileContents( "bossBulletRange", &error, true ); if( error ) { // default bossBulletRange = 30; } error = false; double bossBulletBaseVelocity = LevelDirectoryManager::readDoubleFileContents( "bossBulletBaseVelocity", &error, true ); if( error ) { // default bossBulletBaseVelocity = 20; } error = false; double bossMinBulletsPerSecond = LevelDirectoryManager::readDoubleFileContents( "bossMinBulletsPerSecond", &error, true ); if( error ) { // default bossMinBulletsPerSecond = .5; } error = false; double bossMaxBulletsPerSecond = LevelDirectoryManager::readDoubleFileContents( "bossMaxBulletsPerSecond", &error, true ); if( error ) { // default bossMaxBulletsPerSecond = .5; } error = false; double bossTimeToGetAngry = LevelDirectoryManager::readDoubleFileContents( "bossTimeToGetAngry", &error, true ); if( error ) { // default bossTimeToGetAngry = 5; } error = false; double bossMaxHealth = LevelDirectoryManager::readDoubleFileContents( "bossMaxHealth", &error, true ); if( error ) { // default bossMaxHealth = 400; } error = false; double bossRecoveryRate = LevelDirectoryManager::readDoubleFileContents( "bossRecoveryRate", &error, true ); if( error ) { // default bossRecoveryRate = 40; } error = false; double bossExplosionTime = LevelDirectoryManager::readDoubleFileContents( "bossExplosionTime", &error, true ); if( error ) { // default bossExplosionTime = 2; } FILE *bossExplosionSoundFILE = LevelDirectoryManager::getStdStream( "bossExplosionSound", true ); error = false; BulletSound *bossExplosionSoundTemplate = new BulletSound( bossExplosionSoundFILE, &error ); if( error ) { printf( "Error reading from bossExplosionSound file\n" ); } if( bossExplosionSoundFILE != NULL ) { fclose( bossExplosionSoundFILE ); } // "stuff" that is spit out when a bullet hits the boss // re-use the ShipBullet code for it FILE *bossDamageFILE = LevelDirectoryManager::getStdStream( "bossDamage", true ); error = false; ShipBullet *bossDamageTemplate = new ShipBullet( bossDamageFILE, &error ); if( error ) { printf( "Error reading from bossDamage file\n" ); } if( bossDamageFILE != NULL ) { fclose( bossDamageFILE ); } error = false; double bossDamageScale = LevelDirectoryManager::readDoubleFileContents( "bossDamageScale", &error, true ); if( error ) { // default bossDamageScale = 1; } error = false; double bossDamageTime = LevelDirectoryManager::readDoubleFileContents( "bossDamageTime", &error, true ); if( error ) { // default bossDamageTime = 1; } mBossDamageManager = new ShipBulletManager( bossDamageTemplate, bossDamageScale, // no sounds for damage NULL, NULL, mMaxXPosition - mMinXPosition, mMaxYPosition - mMinYPosition ); mBossManager = new BossManager( bossTemplate, bossScale, bossExplosionScale, bossExplosionTime, mRandSource->getRandomDouble(), // explosion shape bossMinVelocity, bossMaxVelocity, mShipBulletManager, mBossBulletManager, mBossDamageManager, bossBulletRange, bossBulletBaseVelocity, bossMinBulletsPerSecond, // (calm) bossMaxBulletsPerSecond, // (angry) bossDamageTime, bossTimeToGetAngry, bossMaxHealth, bossRecoveryRate, new Vector3D( mMinXPosition + 20, 0, 0 ), mSoundPlayer, bossExplosionSoundTemplate ); FILE *portalFILE = LevelDirectoryManager::getStdStream( "portal", true ); error = false; ParameterizedObject *portalTemplate = new ParameterizedObject( portalFILE, &error ); if( portalFILE != NULL ) { fclose( portalFILE ); } error = false; double portalScale = LevelDirectoryManager::readDoubleFileContents( "portalScale", &error, true ); if( error ) { // default portalScale = 3; } error = false; double portalFadeTime = LevelDirectoryManager::readDoubleFileContents( "portalFadeTime", &error, true ); if( error ) { // default portalFadeTime = 2; } mPortalManager = new PortalManager( portalTemplate, portalScale, portalFadeTime, mMaxXPosition - mMinXPosition ); mShipInPortal = false; delete currentLevelDirectory; delete levelsDirectory; mCurrentShipRadius = 0; for( i=0; isetMusicPlayer( NULL ); delete mMusicPlayer; delete mSculptureManager; delete mBossBulletManager; delete mBossDamageManager; delete mBossManager; delete mPortalManager; delete mWaveTable; delete mCurrentShipVelocityVector; delete mBackgroundColor; delete mNearBossGridColor; delete mFarBossGridColor; delete mWeakUmbilicalColor; delete mStrongUmbilicalColor; } void GameSceneHandler::addRandomEnemy() { double x, y; x = mRandSource->getRandomDouble(); y = mRandSource->getRandomDouble(); // round one param at random to push all enemies to edges of world int coin = mRandSource->getRandomBoundedInt( 0, 1 ); if( coin == 0 ) { x = rint( x ); } else { y = rint( y ); } // enemies start out outside of world double maxXEnemyPosition = mMaxXPosition + 50; double minXEnemyPosition = mMinXPosition - 50; double maxYEnemyPosition = mMaxYPosition + 50; double minYEnemyPosition = mMinYPosition - 50; x = x * ( maxXEnemyPosition - minXEnemyPosition ) + minXEnemyPosition; y = y * ( maxYEnemyPosition - minYEnemyPosition ) + minYEnemyPosition; double randRotationZ = mRandSource->getRandomDouble() * 2 * M_PI; Vector3D *position = new Vector3D( x, y, 0 ); Angle3D *rotation = new Angle3D( 0, 0, randRotationZ ); double explosionShape = mRandSource->getRandomDouble(); // enemy shape determines bullet shape // thus, enemies that look the similar fire similar bullets double enemyShape = mRandSource->getRandomDouble(); double bulletClose = enemyShape; double bulletFar = 1 - enemyShape; mEnemyManager->addEnemy( enemyShape, explosionShape, 1, bulletClose, bulletFar, position, rotation ); } void GameSceneHandler::drawScene() { glClearColor( mBackgroundColor->r, mBackgroundColor->g, mBackgroundColor->b, mBackgroundColor->a ); glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); Vector3D *viewPosition = mScreen->getViewPosition(); viewPosition->mZ = 0; // draw a large, semi-transparent square to make trails fade over time /* glBegin( GL_QUADS ); glColor4f( 0, 0, 0, 0.75 / exp( mCurrentShipJarForce ) + .25 ); glVertex2d( mMinXPosition, mMinYPosition ); glVertex2d( mMinXPosition, mMaxYPosition ); glVertex2d( mMaxXPosition, mMaxYPosition ); glVertex2d( mMaxXPosition, mMinYPosition ); glEnd(); */ // draw a grid // grid color based on boss position Vector3D *bossPostion = mBossManager->getBossPosition(); glLineWidth( 2 ); glBegin( GL_LINES ); for( double x=mMinXPosition; x<=mMaxXPosition; x+=mGridSpacing ) { for( double y=mMinYPosition; y<=mMaxYPosition; y+=mGridSpacing ) { Vector3D *gridSpot = new Vector3D( x, y, 0 ); double bossDistance = gridSpot->getDistance( bossPostion ); double shipDistance = gridSpot->getDistance( viewPosition ); delete gridSpot; double nearColorWeight = 1 - bossDistance / ( mMaxXPosition - mMinXPosition ); if( shipDistance < 20 ) { // ship override's boss' effect on grid double shipFactor = 1 - ( shipDistance / 19 ); nearColorWeight -= shipFactor; } if( nearColorWeight < 0 ) { nearColorWeight = 0; } double farColorWeight = 1 - nearColorWeight; glColor4f( nearColorWeight * mNearBossGridColor->r + farColorWeight * mFarBossGridColor->r, nearColorWeight * mNearBossGridColor->g + farColorWeight * mFarBossGridColor->g, nearColorWeight * mNearBossGridColor->b + farColorWeight * mFarBossGridColor->b, nearColorWeight * mNearBossGridColor->a + farColorWeight * mFarBossGridColor->a ); glVertex2d( x, y ); if( y != mMinYPosition && y != mMaxYPosition ) { // draw start vertex for next segment glVertex2d( x, y ); } } } for( double y=mMinYPosition; y<=mMaxYPosition; y+=mGridSpacing ) { for( double x=mMinXPosition; x<=mMaxXPosition; x+=mGridSpacing ) { Vector3D *gridSpot = new Vector3D( x, y, 0 ); double bossDistance = gridSpot->getDistance( bossPostion ); double shipDistance = gridSpot->getDistance( viewPosition ); delete gridSpot; double nearColorWeight = 1 - bossDistance / ( mMaxXPosition - mMinXPosition ); if( shipDistance < 20 ) { // ship override's boss' effect on grid double shipFactor = 1 - ( shipDistance / 19 ); nearColorWeight -= shipFactor; } if( nearColorWeight < 0 ) { nearColorWeight = 0; } double farColorWeight = 1 - nearColorWeight; glColor4f( nearColorWeight * mNearBossGridColor->r + farColorWeight * mFarBossGridColor->r, nearColorWeight * mNearBossGridColor->g + farColorWeight * mFarBossGridColor->g, nearColorWeight * mNearBossGridColor->b + farColorWeight * mFarBossGridColor->b, nearColorWeight * mNearBossGridColor->a + farColorWeight * mFarBossGridColor->a ); glVertex2d( x, y ); if( x != mMinXPosition && x != mMaxXPosition ) { // draw start vertex for next segment glVertex2d( x, y ); } } } glEnd(); delete bossPostion; unsigned long lastMillisecondDelta = mFrameMillisecondDelta; // how many milliseconds have passed since the last frame mFrameMillisecondDelta = Time::getMillisecondsSince( mLastFrameSeconds, mLastFrameMilliseconds ); // lock down to 30 frames per second unsigned long minFrameTime = (unsigned long)( 1000 / mMaxFrameRate ); if( mFrameMillisecondDelta < minFrameTime ) { unsigned long timeToSleep = minFrameTime - mFrameMillisecondDelta; Thread::staticSleep( timeToSleep ); // get new frame second delta, including sleep time mFrameMillisecondDelta = Time::getMillisecondsSince( mLastFrameSeconds, mLastFrameMilliseconds ); } // avoid huge position "jumps" if we have a very large delay during a frame // (possibly caused by something going on in the background) // This will favor a slight visual slow down, but this is better than // a disorienting jump // skip this check if we are just starting up if( lastMillisecondDelta != 0 ) { if( mFrameMillisecondDelta > 6 * lastMillisecondDelta ) { // limit: this frame represents at most twice the jump of the last // frame // printf( "Limiting time jump (requested=%lu ms, last=%lu ms)\n", // mFrameMillisecondDelta, lastMillisecondDelta ); if( mFrameMillisecondDelta > 10000 ) { printf( "Time between frames more than 10 seconds:\n" ); // way too big... investigate printf( "Last time = %lu s, %lu ms\n", mLastFrameSeconds, mLastFrameMilliseconds ); Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); printf( "current time = %lu s, %lu ms\n", mLastFrameSeconds, mLastFrameMilliseconds ); } mFrameMillisecondDelta = 2 * lastMillisecondDelta; } } double frameSecondsDelta = (double)mFrameMillisecondDelta / 1000.0; // record the time that this frame was drawn Time::getCurrentTime( &mLastFrameSeconds, &mLastFrameMilliseconds ); if( !mPaused ) { // tell managers about the time delta mShipBulletManager->passTime( frameSecondsDelta ); mEnemyBulletManager->passTime( frameSecondsDelta ); mEnemyManager->passTime( frameSecondsDelta, viewPosition ); mSculptureManager->passTime( frameSecondsDelta ); mBossBulletManager->passTime( frameSecondsDelta ); mBossDamageManager->passTime( frameSecondsDelta ); mBossManager->passTime( frameSecondsDelta, viewPosition, mCurrentShipVelocityVector ); mPortalManager->passTime( frameSecondsDelta, viewPosition ); // don't add enemies if boss is dead if( ! mBossManager->isBossDead() ) { // replenish enemy force while( mEnemyManager->getEnemyCount() < mNumEnemies ) { addRandomEnemy(); } } } int i; SimpleVector *shipBulletObjects = mShipBulletManager->getDrawableObjects(); int numShipBulletObjects = shipBulletObjects->size(); SimpleVector *enemyBulletObjects = mEnemyBulletManager->getDrawableObjects(); int numEnemyBulletObjects = enemyBulletObjects->size(); SimpleVector *enemyObjects = mEnemyManager->getDrawableObjects(); int numEnemyObjects = enemyObjects->size(); SimpleVector *sculptureObjects = mSculptureManager->getDrawableObjects(); int numSculptureObjects = sculptureObjects->size(); SimpleVector *bossBulletObjects = mBossBulletManager->getDrawableObjects(); int numBossBulletObjects = bossBulletObjects->size(); SimpleVector *bossObjects = mBossManager->getDrawableObjects(); int numBossObjects = bossObjects->size(); // draw boss damage on top of boss SimpleVector *bossDamageObjects = mBossDamageManager->getDrawableObjects(); int numBossDamageObjects = bossDamageObjects->size(); SimpleVector *portalObjects = mPortalManager->getDrawableObjects(); int numPortalObjects = portalObjects->size(); // draw umbilical cord above grid but under everything else glLineWidth( 10 * mSculptureManager->getBulletPowerModifier() ); glBegin( GL_LINES ); { Color *closeUmbilicalColor = Color::linearSum( mStrongUmbilicalColor, mWeakUmbilicalColor, mSculptureManager->getCloseRangeBulletParameter() ); Color *farUmbilicalColor = Color::linearSum( mStrongUmbilicalColor, mWeakUmbilicalColor, mSculptureManager->getFarRangeBulletParameter() ); glColor4f( closeUmbilicalColor->r, closeUmbilicalColor->g, closeUmbilicalColor->b, 0 ); glVertex2d( viewPosition->mX, viewPosition->mY ); glColor4f( farUmbilicalColor->r, farUmbilicalColor->g, farUmbilicalColor->b, 1 ); glVertex2d( 0, 0 ); delete closeUmbilicalColor; delete farUmbilicalColor; } glEnd(); Vector3D *offsetVector = new Vector3D( 0, 0, 0 ); // draw the bullets and enemies with no extra rotation Angle3D *zeroAngle = new Angle3D( 0, 0, 0 ); // bottom layer is sculpture for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete sculptureObjects; // too confusing... don't draw for now /* // draw recenter dot on top of sculpture glPointSize( 15 ); glBegin( GL_POINTS ); { Vector3D *recenterPosition = mSculptureManager->getPiecePositionNeededToRecenter(); glColor4f( mWeakUmbilicalColor->r, mWeakUmbilicalColor->g, mWeakUmbilicalColor->b, 1 ); // don't draw if sculpture is centered if( recenterPosition->mX != 0 || recenterPosition->mY != 0 ) { glVertex2d( recenterPosition->mX, recenterPosition->mY ); } delete recenterPosition; } glEnd(); */ // draw music cursor on top of sculpture but under other stuff glLineWidth( 2 ); glBegin( GL_LINES ); { if( mSculptureManager->getNumPiecesInSculpture() > 0 ) { // music is playing // draw the music cursor double musicCursorPosition = mMusicPlayer->getCurrentPartGridPosition(); // move it back by half a grid space musicCursorPosition -= mGridSpacing / 2; glColor4f( 0, 1, 0, 0 ); glVertex2d( musicCursorPosition, mMinYPosition ); glColor4f( 0, 1, 0, 0.75 ); glVertex2d( musicCursorPosition, mMinYPosition / 2 ); glVertex2d( musicCursorPosition, mMinYPosition / 2 ); glColor4f( 1, 1, 0, 0.75 ); glVertex2d( musicCursorPosition, 0 ); glVertex2d( musicCursorPosition, 0 ); glColor4f( 1, 0, 0, 0.75 ); glVertex2d( musicCursorPosition, mMaxYPosition / 2 ); glVertex2d( musicCursorPosition, mMaxYPosition / 2 ); glColor4f( 1, 0, 0, 0 ); glVertex2d( musicCursorPosition, mMaxYPosition ); } } glEnd(); // next layer is bullets for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete enemyBulletObjects; for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete bossBulletObjects; for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete shipBulletObjects; // then enemies for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete enemyObjects; for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete bossObjects; // then boss damage for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete bossDamageObjects; // finally portal for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); delete component; } delete portalObjects; delete zeroAngle; delete offsetVector; // map the speed into the range [0,1], with full-speed backward at 0, // stationary at 0.5, and full-speed forward at 1 double shipParameter = ( ( mForwardBackwardMoveRate / mMaxMoveRate ) / 2 ) + 0.5; Angle3D *viewOrientation = mScreen->getViewOrientation(); double rotationRate; SimpleVector *shipObjects = mShipParameterSpace->getDrawableObjects( shipParameter, &rotationRate ); int numComponentObjects = shipObjects->size(); // compute the half radius of this ship as we draw it double minRadius = DBL_MAX; double maxRadius = 0; // draw ship components for( int j=0; jgetElement( j ) ); component->draw( mShipScale, viewOrientation, viewPosition ); component->scale( mShipScale ); component->rotate( viewOrientation ); component->move( viewPosition ); double componentMinRadius = component->getBorderMinDistance( viewPosition ); double componentMaxRadius = component->getBorderMaxDistance( viewPosition ); if( componentMinRadius < minRadius ) { minRadius = componentMinRadius; } if( componentMaxRadius > maxRadius ) { maxRadius = componentMaxRadius; } delete component; } mCurrentShipRadius = ( minRadius + maxRadius ) / 2; delete shipObjects; delete viewPosition; // draw an overlay rectangle if we are fading out if( mFadeLevel < 1 ) { glBegin( GL_QUADS ); glColor4f( 0, 0, 0, 1 - mFadeLevel ); glVertex2d( 20 * mMinXPosition, 20 * mMinYPosition ); glVertex2d( 20 * mMinXPosition, 20 * mMaxYPosition ); glVertex2d( 20 * mMaxXPosition, 20 * mMaxYPosition ); glVertex2d( 20 * mMaxXPosition, 20 * mMinYPosition ); glEnd(); } mNumFrames ++; if( mPrintFrameRate ) { if( mNumFrames % mFrameBatchSize == 0 ) { // finished a batch unsigned long timeDelta = Time::getMillisecondsSince( mFrameBatchStartTimeSeconds, mFrameBatchStartTimeMilliseconds ); double frameRate = 1000 * (double)mFrameBatchSize / (double)timeDelta; printf( "Frame rate = %f frames/second\n", frameRate ); mFrameBatchStartTimeSeconds = mLastFrameSeconds; mFrameBatchStartTimeMilliseconds = mLastFrameMilliseconds; } } } void GameSceneHandler::keyPressed( unsigned char inKey, int inX, int inY ) { if( mPaused ) { // ignore all keys except for un-pause and quit if( inKey == 'p' || inKey == 'P' ) { mPaused = !mPaused; } else if( inKey == 'q' || inKey == 'Q' ) { // quit ::exit( 0 ); } return; } if( mShipInPortal ) { // ignore all other keys return; } if( inKey == ' ' ) { // make sure there are not already too many bullets if( mShipBulletManager->getBulletCount() < mMaxNumShipBullets ) { // fire a bullet Vector3D *viewPosition = mScreen->getViewPosition(); viewPosition->mZ = 0; Angle3D *viewAngle = new Angle3D( mScreen->getViewOrientation() ); Vector3D *velocityVector = new Vector3D( 0, mShipBulletBaseVelocity, 0 ); velocityVector->rotate( viewAngle ); velocityVector->add( mCurrentShipVelocityVector ); double bulletMoveRate = velocityVector->getLength(); mShipBulletManager->addBullet( mSculptureManager->getCloseRangeBulletParameter(), mSculptureManager->getFarRangeBulletParameter(), mSculptureManager->getBulletPowerModifier(), ( mShipBulletRange / mShipBulletBaseVelocity ) * bulletMoveRate, viewPosition, viewAngle, velocityVector ); } } if( inKey == 'd' || inKey == 'D' ) { if( mCurrentPieceCarried != -1 ) { // drop the piece and align it to the grid Vector3D *droppedPiecePosition = mScreen->getViewPosition(); double pieceX = droppedPiecePosition->mX; double pieceY = droppedPiecePosition->mY; // round to closest multiples of 10 (grid points) pieceX = pieceX / 10; pieceY = pieceY / 10; pieceX = 10 * rint( pieceX ); pieceY = 10 * rint( pieceY ); droppedPiecePosition->mX = pieceX; droppedPiecePosition->mY = pieceY; droppedPiecePosition->mZ = 0; // enable smooth move to closest grid point mSculptureManager->turnMagnetModeOn( mCurrentPieceCarried ); mSculptureManager->setPiecePosition( mCurrentPieceCarried, droppedPiecePosition ); delete droppedPiecePosition; mCurrentPieceCarried = -1; } else { // try picking up a piece; Vector3D *viewPosition = mScreen->getViewPosition(); viewPosition->mZ = 0; // double ship radius to make piece pick-up easier mCurrentPieceCarried = mSculptureManager->getSculpturePieceInCircle( viewPosition, 2 * mCurrentShipRadius ); delete viewPosition; if( mCurrentPieceCarried != -1 ) { // enable smooth piece move as we pick up the piece mSculptureManager->turnMagnetModeOn( mCurrentPieceCarried ); } } } else if( inKey == 's' || inKey == 'S' ) { mMovingLeft = true; } else if( inKey == 'f' || inKey == 'F' ) { mMovingRight = true; } else if( inKey == 'p' || inKey == 'P' ) { mPaused = !mPaused; } else if( inKey == 'q' || inKey == 'Q' ) { // quit ::exit( 0 ); } } void GameSceneHandler::keyReleased( unsigned char inKey, int inX, int inY ) { // never ignore key releases if( inKey == 's' || inKey == 'S' ) { mMovingLeft = false; } else if( inKey == 'f' || inKey == 'F' ) { mMovingRight = false; } /* else if( inKey == '8' ) { // cheat... skip to next level // jump to full fade-out to show proper fade-in when loading // next level mFadeLevel = 0; destroyLevel(); loadNextLevel(); } */ } void GameSceneHandler::specialKeyPressed( int inKey, int inX, int inY ) { if( mPaused || mShipInPortal ) { // ignore keys return; } if( inKey == GLUT_KEY_UP ) { mMovingUp = true; } else if( inKey == GLUT_KEY_DOWN ) { mMovingDown = true; } else if( inKey == GLUT_KEY_LEFT ) { mRotatingCounterClockwise = true; } else if( inKey == GLUT_KEY_RIGHT ) { mRotatingClockwise = true; } } void GameSceneHandler::specialKeyReleased( int inKey, int inX, int inY ) { // never ignore key releases if( inKey == GLUT_KEY_UP ) { mMovingUp = false; } else if( inKey == GLUT_KEY_DOWN ) { mMovingDown = false; } else if( inKey == GLUT_KEY_LEFT ) { mRotatingCounterClockwise = false; } else if( inKey == GLUT_KEY_RIGHT ) { mRotatingClockwise = false; } } void GameSceneHandler::fireRedraw() { if( mPaused ) { // ignore redraw event return; } double frameSecondsDelta = (double)mFrameMillisecondDelta / 1000.0; Vector3D *moveVector = new Vector3D( 0, 0, 0 ); if( mMovingUp ) { mForwardBackwardMoveRate += mShipAccelleration * frameSecondsDelta; if( mForwardBackwardMoveRate > mMaxMoveRate ) { mForwardBackwardMoveRate = mMaxMoveRate; } } if( mMovingDown ) { mForwardBackwardMoveRate -= mShipAccelleration * frameSecondsDelta; if( mForwardBackwardMoveRate < -mMaxMoveRate ) { mForwardBackwardMoveRate = -mMaxMoveRate; } } moveVector->mY += mForwardBackwardMoveRate; if( mMovingRight ) { mRightLeftMoveRate += mShipAccelleration * frameSecondsDelta; if( mRightLeftMoveRate > mMaxMoveRate ) { mRightLeftMoveRate = mMaxMoveRate; } } if( mMovingLeft ) { mRightLeftMoveRate -= mShipAccelleration * frameSecondsDelta; if( mRightLeftMoveRate < -mMaxMoveRate ) { mRightLeftMoveRate = -mMaxMoveRate; } } // x axis flipped on screen moveVector->mX -= mRightLeftMoveRate; // friction to slow ship down if( !mMovingUp && mForwardBackwardMoveRate > 0 ) { mForwardBackwardMoveRate -= mShipFriction * frameSecondsDelta; if( mForwardBackwardMoveRate < 0 ) { mForwardBackwardMoveRate = 0; } } if( !mMovingDown && mForwardBackwardMoveRate < 0 ) { mForwardBackwardMoveRate += mShipFriction * frameSecondsDelta; if( mForwardBackwardMoveRate > 0 ) { mForwardBackwardMoveRate = 0; } } if( !mMovingRight && mRightLeftMoveRate > 0 ) { mRightLeftMoveRate -= mShipFriction * frameSecondsDelta; if( mRightLeftMoveRate < 0 ) { mRightLeftMoveRate = 0; } } if( !mMovingLeft && mRightLeftMoveRate < 0 ) { mRightLeftMoveRate += mShipFriction * frameSecondsDelta; if( mRightLeftMoveRate > 0 ) { mRightLeftMoveRate = 0; } } Vector3D *viewPosition = mScreen->getViewPosition(); // zoom out at high speeds double netMotionRate = sqrt( mForwardBackwardMoveRate * mForwardBackwardMoveRate + mRightLeftMoveRate * mRightLeftMoveRate ); viewPosition->mZ = baseViewZ - 40 * netMotionRate / mMaxMoveRate; // also zoom from way out when we are fading in // and zoom to way out when we are fading out viewPosition->mZ -= 1000 * ( 1 - mFadeLevel ); mScreen->setViewPosition( viewPosition ); delete viewPosition; // we rotate at a fixed number of angle units per second if( mRotatingClockwise ) { if( mRotationRate <= 0 ) { // jump right to base rate mRotationRate = mBaseRotationRate; } // add accelleration mRotationRate += mShipRotationAccelleration * frameSecondsDelta; if( mRotationRate > mMaxRotationRate ) { mRotationRate = mMaxRotationRate; } } else if( mRotatingCounterClockwise ) { if( mRotationRate >= 0 ) { // jump right to base rate mRotationRate = -mBaseRotationRate; } // add accelleration mRotationRate -= mShipRotationAccelleration * frameSecondsDelta; if( mRotationRate < -mMaxRotationRate ) { mRotationRate = -mMaxRotationRate; } } else { // unlike motion, we stop rotating instantly when key is released mRotationRate = 0; } Angle3D *rotationDelta = new Angle3D( 0, 0, mRotationRate * frameSecondsDelta ); if( !mShipInPortal ) { mScreen->rotateView( rotationDelta ); } delete rotationDelta; Angle3D *rotation = new Angle3D( mScreen->getViewOrientation() ); moveVector->rotate( rotation ); // moveVector in units per second // save it as our current velocity vector delete mCurrentShipVelocityVector; mCurrentShipVelocityVector = new Vector3D( moveVector ); // must be scaled by how many seconds are in our frame moveVector->scale( frameSecondsDelta ); if( !mShipInPortal ) { mScreen->moveView( moveVector ); } delete rotation; delete moveVector; // wrap around if we go out of bounds Vector3D *jumpToWrapVector = new Vector3D( 0, 0, 0 ); Vector3D *currentPosition = mScreen->getViewPosition(); if( currentPosition->mX > mMaxXPosition ) { //jumpToWrapVector->mX = -100; jumpToWrapVector->mX = mMaxXPosition - currentPosition->mX; mCurrentShipVelocityVector->mX = 0; } if( currentPosition->mX < mMinXPosition ) { //jumpToWrapVector->mX = 100; jumpToWrapVector->mX = mMinXPosition - currentPosition->mX; mCurrentShipVelocityVector->mX = 0; } if( currentPosition->mY > mMaxYPosition ) { //jumpToWrapVector->mY = -100; jumpToWrapVector->mY = mMaxYPosition - currentPosition->mY; mCurrentShipVelocityVector->mY = 0; } if( currentPosition->mY < mMinYPosition ) { //jumpToWrapVector->mY = 100; jumpToWrapVector->mY = mMinYPosition - currentPosition->mY; mCurrentShipVelocityVector->mY = 0; } if( !mShipInPortal ) { mScreen->moveView( jumpToWrapVector ); } delete jumpToWrapVector; delete currentPosition; currentPosition = mScreen->getViewPosition(); currentPosition->mZ = 0; // test if we have been hit by an enemy bullet double enemyBulletPower = mEnemyBulletManager->getBulletPowerInCircle( currentPosition, mCurrentShipRadius ); double bossBulletPower = mBossBulletManager->getBulletPowerInCircle( currentPosition, mCurrentShipRadius ); // scale bullet powers by time delta to make it framerate independent double jarForceIncrease = frameSecondsDelta * enemyBulletPower * mEnemyBulletShipJarPower + frameSecondsDelta * bossBulletPower * mBossBulletShipJarPower; // increased by being hit by bullets mCurrentShipJarForce += jarForceIncrease; // decayed by friction mCurrentShipJarForce -= mShipAccelleration * frameSecondsDelta; // never negative if( mCurrentShipJarForce < 0 ) { mCurrentShipJarForce = 0; } // jar ship toward center Vector3D *jarVector = new Vector3D( 0, 0, 0 ); jarVector->subtract( currentPosition ); // don't jar if already close to center double distanceFromCenter = jarVector->getLength(); if( distanceFromCenter > 1 ) { jarVector->normalize(); // compute jar velocity vector jarVector->scale( mCurrentShipJarForce ); //mCurrentShipVelocityVector->add( jarVector ); // scale by time delta to get actual distance jarred during this frame jarVector->scale( frameSecondsDelta ); if( jarVector->getLength() > distanceFromCenter ) { // this jar vector will move us past the center // ignore it and cancel the jar force mCurrentShipJarForce = 0; // move the ship to the center Vector3D *moveToCenterVector = new Vector3D( 0, 0, 0 ); moveToCenterVector->subtract( currentPosition ); if( !mShipInPortal ) { mScreen->moveView( moveToCenterVector ); } delete moveToCenterVector; } else { if( !mShipInPortal ) { mScreen->moveView( jarVector ); } } } else { // close to center, so jar force immediately decays mCurrentShipJarForce = 0; } delete jarVector; delete currentPosition; currentPosition = mScreen->getViewPosition(); currentPosition->mZ = 0; // move the piece we are carrying if( mCurrentPieceCarried != -1 ) { if( mSculptureManager->isPieceJarred( mCurrentPieceCarried ) || mCurrentShipJarForce > 0 ) { // drop piece if it is being jarred or if we are jarred mCurrentPieceCarried = -1; } else { mSculptureManager->setPiecePosition( mCurrentPieceCarried, currentPosition ); } } if( mBossManager->isBossDead() && ! mPortalManager->isPortalVisible() ) { // show portal where boss died mPortalManager->showPortal( mBossManager->getBossPosition() ); // destroy all enemies mEnemyManager->explodeAllEnemies(); } else if( mBossManager->isBossDead() && mPortalManager->isPortalVisible() && mPortalManager->isShipInPortal( currentPosition ) ) { mShipInPortal = true; // handle fade out if( mFadeLevel > 0 ) { // fade out more double fadeDelta = frameSecondsDelta / mFadeTime; mFadeLevel -= fadeDelta; if( mFadeLevel < 0 ) { mFadeLevel = 0; } // fade music loudness too to avoid clicks when we stop // this level's music mSoundPlayer->setMusicLoudness( mMusicLoudness * mFadeLevel ); } if( mFadeLevel == 0 ) { // switch to next level destroyLevel(); loadNextLevel(); } } // handle fade in if( ! mBossManager->isBossDead() && mFadeLevel < 1 ) { // fade in more double fadeDelta = frameSecondsDelta / mFadeTime; mFadeLevel += fadeDelta; if( mFadeLevel > 1 ) { mFadeLevel = 1; } // fade music loudness back in mSoundPlayer->setMusicLoudness( mMusicLoudness * mFadeLevel ); } delete currentPosition; } transcend-0.3+dfsg2.orig/Transcend/game/BulletSound.h0000640000175000017500000000325610112311536021167 0ustar pabspabs/* * Modification History * * 2004-August-15 Jason Rohrer * Created. */ #ifndef BULLET_SOUND_INCLUDED #define BULLET_SOUND_INCLUDED #include "ParameterizedStereoSound.h" #include "PlayableSound.h" /** * A bullet sound that can be controled with 2 parameters. * * @author Jason Rohrer. */ class BulletSound { public: /** * Constructs a bullet sound by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading the bullet * from inFILE fails. */ BulletSound( FILE *inFILE, char *outError ); virtual ~BulletSound(); /** * Get a playable sound objects from this sound template. * * @param inCloseRangeParameter a parameter in the range [0,1] to * control the shape/power of the bullet at close range. * @param inFarRangeParameter a parameter in the range [0,1] to * control the shape/power of the bullet at far range. * @param inSamplesPerSecond the current sample rate. * * @return a playable sound. * Must be destroyed by caller. */ PlayableSound *getPlayableSound( double inCloseRangeParameter, double inFarRangeParameter, unsigned long inSamplesPerSecond ); protected: ParameterizedStereoSound *mCloseRangeSpace; ParameterizedStereoSound *mFarRangeSpace; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/MusicPlayer.h0000640000175000017500000000461110112665172021170 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. */ #ifndef MUSIC_PLAYER_INCLUDED #define MUSIC_PLAYER_INCLUDED #include "SoundSamples.h" #include "SculptureManager.h" #include "MusicNoteWaveTable.h" #include "minorGems/util/SimpleVector.h" /** * Class that plays music notes * * @author Jason Rohrer */ class MusicPlayer { public: /** * Constructs a player. * Reads configuration using the LevelDirectoryManager. * * @param inSamplesPerSecond the sample rate. * @param inSculptureManager the sculpture manager to * get music notes from. * Must be destroyed by caller after this class is destroyed. * @param inWaveTable the wave table to use when rendering notes. * Must be destroyed by caller after this class is destroyed. * @param inWorldWidth the width of the world. * @param inWorldWidth the height of the world. * @param inGridSpaceWidth the width of each grid space in the world. */ MusicPlayer( unsigned long inSamplesPerSecond, SculptureManager *inSculptureManager, MusicNoteWaveTable *inWaveTable, double inWorldWidth, double inWorldHeight, double inGridSpaceWidth ); ~MusicPlayer(); /** * Gets more samples of music from this player. * * @param inNumSamples the number of samples to get. * * @return a buffer of samples. Must be destroyed by caller. */ SoundSamples *getMoreMusic( unsigned long inNumSamples ); /** * Gets the grid position in the world that is currently being * played. * * @return the grid position in world units. */ double getCurrentPartGridPosition(); protected: SculptureManager *mSculptureManager; MusicNoteWaveTable *mWaveTable; SimpleVector *mActiveNotes; SimpleVector *mNotePositions; double mPartLengthInSeconds; double mCurrentPartGridPosition; double mSampleRate; double mWorldWidth; double mWorldHeight; double mGridSpaceWidth; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SoundSamples.h0000640000175000017500000000434210106647550021354 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added a constructor that can specify all sound data. */ #ifndef SOUND_SAMPLES_INCLUDED #define SOUND_SAMPLES_INCLUDED /** * Class that encapsulates a buffer of sound samples. * * @author Jason Rohrer */ class SoundSamples { public: unsigned long mSampleCount; float *mLeftChannel; float *mRightChannel; /** * Constructs a sound samples object. * * @param inSampleCount the number of samples. * @param inLeftChannel samples for the left channel. * Will be destroyed when this class is destroyed. * @param inRightChannel samples for the right channel. * Will be destroyed when this class is destroyed. */ SoundSamples( unsigned long inSampleCount, float *inLeftChannel, float *inRightChannel ); /** * Constructs a sound samples object filled with 0 samples. * * @param inSampleCount the number of samples. */ SoundSamples( unsigned long inSampleCount ); /** * Constructs a sound samples object by copying another object. * * @param inSamplesToCopy the object to copy. * Must be destroyed by caller. */ SoundSamples( SoundSamples *inSamplesToCopy ); /** * Constructs a sound samples object by copying samples from * another object. * * @param inSamplesToCopy the object to copy. * Must be destroyed by caller. * @param inNumToCopy the number of samples from the start of * inSamplesToCopy to take. */ SoundSamples( SoundSamples *inSamplesToCopy, unsigned long inNumToCopy ); ~SoundSamples(); /** * Trims samples from the beginning of this sound. * * @param inNumSamplesToDrop the number of samples at the beginning * of this sound to drop. */ void trim( unsigned long inNumSamplesToDrop ); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/StereoSoundParameterSpaceControlPoint.cpp0000640000175000017500000000700310107703602026721 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added support for getting blocks of samples. * * 2004-August-15 Jason Rohrer * Added function that generates a playable sound. */ #include "StereoSoundParameterSpaceControlPoint.h" #include "OnePointPlayableSound.h" StereoSoundParameterSpaceControlPoint::StereoSoundParameterSpaceControlPoint( SoundParameterSpaceControlPoint *inLeftPoint, SoundParameterSpaceControlPoint *inRightPoint ) : mLeftPoint( inLeftPoint ), mRightPoint( inRightPoint ) { } StereoSoundParameterSpaceControlPoint::StereoSoundParameterSpaceControlPoint( FILE *inFILE, char *outError ) { char errorLeft = false; char errorRight = false; mLeftPoint = new SoundParameterSpaceControlPoint( inFILE, &errorLeft ); mRightPoint = new SoundParameterSpaceControlPoint( inFILE, &errorRight ); *outError = errorLeft || errorRight; } StereoSoundParameterSpaceControlPoint:: ~StereoSoundParameterSpaceControlPoint() { delete mLeftPoint; delete mRightPoint; } ParameterSpaceControlPoint *StereoSoundParameterSpaceControlPoint::copy() { // make copy of each channel point return new StereoSoundParameterSpaceControlPoint( (SoundParameterSpaceControlPoint *)( mLeftPoint->copy() ), (SoundParameterSpaceControlPoint *)( mRightPoint->copy() ) ); } ParameterSpaceControlPoint *StereoSoundParameterSpaceControlPoint:: createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ) { StereoSoundParameterSpaceControlPoint *otherPoint = (StereoSoundParameterSpaceControlPoint *)inOtherPoint; SoundParameterSpaceControlPoint *blendLeft = (SoundParameterSpaceControlPoint *)( mLeftPoint->createLinearBlend( otherPoint->mLeftPoint, inWeightOfOtherPoint ) ); SoundParameterSpaceControlPoint *blendRight = (SoundParameterSpaceControlPoint *)( mRightPoint->createLinearBlend( otherPoint->mRightPoint, inWeightOfOtherPoint ) ); return new StereoSoundParameterSpaceControlPoint( blendLeft, blendRight ); } SoundSamples *StereoSoundParameterSpaceControlPoint::getSoundSamples( unsigned long inStartSample, unsigned long inSampleCount, unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ) { float *leftSamples = mLeftPoint->getSoundSamples( inStartSample, inSampleCount, inSamplesPerSecond, inSoundLengthInSeconds ); float *rightSamples = mRightPoint->getSoundSamples( inStartSample, inSampleCount, inSamplesPerSecond, inSoundLengthInSeconds ); return new SoundSamples( inSampleCount, leftSamples, rightSamples ); } PlayableSound *StereoSoundParameterSpaceControlPoint::getPlayableSound( unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ) { return new OnePointPlayableSound( (StereoSoundParameterSpaceControlPoint *)( this->copy() ), inSoundLengthInSeconds, inSamplesPerSecond ); } transcend-0.3+dfsg2.orig/Transcend/game/SoundFilter.h0000640000175000017500000000164210100010467021156 0ustar pabspabs/* * Modification History * * 2004-July-22 Jason Rohrer * Created. */ #ifndef SOUND_FILTER_INCLUDED #define SOUND_FILTER_INCLUDED #include "SoundSamples.h" /** * Interface for a class that can filter sound. * * @author Jason Rohrer */ class SoundFilter { public: /** * Filters sound samples. * * @param inSamples the samples to filter. * Must be destroyed by caller. * * @return the resulting samples in a newly constructed object. * Must be destroyed by caller. */ virtual SoundSamples *filterSamples( SoundSamples *inSamples ) = 0; // virtual destructor to ensure proper destruction of classes that // implement this interface virtual ~SoundFilter(); }; // does nothing, needed to make compiler happy inline SoundFilter::~SoundFilter() { } #endif transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedStereoSound.cpp0000640000175000017500000000623610107703602024255 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #include "ParameterizedStereoSound.h" #include "StereoSoundParameterSpaceControlPoint.h" #include "SoundParameterSpaceControlPoint.h" #include "OnePointPlayableSound.h" ParameterizedStereoSound::ParameterizedStereoSound( FILE *inFILE, char *outError ) { char readError = false; // read the sound length int numRead = fscanf( inFILE, "%lf", &mSoundLengthInSeconds ); if( numRead != 1 ) { readError = true; printf( "Error: failed to read sound length from sound space.\n" ); } SimpleVector *controlPoints = new SimpleVector(); SimpleVector *controlPointParameterAnchors = new SimpleVector(); // keep reading parameter anchors and control points until we // can read no more while( !readError ) { // read the parameter space anchor double anchor = 0; numRead = fscanf( inFILE, "%lf", &anchor ); if( numRead != 1 ) { readError = true; } else { // read the control point StereoSoundParameterSpaceControlPoint *point = new StereoSoundParameterSpaceControlPoint( inFILE, &readError ); if( !readError ) { controlPointParameterAnchors->push_back( anchor ); controlPoints->push_back( point ); } else { delete point; } } } mNumControlPoints = controlPoints->size(); mControlPoints = controlPoints->getElementArray(); mControlPointParameterAnchors = controlPointParameterAnchors->getElementArray(); delete controlPoints; delete controlPointParameterAnchors; if( mNumControlPoints >= 2 ) { *outError = false; } else { // we didn't read enough control points *outError = true; } } PlayableSound *ParameterizedStereoSound::getPlayableSound( double inParameter, unsigned long inSamplesPerSecond ) { // blend the two points, using the distance to weight them StereoSoundParameterSpaceControlPoint *blendedPoint = getBlendedControlPoint( inParameter ); if( blendedPoint != NULL ) { return new OnePointPlayableSound( blendedPoint, mSoundLengthInSeconds, inSamplesPerSecond ); } else { printf( "Error: no control points in sound space.\n" ); return NULL; } } StereoSoundParameterSpaceControlPoint *ParameterizedStereoSound:: getBlendedControlPoint( double inParameter ) { // cast result of super-class function call and return it return (StereoSoundParameterSpaceControlPoint*) ParameterizedSpace::getBlendedControlPoint( inParameter ); } double ParameterizedStereoSound::getSoundLengthInSeconds() { return mSoundLengthInSeconds; } transcend-0.3+dfsg2.orig/Transcend/game/PortalManager.cpp0000640000175000017500000000753610133727317022036 0ustar pabspabs/* * Modification History * * 2004-October-13 Jason Rohrer * Created. */ #include "PortalManager.h" PortalManager::PortalManager( ParameterizedObject *inPortalTemplate, double inPortalScale, double inPortalFadeInTimeSeconds, double inGridWidth ) : mPortalTemplate( inPortalTemplate ), mPortalScale( inPortalScale ), mFadeInTimeSeconds( inPortalFadeInTimeSeconds ), mFadeFactor( 0 ), mGridWidth( inGridWidth ), mCurrentPosition( NULL ), mCurrentRadius( 0 ), mCurrentRotation( new Angle3D( 0, 0, 0 ) ) { } PortalManager::~PortalManager() { delete mPortalTemplate; if( mCurrentPosition != NULL ) { delete mCurrentPosition; } delete mCurrentRotation; } void PortalManager::showPortal( Vector3D *inPosition ) { mCurrentPosition = inPosition; } char PortalManager::isPortalVisible() { if( mCurrentPosition != NULL ) { return true; } else { return false; } } void PortalManager::passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition ) { // if portal has been shown if( mCurrentPosition != NULL ) { double distanceToShip = mCurrentPosition->getDistance( inShipPosition ); // 25 units, up to 20 units from target, is the range double compressedDistance = ( distanceToShip - 20 ) / ( 25 ); // limit the range if( compressedDistance > 1 ) { compressedDistance = 1; } else if( compressedDistance < 0 ) { compressedDistance = 0; } // change shape based on ship distance mPortalShapeParameter = compressedDistance; // rotate mCurrentRotation->mZ += mCurrentRotationRate * inTimeDeltaInSeconds; // fade in if( mFadeFactor < 1 ) { mFadeFactor += (double)inTimeDeltaInSeconds / (double) mFadeInTimeSeconds; if( mFadeFactor > 1 ) { mFadeFactor = 1; } } } } char PortalManager::isShipInPortal( Vector3D *inShipPosition ) { if( mCurrentPosition != NULL ) { double distance = mCurrentPosition->getDistance( inShipPosition ); if( distance < mCurrentRadius ) { return true; } else { return false; } } else { // portal not shown return false; } } SimpleVector *PortalManager::getDrawableObjects() { // if portal has been shown if( mCurrentPosition != NULL ) { SimpleVector *objects = mPortalTemplate->getDrawableObjects( mPortalShapeParameter, &mCurrentRotationRate ); // scale, rotate, position, and fade the objects int numObjects = objects->size(); // compute the maximum radius of this enemy double maxRadius = 0; for( int j=0; jgetElement( j ) ); currentObject->scale( mPortalScale ); currentObject->rotate( mCurrentRotation ); currentObject->move( mCurrentPosition ); currentObject->fade( mFadeFactor ); double radius = currentObject->getBorderMaxDistance( mCurrentPosition ); if( radius > maxRadius ) { maxRadius = radius; } } mCurrentRadius = maxRadius; return objects; } else { // return an empty vector return new SimpleVector(); } } transcend-0.3+dfsg2.orig/Transcend/game/ObjectParameterSpaceControlPoint.cpp0000640000175000017500000004642710114756117025701 0ustar pabspabs/* * Modification History * * 2004-June-12 Jason Rohrer * Created. * * 2004-June-15 Jason Rohrer * Added a copy function. * * 2004-June-18 Jason Rohrer * Changed to use triangles instead of polygons. * * 2004-August-9 Jason Rohrer * Made a subclass of ParameterSpaceControlPoint. * * 2004-August-12 Jason Rohrer * Optimized Color constructor. * Optimized blendColorArrays function. * * 2004-August-26 Jason Rohrer * Added scaling of rotated copies to support spiral shapes. * * 2004-August-27 Jason Rohrer * Added function for writing out to file. * * 2004-August-29 Jason Rohrer * Added a scale factor for the angle of rotated copies. * * 2004-August-30 Jason Rohrer * Optimization: avoid object blending whenever possible. */ #include "ObjectParameterSpaceControlPoint.h" #include "NamedColorFactory.h" #include ObjectParameterSpaceControlPoint::ObjectParameterSpaceControlPoint( int inNumTriangleVertices, Vector3D **inTriangleVertices, Color **inTriangleVertexFillColors, int inNumBorderVertices, Vector3D **inBorderVertices, Color **inBorderVertexColors, double inBorderWidth, double inNumRotatedCopies, double inRotatedCopyScaleFactor, double inRotatedCopyAngleScaleFactor, double inRotationRate ) : mNumTriangleVertices( inNumTriangleVertices ), mTriangleVertices( inTriangleVertices ), mTriangleVertexFillColors( inTriangleVertexFillColors ), mNumBorderVertices( inNumBorderVertices ), mBorderVertices( inBorderVertices ), mBorderVertexColors( inBorderVertexColors ), mBorderWidth( inBorderWidth ), mNumRotatedCopies( inNumRotatedCopies ), mRotatedCopyScaleFactor( inRotatedCopyScaleFactor ), mRotatedCopyAngleScaleFactor( inRotatedCopyAngleScaleFactor ), mRotationRate( inRotationRate ) { } ObjectParameterSpaceControlPoint::ObjectParameterSpaceControlPoint( FILE *inFILE, char *outError ) { int totalNumRead = 0; mNumTriangleVertices = 0; totalNumRead += fscanf( inFILE, "%d", &mNumTriangleVertices ); // how many values should we successfully read (cheap error checking) int totalToRead = 1 + // read the number of triangle vertices mNumTriangleVertices * ( 2 + 1); // each vertex, read x, y, and 1 // color mTriangleVertices = new Vector3D*[ mNumTriangleVertices ]; mTriangleVertexFillColors = new Color*[ mNumTriangleVertices ]; // read coordinates and colors for each vertex for( int i=0; imX ); fprintf( inFILE, "%f\n", mTriangleVertices[i]->mY ); writeColorToFile( inFILE, mTriangleVertexFillColors[i] ); } fprintf( inFILE, "\n" ); fprintf( inFILE, "%d\n\n", mNumBorderVertices ); // read coordinates and colors for each vertex for( int i=0; imX ); fprintf( inFILE, "%f\n", mBorderVertices[i]->mY ); writeColorToFile( inFILE, mBorderVertexColors[i] ); } fprintf( inFILE, "\n" ); fprintf( inFILE, "%f\n", mBorderWidth ); fprintf( inFILE, "%f\n", mNumRotatedCopies ); fprintf( inFILE, "%f\n", mRotatedCopyScaleFactor ); fprintf( inFILE, "%f\n", mRotatedCopyAngleScaleFactor ); fprintf( inFILE, "%f\n", mRotationRate ); } ParameterSpaceControlPoint *ObjectParameterSpaceControlPoint::copy() { return new ObjectParameterSpaceControlPoint ( mNumTriangleVertices, duplicateVertextArray( mTriangleVertices, mNumTriangleVertices ), duplicateColorArray( mTriangleVertexFillColors, mNumTriangleVertices ), mNumBorderVertices, duplicateVertextArray( mBorderVertices, mNumBorderVertices ), duplicateColorArray( mBorderVertexColors, mNumBorderVertices ), mBorderWidth, mNumRotatedCopies, mRotatedCopyScaleFactor, mRotatedCopyAngleScaleFactor, mRotationRate ); } ParameterSpaceControlPoint * ObjectParameterSpaceControlPoint::createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ) { // cast ObjectParameterSpaceControlPoint *otherPoint = (ObjectParameterSpaceControlPoint*)inOtherPoint; // skip blending if we are at either end of our range if( inWeightOfOtherPoint == 0 ) { return this->copy(); } else if( inWeightOfOtherPoint == 1 ) { return otherPoint->copy(); } double weightOfThisPoint = 1 - inWeightOfOtherPoint; int numBlendTriangleVertices; Vector3D **blendTriangleVertices = blendVertexArrays( mTriangleVertices, mNumTriangleVertices, weightOfThisPoint, otherPoint->mTriangleVertices, otherPoint->mNumTriangleVertices, &numBlendTriangleVertices ); int numBlendTriangleVertexFillColors; Color **blendTriangleVertexFillColors = blendColorArrays( mTriangleVertexFillColors, mNumTriangleVertices, weightOfThisPoint, otherPoint->mTriangleVertexFillColors, otherPoint->mNumTriangleVertices, &numBlendTriangleVertexFillColors ); if( numBlendTriangleVertexFillColors != numBlendTriangleVertices ) { printf( "Error in ObjectParameterSpaceControlPoint:\n" " Number of blended triangle colors does not match number\n" " of blended vertices.\n" ); } int numBlendBorderVertices; Vector3D **blendBorderVertices = blendVertexArrays( mBorderVertices, mNumBorderVertices, weightOfThisPoint, otherPoint->mBorderVertices, otherPoint->mNumBorderVertices, &numBlendBorderVertices ); int numBlendBorderVertexColors; Color **blendBorderVertexColors = blendColorArrays( mBorderVertexColors, mNumBorderVertices, weightOfThisPoint, otherPoint->mBorderVertexColors, otherPoint->mNumBorderVertices, &numBlendBorderVertexColors ); if( numBlendBorderVertexColors != numBlendBorderVertices ) { printf( "Error in ObjectParameterSpaceControlPoint:\n" " Number of blended border colors does not match number of\n" " blended vertices.\n" ); } double blendBorderWidth = inWeightOfOtherPoint * otherPoint->mBorderWidth + weightOfThisPoint * mBorderWidth; double blendNumRotatedCopies = inWeightOfOtherPoint * otherPoint->mNumRotatedCopies + weightOfThisPoint * mNumRotatedCopies; double blendRotatedCopyScaleFactor = inWeightOfOtherPoint * otherPoint->mRotatedCopyScaleFactor + weightOfThisPoint * mRotatedCopyScaleFactor; double blendRotatedCopyAngleScaleFactor = inWeightOfOtherPoint * otherPoint->mRotatedCopyAngleScaleFactor + weightOfThisPoint * mRotatedCopyAngleScaleFactor; double blendRotationRate = inWeightOfOtherPoint * otherPoint->mRotationRate + weightOfThisPoint * mRotationRate; return new ObjectParameterSpaceControlPoint( numBlendTriangleVertices, blendTriangleVertices, blendTriangleVertexFillColors, numBlendBorderVertices, blendBorderVertices, blendBorderVertexColors, blendBorderWidth, blendNumRotatedCopies, blendRotatedCopyScaleFactor, blendRotatedCopyAngleScaleFactor, blendRotationRate ); } SimpleVector * ObjectParameterSpaceControlPoint::getDrawableObjects() { SimpleVector *returnVector = new SimpleVector(); Angle3D *angleBetweenRotatedCopies = new Angle3D( 0, 0, 2 * M_PI / ( mNumRotatedCopies + 1 ) ); angleBetweenRotatedCopies->scale( mRotatedCopyAngleScaleFactor ); // start out with a copy of the vertices and rotate them, // bit by bit, to create each reflection Vector3D **workingTriangleVertices = duplicateVertextArray( mTriangleVertices, mNumTriangleVertices ); Vector3D **workingBorderVertices = duplicateVertextArray( mBorderVertices, mNumBorderVertices ); // if we have a non-integral number of reflections, draw one // extra reflection (it will overlap another of the reflections, // but this eliminates reflection "pop-in" when transitioning between two // control points that have a different number of reflections int numRotatedCopiesToDraw = (int)ceil( mNumRotatedCopies ); char drawingExtraReflection = false; if( numRotatedCopiesToDraw - mNumRotatedCopies > 0 ) { drawingExtraReflection = true; } for( int s=0; s<=numRotatedCopiesToDraw; s++ ) { Color **reflectedFillColors; Color **reflectedBorderColors; if( drawingExtraReflection && s == numRotatedCopiesToDraw ) { // this is our extra reflection // alpha-fade it in based on the fractional part of our number // of reflections double fractionalPart = mNumRotatedCopies - floor( mNumRotatedCopies ); reflectedFillColors = duplicateColorArray( mTriangleVertexFillColors, mNumTriangleVertices, (float)fractionalPart ); reflectedBorderColors = duplicateColorArray( mBorderVertexColors, mNumBorderVertices, (float)fractionalPart ); } else { // not an extra reflection // do not alpha-fade it at all reflectedFillColors = duplicateColorArray( mTriangleVertexFillColors, mNumTriangleVertices ); reflectedBorderColors = duplicateColorArray( mBorderVertexColors, mNumBorderVertices ); } DrawableObject *reflectedObject = new DrawableObject( mNumTriangleVertices, duplicateVertextArray( workingTriangleVertices, mNumTriangleVertices ), reflectedFillColors, mNumBorderVertices, duplicateVertextArray( workingBorderVertices, mNumBorderVertices ), reflectedBorderColors, mBorderWidth ); returnVector->push_back( reflectedObject ); // rotate the working vertices by one reflection angle int i; for( i=0; irotate( angleBetweenRotatedCopies ); workingTriangleVertices[i]->scale( mRotatedCopyScaleFactor ); } for( i=0; irotate( angleBetweenRotatedCopies ); workingBorderVertices[i]->scale( mRotatedCopyScaleFactor ); } } // delete the working vertices int i; for( i=0; icopy(); returnArray[i]->a *= inAlphaMultiplier; } return returnArray; } Color *ObjectParameterSpaceControlPoint::readColorFromFile( FILE *inFILE ) { int numRead; // try reading the red component to test if we have RGBA or a named color float r, g, b, a; Color *returnColor = NULL; numRead = fscanf( inFILE, "%f", &r ); if( numRead == 1 ) { // color present as RGBA components numRead += fscanf( inFILE, "%f", &g ); numRead += fscanf( inFILE, "%f", &b ); numRead += fscanf( inFILE, "%f", &a ); if( numRead == 4 ) { // read all 4 returnColor = new Color( r, g, b, a, false ); } } else { // color might be a color name char *colorName = new char[100]; numRead = fscanf( inFILE, "%99s", colorName ); if( numRead == 1 ) { returnColor = NamedColorFactory::getColor( colorName ); } delete [] colorName; } return returnColor; } void ObjectParameterSpaceControlPoint::writeColorToFile( FILE *inFILE, Color *inColor ) { fprintf( inFILE, "%f ", inColor->r ); fprintf( inFILE, "%f ", inColor->g ); fprintf( inFILE, "%f ", inColor->b ); fprintf( inFILE, "%f\n", inColor->a ); } Color **ObjectParameterSpaceControlPoint::blendColorArrays( Color **inFirstArray, int inFirstArrayLength, double inWeightFirstArray, Color **inSecondArray, int inSecondArrayLength, int *outResultLength ) { double weightOfSecondArray = 1 - inWeightFirstArray; // blend has the same number of elements as the larger control array int resultLength = inFirstArrayLength; if( inSecondArrayLength > resultLength ) { resultLength = inSecondArrayLength; } Color **blendColors = new Color*[ resultLength ]; // map the larger array nto the smaller array to // blend int sizeLargerArray; int sizeSmallerArray; Color **arrayWithMoreColors; Color **arrayWithFewerColors; double weightOfLargerSet; double weightOfSmallerSet; if( inFirstArrayLength > inSecondArrayLength ) { sizeLargerArray = inFirstArrayLength; sizeSmallerArray = inSecondArrayLength; arrayWithMoreColors = inFirstArray; arrayWithFewerColors = inSecondArray; weightOfLargerSet = inWeightFirstArray; weightOfSmallerSet = weightOfSecondArray; } else { sizeLargerArray = inSecondArrayLength; sizeSmallerArray = inFirstArrayLength; arrayWithMoreColors = inSecondArray; arrayWithFewerColors = inFirstArray; weightOfLargerSet = weightOfSecondArray; weightOfSmallerSet = inWeightFirstArray; } // size of blend array is same as size of larger set // factor to map large array indices into the smaller array double mapFactor = (double)( sizeSmallerArray - 1 ) / (double)(sizeLargerArray - 1 ); for( int i=0; i mSoundLengthInSamples ) { numSamples = mSoundLengthInSamples - mCurrentSoundPositionInSamples; } SoundSamples *resultSamples = mControlPoint->getSoundSamples( mCurrentSoundPositionInSamples, numSamples, mSamplesPerSecond, mSoundLengthInSeconds ); mCurrentSoundPositionInSamples += numSamples; return resultSamples; } PlayableSound *OnePointPlayableSound::copy() { return new OnePointPlayableSound( (StereoSoundParameterSpaceControlPoint *)( mControlPoint->copy() ), mSoundLengthInSeconds, mSamplesPerSecond ); } transcend-0.3+dfsg2.orig/Transcend/game/SculptureManager.cpp0000640000175000017500000007373010302401465022551 0ustar pabspabs/* * Modification History * * 2004-June-21 Jason Rohrer * Created. * * 2004-August-6 Jason Rohrer * Fixed consistency bugs in test for in/out of sculpture status. * * 2004-August-22 Jason Rohrer * Added music parts. * * 2004-August-23 Jason Rohrer * Added function for getting number of pieces in sculpture. * * 2004-August-29 Jason Rohrer * Changed so that only pieces in sculpture are animated. * * 2004-August-30 Jason Rohrer * Optimization: avoid object blending whenever possible. * Added function for getting piece position needed to recenter. * Weakened the power modifier curve. * * 2004-September-3 Jason Rohrer * Changed to use pure distance between pieces instead of gap for in-sculpture * test. * * 2005-August-22 Jason Rohrer * Changed so that isPieceJarred returns true only if jar force is increasing. * Added magnet mode to smooth piece pick-up and drop. */ #include "SculptureManager.h" #include SculptureManager::SculptureManager( ParameterizedObject *inFirstSculptureTemplate, ParameterizedObject *inSecondSculptureTemplate, double inSculptureScale, double inMaxDistanceToBeInSculpture, double inAnimationLoopTime, int inNumSculpturePieces, double *inPieceShapeParameters, Vector3D **inPieceStartingPositions, Angle3D **inPieceStartingRotations, MusicPart **inPieceMusicParts, FILE *inSculpturePowerUpSpaceFILE, char *outError, ShipBulletManager *inEnemyBulletManager, double inEnemyBulletJarPower, ShipBulletManager *inBossBulletManager, double inBossBulletJarPower, double inFriction, double inWorldWidth, double inWorldHeight ) : mFirstSculpturePieceTemplate( inFirstSculptureTemplate ), mSecondSculpturePieceTemplate( inSecondSculptureTemplate ), mSculptureScale( inSculptureScale ), mMaxDistanceToBePartOfSculpture( inMaxDistanceToBeInSculpture ), mAnimationLoopTime( inAnimationLoopTime ), mCurrentAnimationPosition( 0 ), mCurrentAnimationDirection( 1 ), mNumSculpturePieces( inNumSculpturePieces ), mSculpturePieceParameters( inPieceShapeParameters ), mCurrentPiecePositions( inPieceStartingPositions ), mCurrentPieceRotations( inPieceStartingRotations ), mPieceMusicParts( inPieceMusicParts ), mEnemyBulletManager( inEnemyBulletManager ), mEnemyBulletJarPower( inEnemyBulletJarPower ), mBossBulletManager( inBossBulletManager ), mBossBulletJarPower( inBossBulletJarPower ), mFriction( inFriction ), mMaxXPosition( inWorldWidth / 2 ), mMinXPosition( - inWorldWidth / 2 ), mMaxYPosition( inWorldHeight / 2 ), mMinYPosition( - inWorldHeight / 2 ) { mPieceMagnetModes = new char[ mNumSculpturePieces ]; mCurrentPieceTargetPositions = new Vector3D*[ mNumSculpturePieces ]; mCurrentTowardTargetVelocities = new double[ mNumSculpturePieces ]; mCurrentPieceRotationRates = new double[ mNumSculpturePieces ]; mCurrentPieceRadii = new double[ mNumSculpturePieces ]; mCurrentJarForces = new double[ mNumSculpturePieces ]; mJarForcesIncreasing = new char[ mNumSculpturePieces ]; mNumPiecesInSculpture = 0; mInSculptureFlags = new char[ mNumSculpturePieces ]; mDelayAnimationStartFlags = new char[ mNumSculpturePieces ]; mDelayAnimationStopFlags = new char[ mNumSculpturePieces ]; int i; for( i=0; i 1 ) { closeRangeSum = 1; } return closeRangeSum; } double SculptureManager::getFarRangeBulletParameter() { double farRangeSum = 0; for( int i=0; i 1 ) { farRangeSum = 1; } return farRangeSum; } Vector3D *SculptureManager::getSculptureCenter() { Vector3D *positionSum = new Vector3D( 0, 0, 0 ); int numPiecesInSculpture = 0; for( int i=0; iadd( mCurrentPiecePositions[i] ); numPiecesInSculpture++; } } if( numPiecesInSculpture > 0 ) { // convert sum to average positionSum->scale( 1.0 / (double)numPiecesInSculpture ); } return positionSum; } double SculptureManager::getBulletPowerModifier() { Vector3D *center = getSculptureCenter(); Vector3D *zeroVector = new Vector3D( 0, 0, 0 ); double distanceFromCenter = zeroVector->getDistance( center ); delete zeroVector; delete center; // use inverse exponential (power of e) to map the [0, inf] distance // into [1,0] // scale the distance somewhat so the curve is not so extreme double returnValue = 1 / exp( 0.05 * distanceFromCenter ); // lower limit of 0.25 return returnValue * 0.75 + 0.25; } void SculptureManager::passTime( double inTimeDeltaInSeconds ) { // full animation loop (forward and back) is 2 parameter space units double animationDelta = 2 * inTimeDeltaInSeconds / mAnimationLoopTime; mCurrentAnimationPosition += mCurrentAnimationDirection * animationDelta; if( mCurrentAnimationPosition > 1 ) { // bounce back into range double extra = mCurrentAnimationPosition - 1; mCurrentAnimationPosition = 1 - extra; // reverse animation direction mCurrentAnimationDirection = -1; if( mCurrentAnimationPosition < 0 ) { // we bounced back too far // default to known safe state mCurrentAnimationPosition = 0; mCurrentAnimationDirection = 1; } } else if( mCurrentAnimationPosition < 0 ) { // bounce back into range double extra = 0 - mCurrentAnimationPosition; mCurrentAnimationPosition = 0 + extra; // reverse animation direction mCurrentAnimationDirection = 1; if( mCurrentAnimationPosition > 1 ) { // we bounced back too far // default to known safe state mCurrentAnimationPosition = 1; mCurrentAnimationDirection = -1; } // we have reached the animation starting point // start any animations that are waiting to start, and stop // any that are waiting to stop (to ensure clean starts and stops) for( int i=0; imZ += inTimeDeltaInSeconds * mCurrentPieceRotationRates[i]; // check if piece being hit by bullet double enemyBulletPower = mEnemyBulletManager->getBulletPowerInCircle( mCurrentPiecePositions[i], mCurrentPieceRadii[i] ); double bossBulletPower = mBossBulletManager->getBulletPowerInCircle( mCurrentPiecePositions[i], mCurrentPieceRadii[i] ); // scale bullet power by time delta double jarForceIncrease = inTimeDeltaInSeconds * enemyBulletPower * mEnemyBulletJarPower + inTimeDeltaInSeconds * bossBulletPower * mBossBulletJarPower; // save last jar force to detect whether force has increased double lastJarForce = mCurrentJarForces[i]; // increased by being hit by bullets mCurrentJarForces[i] += jarForceIncrease; // decayed by friction mCurrentJarForces[i] -= mFriction * inTimeDeltaInSeconds; // never negative if( mCurrentJarForces[i] < 0 ) { mCurrentJarForces[i] = 0; } double jarForceDelta = mCurrentJarForces[i] - lastJarForce; if( jarForceDelta > 0 ) { mJarForcesIncreasing[i] = true; } else { mJarForcesIncreasing[i] = false; } if( mCurrentJarForces[i] > 0 ) { // jar piece away from center Vector3D *jarVector = new Vector3D( 0, 0, 0 ); jarVector->subtract( mCurrentPiecePositions[i] ); jarVector->scale( -1 ); if( jarVector->getLength() > 0 ) { jarVector->normalize(); } else { // cannot compute a proper vector for moving piece "away" // from center, since it is at center. // default to moving piece in y direction jarVector->mY = 1; } jarVector->scale( mCurrentJarForces[i] * inTimeDeltaInSeconds ); mCurrentPiecePositions[i]->add( jarVector ); Vector3D *currentPosition = mCurrentPiecePositions[i]; // bound jarring action to inside of world // jarring stops when we hit a world edge if( currentPosition->mX > mMaxXPosition ) { currentPosition->mX = mMaxXPosition; mCurrentJarForces[i] = 0; } if( currentPosition->mX < mMinXPosition ) { currentPosition->mX = mMinXPosition; mCurrentJarForces[i] = 0; } if( currentPosition->mY > mMaxYPosition ) { currentPosition->mY = mMaxYPosition; mCurrentJarForces[i] = 0; } if( currentPosition->mY < mMinYPosition ) { currentPosition->mY = mMinYPosition; mCurrentJarForces[i] = 0; } delete jarVector; updateInOutStatusOfAllPieces(); } if( mPieceMagnetModes[i] ) { // move piece toward target // velocity toward target increases at rate of 20 unit/sec per sec mCurrentTowardTargetVelocities[i] += 20; double totalDistance = mCurrentPieceTargetPositions[i]->getDistance( mCurrentPiecePositions[i] ); double moveDistance = inTimeDeltaInSeconds * mCurrentTowardTargetVelocities[i]; if( moveDistance >= totalDistance ) { // we will hit or pass our target with this move delete mCurrentPiecePositions[i]; mCurrentPiecePositions[i] = new Vector3D( mCurrentPieceTargetPositions[i] ); // velocity goes back to 0 instantly mCurrentTowardTargetVelocities[i] = 0; // turn off magnet mode mPieceMagnetModes[i] = false; } else { // this move does not hit or pass our target, so execute it Vector3D *moveVector = new Vector3D( mCurrentPieceTargetPositions[i] ); moveVector->subtract( mCurrentPiecePositions[i] ); moveVector->normalize(); moveVector->scale( moveDistance ); mCurrentPiecePositions[i]->add( moveVector ); delete moveVector; } } } } int SculptureManager::getSculpturePieceInCircle( Vector3D *inCenter, double inRadius ) { int closestPiece = -1; double closestDistance = DBL_MAX; for( int i=0; igetDistance( mCurrentPiecePositions[i] ); distance = distance - mCurrentPieceRadii[i]; if( distance <= inRadius && distance < closestDistance ) { closestPiece = i; closestDistance = distance; } } return closestPiece; } Vector3D *SculptureManager::getPositionOfClosestSculpturePiece( Vector3D *inPosition ) { Vector3D *closestPiecePosition = NULL; double closestDistance = DBL_MAX; for( int i=0; igetDistance( mCurrentPiecePositions[i] ); if( distance < closestDistance ) { closestPiecePosition = mCurrentPiecePositions[i]; closestDistance = distance; } } } if( closestPiecePosition != NULL ) { return new Vector3D( closestPiecePosition ); } else { return NULL; } } int SculptureManager::getNumPiecesInSculpture() { int count = 0; for( int i=0; i *positions = new SimpleVector(); for( int i=0; ipush_back( new Vector3D( mCurrentPiecePositions[i] ) ); } } *outNumPieces = positions->size(); Vector3D **returnArray = positions->getElementArray(); delete positions; return returnArray; } MusicPart **SculptureManager::getPieceMusicParts( int *outNumPieces ) { SimpleVector *parts = new SimpleVector(); for( int i=0; ipush_back( mPieceMusicParts[i] ); } } *outNumPieces = parts->size(); MusicPart **returnArray = parts->getElementArray(); delete parts; return returnArray; } void SculptureManager::updateInOutStatusOfAllPieces() { // first, flag all pieces as out, except those that are close to origin // origin position Vector3D *zeroVector = new Vector3D( 0, 0, 0 ); // update our count int oldCount = mNumPiecesInSculpture; mNumPiecesInSculpture = 0; // remember old flags char *oldFlags = new char[ mNumSculpturePieces ]; memcpy( oldFlags, mInSculptureFlags, sizeof( char ) * mNumSculpturePieces ); int i; for( i=0; igetDistance( mCurrentPiecePositions[ i ] ); if( distanceToOrigin <= mMaxDistanceToBePartOfSculpture ) { mInSculptureFlags[i] = true; mNumPiecesInSculpture++; } else { mInSculptureFlags[i] = false; } } delete zeroVector; // now walk through pieces over and over and expand the set by looking // for pieces not in the set that are close to pieces that are already // in the set // stop when no piece was added in the last round char piecesAdded = true; while( piecesAdded ) { piecesAdded = false; for( i=0; igetDistance( mCurrentPiecePositions[j] ); if( distance <= mMaxDistanceToBePartOfSculpture ) { mInSculptureFlags[i] = true; mNumPiecesInSculpture++; added = true; piecesAdded = true; } } } } } } char animationReset = false; if( oldCount == 0 && mNumPiecesInSculpture != 0 ) { // sculpture used to be empty... but now has pieces // make sure no piece is in the process of its stop animation char pieceStopping = false; for( i=0; iscale( -numPieces ); return center; } void SculptureManager::turnMagnetModeOn( int inPieceHandle ) { mPieceMagnetModes[ inPieceHandle ] = true; } void SculptureManager::setPiecePosition( int inPieceHandle, Vector3D *inNewPosition ) { Vector3D *position; if( mPieceMagnetModes[ inPieceHandle ] ) { // piece in magnet mode, set target position position = mCurrentPieceTargetPositions[ inPieceHandle ]; } else { // not in magnet mode, set current position position = mCurrentPiecePositions[ inPieceHandle ]; } position->mX = inNewPosition->mX; position->mY = inNewPosition->mY; position->mZ = inNewPosition->mZ; updateInOutStatusOfAllPieces(); } SimpleVector *SculptureManager::getDrawableObjects() { SimpleVector *returnVector = new SimpleVector(); for( int i=0; igetBlendedControlPoint( mSculpturePieceParameters[i] ); } else if( animPosition == 1 ) { // use pure second point animationPoint = mSecondSculpturePieceTemplate->getBlendedControlPoint( mSculpturePieceParameters[i] ); } else { ObjectParameterSpaceControlPoint *firstControlPoint = mFirstSculpturePieceTemplate->getBlendedControlPoint( mSculpturePieceParameters[i] ); ObjectParameterSpaceControlPoint *secondControlPoint = mSecondSculpturePieceTemplate->getBlendedControlPoint( mSculpturePieceParameters[i] ); animationPoint = (ObjectParameterSpaceControlPoint *)( firstControlPoint->createLinearBlend( secondControlPoint, animPosition ) ); delete firstControlPoint; delete secondControlPoint; } pieceRotationRate = animationPoint->getRotationRate(); SimpleVector *pieceObjects = animationPoint->getDrawableObjects(); delete animationPoint; mCurrentPieceRotationRates[i] = pieceRotationRate; int numObjects = pieceObjects->size(); double maxRadius = 0; for( int j=0; jgetElement( j ) ); currentObject->scale( mSculptureScale ); currentObject->rotate( mCurrentPieceRotations[i] ); currentObject->move( mCurrentPiecePositions[i] ); double radius = currentObject->getBorderMaxDistance( mCurrentPiecePositions[i] ); if( radius > maxRadius ) { maxRadius = radius; } returnVector->push_back( currentObject ); } mCurrentPieceRadii[i] = maxRadius; delete pieceObjects; } return returnVector; } char SculptureManager::isPieceJarred( int inPieceHandle ) { // piece is only being jarred if force is increasing if( mJarForcesIncreasing[ inPieceHandle ] > 0 ) { return true; } else { return false; } } void SculptureManager::mapParameters( double inParameter, double *outCloseRangePowerUp, double *outFarRangePowerUp ) { if( mNumParameterMapAnchors >= 2 ) { // find the 2 surrounding anchor points // all distances will be no more than 1 double distanceFromClosestLargerPoint = 2; double distanceFromClosestSmallerPoint = 3; int indexOfClosestLargerPoint = -1; int indexOfClosestSmallerPoint = -1; for( int i=0; i= inParameter ) { // a larger point if( distanceFromThisPoint < distanceFromClosestLargerPoint ) { // closer than our closest largerpoint indexOfClosestLargerPoint = i; distanceFromClosestLargerPoint = distanceFromThisPoint; } } else { // a smaller point if( distanceFromThisPoint < distanceFromClosestSmallerPoint ) { // closer than our closest smallerpoint indexOfClosestSmallerPoint = i; distanceFromClosestSmallerPoint = distanceFromThisPoint; } } } if( indexOfClosestLargerPoint != -1 && indexOfClosestSmallerPoint != -1 ) { // found two points // compute weights double distanceBetweenSurroundingPoints = fabs( mParameterMapAnchors[ indexOfClosestLargerPoint ] - mParameterMapAnchors[ indexOfClosestSmallerPoint ] ); double weightOnLargerPoint = distanceFromClosestSmallerPoint / distanceBetweenSurroundingPoints; double weightOnSmallerPoint = 1 - weightOnLargerPoint; // blend the two points, using the distance to weight them *outCloseRangePowerUp = weightOnLargerPoint * mParameterMapCloseRangeValues[ indexOfClosestLargerPoint ] + weightOnSmallerPoint * mParameterMapCloseRangeValues[ indexOfClosestSmallerPoint ]; *outFarRangePowerUp = weightOnLargerPoint * mParameterMapFarRangeValues[ indexOfClosestLargerPoint ] + weightOnSmallerPoint * mParameterMapFarRangeValues[ indexOfClosestSmallerPoint ]; } else { // found only one point int indexOfPoint; if( indexOfClosestLargerPoint != -1 ) { indexOfPoint = indexOfClosestLargerPoint; } else if( indexOfClosestLargerPoint != -1 ) { indexOfPoint = indexOfClosestSmallerPoint; } else { printf( "Error: found no closest sculpture parameter map " " anchor point.\n" ); indexOfPoint = 0; } // return this point's power ups *outCloseRangePowerUp = mParameterMapCloseRangeValues[ indexOfPoint ]; *outCloseRangePowerUp = mParameterMapCloseRangeValues[ indexOfPoint ]; } } else if( mNumParameterMapAnchors == 1 ) { // only one anchor point... return its power ups *outCloseRangePowerUp = mParameterMapCloseRangeValues[ 0 ]; *outCloseRangePowerUp = mParameterMapCloseRangeValues[ 0 ]; } else { printf( "Error: no anchor points in sculpture parameter space.\n" ); *outCloseRangePowerUp = 0; *outFarRangePowerUp = 0; } } transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedObject.cpp0000640000175000017500000000554110106045337023212 0ustar pabspabs/* * Modification History * * 2004-June-14 Jason Rohrer * Created. * * 2004-June-15 Jason Rohrer * Added a function for getting a blended control point. * * 2004-June-22 Jason Rohrer * Fixed algorithmic errors in linear blend function. */ #include "ParameterizedObject.h" ParameterizedObject::ParameterizedObject( FILE *inFILE, char *outError ) { SimpleVector *controlPoints = new SimpleVector(); SimpleVector *controlPointParameterAnchors = new SimpleVector(); // keep reading parameter anchors and control points until we // can read no more char readError = false; while( !readError ) { // read the parameter space anchor double anchor = 0; int numRead = fscanf( inFILE, "%lf", &anchor ); if( numRead != 1 ) { readError = true; } else { // read the control point ObjectParameterSpaceControlPoint *point = new ObjectParameterSpaceControlPoint( inFILE, &readError ); if( !readError ) { controlPointParameterAnchors->push_back( anchor ); controlPoints->push_back( point ); } else { delete point; } } } mNumControlPoints = controlPoints->size(); mControlPoints = controlPoints->getElementArray(); mControlPointParameterAnchors = controlPointParameterAnchors->getElementArray(); delete controlPoints; delete controlPointParameterAnchors; if( mNumControlPoints >= 2 ) { *outError = false; } else { // we didn't read enough control points *outError = true; } } SimpleVector *ParameterizedObject::getDrawableObjects( double inParameter, double *outRotationRate ) { // blend the two points, using the distance to weight them ObjectParameterSpaceControlPoint *blendedPoint = getBlendedControlPoint( inParameter ); if( blendedPoint != NULL ) { SimpleVector *drawableObjects = blendedPoint->getDrawableObjects(); *outRotationRate = blendedPoint->getRotationRate(); delete blendedPoint; return drawableObjects; } else { printf( "Error: no control points in object space.\n" ); *outRotationRate = 0; return NULL; } } ObjectParameterSpaceControlPoint *ParameterizedObject::getBlendedControlPoint( double inParameter ) { // cast result of super-class function call and return it return (ObjectParameterSpaceControlPoint*) ParameterizedSpace::getBlendedControlPoint( inParameter ); } transcend-0.3+dfsg2.orig/Transcend/game/OnePointPlayableSound.h0000640000175000017500000000273010106045337023147 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #ifndef ONE_POINT_PLAYABLE_SOUND_INCLUDED #define ONE_POINT_PLAYABLE_SOUND_INCLUDED #include "PlayableSound.h" #include "StereoSoundParameterSpaceControlPoint.h" /** * Implementation of PlayableSound that plays one sound control * point through the duration of the sound. * * @author Jason Rohrer */ class OnePointPlayableSound : public PlayableSound { public: /** * Constructs a playable sound. * * @param inControlPoint the sound control point. * Will be destroyed when this class is destroyed. * @param inSoundLengthInSeconds the length of the sound in seconds. * @param inSamplesPerSecond the sample rate. */ OnePointPlayableSound( StereoSoundParameterSpaceControlPoint *inControlPoint, double inSoundLengthInSeconds, int inSamplesPerSecond ); ~OnePointPlayableSound(); // implements the PlayableSound interface virtual SoundSamples *getMoreSamples( unsigned long inNumSamples ); virtual PlayableSound *copy(); protected: StereoSoundParameterSpaceControlPoint *mControlPoint; double mSoundLengthInSeconds; int mSamplesPerSecond; unsigned long mSoundLengthInSamples; unsigned long mCurrentSoundPositionInSamples; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/DrawableObject.h0000640000175000017500000001156210065534277021616 0ustar pabspabs/* * Modification History * * 2004-June-11 Jason Rohrer * Created. * * 2004-June-16 Jason Rohrer * Added fuctions for modifying object position, rotation, and transparency. * * 2004-June-18 Jason Rohrer * Changed to use triangles instead of polygons. * * 2004-June-20 Jason Rohrer * Added fuction for modifying scale. * Added function for testing circle containment. * * 2004-June-21 Jason Rohrer * Added fuction for getting minimum border distance. */ #ifndef DRAWABLE_OBJECT_INCLUDED #define DRAWABLE_OBJECT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include "minorGems/graphics/Color.h" /** * A 2d object that can draw itself into the current OpenGL context. * * @author Jason Rohrer. */ class DrawableObject { public: /** * Constructs an object. * * @param inNumTriangleVertices the number of triangle vertices. * Must be a multiple of 3. * @param inTriangleVertices the triangle vertices. * Only the x and y components have an effect. This array and * the vertices it contains will be destroyed when this class is * destroyed. * @param inTriangleVertexFillColors a fill color for each triangle * vertex. * This array and the colors it contains will be destroyed when * this class is destroyed. * @param inNumBorderVertices the number of border vertices. * @param inBorderVertices the borer vertices. * Only the x and y components have an effect. This array and * the vertices it contains will be destroyed when this class is * destroyed. * @param inVertextBorderColors a border color for each vertex. * This array and the colors it contains will be destroyed when * this class is destroyed. * @param inBorderWidth the width of the border, in pixels. */ DrawableObject( int inNumTriangleVertices, Vector3D **inTriangleVertices, Color **inTriangleVertexFillColors, int inNumBorderVertices, Vector3D **inBorderVertices, Color **inBorderVertextColors, float inBorderWidth ); virtual ~DrawableObject(); /** * Rotates this object. * * @param inRotation the angle to rotate it by. * Must be destroyed by caller. */ void rotate( Angle3D *inRotation ); /** * Moves this object. * * @param inPosition the vector to move it by. * Must be destroyed by caller. */ void move( Vector3D *inPosition ); /** * Scales this object. * * @param inScale the multiplier to scale it by. */ void scale( double inScale ); /** * Fades the colors of this object (makes them more transparent). * * @param inAlphaScale the value, in the range [0,1] to multiply * alpha values by. */ void fade( double inAlphaScale ); /** * Tests whether any of this object's border vertices are in a circle. * * @param inCenter the center of the circle. * Must be destroyed by caller. * @param inRadius the radius of the circle. */ char isBorderInCircle( Vector3D *inCenter, double inRadius ); /** * Gets the maximum distance of the border vertices from a given * point. * * @param inPoint the point to get the distance from. * Must be destroyed by caller. */ double getBorderMaxDistance( Vector3D *inPoint ); /** * Gets the minimum distance of the border vertices from a given * point. * * @param inPoint the point to get the distance from. * Must be destroyed by caller. */ double getBorderMinDistance( Vector3D *inPoint ); /** * Draws this object into the current OpenGL context. * * @param inScale the scale factor. * @param inRotation the rotation of the object. * Must be destroyed by caller. * @param inPosition the position of the object. * Must be destroyed by caller. */ void draw( double inScale, Angle3D *inRotation, Vector3D *inPosition ); protected: int mNumTriangleVertices; Vector3D **mTriangleVertices; Color **mTriangleVertexFillColors; int mNumBorderVertices; Vector3D **mBorderVertices; Color **mBorderVertexColors; float mBorderWidth; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/LevelDirectoryManager.h0000640000175000017500000001072610065645144023172 0ustar pabspabs/* * Modification History * * 2004-June-15 Jason Rohrer * Created. * * 2004-June-17 Jason Rohrer * Added function for reading values from level files. * * 2004-June-21 Jason Rohrer * Added function for reading int values. */ #ifndef LEVEL_DIRECTORY_MANAGER_INCLUDED #define LEVEL_DIRECTORY_MANAGER_INCLUDED #include "minorGems/io/file/File.h" #include /** * A wrapper class to ensure destruction of a file object at system exit. */ class StaticLevelDirectoryFileWrapper { public: StaticLevelDirectoryFileWrapper(); ~StaticLevelDirectoryFileWrapper(); File *mFile; }; /** * A class with static functions for setting and obtaining the current * level directory. * * @author Jason Rohrer. */ class LevelDirectoryManager { public: /** * Sets the directory where level files are stored. * * @param inFile the directory file object, or NULL to switch * back to the default location. * Will be destroyed by this class. */ static void setLevelDirectory( File *inFile ); /** * Gets the current level directory. * * @return the directory file object. * Must be destroyed by caller. */ static File *getLevelDirectory(); /** * Gets a File object for a level file. * * @param inFileName the name of the file. * Must be destroyed by caller. * @param inShowErrorMessage true to automatically print an error * message to std out. Defaults to false. * * @return the stream, or NULL if getting the file from the directory * fails. Must be closed by caller. */ static File *getLevelFile( char *inFileName, char inPrintErrorMessage = false ); /** * Gets a std stream for a level file. * * @param inFileName the name of the file. * Must be destroyed by caller. * @param inShowErrorMessage true to automatically print an error * message to std out. Defaults to false. * * @return the stream, or NULL if opening the stream fails. * Must be closed by caller. */ static FILE *getStdStream( char *inFileName, char inPrintErrorMessage = false ); /** * Reads the contents of a level file as a string. * * @param inFileName the name of the file. * Must be destroyed by caller. * @param inShowErrorMessage true to automatically print an error * message to std out. Defaults to false. * * @return the file contents, or NULL if reading from the file fails. * Must be destroyed by caller. */ static char *readFileContents( char *inFileName, char inPrintErrorMessage = false ); /** * Reads a single double value from a level file. * * @param inFileName the name of the file. * Must be destroyed by caller. * @param outError pointer to where the error flag should be returned. * Will be set to true if reading a double from the file fails. * @param inShowErrorMessage true to automatically print an error * message to std out. Defaults to false. * * @return the first double value in the file. */ static double readDoubleFileContents( char *inFileName, char *outError, char inPrintErrorMessage = false ); /** * Reads a single int value from a level file. * * @param inFileName the name of the file. * Must be destroyed by caller. * @param outError pointer to where the error flag should be returned. * Will be set to true if reading a double from the file fails. * @param inShowErrorMessage true to automatically print an error * message to std out. Defaults to false. * * @return the first int value in the file. */ static int readIntFileContents( char *inFileName, char *outError, char inPrintErrorMessage = false ); protected: static StaticLevelDirectoryFileWrapper mFileWrapper; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SoundParameterSpaceControlPoint.cpp0000640000175000017500000002276110116152533025550 0ustar pabspabs/* * Modification History * * 2004-August-6 Jason Rohrer * Created. * * 2004-August-9 Jason Rohrer * Changed to use sine components. * * 2004-August-12 Jason Rohrer * Added support for getting blocks of samples. * * 2004-August-19 Jason Rohrer * Fixed bug in walking through wavetable. * * 2004-September-3 Jason Rohrer * Added brief fade in/out at beginning/end of sound to avoid clicks. */ #include "SoundParameterSpaceControlPoint.h" #include "minorGems/math/geometry/Vector3D.h" #include SoundParameterSpaceControlPoint::SoundParameterSpaceControlPoint( int inNumWaveComponents, double *inWaveComponentFrequencies, double *inWaveComponentAmplitudes, double inStartFrequency, double inEndFrequency, double inStartLoudness, double inEndLoudness ) : mNumWaveComponents( inNumWaveComponents ), mWaveComponentFrequencies( inWaveComponentFrequencies ), mWaveComponentAmplitudes( inWaveComponentAmplitudes ), mStartFrequency( inStartFrequency ), mEndFrequency( inEndFrequency ), mStartLoudness( inStartLoudness ), mEndLoudness( inEndLoudness ), mCurrentWavePoint( 0 ) { } SoundParameterSpaceControlPoint::SoundParameterSpaceControlPoint( FILE *inFILE, char *outError ) { int totalNumRead = 0; mNumWaveComponents = 0; totalNumRead += fscanf( inFILE, "%d", &mNumWaveComponents ); // how many values should we successfully read (cheap error checking) int totalToRead = 1 + // read the number of wave points mNumWaveComponents * 2 + // for each point, read x and y + 4; // read Start/End frequency and loudness mWaveComponentFrequencies = new double[ mNumWaveComponents ]; mWaveComponentAmplitudes = new double[ mNumWaveComponents ]; // read frequency and amplitude for each component for( int i=0; imNumWaveComponents; double *otherWaveComponentFrequencies = otherPoint->mWaveComponentFrequencies; double *otherWaveComponentAmplitudes = otherPoint->mWaveComponentAmplitudes; // pack our waveform control points into vector3D arrays so // they can be processed by the blend function Vector3D **thisWaveComponents = new Vector3D*[ mNumWaveComponents ]; Vector3D **otherWaveComponents = new Vector3D*[ otherPoint->mNumWaveComponents ]; int i; for( i=0; imX; resultWaveComponentAmplitudes[i] = resultWaveComponents[i]->mY; delete resultWaveComponents[i]; } delete [] resultWaveComponents; return new SoundParameterSpaceControlPoint( resultNumWaveComponents, resultWaveComponentFrequencies, resultWaveComponentAmplitudes, weightOfThisPoint * mStartFrequency + inWeightOfOtherPoint * otherPoint->mStartFrequency, weightOfThisPoint * mEndFrequency + inWeightOfOtherPoint * otherPoint->mEndFrequency, weightOfThisPoint * mStartLoudness + inWeightOfOtherPoint * otherPoint->mStartLoudness, weightOfThisPoint * mEndLoudness + inWeightOfOtherPoint * otherPoint->mEndLoudness ); } float *SoundParameterSpaceControlPoint::getSoundSamples( unsigned long inStartSample, unsigned long inSampleCount, unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ) { if( inStartSample == 0 ) { // reset our wave pointer mCurrentWavePoint = 0; } double sampleDeltaInSeconds = 1.0 / inSamplesPerSecond; float *samples = new float[ inSampleCount ]; unsigned long soundLengthInSamples = (unsigned long)( inSoundLengthInSeconds * inSamplesPerSecond ); // try to fade in and out for 100 samples to avoid a click // at the start and end of the note unsigned long numFadeInSamples = 100; if( numFadeInSamples > soundLengthInSamples ) { numFadeInSamples = soundLengthInSamples / 2; } unsigned long numFadeOutSamples = 100; if( numFadeOutSamples > soundLengthInSamples ) { numFadeOutSamples = soundLengthInSamples / 2; } for( unsigned long i=0; i= soundLengthInSamples - numFadeOutSamples ) { fadeOutFactor = (double)( soundLengthInSamples - currentSample - 1 ) / (double)( numFadeOutSamples - 1 ); } double fadeFactor = fadeInFactor * fadeOutFactor; // add up the components at this time point // sine function cycles once every 2*pi // we need to adjust the sine function to cycle according to // our wave's freqency double adjustedTime = mCurrentWavePoint * ( 2 * M_PI ); double componentSum = 0; for( int j=0; j EnemyManager::EnemyManager( Enemy *inEnemyTemplate, double inEnemyScale, double inExplosionScale, double inEnemyVelocity, SculptureManager *inSculptureManager, ShipBulletManager *inShipBulletManager, ShipBulletManager *inEnemyBulletManager, double inEnemyBulletRange, double inEnemyBulletBaseVelocity, double inEnemyBulletsPerSecond, SoundPlayer *inSoundPlayer, BulletSound *inEnemyExplosionSoundTemplate, double inWorldWidth, double inWorldHeight ) : mEnemyTemplate( inEnemyTemplate ), mSculptureManager( inSculptureManager ), mShipBulletManager( inShipBulletManager ), mEnemyBulletManager( inEnemyBulletManager ), mEnemyBulletRange( inEnemyBulletRange ), mEnemyBulletBaseVelocity( inEnemyBulletBaseVelocity ), mEnemyBulletsPerSecond( inEnemyBulletsPerSecond ), mEnemyTimeBetweenBullets( 1 / inEnemyBulletsPerSecond ), mSoundPlayer( inSoundPlayer ), mEnemyExplosionSoundTemplate( inEnemyExplosionSoundTemplate ), mEnemyScale( inEnemyScale ), mExplosionScale( inExplosionScale ), mEnemyVelocity( inEnemyVelocity ), mEnemyShapeParameters( new SimpleVector() ), mExplosionShapeParameters( new SimpleVector() ), mExplosionTimesInSeconds( new SimpleVector() ), mCurrentlyExplodingFlags( new SimpleVector() ), mExplosionProgress( new SimpleVector() ), mFadeProgress( new SimpleVector() ), mBulletCloseParameters( new SimpleVector() ), mBulletFarParameters( new SimpleVector() ), mTimesSinceLastBullet( new SimpleVector() ), mCurrentPositions( new SimpleVector() ), mCurrentAnglesToPointAt( new SimpleVector() ), mCurrentRadii( new SimpleVector() ), mCurrentRotations( new SimpleVector() ), mCurrentRotationRates( new SimpleVector() ), mSholdBeDestroyedFlags( new SimpleVector() ), mShipDistanceParameters( new SimpleVector() ) { mMaxXPosition = inWorldWidth / 2; mMinXPosition = -mMaxXPosition; mMaxYPosition = inWorldHeight / 2; mMinYPosition = -mMaxYPosition; /* // create explosion sound int numSamples = 5513; mEnemyExplosionSound = new SoundSamples( numSamples ); for( int j=0; jmLeftChannel[j] = 0.1 * sin( (double)j / wavelength ) * ( 1 - weight ); mEnemyExplosionSound->mRightChannel[j] = 0.1 * sin( (double)j / wavelength ) * ( 1 - weight ); } */ } EnemyManager::~EnemyManager() { delete mEnemyTemplate; delete mEnemyShapeParameters; delete mExplosionShapeParameters; delete mCurrentlyExplodingFlags; delete mExplosionTimesInSeconds; delete mExplosionProgress; delete mFadeProgress; delete mBulletCloseParameters; delete mBulletFarParameters; delete mTimesSinceLastBullet; int numEnemies = mCurrentPositions->size(); for( int i=0; igetElement( i ) ); delete *( mCurrentAnglesToPointAt->getElement( i ) ); delete *( mCurrentRotations->getElement( i ) ); } delete mCurrentPositions; delete mCurrentAnglesToPointAt; delete mCurrentRotations; delete mCurrentRadii; delete mCurrentRotationRates; delete mSholdBeDestroyedFlags; delete mShipDistanceParameters; delete mEnemyExplosionSoundTemplate; } void EnemyManager::addEnemy( double inEnemyShapeParameter, double inExplosionShapeParameter, double inExplosionTimeInSeconds, double inBulletCloseParameter, double inBulletFarParameter, Vector3D *inStartingPosition, Angle3D *inStartingRotation ) { mEnemyShapeParameters->push_back( inEnemyShapeParameter ); mExplosionShapeParameters->push_back( inExplosionShapeParameter ); mCurrentlyExplodingFlags->push_back( false ); mExplosionTimesInSeconds->push_back( inExplosionTimeInSeconds ); mExplosionProgress->push_back( 0 ); // enemy starts out completly faded mFadeProgress->push_back( 1 ); mBulletCloseParameters->push_back( inBulletCloseParameter ); mBulletFarParameters->push_back( inBulletFarParameter ); mTimesSinceLastBullet->push_back( 0 ); mCurrentRotations->push_back( inStartingRotation ); mCurrentRotationRates->push_back( 0 ); mCurrentPositions->push_back( inStartingPosition ); mCurrentAnglesToPointAt->push_back( new Angle3D( inStartingRotation ) ); mCurrentRadii->push_back( 0 ); mSholdBeDestroyedFlags->push_back( false ); mShipDistanceParameters->push_back( 0 ); } void EnemyManager::explodeAllEnemies() { int numEnemies = mCurrentPositions->size(); for( int i=0; igetElement( i ) ) = true; } } int EnemyManager::getEnemyCount() { return mCurrentPositions->size(); } void EnemyManager::passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition ) { int numEnemies = mCurrentPositions->size(); Vector3D *centerPosition = new Vector3D( 0, 0, 0 ); int i; for( i=0; igetElement( i ) ); double rotationRate = *( mCurrentRotationRates->getElement( i ) ); currentRotation->mZ += rotationRate * inTimeDeltaInSeconds; Vector3D *currentPosition = *( mCurrentPositions->getElement( i ) ); // fly toward either the ship or a sculpture piece, whichever is // closer char targetIsShip = false; char targetIsBorder = false; Vector3D *targetPosition; double distanceToPiece = DBL_MAX; double distanceToShip = DBL_MAX; double closestApproachToTarget; Vector3D *closestSculpturePiecePosition = mSculptureManager->getPositionOfClosestSculpturePiece( currentPosition ); if( closestSculpturePiecePosition != NULL ) { distanceToPiece = closestSculpturePiecePosition->getDistance( currentPosition ); } distanceToShip = inShipPosition->getDistance( currentPosition ); // if ship closer than closest piece // AND if ship not already knocked to center if( distanceToShip < distanceToPiece && inShipPosition->getDistance( centerPosition ) >= 10 ) { targetIsShip = true; targetPosition = new Vector3D( inShipPosition ); closestApproachToTarget = 10; if( closestSculpturePiecePosition != NULL ) { delete closestSculpturePiecePosition; } } else if( closestSculpturePiecePosition != NULL ) { targetPosition = closestSculpturePiecePosition; closestApproachToTarget = 10; } else { // ship has already been knocked to center // and there are no active sculpture pieces to attack // lose interest and head toward border targetIsBorder = true; // don't stop until enemy is right on top // of the border. closestApproachToTarget = 0; // pick closest border double x, y; if( fabs( currentPosition->mX ) < fabs( currentPosition->mY ) ) { // head to y border x = currentPosition->mX; if( currentPosition->mY < 0 ) { y = mMinYPosition; } else { y = mMaxYPosition; } } else { // head to x border y = currentPosition->mY; if( currentPosition->mX < 0 ) { x = mMinXPosition; } else { x = mMaxXPosition; } } targetPosition = new Vector3D( x, y, 0 ); } // move enemy toward target Vector3D *moveVector = new Vector3D( targetPosition ); moveVector->subtract( currentPosition ); double distanceToTarget = moveVector->getLength(); // Save a copy of the normalized move vector, // since we may be scaling it by zero Vector3D *normalizedMoveVector; // we cannot properly normalize a length-zero vector if( distanceToTarget == 0 ) { // use a default normalized vector as the direction below // point at center Vector3D *defaultMoveVector = new Vector3D( centerPosition ); defaultMoveVector->subtract( currentPosition ); if( defaultMoveVector->getLength() > 0 ) { defaultMoveVector->normalize(); normalizedMoveVector = new Vector3D( defaultMoveVector ); } else { // we must be at the center, so use a default move direction normalizedMoveVector = new Vector3D( 0, 1, 0 ); } delete defaultMoveVector; } else { moveVector->normalize(); normalizedMoveVector = new Vector3D( moveVector ); /* moveVector->scale( mEnemyVelocity * inTimeDeltaInSeconds ); double moveLength = moveVector->getLength(); if( distanceToTarget - moveLength < closestApproachToTarget ) { // this move will put us too close to the target // don't move at all } else { // not already too close to target if( ! *( mCurrentlyExplodingFlags->getElement( i ) ) ) { // not exploding currentPosition->add( moveVector ); } } */ } delete moveVector; // point enemy toward target Vector3D *yVector = new Vector3D( 0, -1, 0 ); Angle3D *angleToPointAt = yVector->getZAngleTo( normalizedMoveVector ); if( ! *( mCurrentlyExplodingFlags->getElement( i ) ) ) { // not exploding Angle3D *currentAngleToPointAt = *( mCurrentAnglesToPointAt->getElement(i) ); // the angle we want to point at double goalZAngle = angleToPointAt->mZ; // the angle we are currently pointing at double currentZAngle = currentAngleToPointAt->mZ; // we want a smooth transition between these angles to // ensure a smooth rotation when targets change // use enemy velocity here as rotation rate in radians per sec double rotDelta = mEnemyVelocity * inTimeDeltaInSeconds; double angleLeftToRotate = goalZAngle - currentZAngle; if( fabs( angleLeftToRotate ) > M_PI ) { if( angleLeftToRotate < 0 ) { angleLeftToRotate += 2 * M_PI; } else { angleLeftToRotate -= 2 * M_PI; } //angleLeftToRotate = -angleLeftToRotate; } if( angleLeftToRotate < 0 ) { rotDelta = -rotDelta; } if( fabs( angleLeftToRotate ) < fabs( rotDelta ) ) { rotDelta = angleLeftToRotate; } double newZAngle = currentZAngle + rotDelta; currentRotation->mZ = newZAngle; // save the new z angle as our current angle currentAngleToPointAt->mZ = newZAngle; // compute our true move vector using the angle we are pointing at Vector3D *trueMoveVector = new Vector3D( yVector ); trueMoveVector->rotate( currentAngleToPointAt ); trueMoveVector->scale( mEnemyVelocity * inTimeDeltaInSeconds ); double moveLength = trueMoveVector->getLength(); if( distanceToTarget - moveLength < closestApproachToTarget ) { // this move will put us too close to the target // don't move at all } else { // not already too close to target currentPosition->add( trueMoveVector ); } delete trueMoveVector; } delete yVector; // check if enemy should fire double timeSinceLastFire = *( mTimesSinceLastBullet->getElement( i ) ); timeSinceLastFire += inTimeDeltaInSeconds; if( timeSinceLastFire >= mEnemyTimeBetweenBullets && ! targetIsBorder ) { // don't fire if heading toward border // don't fire if exploding if( ! *( mCurrentlyExplodingFlags->getElement( i ) ) ) { if( currentPosition->getDistance( targetPosition ) <= mEnemyBulletRange ) { // close enough to hit target // fire double bulletMoveRate = mEnemyBulletBaseVelocity; Vector3D *bulletVelocityVector = new Vector3D( 0, -bulletMoveRate, 0 ); bulletVelocityVector->rotate( angleToPointAt ); mEnemyBulletManager->addBullet( *( mBulletCloseParameters->getElement( i ) ), *( mBulletFarParameters->getElement( i ) ), 1, mEnemyBulletRange, new Vector3D( currentPosition ), new Angle3D( angleToPointAt ), bulletVelocityVector ); timeSinceLastFire = 0; } } } *( mTimesSinceLastBullet->getElement( i ) ) = timeSinceLastFire; ; delete angleToPointAt; delete normalizedMoveVector; delete targetPosition; double bulletPowerNearThisEnemy = mShipBulletManager->getBulletPowerInCircle( *( mCurrentPositions->getElement( i ) ), *( mCurrentRadii->getElement( i ) ) ); if( bulletPowerNearThisEnemy > 0 ) { // enemy hit by bullet if( ! *( mCurrentlyExplodingFlags->getElement( i ) ) ) { // not already exploding // start exploding *( mCurrentlyExplodingFlags->getElement( i ) ) = true; // play the sound PlayableSound *sound = mEnemyExplosionSoundTemplate->getPlayableSound( *( mBulletCloseParameters->getElement( i ) ), *( mBulletFarParameters->getElement( i ) ), mSoundPlayer->getSampleRate() ); // enemy explosion is low priority mSoundPlayer->playSoundNow( sound, false, 1 ); //*( mExplosionShapeParameters->getElement( i ) ) ); delete sound; } } if( *( mCurrentlyExplodingFlags->getElement( i ) ) ) { // this enemy is exploding // compute new explosion progress double explosionTime = *( mExplosionTimesInSeconds->getElement( i ) ); // convert time delta to a progress increment for the progress // range [0,1] double progressFractionDelta = inTimeDeltaInSeconds / explosionTime; double progress = *( mExplosionProgress->getElement( i ) ); progress += progressFractionDelta; if( progress > 1 ) { progress = 1; } *( mExplosionProgress->getElement( i ) ) = progress; if( progress == 1 ) { // end of explosion // fade out last frame double fadeProgress = *( mFadeProgress->getElement( i ) ); fadeProgress += progressFractionDelta; if( fadeProgress >= 1 ) { // explosion done fading fadeProgress = 1; // enemy should be destroyed *( mSholdBeDestroyedFlags->getElement( i ) ) = true; } *( mFadeProgress->getElement( i ) ) = fadeProgress; } } else { // not exploding // check if we are fading in after enemy creation double fadeProgress = *( mFadeProgress->getElement( i ) ); if( fadeProgress != 0 ) { // still fading in // use explosion time as fade-in time // (makes sense, since we also use the explosion time for // the fade-out time after the explosion finishes) double totalFadeTime = *( mExplosionTimesInSeconds->getElement( i ) ); // convert time delta to a progress increment for the progress // range [0,1] double progressFractionDelta = inTimeDeltaInSeconds / totalFadeTime; fadeProgress -= progressFractionDelta; if( fadeProgress < 0 ) { fadeProgress = 0; } *( mFadeProgress->getElement( i ) ) = fadeProgress; } } // convert the distance to target to the range [0,1] and save it if( targetIsBorder ) { // don't have enemy change as it gets closer to the border... // use the distance from the ship to control its form distanceToTarget = distanceToShip; } // 25 units, up to 20 units from target, is the range double compressedDistance = ( distanceToTarget - 20 ) / ( 25 ); // limit the range if( compressedDistance > 1 ) { compressedDistance = 1; } else if( compressedDistance < 0 ) { compressedDistance = 0; } *( mShipDistanceParameters->getElement( i ) ) = compressedDistance; } // walk through vectors and destroy enemies that are flagged // note that our loop range is adjusted as the vector length shrinks for( i=0; isize(); i++ ) { if( *( mSholdBeDestroyedFlags->getElement( i ) ) ) { mEnemyShapeParameters->deleteElement( i ); mExplosionShapeParameters->deleteElement( i ); mCurrentlyExplodingFlags->deleteElement( i ); mExplosionTimesInSeconds->deleteElement( i ); mExplosionProgress->deleteElement( i ); mFadeProgress->deleteElement( i ); mBulletCloseParameters->deleteElement( i ); mBulletFarParameters->deleteElement( i ); mTimesSinceLastBullet->deleteElement( i ); delete *( mCurrentRotations->getElement( i ) ); mCurrentRotations->deleteElement( i ); mCurrentRotationRates->deleteElement( i ); delete *( mCurrentPositions->getElement( i ) ); mCurrentPositions->deleteElement( i ); delete *( mCurrentAnglesToPointAt->getElement( i ) ); mCurrentAnglesToPointAt->deleteElement( i ); mCurrentRadii->deleteElement( i ); mSholdBeDestroyedFlags->deleteElement( i ); mShipDistanceParameters->deleteElement( i ); } } delete centerPosition; } SimpleVector *EnemyManager::getDrawableObjects() { SimpleVector *returnVector = new SimpleVector(); int numEnemies = mEnemyShapeParameters->size(); for( int i=0; i *enemyObjects = mEnemyTemplate->getDrawableObjects( *( mEnemyShapeParameters->getElement( i ) ), *( mShipDistanceParameters->getElement( i ) ), *( mExplosionShapeParameters->getElement( i ) ), *( mExplosionProgress->getElement( i ) ), ¤tRotationRate ); *( mCurrentRotationRates->getElement( i ) ) = currentRotationRate; // fade out at end of explosion double fadeValue = *( mFadeProgress->getElement( i ) ); double alphaMultiplier = 1 - fadeValue; // compute the scale by weighting the enemy and explosion scales double explosionProgress = *( mExplosionProgress->getElement( i ) ); double scale = explosionProgress * mExplosionScale + ( 1 - explosionProgress ) * mEnemyScale; int numObjects = enemyObjects->size(); // compute the maximum radius of this enemy double maxRadius = 0; for( int j=0; jgetElement( j ) ); currentObject->scale( scale ); currentObject->rotate( *( mCurrentRotations->getElement( i ) ) ); currentObject->move( *( mCurrentPositions->getElement( i ) ) ); currentObject->fade( alphaMultiplier ); double radius = currentObject->getBorderMaxDistance( *( mCurrentPositions->getElement( i ) ) ); if( radius > maxRadius ) { maxRadius = radius; } returnVector->push_back( currentObject ); } *( mCurrentRadii->getElement( i ) ) = maxRadius; delete enemyObjects; } return returnVector; } transcend-0.3+dfsg2.orig/Transcend/game/MusicNoteWaveTable.cpp0000640000175000017500000001360010115266320022760 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-31 Jason Rohrer * Added brief fade-in at note start to reduce clicks. */ #include "MusicNoteWaveTable.h" #include "LevelDirectoryManager.h" #include "minorGems/util/SimpleVector.h" #include #include MusicNote::MusicNote( int inFrequencyIndex, int inLengthIndex, char inReversed ) : mFrequencyIndex( inFrequencyIndex ), mLengthIndex( inLengthIndex ), mReversed( inReversed ) { } MusicNote *MusicNote::copy() { return new MusicNote( mFrequencyIndex, mLengthIndex, mReversed ); } MusicNoteWaveTable::MusicNoteWaveTable( unsigned long inSamplesPerSecond ){ // read frequencies and lengths from files SimpleVector *frequencyVector = new SimpleVector(); SimpleVector *lengthVector = new SimpleVector(); FILE *musicNotePitchesFILE = LevelDirectoryManager::getStdStream( "musicNotePitches", true ); FILE *musicNoteLengthsFILE = LevelDirectoryManager::getStdStream( "musicNoteLengths", true ); if( musicNotePitchesFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNotePitchesFILE, "%lf", &readValue ); if( numRead == 1 ) { frequencyVector->push_back( readValue ); } } fclose( musicNotePitchesFILE ); } else { // default to one pitch frequencyVector->push_back( 400.00 ); } if( musicNoteLengthsFILE != NULL ) { double readValue; int numRead = 1; while( numRead == 1 ) { numRead = fscanf( musicNoteLengthsFILE, "%lf", &readValue ); if( numRead == 1 ) { lengthVector->push_back( readValue ); } } fclose( musicNoteLengthsFILE ); } else { // default to one pitch lengthVector->push_back( 400.00 ); } mFrequencyCount = frequencyVector->size(); mLengthCount = lengthVector->size(); double *frequencies = frequencyVector->getElementArray(); mLengthsInSeconds = lengthVector->getElementArray(); delete frequencyVector; delete lengthVector; mSampleTable = new float**[ mFrequencyCount ]; mSampleCounts = new unsigned long[ mLengthCount ]; for( int F=0; F lengthInSamples ) { numFadeInSamples = lengthInSamples / 2; } for( unsigned long i=0; imFrequencyIndex, inNote->mLengthIndex, outNumSamples ); } transcend-0.3+dfsg2.orig/Transcend/game/MusicPart.cpp0000640000175000017500000001043010113264470021166 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-26 Jason Rohrer * Added parameter to control character of part. */ #include "MusicPart.h" #include "LevelDirectoryManager.h" #include MusicPart::MusicPart( MusicNoteWaveTable *inWaveTable, RandomSource *inRandSource, double inParameter ) : mWaveTable( inWaveTable ), mNotes( new SimpleVector() ) { int frequencyCount = inWaveTable->getFrequencyCount(); int lengthCount = inWaveTable->getLengthCount(); char error= false; double musicChanceOfReversedNote = LevelDirectoryManager::readDoubleFileContents( "musicChanceOfReversedNote", &error, true ); if( error ) { // default to all notes playing forward musicChanceOfReversedNote = 0.0; } error = false; double musicPartLength = LevelDirectoryManager::readDoubleFileContents( "musicPartLength", &error, true ); if( error ) { // default to 10 second parts musicPartLength = 10.0; } // populate our note vector with randomized notes, all of the same length // decide note length using our parameter int lengthIndex = (int)( rint( inParameter * ( lengthCount - 1 ) ) ); double totalLength = 0; while( totalLength < musicPartLength ) { // add another note int frequencyIndex = inRandSource->getRandomBoundedInt( 0, frequencyCount - 1 ); char noteReversed; // flip a weighted coin to determine if this note should be played // in reverse if( inRandSource->getRandomDouble() < musicChanceOfReversedNote ) { noteReversed = true; } else { noteReversed = false; } mNotes->push_back( new MusicNote( frequencyIndex, lengthIndex, noteReversed ) ); // add this note's length to our total totalLength += inWaveTable->getLengthInSeconds( lengthIndex ); } // note lengths sum to a total length that may be beyond the limit if( totalLength > musicPartLength ) { // drop the last note int lastNoteIndex = mNotes->size() - 1; delete *( mNotes->getElement( lastNoteIndex ) ); mNotes->deleteElement( lastNoteIndex ); // could do something more intelligent here... // like drop the note that results in a total length that // is closest to the musicPartLength } } MusicPart::~MusicPart() { int numNotes = mNotes->size(); for( int i=0; igetElement( i ) ); } delete mNotes; } double MusicPart::getPartLengthInSeconds() { return mPartLengthInSeconds; } int MusicPart::getNotesStartingInInterval( double inStartTimeInSeconds, double inLengthInSeconds, MusicNote ***outNotes, double **outNoteStartOffsetsInSeconds ) { SimpleVector *returnNotes = new SimpleVector(); SimpleVector *returnStartOffsets = new SimpleVector(); double endTimeInSeconds = inStartTimeInSeconds + inLengthInSeconds; // walk through notes looking for those that start in the interval int numNotes = mNotes->size(); double currentNoteStartTime = 0; for( int i=0; igetElement( i ) ); double noteLength = mWaveTable->getLengthInSeconds( note->mLengthIndex ); if( currentNoteStartTime >= inStartTimeInSeconds ) { // add the note returnNotes->push_back( note->copy() ); returnStartOffsets->push_back( currentNoteStartTime - inStartTimeInSeconds ); } // else skip the note currentNoteStartTime += noteLength; } int numNotesReturned = returnNotes->size(); *outNotes = returnNotes->getElementArray(); *outNoteStartOffsetsInSeconds = returnStartOffsets->getElementArray(); delete returnNotes; delete returnStartOffsets; return numNotesReturned; } transcend-0.3+dfsg2.orig/Transcend/game/MusicPart.h0000640000175000017500000000545610113264471020650 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-26 Jason Rohrer * Added parameter to control character of part. */ #ifndef MUSIC_PART_INCLUDED #define MUSIC_PART_INCLUDED #include "MusicNoteWaveTable.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/random/RandomSource.h" /** * A sequence of music notes. * * @author Jason Rohrer */ class MusicPart { public: /** * Constructs a randomized part. * Reads configuration using the LevelDirectoryManager. * * @param inWaveTable the wave table to pick random notes from. * Must be destroyed by caller after this class is destroyed. * @param inRandSource the source for random numbers. * Must be destroyed by caller (can be destroyed after * this constructor returns). * @param inParameter parameter in the range [0,1] that controls * the character of this music part. */ MusicPart( MusicNoteWaveTable *inWaveTable, RandomSource *inRandSource, double inParameter ); ~MusicPart(); /** * Gets this part's length. * * @return the length in seconds. */ double getPartLengthInSeconds(); /** * Gets the notes that should start playing in a given time interval. * * Note that only notes that *start* in the interval are returned. * Notes that are playing during the interval, but that start before * the interval, are ignored. * * @param inStartTimeInSeconds the start of the interval * Must be in the range [ 0, getPartLengthInSeconds() ]. * @param inLengthInSeconds the length of the interval. * @param outNotes pointer to where an array of notes * should be returned. * The returned array and the notes must be destroyed by caller. * @param outNoteStartOffsetsInSeconds pointer to where an array of * note start offsets should be returned. * Offsets are in seconds beyond inStartTimeInSeconds. * The returned array must be destroyed by caller. * * @return the number of notes that start in the interval * (in other words, the length of the returned arrays). */ int getNotesStartingInInterval( double inStartTimeInSeconds, double inLengthInSeconds, MusicNote ***outNotes, double **outNoteStartOffsetsInSeconds ); protected: MusicNoteWaveTable *mWaveTable; SimpleVector *mNotes; double mPartLengthInSeconds; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/BossManager.h0000640000175000017500000001532010302410063021117 0ustar pabspabs/* * Modification History * * 2004-June-29 Jason Rohrer * Created. * * 2004-August-19 Jason Rohrer * Added explosion sounds. * * 2004-August-24 Jason Rohrer * Added extra parameter for distance from ship. * * 2004-August-25 Jason Rohrer * Added function to check for boss death. * * 2005-August-22 Jason Rohrer * Started work on boss damage graphics. */ #ifndef BOSS_MANAGER_INCLUDED #define BOSS_MANAGER_INCLUDED #include "Enemy.h" #include "ShipBulletManager.h" #include "SoundPlayer.h" #include "BulletSound.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" /** * A class that manages and draws all enemies in the environment. * * @author Jason Rohrer. */ class BossManager { public: /** * Constructs a manager. * * @param inBossTemplate the class to use to draw all enemies. * Will be destroyed by this class. * @param inBossScale the value to multiply boss size by. * @param inExplosionScale the value to multiply explosion size by. * @param inExplosionTimeInSeconds the time it takes the explosion * to complete. * @param inExplosionShapeParameter the parameter that controls * explosion shape, in [0,1]. * @param inBossMinVelocity the minimum speed of the boss. * @param inBossMaxVelocity the maximum speed of the boss. * @param inShipBulletManager the ship bullet manager. * Must be destroyed by caller after this class is destroyed. * @param inBossBulletManager the boss bullet manager. * Must be destroyed by caller after this class is destroyed. * @param inBossDamageManager the manager for boss damage objects. * Must be destroyed by caller after this class is destroyed. * @param inBossBulletRange the range of boss bullets. * @param inBossBulletVelocity the velocity of boss bullets. * @param inMinBossBulletsPerSecond the minimum number of bullets a * boss can fire per second. * @param inMaxBossBulletsPerSecond the maximum number of bullets a * boss can fire per second. * @param inBossDamageTime how long each damage object is displayed * in seconds. * @param inTimeUntilFullShootingPower how long the ship has to * be in range before the boss reaches its full shooting power. * @param inMaxBossHealth the maximum health level of the boss. * @param inBossHealthRecoveryRate how fast the boss recovers health. * @param inBossPosition the position of the boss. * Will be destroyed when this class is destroyed. * @param inSoundPlayer the player to send boss sounds to. * Must be destroyed by caller after this class is destroyed. * @param inBossExplosionSoundTemplate the class to use to play * boss explosion sounds. * Will be destroyed by this class. */ BossManager( Enemy *inBossTemplate, double inBossScale, double inExplosionScale, double inExplosionTimeInSeconds, double inExplosionShapeParameter, double inBossMinVelocity, double inBossMaxVelocity, ShipBulletManager *inShipBulletManager, ShipBulletManager *inBossBulletManager, ShipBulletManager *inBossDamageManager, double inBossBulletRange, double inBossBulletBaseVelocity, double inMinBossBulletsPerSecond, double inMaxBossBulletsPerSecond, double inBossDamageTime, double inTimeUntilFullShootingPower, double inMaxBossHealth, double inBossHealthRecoveryRate, Vector3D *inBossPosition, SoundPlayer *inSoundPlayer, BulletSound *inBossExplosionSoundTemplate ); virtual ~BossManager(); /** * Tell this manager that time has passed. * * @param inTimeDeltaInSeconds the amount of time that has passed. * @param inShipPosition the position of the ship (boss shoots * toward it). * Must be destroyed by caller. * @param inShipVelocity the velocity of the ship (boss compensates * shooting aim). * Must be destroyed by caller. */ void passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition, Vector3D *inShipVelocity ); /** * Gets drawable objects for the boss. * * @return boss as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); /** * Gets the position of the boss. * * @return the position. * Must be destroyed by caller. */ Vector3D *getBossPosition(); /** * Gets whether the boss is dead (explosion complete). * * @return true if the boss is dead. */ char isBossDead(); protected: Enemy *mBossTemplate; ShipBulletManager *mShipBulletManager; ShipBulletManager *mBossBulletManager; ShipBulletManager *mBossDamageManager; double mBossScale; double mExplosionScale; double mExplosionTimeInSeconds; double mExplosionShapeParameter; double mBossMinVelocity; double mBossMaxVelocity; double mBossBulletRange; double mBossBulletBaseVelocity; double mMinBossTimeBetweenBullets; double mMaxBossTimeBetweenBullets; double mBossDamageTime; double mTimeUntilFullShootingPower; double mMaxBossHealth; double mBossHealth; double mHealthRecoveryRate; Vector3D *mBossPosition; SoundPlayer *mSoundPlayer; BulletSound *mBossExplosionSoundTemplate; double mBossDistanceFromCenter; char mCurrentlyExploding; double mExplosionProgress; double mExplosionFadeProgress; double mTimeSinceLastBullet; double mTimeSinceShipEnteredRange; double mAngerLevel; double mCurrentRadius; double mCurrentRotationRate; Angle3D *mCurrentRotation; double mShipDistanceParameter; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ShipBulletManager.h0000640000175000017500000001612610302660571022304 0ustar pabspabs/* * Modification History * * 2004-June-16 Jason Rohrer * Created. * * 2004-August-15 Jason Rohrer * Added sound support. * * 2004-August-30 Jason Rohrer * Added bullet scaling parameters. * * 2004-October-13 Jason Rohrer * Added function for getting bullet count. * * 2005-August-22 Jason Rohrer * Started work on boss damage graphics. * * 2005-August-23 Jason Rohrer * Finished boss damage graphics. */ #ifndef SHIP_BULLET_MANAGER_INCLUDED #define SHIP_BULLET_MANAGER_INCLUDED #include "ShipBullet.h" #include "SoundPlayer.h" #include "BulletSound.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" #include /** * A class that manages and draws all ship bullets in the environment. * * @author Jason Rohrer. */ class ShipBulletManager { public: /** * Constructs a manager. * * @param inBulletTemplate the class to use to draw all bullets. * Will be destroyed by this class. * @param inBulletScale the scale factor for bullet * graphics. * @param inPlayer the player to direct bullet sounds to, or NULL * to not play sounds. * Must be destroyed by caller after this class is destroyed. * @param inBulletSoundTemplate the class to use to generate bullet * Sounds, or NULL to not play sounds. * Will be destroyed by this class. * @param inWorldWidth the width of the world (for wrap-around). * @param inWorldHeight the height of the world (for wrap-around). */ ShipBulletManager( ShipBullet *inBulletTemplate, double inBulletScale, SoundPlayer *inPlayer, BulletSound *inBulletSoundTemplate, double inWorldWidth, double inWorldHeight ); virtual ~ShipBulletManager(); /** * Adds a bullet to this manager. * * @param inCloseRangeParameter the parameter in [0,1] that controls * the shape and power of the bullet at close range. * @param inFarRangeParameter the parameter in [0,1] that controls * the shape and power of the bullet at far range. * @param inPowerModifier a value in [0,1] that further modifies * (multiplicatively) the power of the bullet (visually represented * by bullet transparency. * @param inRangeInScreenUnits the length of this bullet's range * in screen units. * This will be interpreted as bullet life time in seconds if the * bullet velocity vector (inVelocityInScreenUnitsPerSecond) is 0. * @param inStartingPosition the starting position of this bullet. * Will be destroyed by this class. * @param inStartingRotation the starting rotation angle. * Will be destroyed by this class. * @param inVelocityInScreenUnitsPerSecond the velocity vector for * this bullet. * Will be destroyed by this class. */ void addBullet( double inCloseRangeParameter, double inFarRangeParameter, double inPowerModifier, double inRangeInScreenUnits, Vector3D *inStartingPosition, Angle3D *inStartingRotation, Vector3D *inVelocityInScreenUnitsPerSecond ); /** * Gets the number of active bullets. * * @return the number of bullets currently being handled by this * manager. */ int getBulletCount(); /** * Gets the sum of bullet powers in a circular region of the world. * Wraps around if circle extends off the edge of the world. * * @param inCircleCenter the center of the circle. * Must be destroyed by caller. * @param inCircleRadius the radius of the circle. */ double getBulletPowerInCircle( Vector3D *inCircleCenter, double inCircleRadius ); /** * Gets the positions of all bullets in a circular region of the world. * Wraps around if circle extends off the edge of the world. * * @param inCircleCenter the center of the circle. * Must be destroyed by caller. * @param inCircleRadius the radius of the circle. * @param outNumBullets pointer to where bullet count should be * returned. * * @return an array of vector positions. * Array and vectors must be destroyed by caller. */ Vector3D **getBulletPositionsInCircle( Vector3D *inCircleCenter, double inCircleRadius, int *outNumBullets ); /** * Tell this manager that time has passed. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); /** * Gets drawable objects for all bullets in their current * positions/states. * * @return all bullets as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); protected: ShipBullet *mBulletTemplate; double mBulletScale; SoundPlayer *mSoundPlayer; BulletSound *mBulletSoundTemplate; double mMaxXPosition; double mMinXPosition; double mMaxYPosition; double mMinYPosition; SimpleVector *mCloseRangeParameters; SimpleVector *mFarRangeParameters; SimpleVector *mPowerModifiers; SimpleVector *mCurrentPowers; // how long each bullet range is in screen units SimpleVector *mRangesInScreenUnits; // how far each bullet is along in its range, in [0,1] SimpleVector *mRangeFractions; // how much time has passed during the life of this bullet SimpleVector *mTimePassed; // since velocities are constant, we can always // compute bullet position from the starting position, the time, // and the velocity vector SimpleVector *mStartingPositions; SimpleVector *mVelocitiesInScreenUnitsPerSecond; SimpleVector *mCurrentPositions; // rotation rates can vary across a bullet's lifespan, so we must // keep a current angle for each bullet and adjust the angle // for each time delta SimpleVector *mCurrentRotations; SimpleVector *mCurrentRotationRates; SimpleVector *mSholdBeDestroyedFlags; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ParameterSpaceControlPoint.h0000640000175000017500000000461410105756452024211 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #ifndef PARAMETER_SPACE_CONTROL_POINT_INCLUDED #define PARAMETER_SPACE_CONTROL_POINT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/util/SimpleVector.h" /** * A control point in a 1-D parameterized object space. * * @author Jason Rohrer. */ class ParameterSpaceControlPoint { public: virtual ~ParameterSpaceControlPoint(); /** * Makes a deep copy of this control point. * * @return a copy of this point. * Must be destroyed by caller. */ virtual ParameterSpaceControlPoint *copy() = 0; /** * Creates a new control point by blending this point with another * point. * * @param inOtherPoint the other control point. * Must be destroyed by caller. * @param inWeightOfOtherPoint the weight of the other point, * in the range [0,1]. * * @return the new, blended point. * Must be destroyed by caller. */ virtual ParameterSpaceControlPoint *createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ) = 0; protected: /** * Blends two vertex arrays. * * @param inFirstArray the first array of vertices. * Vertices and array must be destroyed by caller. * @param inFirstArrayLength the number of vertices in the first array. * @param inWeightFirstArray the weight of the first array, in [0,1]. * @param inSecondArray the first array of vertices. * Vertices and array must be destroyed by caller. * @param inSecondArrayLength the number of vertices in the first array. * @param outResultLength pointer to where the length of the resulting * array should be returned. * * @return the blended array. * Vertices and array must be destroyed by caller. */ static Vector3D **blendVertexArrays( Vector3D **inFirstArray, int inFirstArrayLength, double inWeightFirstArray, Vector3D **inSecondArray, int inSecondArrayLength, int *outResultLength ); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/Makefile.all0000640000175000017500000000336010133736156020775 0ustar pabspabs# # Modification History # # 2004-June-10 Jason Rohrer # Created. Copied from Monolith text UI. # ## # The portion of Transcend command-line-UI Makefiles common to all # platforms. # Should not be made manually---used by Transcend/configure to build Makefiles. ## ROOT_PATH = ../.. LAYER_SOURCE = \ game.cpp \ DrawableObject.cpp \ LevelDirectoryManager.cpp \ NamedColorFactory.cpp \ ParameterizedSpace.cpp \ ParameterSpaceControlPoint.cpp \ ObjectParameterSpaceControlPoint.cpp \ ParameterizedObject.cpp \ ShipBullet.cpp \ ShipBulletManager.cpp \ Enemy.cpp \ EnemyManager.cpp \ SculptureManager.cpp \ BossManager.cpp \ PortalManager.cpp \ SoundSamples.cpp \ SoundPlayer.cpp \ ReverbSoundFilter.cpp \ SoundParameterSpaceControlPoint.cpp \ StereoSoundParameterSpaceControlPoint.cpp \ ParameterizedStereoSound.cpp \ OnePointPlayableSound.cpp \ BulletSound.cpp \ MusicNoteWaveTable.cpp \ MusicPart.cpp \ MusicPlayer.cpp LAYER_OBJECTS = ${LAYER_SOURCE:.cpp=.o} NEEDED_MINOR_GEMS_OBJECTS = \ ${SCREEN_GL_O} \ ${TYPE_IO_O} \ ${STRING_UTILS_O} \ ${STRING_BUFFER_OUTPUT_STREAM_O} \ ${PATH_O} \ ${TIME_O} \ ${THREAD_O} \ ${MUTEX_LOCK_O} TEST_SOURCE = TEST_OBJECTS = ${TEST_SOURCE:.cpp=.o} DEPENDENCY_FILE = Makefile.dependencies # targets all: Transcend clean: rm -f ${DEPENDENCY_FILE} ${LAYER_OBJECTS} ${TEST_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} Transcend Transcend: ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${EXE_LINK} -o Transcend ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${PLATFORM_LINK_FLAGS} # build the dependency file ${DEPENDENCY_FILE}: ${LAYER_SOURCE} ${TEST_SOURCE} rm -f ${DEPENDENCY_FILE} ${COMPILE} -MM ${LAYER_SOURCE} ${TEST_SOURCE} >> ${DEPENDENCY_FILE} include ${DEPENDENCY_FILE} transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedStereoSound.h0000640000175000017500000000463410107703602023722 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #ifndef PARAMETERIZED_STEREO_SOUND_INCLUDED #define PARAMETERIZED_STEREO_SOUND_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/util/SimpleVector.h" #include "ParameterizedSpace.h" #include "PlayableSound.h" #include "StereoSoundParameterSpaceControlPoint.h" /** * A 1-D space of sound control points. * * @author Jason Rohrer. */ class ParameterizedStereoSound : public ParameterizedSpace { public: /** * Constructs a sound by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading the sound * from inFILE fails. */ ParameterizedStereoSound( FILE *inFILE, char *outError ); /** * Gets a playable sound from this sound space. * * @param inParameter the parameter in the range [0,1] to map * into the sound space. * @param inSamplesPerSecond the current sample rate. * * @return a playable sound * Can return NULL if this space was not properly initialized. * Must be destroyed by caller. */ PlayableSound *getPlayableSound( double inParameter, unsigned long inSamplesPerSecond ); /** * Gets a blended sound control point from this sound space. * * @param inParameter the parameter in the range [0,1] to map * into the sound space. * * @return the blended control point. * Can return NULL if this space was not properly initialized. * Must be destroyed by caller. */ StereoSoundParameterSpaceControlPoint *getBlendedControlPoint( double inParameter ); /** * Gets the length of the sound represented by this space. * * @return the sound length in seconds. */ double getSoundLengthInSeconds(); protected: double mSoundLengthInSeconds; // inherit other protected members from ParameterizedSpace }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SoundPlayer.h0000640000175000017500000001453210133736156021207 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Changed to use a callback to reduce latency. * * 2004-August-9 Jason Rohrer * Added a limit on the number of simultaneous sounds. * * 2004-August-12 Jason Rohrer * Parameterized the sample rate. * * 2004-August-13 Jason Rohrer * Added Mutex to protect members that are accessed by multiple threads. * * 2004-August-14 Jason Rohrer * Changed to fade sounds out before dropping them. * * 2004-August-15 Jason Rohrer * Added a function for getting the sample rate. * Added volume modifier parameter to playSoundNow function. * * 2004-August-20 Jason Rohrer * Added priority flags. * * 2004-August-23 Jason Rohrer * Added music. * * 2004-August-25 Jason Rohrer * Added function for setting music loudness. * * 2004-August-31 Jason Rohrer * Added function for removing filters. */ #ifndef SOUND_PLAYER_INCLUDED #define SOUND_PLAYER_INCLUDED #include "SoundSamples.h" #include "SoundFilter.h" #include "PlayableSound.h" #include "Transcend/portaudio/pa_common/portaudio.h" #include "Transcend/portaudio/pablio/pablio.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/system/MutexLock.h" /** * Class that plays both running background music and realtime sounds. * * @author Jason Rohrer */ class SoundPlayer { public: /** * Constructs a sound player. * * @param inSampleRate the number of samples per second. * Should be a "standard" rate, like 44100, 22050, etc. * @param inMaxSimultaneousRealtimeSounds the number of simultaneous * realtime sounds to allow. When this limit is reached, * older sounds are silenced prematurely to make way for * newer sounds. * @param inMusicPlayer the player to get music from, or NULL * to disable music. Defaults to NULL. * Typed as (void*) to avoid an include loop. * Must be destroyed by caller after this class is destroyed. * @param inMusicLoudness an adjustment for music loudness in the * range [0,1]. Defaults to 1. */ SoundPlayer( int inSampleRate, int inMaxSimultaneousRealtimeSounds, void *inMusicPlayer = NULL, double inMusicLoudness = 1 ); ~SoundPlayer(); /** * Sets the music player. * * @param inMusicPlayer the player to get music from, or NULL * to disable music. Typed as (void*) to avoid an include loop. * Must be destroyed by caller after this class is destroyed. */ void setMusicPlayer( void *inMusicPlayer ); /** * Sets the loudess of the music. * * @param inMusicLoudness the loudness in the range [0,1]. */ void setMusicLoudness( double inMusicLoudness ); /** * Mixes a sound to the speakers as quickly as possible. * * This call does not adjust the volume level of the samples * before mixing them with other realtime sounds or the background * music. * * @param inSamples the samples to play. * Must be destroyed by caller. * @param inPriorityFlag true if this sound should have * high priority, or false for low priority. Defaults to false. * @param inLoudnessModifier the value to adjust the sound's * loudness by, in [0,1], when playing. Defaults to 1. */ void playSoundNow( SoundSamples *inSamples, char inPriorityFlag = false, double inLoudnessModifier = 1.0 ); /** * Same as earlier playSoundNow, except that it takes a PlayableSound * that must be destroyed by the caller. */ void playSoundNow( PlayableSound *inSound, char inPriorityFlag = false, double inLoudnessModifier = 1.0 ); /** * Add the next section of music to be played. * * This call drives the sound player to send audio to the speakers. * * @param inSamples the samples to play. * Must be destroyed by caller. */ void addMoreMusic( SoundSamples *inSamples ); /** * Called by the internal portaudio callback function. */ void getSamples( void *outputBuffer, unsigned long inFramesInBuffer ); /** * Adds a filter to the end of the chain that will process * samples before sending them to the speakers. * * @param inFilter the filter to add. * Will be destroyed when this class is destroyed. */ void addFilter( SoundFilter *inFilter ); /** * Removes and destroys all filters. */ void removeAllFilters(); /** * Gets the current sample rate. * * @return the sample rate, in samples per second. */ unsigned long getSampleRate(); protected: MutexLock *mLock; unsigned long mSampleRate; char mAudioInitialized; int mMaxSimultaneousRealtimeSounds; // Typed as (void*) to avoid an include loop. void *mMusicPlayer; double mMusicLoudness; PortAudioStream *mAudioStream; // realtime sounds that should be mixed into the next to-speaker call SimpleVector *mRealtimeSounds; SimpleVector *mPriorityFlags; // one modifier for each realtime sound SimpleVector *mSoundLoudnessModifiers; // one flag for each realtime sound, indicating whether it should // be dropped (faded out) during the next frame SimpleVector *mSoundDroppedFlags; SimpleVector *mFilterChain; /** * Checks the sound set to ensure that our max simultaneous limit * is being met. * * Not thread-safe: should be called with mLock already locked. */ void checkForExcessSounds(); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/EnemyManager.h0000640000175000017500000001730110302142777021305 0ustar pabspabs/* * Modification History * * 2004-June-18 Jason Rohrer * Created. * * 2004-June-20 Jason Rohrer * Added fade-out of last explosion frame. Added scaling. * * 2004-August-6 Jason Rohrer * Added enemy shot sounds. * * 2004-August-17 Jason Rohrer * Removed outdated enemy shot sound. Still need to replace explosion sound. * * 2004-October-14 Jason Rohrer * Added function for exploding all active enemies. * * 2005-August-21 Jason Rohrer * Added fade-in upon enemy creation. * Made target-switching rotations smooth. */ #ifndef ENEMY_MANAGER_INCLUDED #define ENEMY_MANAGER_INCLUDED #include "Enemy.h" #include "BulletSound.h" #include "ShipBulletManager.h" #include "SculptureManager.h" #include "SoundPlayer.h" #include "SoundSamples.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" /** * A class that manages and draws all enemies in the environment. * * @author Jason Rohrer. */ class EnemyManager { public: /** * Constructs a manager. * * @param inEnemyTemplate the class to use to draw all enemies. * Will be destroyed by this class. * @param inEnemyScale the value to multiply enemy size by. * @param inExplosionScale the value to multiply explosion size by. * @param inEnemyVelocity the speed of enemies. * @param inSculptureManager the sculpture manager. * Must be destroyed by caller after this class is destroyed. * @param inShipBulletManager the ship bullet manager. * Must be destroyed by caller after this class is destroyed. * @param inEnemyBulletManager the enemy bullet manager. * Must be destroyed by caller after this class is destroyed. * @param inEnemyBulletRange the range of enemy bullets. * @param inEnemyBulletBaseVelocity the velocity of enemy bullets. * @param inEnemyBulletsPerSecond the number of bullets an * enemy can fire per second. * @param inSoundPlayer the player to send enemy sounds to. * Must be destroyed by caller after this class is destroyed. * @param inEnemyExplosionSoundTemplate the class to use to play * enemy explosion sounds. * Will be destroyed by this class. * @param inWorldWidth the width of the world (for wrap-around). * @param inWorldHeight the height of the world (for wrap-around). */ EnemyManager( Enemy *inEnemyTemplate, double inEnemyScale, double inExplosionScale, double inEnemyVelocity, SculptureManager *inSculptureManager, ShipBulletManager *inShipBulletManager, ShipBulletManager *inEnemyBulletManager, double inEnemyBulletRange, double inEnemyBulletBaseVelocity, double inEnemyBulletsPerSecond, SoundPlayer *inSoundPlayer, BulletSound *inEnemyExplosionSoundTemplate, double inWorldWidth, double inWorldHeight ); virtual ~EnemyManager(); /** * Adds a enemy to this manager. * * @param inEnemyShapeParameter a parameter in the range [0,1] to * control the shape of the enemy. * @param inExplosionShapeParameter a parameter in the range [0,1] to * control the shape of the enemy's explosion. * @param inExplosionTimeInSeconds the time it takes this enemy * to complete its explosion. * @param inBulletCloseParameter a parameter in the range [0,1] to * control the shape of the enemy's close-range bullets. * @param inBulletFarParameter a parameter in the range [0,1] to * control the shape of the enemy's far-range bullets. * @param inStartingPosition the starting position of this enemy. * Will be destroyed by this class. * @param inStartingRotation the starting rotation angle. * Will be destroyed by this class. */ void addEnemy( double inEnemyShapeParameter, double inExplosionShapeParameter, double inExplosionTimeInSeconds, double inBulletCloseParameter, double inBulletFarParameter, Vector3D *inStartingPosition, Angle3D *inStartingRotation ); /** * Causes all active enemies to start exploding. */ void explodeAllEnemies(); /** * Gets the number of enemies. * * @return the number of enemies. */ int getEnemyCount(); /** * Tell this manager that time has passed. * * @param inTimeDeltaInSeconds the amount of time that has passed. * @param inShipPosition the position of the ship (enemies head * toward it). * Must be destroyed by caller. */ void passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition ); /** * Gets drawable objects for all enemies in their current * positions/states. * * @return all enemies as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); protected: Enemy *mEnemyTemplate; SculptureManager *mSculptureManager; ShipBulletManager *mShipBulletManager; ShipBulletManager *mEnemyBulletManager; double mEnemyBulletRange; double mEnemyBulletBaseVelocity; double mEnemyBulletsPerSecond; double mEnemyTimeBetweenBullets; SoundPlayer *mSoundPlayer; BulletSound *mEnemyExplosionSoundTemplate; double mEnemyScale; double mExplosionScale; double mEnemyVelocity; double mMaxXPosition; double mMinXPosition; double mMaxYPosition; double mMinYPosition; SimpleVector *mEnemyShapeParameters; SimpleVector *mExplosionShapeParameters; // the explosion time for each enemy SimpleVector *mExplosionTimesInSeconds; // flags indicating which enemies are exploding SimpleVector *mCurrentlyExplodingFlags; // how far each enemy is towards a complete explosion, in [0,1] SimpleVector *mExplosionProgress; // how far each enemy is through its explosion fade-out or its // initial creation fade-in, in [0,1] SimpleVector *mFadeProgress; SimpleVector *mBulletCloseParameters; SimpleVector *mBulletFarParameters; SimpleVector *mTimesSinceLastBullet; SimpleVector *mCurrentPositions; SimpleVector *mCurrentAnglesToPointAt; SimpleVector *mCurrentRadii; // rotation rates can vary across a enemy's lifespan, so we must // keep a current angle for each enemy and adjust the angle // for each time delta SimpleVector *mCurrentRotations; SimpleVector *mCurrentRotationRates; SimpleVector *mSholdBeDestroyedFlags; // parameters for each enemy in the range [0,1] representing // distance of enemy from the ship SimpleVector *mShipDistanceParameters; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ReverbSoundFilter.h0000640000175000017500000000163710100010467022330 0ustar pabspabs/* * Modification History * * 2004-July-22 Jason Rohrer * Created. */ #ifndef REVERB_SOUND_FILTER_INCLUDED #define REVERB_SOUND_FILTER_INCLUDED #include "SoundFilter.h" /** * A reverb filter. * * @author Jason Rohrer */ class ReverbSoundFilter : public SoundFilter { public: /** * Constructs a filter. * * @param inDelayInSamples the delay of the reverb. * @param inGain the gain level in the range [0,1]. */ ReverbSoundFilter( unsigned long inDelayInSamples, double inGain ); virtual ~ReverbSoundFilter(); // implements the SoundFilter interface virtual SoundSamples *filterSamples( SoundSamples *inSamples ); private: SoundSamples *mDelayBuffer; double mGain; unsigned long mDelayBufferPosition; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedSpace.cpp0000640000175000017500000001341510105756452023044 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #include "ParameterizedSpace.h" #include ParameterizedSpace::~ParameterizedSpace() { for( int i=0; i= 2 ) { // find the 2 surrounding control points // all distances will be no more than 1 double distanceFromClosestLargerPoint = 2; double distanceFromClosestSmallerPoint = 3; int indexOfClosestLargerPoint = -1; int indexOfClosestSmallerPoint = -1; for( int i=0; i= inParameter ) { // a larger point if( distanceFromThisPoint < distanceFromClosestLargerPoint ) { // closer than our closest largerpoint indexOfClosestLargerPoint = i; distanceFromClosestLargerPoint = distanceFromThisPoint; } } else { // a smaller point if( distanceFromThisPoint < distanceFromClosestSmallerPoint ) { // closer than our closest smallerpoint indexOfClosestSmallerPoint = i; distanceFromClosestSmallerPoint = distanceFromThisPoint; } } } if( indexOfClosestLargerPoint != -1 && indexOfClosestSmallerPoint != -1 ) { // found two points // compute weights double distanceBetweenSurroundingPoints = fabs( mControlPointParameterAnchors[ indexOfClosestLargerPoint ] - mControlPointParameterAnchors[ indexOfClosestSmallerPoint ] ); double weightOnLargerPoint = distanceFromClosestSmallerPoint / distanceBetweenSurroundingPoints; // blend the two points, using the distance to weight them return mControlPoints[ indexOfClosestSmallerPoint ]-> createLinearBlend( mControlPoints[ indexOfClosestLargerPoint ], weightOnLargerPoint ); } else { // found only one point int indexOfPoint; if( indexOfClosestLargerPoint != -1 ) { indexOfPoint = indexOfClosestLargerPoint; } else if( indexOfClosestLargerPoint != -1 ) { indexOfPoint = indexOfClosestSmallerPoint; } else { printf( "Error: found no closest space parameter map " " anchor point.\n" ); indexOfPoint = 0; } // return copy of this point return mControlPoints[ indexOfPoint ]->copy(); } } else if( mNumControlPoints == 1 ) { // only one anchor point... return its parameters return mControlPoints[0]->copy(); } else { printf( "Error: no anchor points in sculpture parameter space.\n" ); return NULL; } /* if( mNumControlPoints >= 2 ) { // find the 2 closest control points // all distances will be no more than 1 double distanceFromClosestPoint = 2; double distanceFromNextClosestPoint = 3; int indexOfClosestPoint = -1; int indexOfNextClosestPoint = -1; for( int i=0; icreateLinearBlend( mControlPoints[ indexOfClosestPoint ], weightOnClosestPoint ); } else if( mNumControlPoints == 1 ) { // only one control point... return a copy return mControlPoints[0]->copy(); } else { printf( "Error: no control points in space space.\n" ); return NULL; } */ } transcend-0.3+dfsg2.orig/Transcend/game/MusicPlayer.cpp0000640000175000017500000002503410112704537021524 0ustar pabspabs/* * Modification History * * 2004-August-22 Jason Rohrer * Created. * * 2004-August-23 Jason Rohrer * Fixed bug in note offsets. * * 2004-August-24 Jason Rohrer * Added missing support for reversed notes and stereo split. * Changed to use constant power panning. */ #include "MusicPlayer.h" #include "LevelDirectoryManager.h" MusicPlayer::MusicPlayer( unsigned long inSamplesPerSecond, SculptureManager *inSculptureManager, MusicNoteWaveTable *inWaveTable, double inWorldWidth, double inWorldHeight, double inGridSpaceWidth ) : mSculptureManager( inSculptureManager ), mWaveTable( inWaveTable ), mActiveNotes( new SimpleVector() ), mNotePositions( new SimpleVector() ), mSampleRate( inSamplesPerSecond ), mWorldWidth( inWorldWidth ), mWorldHeight( inWorldHeight ), mGridSpaceWidth( inGridSpaceWidth ) { // start at far left mCurrentPartGridPosition = -mWorldWidth / 2; char error = false; mPartLengthInSeconds = LevelDirectoryManager::readDoubleFileContents( "musicPartLength", &error, true ); if( error ) { // default to 10 second parts mPartLengthInSeconds = 10.0; } } MusicPlayer::~MusicPlayer() { int numActiveNotes = mActiveNotes->size(); for( int i=0; igetElement( i ) ); } delete mActiveNotes; delete mNotePositions; } SoundSamples *MusicPlayer::getMoreMusic( unsigned long inNumSamples ) { double halfWorldWidth = mWorldWidth / 2; double bufferLengthInSeconds = (double)inNumSamples / (double)mSampleRate; double bufferLengthInWorldUnits = ( bufferLengthInSeconds / mPartLengthInSeconds ) * mGridSpaceWidth; int numPieces; Vector3D **positions = mSculptureManager->getPiecePositions( &numPieces ); MusicPart **musicParts = mSculptureManager->getPieceMusicParts( &numPieces ); if( numPieces == 0 ) { // no pieces in sculpture... nothing to play delete [] positions; delete [] musicParts; // return silent samples return new SoundSamples( inNumSamples ); } // find right-most and left-most piece positions (bounds of song) double rightMostPiecePosition = -halfWorldWidth; double leftMostPiecePosition = halfWorldWidth; int i; for( i=0; imX; if( x < leftMostPiecePosition ) { leftMostPiecePosition = x; } if( x > rightMostPiecePosition ) { rightMostPiecePosition = x; } } if( mCurrentPartGridPosition < leftMostPiecePosition ) { // jump past empty space at far left mCurrentPartGridPosition = leftMostPiecePosition; } if( mCurrentPartGridPosition > rightMostPiecePosition + mGridSpaceWidth ) { // beyond music part of right-most sculpture piece // wrap around to the left-most in-sculpture piece mCurrentPartGridPosition = leftMostPiecePosition; } // find the pieces that play during this buffer for( i=0; imX; double y = positions[i]->mY; // if piece either starts during this buffer or started in a previous // buffer but is still playing during this buffer, then play notes // from it char playPiece = false; double offsetBeforePiece = 0; double offsetIntoPiece = 0; if( x >= mCurrentPartGridPosition && x <= mCurrentPartGridPosition + bufferLengthInWorldUnits ) { playPiece = true; offsetBeforePiece = x - mCurrentPartGridPosition; // convert from world units into seconds offsetBeforePiece = ( offsetBeforePiece / mGridSpaceWidth ) * mPartLengthInSeconds; } if ( x < mCurrentPartGridPosition && x + mGridSpaceWidth >= mCurrentPartGridPosition ) { playPiece = true; offsetIntoPiece = mCurrentPartGridPosition - x; // convert from world units into seconds offsetIntoPiece = ( offsetIntoPiece / mGridSpaceWidth ) * mPartLengthInSeconds; } if( playPiece ) { MusicPart *part = musicParts[i]; MusicNote **notes; double *noteStartOffsets; int numNotes = part->getNotesStartingInInterval( offsetIntoPiece, bufferLengthInSeconds, ¬es, ¬eStartOffsets ); // render each note and add it to our list of active notes for( int j=0; jmapNoteToSamples( notes[j], &numNoteSamples ); unsigned long totalSamples = numSilentSamples + numNoteSamples; SoundSamples *samplesObject = new SoundSamples( totalSamples ); float *leftChannel = samplesObject->mLeftChannel; float *rightChannel = samplesObject->mRightChannel; // compute stereo panning position /* * Notes by Phil Burk (creator of portaudio): * * If you want to keep the power constant, then (L^2 + R^2) * should be constant. One way to do that is to use sine and * cosine curves for left and right because * (sin^2 + cos^2) = 1. * * pan = 0.0 to PI/2 * LeftGain(pan) = cos(pan) * RightGain(pan) = sin(pan) */ // pan smoothly between // -( mWorldHeight / 4 ) and +( mWorldHeight / 4 ) // beyond this range, have constant right or left double panPosition; if( y >= -( mWorldHeight / 4 ) && y <= ( mWorldHeight / 4 ) ) { // convert y position into a pan position in the range // 0 to pi/2 panPosition = y / ( mWorldHeight / 4 ); panPosition = ( panPosition + 1 ) / 2; panPosition *= M_PI / 2; } else if( y < -( mWorldHeight / 4 ) ) { // hard left panPosition = 0; } else { // hard right panPosition = M_PI / 2; } float leftGain = (float)( cos( panPosition ) ); float rightGain = (float)( sin( panPosition ) ); // samplesObject starts out with all 0 samples // just fill in the note samples beyond the starting silent // region char reversed = notes[j]->mReversed; for( unsigned long k=0; kpush_back( samplesObject ); mNotePositions->push_back( 0 ); delete notes[j]; } delete [] notes; delete [] noteStartOffsets; } delete positions[i]; } delete [] positions; delete [] musicParts; // now we have added all notes that start playing sometime during // this buffer // next mix the samples from active notes that play during this buffer SoundSamples *returnSamples = new SoundSamples( inNumSamples ); float *returnLeftChannel = returnSamples->mLeftChannel; float *returnRightChannel = returnSamples->mRightChannel; for( i=0; isize(); i++ ) { SoundSamples *noteSamples = *( mActiveNotes->getElement( i ) ); unsigned long notePosition = *( mNotePositions->getElement( i ) ); unsigned long numNoteSamples = noteSamples->mSampleCount; unsigned long numSamplesToPlay = inNumSamples; char noteFinished = false; if( numSamplesToPlay + notePosition > numNoteSamples ) { // buffer goes beyond the end of this note numSamplesToPlay = numNoteSamples - notePosition; noteFinished = true; } float *noteLeftChannel = noteSamples->mLeftChannel; float *noteRightChannel = noteSamples->mRightChannel; for( unsigned long j=0; jgetElement( i ) ) = notePosition; if( noteFinished ) { delete noteSamples; mActiveNotes->deleteElement( i ); mNotePositions->deleteElement( i ); // back up in for loop to reflect this note dropping out i--; } } // advance the grid position mCurrentPartGridPosition += bufferLengthInWorldUnits; return returnSamples; } double MusicPlayer::getCurrentPartGridPosition() { return mCurrentPartGridPosition; } transcend-0.3+dfsg2.orig/Transcend/game/SoundPlayerActive.h0000640000175000017500000000372110133736156022341 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Switched to a passive (callback-based) player. Old player saved here. */ #ifndef SOUND_PLAYER_INCLUDED #define SOUND_PLAYER_INCLUDED #include "SoundSamples.h" #include "Transcend/portaudio/pa_common/portaudio.h" #include "Transcend/portaudio/pablio/pablio.h" #include "minorGems/util/SimpleVector.h" /** * Class that plays both running background music and realtime sounds. * * @author Jason Rohrer */ class SoundPlayer { public: /** * Constructs a sound player. */ SoundPlayer(); ~SoundPlayer(); /** * Mixes a sound to the speakers as quickly as possible. * * This call does not adjust the volume level of the samples * before mixing them with other realtime sounds or the background * music. * * @param inSamples the samples to play. * Must be destroyed by caller. */ void playSoundNow( SoundSamples *inSamples ); /** * Gets the number of music samples that could be added to * this player without blocking. * * @return the number of samples. */ unsigned long getNumMusicSamplesNeeded(); /** * Add the next section of music to be played. * * This call drives the sound player to send audio to the speakers. * * @param inSamples the samples to play. * Must be destroyed by caller. */ void addMoreMusic( SoundSamples *inSamples ); protected: char mAudioInitialized; PABLIO_Stream *mAudioStream; // realtime sounds that should be mixed into the next to-speaker call SimpleVector *mRealtimeSounds; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/Enemy.cpp0000640000175000017500000001535710114756117020356 0ustar pabspabs/* * Modification History * * 2004-June-18 Jason Rohrer * Created. * * 2004-August-24 Jason Rohrer * Added extra parameter for enemy distance from ship. * * 2004-August-30 Jason Rohrer * Optimization: avoid object blending whenever possible. */ #include "Enemy.h" #include "LevelDirectoryManager.h" #include Enemy::Enemy( FILE *inFILE, char *outError ) : mEnemyCloseShapeObject( NULL ), mEnemyFarShapeObject( NULL ), mExplosionShapeObject( NULL ) { char *enemyCloseFileName = new char[ 100 ]; char *enemyFarFileName = new char[ 100 ]; char *explosionFileName = new char[ 100 ]; int numRead = 0; numRead += fscanf( inFILE, "%99s", enemyCloseFileName ); numRead += fscanf( inFILE, "%99s", enemyFarFileName ); numRead += fscanf( inFILE, "%99s", explosionFileName ); if( numRead != 3 ) { *outError = true; delete [] enemyCloseFileName; delete [] enemyFarFileName; delete [] explosionFileName; return; } File *levelDirectory = LevelDirectoryManager::getLevelDirectory(); File *enemyCloseFile = levelDirectory->getChildFile( enemyCloseFileName ); File *enemyFarFile = levelDirectory->getChildFile( enemyFarFileName ); File *explosionFile = levelDirectory->getChildFile( explosionFileName ); delete [] enemyCloseFileName; delete [] enemyFarFileName; delete [] explosionFileName; delete levelDirectory; char *enemyCloseFilePath = enemyCloseFile->getFullFileName(); char *enemyFarFilePath = enemyFarFile->getFullFileName(); char *explosionFilePath = explosionFile->getFullFileName(); delete enemyCloseFile; delete enemyFarFile; delete explosionFile; FILE *enemyCloseFILE = fopen( enemyCloseFilePath, "r" ); if( enemyCloseFILE == NULL ) { printf( "Failed to open file %s\n", enemyCloseFilePath ); *outError = true; delete [] enemyCloseFilePath; delete [] enemyFarFilePath; delete [] explosionFilePath; return; } FILE *enemyFarFILE = fopen( enemyFarFilePath, "r" ); if( enemyFarFILE == NULL ) { printf( "Failed to open file %s\n", enemyFarFilePath ); *outError = true; fclose( enemyCloseFILE ); delete [] enemyCloseFilePath; delete [] enemyFarFilePath; delete [] explosionFilePath; return; } FILE *explosionFILE = fopen( explosionFilePath, "r" ); if( explosionFILE == NULL ) { printf( "Failed to open file %s\n", explosionFilePath ); *outError = true; fclose( enemyCloseFILE ); fclose( enemyFarFILE ); delete [] enemyCloseFilePath; delete [] enemyFarFilePath; delete [] explosionFilePath; return; } delete [] enemyCloseFilePath; delete [] enemyFarFilePath; delete [] explosionFilePath; mEnemyCloseShapeObject = new ParameterizedObject( enemyCloseFILE, outError ); mEnemyFarShapeObject = new ParameterizedObject( enemyFarFILE, outError ); mExplosionShapeObject = new ParameterizedObject( explosionFILE, outError ); fclose( enemyCloseFILE ); fclose( enemyFarFILE ); fclose( explosionFILE ); } Enemy::~Enemy() { delete mEnemyCloseShapeObject; delete mEnemyFarShapeObject; delete mExplosionShapeObject; } SimpleVector *Enemy::getDrawableObjects( double inEnemyShapeParameter, double inEnemyDistanceFromShipParameter, double inExplosionShapeParameter, double inExplosionProgress, double *outRotationRate ) { double explosionWeight = inExplosionProgress; ObjectParameterSpaceControlPoint *enemyControlPoint = NULL; // skip blending whenver we can (at either end of our parameter range) // don't even compute enemy shape if our explosion progress is 1 if( inExplosionProgress < 1 ) { // avoid blending close/far points if ship is at either end of // the distance range if( inEnemyDistanceFromShipParameter == 0 ) { // use close control point enemyControlPoint = mEnemyCloseShapeObject->getBlendedControlPoint( inEnemyShapeParameter ); } else if( inEnemyDistanceFromShipParameter == 1 ) { // use far control point enemyControlPoint = mEnemyFarShapeObject->getBlendedControlPoint( inEnemyShapeParameter ); } else { // use a blend of the points ObjectParameterSpaceControlPoint *enemyCloseControlPoint = mEnemyCloseShapeObject->getBlendedControlPoint( inEnemyShapeParameter ); ObjectParameterSpaceControlPoint *enemyFarControlPoint = mEnemyFarShapeObject->getBlendedControlPoint( inEnemyShapeParameter ); enemyControlPoint = (ObjectParameterSpaceControlPoint*)( enemyCloseControlPoint->createLinearBlend( enemyFarControlPoint, inEnemyDistanceFromShipParameter ) ); delete enemyCloseControlPoint; delete enemyFarControlPoint; } } // again, try to skip blending... // this time, we might need to blend the enemy shape with its explosion // shape ObjectParameterSpaceControlPoint *blendedPoint; if( inExplosionProgress == 0 ) { // use the pure enemy shape blendedPoint = enemyControlPoint; } else if( inExplosionProgress == 1 ) { // enemyControlPoint is NULL // use the pure explosion shape blendedPoint = mExplosionShapeObject->getBlendedControlPoint( inExplosionShapeParameter ); } else { // blend the enemy with the explosion shape ObjectParameterSpaceControlPoint *explosionControlPoint = mExplosionShapeObject->getBlendedControlPoint( inExplosionShapeParameter ); blendedPoint = (ObjectParameterSpaceControlPoint *)( enemyControlPoint->createLinearBlend( explosionControlPoint, explosionWeight ) ); delete enemyControlPoint; delete explosionControlPoint; } SimpleVector *drawableObjects = blendedPoint->getDrawableObjects(); *outRotationRate = blendedPoint->getRotationRate(); delete blendedPoint; return drawableObjects; } transcend-0.3+dfsg2.orig/Transcend/game/ReverbSoundFilter.cpp0000640000175000017500000000312610100010467022656 0ustar pabspabs/* * Modification History * * 2004-July-22 Jason Rohrer * Created. */ #include "ReverbSoundFilter.h" ReverbSoundFilter::ReverbSoundFilter( unsigned long inDelayInSamples, double inGain ) : mDelayBuffer( new SoundSamples( inDelayInSamples ) ), mGain( inGain ), mDelayBufferPosition( 0 ) { } ReverbSoundFilter::~ReverbSoundFilter() { delete mDelayBuffer; } SoundSamples *ReverbSoundFilter::filterSamples( SoundSamples *inSamples ) { unsigned long delaySize = mDelayBuffer->mSampleCount; unsigned long numSamples = inSamples->mSampleCount; // pass the input through to the output SoundSamples *outputSamples = new SoundSamples( inSamples ); for( unsigned long i=0; imLeftChannel[i] += mDelayBuffer->mLeftChannel[ mDelayBufferPosition ]; outputSamples->mRightChannel[i] += mDelayBuffer->mRightChannel[ mDelayBufferPosition ]; // save our gained output in the delay buffer mDelayBuffer->mLeftChannel[ mDelayBufferPosition ] = mGain * outputSamples->mLeftChannel[i]; mDelayBuffer->mRightChannel[ mDelayBufferPosition ] = mGain * outputSamples->mRightChannel[i]; // step through delay buffer, wrapping around at end mDelayBufferPosition++; if( mDelayBufferPosition >= delaySize ) { mDelayBufferPosition = 0; } } return outputSamples; } transcend-0.3+dfsg2.orig/Transcend/game/PortalManager.h0000640000175000017500000000633410133457552021477 0ustar pabspabs/* * Modification History * * 2004-October-13 Jason Rohrer * Created. */ #ifndef PORTAL_MANAGER_INCLUDED #define PORTAL_MANAGER_INCLUDED #include "ParameterizedObject.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" /** * A class that manages and draws all enemies in the environment. * * @author Jason Rohrer. */ class PortalManager { public: /** * Constructs a manager. * * @param inPortalTemplate the class to use to draw the portal. * Will be destroyed by this class. * @param inPortalScale the value to multiply portal size by. * @param inPortalFadeInTimeSeconds how long it takes this portal * to fade in when it is displayed. * @param inGridWidth the width of the grid in screen units. */ PortalManager( ParameterizedObject *inPortalTemplate, double inPortalScale, double inPortalFadeInTimeSeconds, double inGridWidth ); virtual ~PortalManager(); /** * Shows the portal at a particular location. * * @param inPosition the position of the portal. * Will be destroyed by this class. */ void showPortal( Vector3D *inPosition ); /** * Gets whether this portal is visible. * * @return true if this portal has been shown. */ char isPortalVisible(); /** * Tell this manager that time has passed. * * @param inTimeDeltaInSeconds the amount of time that has passed. * @param inShipPosition the position of the ship (portal changes * shape as ship moves closer). * Must be destroyed by caller. */ void passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition ); /** * Gets whether the ship is in the portal. * * @param inShipPosition the position of the ship. * Must be destroyed by caller. * * @return true if the ship is in the portal, or false otherwise. */ char isShipInPortal( Vector3D *inShipPosition ); /** * Gets drawable objects for the portal in its current * positions/states. * * @return portal as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); protected: ParameterizedObject *mPortalTemplate; double mPortalScale; double mFadeInTimeSeconds; double mFadeFactor; double mGridWidth; // the current portal shape, based on distance of ship double mPortalShapeParameter; Vector3D *mCurrentPosition; double mCurrentRadius; // rotation rate can vary across portal's lifespan, so we must // keep a current angle for the portal and adjust the angle // for each time delta double mCurrentRotationRate; Angle3D *mCurrentRotation; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/NamedColorFactory.h0000640000175000017500000000206310063563775022320 0ustar pabspabs/* * Modification History * * 2004-June-12 Jason Rohrer * Created. * * 2004-June-14 Jason Rohrer * Changed to allow specification of a directory to read colors from. * * 2004-June-15 Jason Rohrer * Changed to use LevelDirectoryManager to determine where to read colors from. */ #ifndef NAMED_COLOR_FACTORY_INCLUDED #define NAMED_COLOR_FACTORY_INCLUDED #include "minorGems/graphics/Color.h" /** * A class with static functions for mapping names to colors using * named color files on disk (currently hard coded to look in the "colors" * directory). * * @author Jason Rohrer. */ class NamedColorFactory { public: /** * Maps a color name to a color. * * @param inColorName the name of the color. * Must be destroyed by caller. * * @return the color, or NULL if no color file can be found for * inColorName. * Must be destroyed by caller if non-NULL. */ static Color *getColor( char *inColorName ); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ShipBullet.cpp0000640000175000017500000000703310105756452021346 0ustar pabspabs/* * Modification History * * 2004-June-15 Jason Rohrer * Created. */ #include "ShipBullet.h" #include "LevelDirectoryManager.h" #include ShipBullet::ShipBullet( FILE *inFILE, char *outError ) : mCloseRangeObject( NULL ), mFarRangeObject( NULL ) { char *closeRangeFileName = new char[ 100 ]; char *farRangeFileName = new char[ 100 ]; int numRead; numRead = fscanf( inFILE, "%99s", closeRangeFileName ); if( numRead != 1 ) { *outError = true; delete [] closeRangeFileName; delete [] farRangeFileName; return; } numRead = fscanf( inFILE, "%99s", farRangeFileName ); if( numRead != 1 ) { *outError = true; delete [] closeRangeFileName; delete [] farRangeFileName; return; } File *levelDirectory = LevelDirectoryManager::getLevelDirectory(); File *closeRangeFile = levelDirectory->getChildFile( closeRangeFileName ); File *farRangeFile = levelDirectory->getChildFile( farRangeFileName ); delete [] closeRangeFileName; delete [] farRangeFileName; delete levelDirectory; char *closeRangeFilePath = closeRangeFile->getFullFileName(); char *farRangeFilePath = farRangeFile->getFullFileName(); delete closeRangeFile; delete farRangeFile; FILE *closeRangeFILE = fopen( closeRangeFilePath, "r" ); if( closeRangeFILE == NULL ) { printf( "Failed to open file %s\n", closeRangeFilePath ); *outError = true; delete [] closeRangeFilePath; delete [] farRangeFilePath; return; } FILE *farRangeFILE = fopen( farRangeFilePath, "r" ); if( farRangeFILE == NULL ) { printf( "Failed to open file %s\n", farRangeFilePath ); *outError = true; fclose( farRangeFILE ); delete [] closeRangeFilePath; delete [] farRangeFilePath; return; } delete [] closeRangeFilePath; delete [] farRangeFilePath; mCloseRangeObject = new ParameterizedObject( closeRangeFILE, outError ); mFarRangeObject = new ParameterizedObject( farRangeFILE, outError ); fclose( closeRangeFILE ); fclose( farRangeFILE ); } ShipBullet::~ShipBullet() { delete mCloseRangeObject; delete mFarRangeObject; } SimpleVector *ShipBullet::getDrawableObjects( double inCloseRangeParameter, double inFarRangeParameter, double inPositionInRange, double *outPower, double *outRotationRate ) { double farWeight = inPositionInRange; double closeWeight = 1 - inPositionInRange; *outPower = farWeight * inFarRangeParameter + closeWeight * inCloseRangeParameter; ObjectParameterSpaceControlPoint *closeControlPoint = mCloseRangeObject->getBlendedControlPoint( inCloseRangeParameter ); ObjectParameterSpaceControlPoint *farControlPoint = mFarRangeObject->getBlendedControlPoint( inFarRangeParameter ); ObjectParameterSpaceControlPoint *blendedPoint = (ObjectParameterSpaceControlPoint *)( closeControlPoint->createLinearBlend( farControlPoint, farWeight ) ); delete closeControlPoint; delete farControlPoint; SimpleVector *drawableObjects = blendedPoint->getDrawableObjects(); *outRotationRate = blendedPoint->getRotationRate(); delete blendedPoint; return drawableObjects; } transcend-0.3+dfsg2.orig/Transcend/game/SoundPlayer.cpp0000640000175000017500000003064610115266356021546 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Changed to use a callback to reduce latency. * * 2004-August-9 Jason Rohrer * Added a limit on the number of simultaneous sounds. * * 2004-August-12 Jason Rohrer * Parameterized the sample rate. * * 2004-August-13 Jason Rohrer * Added Mutex to protect members that are accessed by multiple threads. * * 2004-August-14 Jason Rohrer * Changed to fade sounds out before dropping them. * * 2004-August-15 Jason Rohrer * Added a function for getting the sample rate. * Added volume modifier parameter to playSoundNow function. * * 2004-August-20 Jason Rohrer * Added priority flags. * * 2004-August-23 Jason Rohrer * Added music. * * 2004-August-25 Jason Rohrer * Added lock in setMusicPlayer function. * Added function for setting music loudness. * * 2004-August-31 Jason Rohrer * Added function for removing filters. */ #include "SoundPlayer.h" #include "MusicPlayer.h" #include // callback passed into portaudio static int portaudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) { SoundPlayer *player = (SoundPlayer *)userData; player->getSamples( outputBuffer, framesPerBuffer ); return 0; } /** * Class that wraps SoundSamples in a PlayableSound. */ class SamplesPlayableSound : public PlayableSound { public: /** * Constructs a playable sound. * * @param inSamples the samples to play. * Must be destroyed by caller. */ SamplesPlayableSound( SoundSamples *inSamples ); ~SamplesPlayableSound(); // implements the PlayableSound interface virtual SoundSamples *getMoreSamples( unsigned long inNumSamples ); virtual PlayableSound *copy(); protected: SoundSamples *mRemainingSamples; }; SamplesPlayableSound::SamplesPlayableSound( SoundSamples *inSamples ) : mRemainingSamples( new SoundSamples( inSamples ) ) { } SamplesPlayableSound::~SamplesPlayableSound() { delete mRemainingSamples; } SoundSamples *SamplesPlayableSound::getMoreSamples( unsigned long inNumSamples ) { SoundSamples *returnSamples = new SoundSamples( mRemainingSamples, inNumSamples ); mRemainingSamples->trim( returnSamples->mSampleCount ); return returnSamples; } PlayableSound *SamplesPlayableSound::copy() { return new SamplesPlayableSound( mRemainingSamples ); } SoundPlayer::SoundPlayer( int inSampleRate, int inMaxSimultaneousRealtimeSounds, void *inMusicPlayer, double inMusicLoudness ) : mLock( new MutexLock() ), mSampleRate( inSampleRate ), mMaxSimultaneousRealtimeSounds( inMaxSimultaneousRealtimeSounds ), mMusicPlayer( inMusicPlayer ), mMusicLoudness( inMusicLoudness ), mRealtimeSounds( new SimpleVector() ), mPriorityFlags( new SimpleVector() ), mSoundLoudnessModifiers( new SimpleVector() ), mSoundDroppedFlags( new SimpleVector() ), mFilterChain( new SimpleVector() ) { PaError error = Pa_Initialize(); if( error == paNoError ) { error = Pa_OpenStream( &mAudioStream, paNoDevice,// default input device 0, // no input paFloat32, // 32 bit floating point input NULL, Pa_GetDefaultOutputDeviceID(), 2, // stereo output paFloat32, // 32 bit floating point output NULL, mSampleRate, 1024, // frames per buffer 0, // number of buffers, if zero then use default minimum paClipOff, // we won't output out of range samples so // don't bother clipping them portaudioCallback, (void *)this ); // pass self-pointer to callback function if( error == paNoError ) { error = Pa_StartStream( mAudioStream ); if( error == paNoError ) { mAudioInitialized = true; } else { fprintf( stderr, "Error starting audio stream\n" ); Pa_CloseStream( mAudioStream ); } } else { fprintf( stderr, "Error opening audio stream\n" ); Pa_Terminate(); } } else { fprintf( stderr, "Error initializing audio framework\n" ); } if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error ) ); mAudioInitialized = false; } } SoundPlayer::~SoundPlayer() { if( mAudioInitialized ) { PaError error = Pa_StopStream( mAudioStream ); if( error == paNoError ) { error = Pa_CloseStream( mAudioStream ); if( error != paNoError ) { fprintf( stderr, "Error closingaudio stream\n" ); } } else { fprintf( stderr, "Error stopping audio stream\n" ); } Pa_Terminate(); if( error != paNoError ) { fprintf( stderr, "Error number: %d\n", error); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error) ); } } delete mLock; int i; int numSounds = mRealtimeSounds->size(); for( i=0; igetElement( i ) ); } delete mRealtimeSounds; delete mPriorityFlags; delete mSoundLoudnessModifiers; delete mSoundDroppedFlags; int numFilters = mFilterChain->size(); for( i=0; igetElement( i ) ); } delete mFilterChain; } void SoundPlayer::setMusicPlayer( void *inMusicPlayer ) { mLock->lock(); mMusicPlayer = inMusicPlayer; mLock->unlock(); } void SoundPlayer::setMusicLoudness( double inMusicLoudness ) { mLock->lock(); mMusicLoudness = inMusicLoudness; mLock->unlock(); } void SoundPlayer::getSamples( void *outputBuffer, unsigned long inFramesInBuffer ) { SoundSamples *mixingBuffer = new SoundSamples( inFramesInBuffer ); unsigned long bufferLength = inFramesInBuffer; mLock->lock(); // add each pending realtime sound to the buffer int i = 0; // we may be removing sounds from the buffer as we use them up // i is adjusted inside the while loop while( isize() ) { PlayableSound *realtimeSound = *( mRealtimeSounds->getElement( i ) ); double loudnessModifier = *( mSoundLoudnessModifiers->getElement( i ) ); SoundSamples *samples = realtimeSound->getMoreSamples( bufferLength ); unsigned long mixLength = samples->mSampleCount; char shouldDrop = *( mSoundDroppedFlags->getElement( i ) ); // fade out if we should drop float fadeFactor = 1; unsigned long maxJ = mixLength - 1; for( unsigned long j=0; jmLeftChannel[j] += loudnessModifier * fadeFactor * samples->mLeftChannel[j]; mixingBuffer->mRightChannel[j] += loudnessModifier * fadeFactor * samples->mRightChannel[j]; if( shouldDrop ) { fadeFactor = ( maxJ - j ) / (float)maxJ; } } delete samples; if( mixLength < bufferLength || shouldDrop ) { // we have used up all samples of this sound or // it is flagged to be dropped delete realtimeSound; mRealtimeSounds->deleteElement( i ); mPriorityFlags->deleteElement( i ); mSoundLoudnessModifiers->deleteElement( i ); mSoundDroppedFlags->deleteElement( i ); // don't increment i, since the next element drops into the current // index } else { // increment i to move on to the next sound i++; } } if( mMusicPlayer != NULL ) { // cast out of void * MusicPlayer *player = (MusicPlayer *)mMusicPlayer; // mix in the music SoundSamples *musicSamples = player->getMoreMusic( inFramesInBuffer ); for( unsigned long j=0; jmLeftChannel[j] += musicSamples->mLeftChannel[j] * mMusicLoudness; mixingBuffer->mRightChannel[j] += musicSamples->mRightChannel[j] * mMusicLoudness; } delete musicSamples; } // filter the samples int numFilters = mFilterChain->size(); SoundSamples *filteredSamples = new SoundSamples( mixingBuffer ); delete mixingBuffer; for( i=0; igetElement( i ) ); SoundSamples *nextOutput = filter->filterSamples( filteredSamples ); delete filteredSamples; filteredSamples = nextOutput; } mLock->unlock(); float *samples = (float *)outputBuffer; unsigned long numSamples = 2 * bufferLength; unsigned long j; unsigned long frameNumber = 0; for( j=0; jmLeftChannel[frameNumber]; samples[j+1] = filteredSamples->mRightChannel[frameNumber]; frameNumber++; } delete filteredSamples; } void SoundPlayer::checkForExcessSounds() { int numSounds = mRealtimeSounds->size(); if( numSounds > mMaxSimultaneousRealtimeSounds ) { // flag the oldest unflagged, low-priority sound // skip sounds that are already flagged or are priority sounds int i=0; while( igetElement( i ) ) || *( mPriorityFlags->getElement( i ) ) ) ) { i++; } if( igetElement( i ) ) = true; } else { // else all low-priority sounds are already flagged // try again, ignoring priority flags i=0; while( igetElement( i ) ) ) { i++; } if( igetElement( i ) ) = true; } // else all sounds are already flagged } } } void SoundPlayer::playSoundNow( SoundSamples *inSamples, char inPriorityFlag, double inLoudnessModifier ) { mLock->lock(); mRealtimeSounds->push_back( new SamplesPlayableSound( inSamples ) ); mPriorityFlags->push_back( inPriorityFlag ); mSoundLoudnessModifiers->push_back( inLoudnessModifier ); mSoundDroppedFlags->push_back( false ); checkForExcessSounds(); mLock->unlock(); } void SoundPlayer::playSoundNow( PlayableSound *inSound, char inPriorityFlag, double inLoudnessModifier ) { mLock->lock(); mRealtimeSounds->push_back( inSound->copy() ); mPriorityFlags->push_back( inPriorityFlag ); mSoundLoudnessModifiers->push_back( inLoudnessModifier ); mSoundDroppedFlags->push_back( false ); checkForExcessSounds(); mLock->unlock(); } void SoundPlayer::addMoreMusic( SoundSamples *inSamples ) { } void SoundPlayer::addFilter( SoundFilter *inFilter ) { mLock->lock(); mFilterChain->push_back( inFilter ); mLock->unlock(); } void SoundPlayer::removeAllFilters() { mLock->lock(); int numFilters = mFilterChain->size(); for( int i=0; igetElement( i ) ); } mFilterChain->deleteAll(); mLock->unlock(); } unsigned long SoundPlayer::getSampleRate() { return mSampleRate; } transcend-0.3+dfsg2.orig/Transcend/game/BulletSound.cpp0000640000175000017500000000700710112311536021520 0ustar pabspabs/* * Modification History * * 2004-August-15 Jason Rohrer * Created. */ #include "BulletSound.h" #include "LevelDirectoryManager.h" #include BulletSound::BulletSound( FILE *inFILE, char *outError ) : mCloseRangeSpace( NULL ), mFarRangeSpace( NULL ) { char *closeRangeFileName = new char[ 100 ]; char *farRangeFileName = new char[ 100 ]; int numRead; numRead = fscanf( inFILE, "%99s", closeRangeFileName ); if( numRead != 1 ) { *outError = true; delete [] closeRangeFileName; delete [] farRangeFileName; return; } numRead = fscanf( inFILE, "%99s", farRangeFileName ); if( numRead != 1 ) { *outError = true; delete [] closeRangeFileName; delete [] farRangeFileName; return; } File *levelDirectory = LevelDirectoryManager::getLevelDirectory(); File *closeRangeFile = levelDirectory->getChildFile( closeRangeFileName ); File *farRangeFile = levelDirectory->getChildFile( farRangeFileName ); delete [] closeRangeFileName; delete [] farRangeFileName; delete levelDirectory; char *closeRangeFilePath = closeRangeFile->getFullFileName(); char *farRangeFilePath = farRangeFile->getFullFileName(); delete closeRangeFile; delete farRangeFile; FILE *closeRangeFILE = fopen( closeRangeFilePath, "r" ); if( closeRangeFILE == NULL ) { printf( "Failed to open file %s\n", closeRangeFilePath ); *outError = true; delete [] closeRangeFilePath; delete [] farRangeFilePath; return; } FILE *farRangeFILE = fopen( farRangeFilePath, "r" ); if( farRangeFILE == NULL ) { printf( "Failed to open file %s\n", farRangeFilePath ); *outError = true; fclose( farRangeFILE ); delete [] closeRangeFilePath; delete [] farRangeFilePath; return; } delete [] closeRangeFilePath; delete [] farRangeFilePath; mCloseRangeSpace = new ParameterizedStereoSound( closeRangeFILE, outError ); mFarRangeSpace = new ParameterizedStereoSound( farRangeFILE, outError ); fclose( closeRangeFILE ); fclose( farRangeFILE ); } BulletSound::~BulletSound() { delete mCloseRangeSpace; delete mFarRangeSpace; } PlayableSound *BulletSound::getPlayableSound( double inCloseRangeParameter, double inFarRangeParameter, unsigned long inSamplesPerSecond ) { StereoSoundParameterSpaceControlPoint *closeControlPoint = mCloseRangeSpace->getBlendedControlPoint( inCloseRangeParameter ); StereoSoundParameterSpaceControlPoint *farControlPoint = mFarRangeSpace->getBlendedControlPoint( inFarRangeParameter ); // create a 50/50 blend of close and far sounds StereoSoundParameterSpaceControlPoint *blendedPoint = (StereoSoundParameterSpaceControlPoint *)( closeControlPoint->createLinearBlend( farControlPoint, 0.5 ) ); // blend lengths double soundLength = 0.5 * ( mCloseRangeSpace->getSoundLengthInSeconds() + mFarRangeSpace->getSoundLengthInSeconds() ); delete closeControlPoint; delete farControlPoint; PlayableSound *sound = blendedPoint->getPlayableSound( inSamplesPerSecond, soundLength ); delete blendedPoint; return sound; } transcend-0.3+dfsg2.orig/Transcend/game/ParameterSpaceControlPoint.cpp0000640000175000017500000000605410216315562024540 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Optimized blendColorArrays function. * * 2005-March-17 Jason Rohrer * Fixed bug in blendVertexArrays when both arrays have length 1. */ #include "ParameterSpaceControlPoint.h" #include ParameterSpaceControlPoint::~ParameterSpaceControlPoint() { // does nothing... here to make compilers happy } Vector3D **ParameterSpaceControlPoint::blendVertexArrays( Vector3D **inFirstArray, int inFirstArrayLength, double inWeightFirstArray, Vector3D **inSecondArray, int inSecondArrayLength, int *outResultLength ) { double weightOfSecondArray = 1 - inWeightFirstArray; // blend has the same number of elements as the larger control array int resultLength = inFirstArrayLength; if( inSecondArrayLength > resultLength ) { resultLength = inSecondArrayLength; } Vector3D **blendVertices = new Vector3D*[ resultLength ]; // map the larger array nto the smaller array to // blend int sizeLargerArray; int sizeSmallerArray; Vector3D **arrayWithMoreVertices; Vector3D **arrayWithFewerVertices; double weightOfLargerSet; double weightOfSmallerSet; if( inFirstArrayLength > inSecondArrayLength ) { sizeLargerArray = inFirstArrayLength; sizeSmallerArray = inSecondArrayLength; arrayWithMoreVertices = inFirstArray; arrayWithFewerVertices = inSecondArray; weightOfLargerSet = inWeightFirstArray; weightOfSmallerSet = weightOfSecondArray; } else { sizeLargerArray = inSecondArrayLength; sizeSmallerArray = inFirstArrayLength; arrayWithMoreVertices = inSecondArray; arrayWithFewerVertices = inFirstArray; weightOfLargerSet = weightOfSecondArray; weightOfSmallerSet = inWeightFirstArray; } // size of blend array is same as size of larger set // factor to map large array indices into the smaller array double mapFactor; if( sizeLargerArray > 1 ) { mapFactor = (double)( sizeSmallerArray - 1 ) / (double)(sizeLargerArray - 1 ); } else { // above formula might involve divide-by-zero // use 1 here because smaller array must have 1 element also // for a blend to be sensible mapFactor = 1; } for( int i=0; i *getDrawableObjects( double inCloseRangeParameter, double inFarRangeParameter, double inPositionInRange, double *outPower, double *outRotationRate ); protected: ParameterizedObject *mCloseRangeObject; ParameterizedObject *mFarRangeObject; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedSpace.h0000640000175000017500000000207110105756452022505 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. */ #ifndef PARAMETERIZED_SPACE_INCLUDED #define PARAMETERIZED_SPACE_INCLUDED #include "minorGems/util/SimpleVector.h" #include "ParameterSpaceControlPoint.h" /** * A 1-D space of control points. * * @author Jason Rohrer. */ class ParameterizedSpace { public: virtual ~ParameterizedSpace(); /** * Gets a blended control point from this space space. * * @param inParameter the parameter in the range [0,1] to map * into the space. * * @return the blended control point. * Can return NULL if this space was not properly initialized. * Must be destroyed by caller. */ ParameterSpaceControlPoint *getBlendedControlPoint( double inParameter ); protected: int mNumControlPoints; double *mControlPointParameterAnchors; ParameterSpaceControlPoint **mControlPoints; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SoundSamples.cpp0000640000175000017500000000546310106647550021714 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added a constructor that can specify all sound data. */ #include "SoundSamples.h" #include SoundSamples::SoundSamples( unsigned long inSampleCount, float *inLeftChannel, float *inRightChannel ) : mSampleCount( inSampleCount ), mLeftChannel( inLeftChannel ), mRightChannel( inRightChannel ) { } SoundSamples::SoundSamples( unsigned long inSampleCount ) : mSampleCount( inSampleCount ), mLeftChannel( new float[ inSampleCount ] ), mRightChannel( new float[ inSampleCount ] ) { // zero out the channels; for( unsigned long i=0; imSampleCount; mLeftChannel = new float[ mSampleCount ]; mRightChannel = new float[ mSampleCount ]; memcpy( (void *)( mLeftChannel ), (void *)( inSamplesToCopy->mLeftChannel ), mSampleCount * sizeof( float ) ); memcpy( (void *)( mRightChannel ), (void *)( inSamplesToCopy->mRightChannel ), mSampleCount * sizeof( float ) ); } SoundSamples::SoundSamples( SoundSamples *inSamplesToCopy, unsigned long inNumToCopy ) { mSampleCount = inSamplesToCopy->mSampleCount; if( inNumToCopy < mSampleCount ) { mSampleCount = inNumToCopy; } mLeftChannel = new float[ mSampleCount ]; mRightChannel = new float[ mSampleCount ]; memcpy( (void *)( mLeftChannel ), (void *)( inSamplesToCopy->mLeftChannel ), mSampleCount * sizeof( float ) ); memcpy( (void *)( mRightChannel ), (void *)( inSamplesToCopy->mRightChannel ), mSampleCount * sizeof( float ) ); } SoundSamples::~SoundSamples() { delete [] mRightChannel; delete [] mLeftChannel; } void SoundSamples::trim( unsigned long inNumSamplesToDrop ) { unsigned long newSampleCount = mSampleCount - inNumSamplesToDrop; float *newLeftChannel = new float[ newSampleCount ]; float *newRightChannel = new float[ newSampleCount ]; // copy samples, skipping inNumSamplesToDrop memcpy( (void *)( newLeftChannel ), (void *)( &( mLeftChannel[ inNumSamplesToDrop ] ) ), newSampleCount * sizeof( float ) ); memcpy( (void *)( newRightChannel ), (void *)( &( mRightChannel[ inNumSamplesToDrop ] ) ), newSampleCount * sizeof( float ) ); delete [] mLeftChannel; delete [] mRightChannel; mSampleCount = newSampleCount; mLeftChannel = newLeftChannel; mRightChannel = newRightChannel; } transcend-0.3+dfsg2.orig/Transcend/game/StereoSoundParameterSpaceControlPoint.h0000640000175000017500000000674510107703602026402 0ustar pabspabs/* * Modification History * * 2004-August-9 Jason Rohrer * Created. * * 2004-August-12 Jason Rohrer * Added support for getting blocks of samples. * * 2004-August-15 Jason Rohrer * Added function that generates a playable sound. */ #ifndef STEREO_SOUND_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #define STEREO_SOUND_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #include "ParameterSpaceControlPoint.h" #include "SoundParameterSpaceControlPoint.h" #include "SoundSamples.h" #include "PlayableSound.h" #include /** * A control point in a 1-D parameterized stereo sound space. * * @author Jason Rohrer. */ class StereoSoundParameterSpaceControlPoint : public ParameterSpaceControlPoint { public: /** * Constructs a control point. * * @param inLeftPoint the left control point. * Will be destroyed when this class is destroyed. * @param inRightPoint the right control point. * Will be destroyed when this class is destroyed. */ StereoSoundParameterSpaceControlPoint( SoundParameterSpaceControlPoint *inLeftPoint, SoundParameterSpaceControlPoint *inRightPoint ); /** * Constructs a control point by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading a control point * from inFILE fails. */ StereoSoundParameterSpaceControlPoint( FILE *inFILE, char *outError ); virtual ~StereoSoundParameterSpaceControlPoint(); // implements the ParameterSpaceControlPoint interface ParameterSpaceControlPoint *copy(); ParameterSpaceControlPoint *createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ); /** * Gets a block of samples from this control point. * * @param inStartSample the index of the first sample to get. * @param inSampleCount the number of samples to get. * @param inSamplesPerSecond the current sample rate. * @param inSoundLengthInSeconds the total length of the sound being * played. * * @return the samples. * Must be destroyed by caller. */ SoundSamples *getSoundSamples( unsigned long inStartSample, unsigned long inSampleCount, unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ); /** * Gets a playable sound from this control point. * * @param inSamplesPerSecond the sample rate. * @param inSoundLengthInSeconds the length of the sound. * * @return a playable sound. * Must be destroyed by caller. */ PlayableSound *getPlayableSound( unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ); // these are public so that other Stereo Sound points can access // them when performing a blend. SoundParameterSpaceControlPoint *mLeftPoint; SoundParameterSpaceControlPoint *mRightPoint; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/BossManager.cpp0000640000175000017500000003441010304636564021475 0ustar pabspabs/* * Modification History * * 2004-June-29 Jason Rohrer * Created. * * 2004-July-3 Jason Rohrer * Improved boss ship-in-range detection. * * 2004-July-5 Jason Rohrer * Fixed memory leaks. * * 2004-August-19 Jason Rohrer * Added explosion sounds. * * 2004-August-20 Jason Rohrer * Added sound priorities. * * 2004-August-25 Jason Rohrer * Added function to check for boss death. * * 2004-August-29 Jason Rohrer * Improved distance-from-target compression formula. * * 2004-October-21 Jason Rohrer * Fixed bug that caused explosion progress to get larger than 1. * * 2005-August-22 Jason Rohrer * Started work on boss damage graphics. * * 2005-August-23 Jason Rohrer * Finished boss damage graphics. * * 2005-August-26 Jason Rohrer * Added limit to number of damage graphics on screen at once. * * 2005-August-29 Jason Rohrer * Removed print message. */ #include "BossManager.h" #include BossManager::BossManager( Enemy *inBossTemplate, double inBossScale, double inExplosionScale, double inExplosionTimeInSeconds, double inExplosionShapeParameter, double inBossMinVelocity, double inBossMaxVelocity, ShipBulletManager *inShipBulletManager, ShipBulletManager *inBossBulletManager, ShipBulletManager *inBossDamageManager, double inBossBulletRange, double inBossBulletBaseVelocity, double inMinBossBulletsPerSecond, double inMaxBossBulletsPerSecond, double inBossDamageTime, double inTimeUntilFullShootingPower, double inMaxBossHealth, double inBossHealthRecoveryRate, Vector3D *inBossPosition, SoundPlayer *inSoundPlayer, BulletSound *inBossExplosionSoundTemplate ) : mBossTemplate( inBossTemplate ), mShipBulletManager( inShipBulletManager ), mBossBulletManager( inBossBulletManager ), mBossDamageManager( inBossDamageManager ), mBossScale( inBossScale ), mExplosionScale( inExplosionScale ), mExplosionTimeInSeconds( inExplosionTimeInSeconds ), mExplosionShapeParameter( inExplosionShapeParameter ), mBossMinVelocity( inBossMinVelocity ), mBossMaxVelocity( inBossMaxVelocity ), mBossBulletRange( inBossBulletRange ), mBossBulletBaseVelocity( inBossBulletBaseVelocity ), mMinBossTimeBetweenBullets( 1 / inMinBossBulletsPerSecond ), mMaxBossTimeBetweenBullets( 1 / inMaxBossBulletsPerSecond ), mBossDamageTime( inBossDamageTime ), mTimeUntilFullShootingPower( inTimeUntilFullShootingPower ), mMaxBossHealth( inMaxBossHealth ), mBossHealth( inMaxBossHealth ), mHealthRecoveryRate( inBossHealthRecoveryRate ), mBossPosition( inBossPosition ), mSoundPlayer( inSoundPlayer ), mBossExplosionSoundTemplate( inBossExplosionSoundTemplate ), mCurrentlyExploding( false ), mExplosionProgress( 0 ), mExplosionFadeProgress( 0 ), mTimeSinceLastBullet( 0 ), mTimeSinceShipEnteredRange( 0 ), mAngerLevel( 0 ), mCurrentRadius( 0 ), mCurrentRotationRate( 0 ), mCurrentRotation( new Angle3D( 0, 0, 0 ) ), mShipDistanceParameter( 0 ) { mBossDistanceFromCenter = mBossPosition->getLength(); } BossManager::~BossManager() { delete mBossTemplate; delete mBossPosition; delete mCurrentRotation; delete mBossExplosionSoundTemplate; } void BossManager::passTime( double inTimeDeltaInSeconds, Vector3D *inShipPosition, Vector3D *inShipVelocity ) { mCurrentRotation->mZ += mCurrentRotationRate * inTimeDeltaInSeconds; // check if we are being hit by ship bullets double bulletPowerNearBoss = mShipBulletManager->getBulletPowerInCircle( mBossPosition, mCurrentRadius ); if( bulletPowerNearBoss > 0 ) { // boss hit by bullet // scale power by time delta (power is power per second) mBossHealth -= bulletPowerNearBoss * inTimeDeltaInSeconds; if( mBossHealth <= 0 ) { if( !mCurrentlyExploding ) { // start explosion mCurrentlyExploding = true; // play the sound PlayableSound *sound = mBossExplosionSoundTemplate->getPlayableSound( mAngerLevel, mExplosionShapeParameter, mSoundPlayer->getSampleRate() ); // boss explosion is high priority mSoundPlayer->playSoundNow( sound, true, 1 ); delete sound; } // don't allow it to go below 0 mBossHealth = 0; } } else { // not hit, recover health mBossHealth += mHealthRecoveryRate * inTimeDeltaInSeconds; if( mBossHealth > mMaxBossHealth ) { mBossHealth = mMaxBossHealth; } } if( mCurrentlyExploding ) { // boss is exploding // compute new explosion progress // convert time delta to a progress increment for the progress // range [0,1] double progressFractionDelta = inTimeDeltaInSeconds / mExplosionTimeInSeconds; mExplosionProgress += progressFractionDelta; if( mExplosionProgress > 1 ) { mExplosionProgress = 1; } if( mExplosionProgress == 1 ) { // end of explosion // fade out last frame mExplosionFadeProgress += progressFractionDelta; if( mExplosionFadeProgress >= 1 ) { // explosion done fading mExplosionFadeProgress = 1; } } } if( !mCurrentlyExploding ) { double distanceToShip = inShipPosition->getDistance( mBossPosition ); // compute anger level based on how long ship has been in range if( distanceToShip <= mBossBulletRange ) { // ship in range, get angry mTimeSinceShipEnteredRange += inTimeDeltaInSeconds; } else { // ship out of range, calm down mTimeSinceShipEnteredRange -= inTimeDeltaInSeconds; if( mTimeSinceShipEnteredRange < 0 ) { mTimeSinceShipEnteredRange = 0; } } mAngerLevel = mTimeSinceShipEnteredRange / mTimeUntilFullShootingPower; if( mAngerLevel > 1 ) { mAngerLevel = 1; } // boss moves in a cirular path double currentVelocity = mAngerLevel * ( mBossMaxVelocity - mBossMinVelocity ) + mBossMinVelocity; // move perpendicular to center radius vector // center radius vector is our position vector Vector3D *bossVelocityVector = new Vector3D( mBossPosition ); Angle3D *perpendicularAngle = new Angle3D( 0, 0, M_PI / 2 ); bossVelocityVector->rotate( perpendicularAngle ); delete perpendicularAngle; bossVelocityVector->normalize(); bossVelocityVector->scale( currentVelocity ); Vector3D *bossMoveVector = new Vector3D( bossVelocityVector ); bossMoveVector->scale( inTimeDeltaInSeconds ); mBossPosition->add( bossMoveVector ); delete bossMoveVector; // lock down distance from center to avoid cumulative errors double newDistanceFromCenter = mBossPosition->getLength(); double adjustment = mBossDistanceFromCenter / newDistanceFromCenter; mBossPosition->scale( adjustment ); double currentTimeBetweenBullets = mAngerLevel * ( mMaxBossTimeBetweenBullets - mMinBossTimeBetweenBullets ) + mMinBossTimeBetweenBullets; mTimeSinceLastBullet += inTimeDeltaInSeconds; if( mTimeSinceLastBullet >= currentTimeBetweenBullets ) { if( distanceToShip <= mBossBulletRange ) { // close enough to hit target // fire double bulletMoveRate = mBossBulletBaseVelocity; // compute a vector for bullet and the angle of that vector Vector3D *vectorToShip = new Vector3D( inShipPosition ); vectorToShip->subtract( mBossPosition ); // compensate for our velocity when we aim at ship vectorToShip->normalize(); vectorToShip->scale( bulletMoveRate ); vectorToShip->subtract( bossVelocityVector ); // compensate for the velocity of the ship vectorToShip->add( inShipVelocity ); // normalize to turn compensated vector into a length // 1 direction vectorToShip->normalize(); Vector3D *yVector = new Vector3D( 0, -1, 0 ); Angle3D *angleToPointAt = yVector->getZAngleTo( vectorToShip ); delete yVector; vectorToShip->scale( bulletMoveRate ); // adjust by boss motion vectorToShip->add( bossVelocityVector ); // close range based on health // long range based on anger level double healthFraction = mBossHealth / mMaxBossHealth; // get the final move rate of the bullet bulletMoveRate = vectorToShip->getLength(); mBossBulletManager->addBullet( healthFraction, mAngerLevel, 1, bulletMoveRate, // range proportional to move rate new Vector3D( mBossPosition ), angleToPointAt, vectorToShip ); mTimeSinceLastBullet = 0; } } if( bulletPowerNearBoss > 0 ) { // emit damage wherever we are hit by ship bullets // do this down here since we have computed the boss // velocity by now // limit to at most 10 damage graphics in progress at once int limit = 10; double damageParameter = mBossHealth / mMaxBossHealth; int numBullets; Vector3D **bulletPositions = mShipBulletManager->getBulletPositionsInCircle( mBossPosition, // smaller radius to ensure damage // appears only on boss mCurrentRadius / 2, &numBullets ); double bossVelocity = currentVelocity; // how far the damage graphics will travel before they die double damageTravelDistance = bossVelocity * mBossDamageTime; for( int i=0; igetBulletCount() < limit ) { mBossDamageManager->addBullet( damageParameter, damageParameter, 1.0, // full power damageTravelDistance, new Vector3D( bulletPositions[i] ), new Angle3D( mCurrentRotation ), // damage graphics have same velocity as boss new Vector3D( bossVelocityVector ) ); } delete bulletPositions[i]; } delete [] bulletPositions; } delete bossVelocityVector; // convert the distance to ship to the range [0,1] and save it double distanceToTarget = distanceToShip; // starting at 40 units, and up to 20 units from target, is the range double compressedDistance = ( distanceToTarget - 20 ) / ( 40 ); // limit the range if( compressedDistance > 1 ) { compressedDistance = 1; } else if( compressedDistance < 0 ) { compressedDistance = 0; } mShipDistanceParameter = compressedDistance; } } SimpleVector *BossManager::getDrawableObjects() { double healthFraction = mBossHealth / mMaxBossHealth; SimpleVector *bossObjects = mBossTemplate->getDrawableObjects( healthFraction, mShipDistanceParameter, mExplosionShapeParameter, mExplosionProgress, &mCurrentRotationRate ); // fade out at end of explosion double alphaMultiplier = 1 - mExplosionFadeProgress; // compute the scale by weighting the enemy and explosion scales double scale = mExplosionProgress * mExplosionScale + ( 1 - mExplosionProgress ) * mBossScale; int numObjects = bossObjects->size(); // compute the maximum radius of this boss double maxRadius = 0; for( int j=0; jgetElement( j ) ); currentObject->scale( scale ); currentObject->rotate( mCurrentRotation ); currentObject->move( mBossPosition ); currentObject->fade( alphaMultiplier ); double radius = currentObject->getBorderMaxDistance( mBossPosition ); if( radius > maxRadius ) { maxRadius = radius; } } mCurrentRadius = maxRadius; return bossObjects; } Vector3D *BossManager::getBossPosition() { return new Vector3D( mBossPosition ); } char BossManager::isBossDead() { if( mExplosionFadeProgress >= 1 ) { return true; } else { return false; } } transcend-0.3+dfsg2.orig/Transcend/game/Enemy.h0000640000175000017500000000444410112711345020006 0ustar pabspabs/* * Modification History * * 2004-June-18 Jason Rohrer * Created. * * 2004-August-24 Jason Rohrer * Added extra parameter for enemy distance from ship. */ #ifndef ENEMY_INCLUDED #define ENEMY_INCLUDED #include "ParameterizedObject.h" /** * An enemy controled with 2 parameters (one for the general shape * of the enemy, and one to control the shape of the enemy's explosion). * * @author Jason Rohrer. */ class Enemy { public: /** * Constructs an enemy by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading the bullet * from inFILE fails. */ Enemy( FILE *inFILE, char *outError ); virtual ~Enemy(); /** * Gets drawable objects for this enemy. * * @param inEnemyShapeParameter a parameter in the range [0,1] to * control the shape of the enemy. * @param inEnemyDistanceFromShipParameter a parameter in the range * [0,1] representing how far the enemy is from the ship, where 0 * is close and 1 is far. * @param inExplosionShapeParameter a parameter in the range [0,1] to * control the shape of the enemy's explosion. * @param inExplosionProgress how close the enemy is towards a * completed explosion, in the range [0,1]. * @param outRotationRate pointer to where the enemy rotation * rate should be returned. * * @return this enemy as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects( double inEnemyShapeParameter, double inEnemyDistanceFromShipParameter, double inExplosionShapeParameter, double inExplosionProgress, double *outRotationRate ); protected: ParameterizedObject *mEnemyCloseShapeObject; ParameterizedObject *mEnemyFarShapeObject; ParameterizedObject *mExplosionShapeObject; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SoundParameterSpaceControlPoint.h0000640000175000017500000001155610111171546025215 0ustar pabspabs/* * Modification History * * 2004-August-6 Jason Rohrer * Created. * * 2004-August-9 Jason Rohrer * Changed to use sine components. * * 2004-August-12 Jason Rohrer * Added support for getting blocks of samples. * * 2004-August-19 Jason Rohrer * Fixed bug in walking through wavetable. */ #ifndef SOUND_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #define SOUND_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #include "ParameterSpaceControlPoint.h" #include /** * A control point in a 1-D parameterized sound space. * * @author Jason Rohrer. */ class SoundParameterSpaceControlPoint : public ParameterSpaceControlPoint { public: /** * Constructs a control point. * * Note that the waveform is limited to the amplitude range [-1,1] * and will clip if the sine components sum to produce amplitudes * ouside of this range. * * Start/end freqency and loudness are used to create a linear * sweep of these values as the sound plays. * * @param inNumWaveComponents the number of wave components. * @param inWaveComponentFreqencies the frequency of each sine * component. The frequency is in cycles per waveform (in other * words, how many times this sine component cycles during * the length of the waveform). * This array will be destroyed when this class is destroyed. * @param inWaveComponentAmplitudes the peak amplitude for * each sine component. * Each value must be in the range [0,1]. * This array will be destroyed when this class is destroyed. * @param inStartFrequency the number of times the waveform should * play per second at the start of the sound. * @param inEndFrequency the number of times the waveform should * play per second at by the end of the sound. * @param inStartLoudness the loudness of the start of the sound * in the range [0,1]. * @param inEndLoudness the loudness of the end of the sound * in the range [0,1]. */ SoundParameterSpaceControlPoint( int inNumWaveComponents, double *inWaveComponentFreqencies, double *inWaveComponentAmplitudes, double inStartFrequency, double inEndFrequency, double inStartLoudness, double inEndLoudness ); /** * Constructs a control point by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading a control point * from inFILE fails. */ SoundParameterSpaceControlPoint( FILE *inFILE, char *outError ); virtual ~SoundParameterSpaceControlPoint(); // implements the ParameterSpaceControlPoint interface ParameterSpaceControlPoint *copy(); ParameterSpaceControlPoint *createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ); /** * Gets a block of samples from this control point. * * @param inStartSample the index of the first sample to get. * @param inSampleCount the number of samples to get. * @param inSamplesPerSecond the current sample rate. * @param inSoundLengthInSeconds the total length of the sound being * played. * * @return the samples. * Must be destroyed by caller. */ float *getSoundSamples( unsigned long inStartSample, unsigned long inSampleCount, unsigned long inSamplesPerSecond, double inSoundLengthInSeconds ); // these are public so that other Sound points can access // them when performing a blend. int mNumWaveComponents; double *mWaveComponentFrequencies; double *mWaveComponentAmplitudes; double mStartFrequency; double mEndFrequency; double mStartLoudness; double mEndLoudness; protected: /** * Copies an array of doubles, producing a newly constructed array. * * @param inArray the array to copy. * Must be destroyed by caller. * @param inLength the length of inArray. * * @return the copied array. * Must be destroyed by caller. */ double *copyDoubleArray( double *inArray, int inLength ); // used when generating sound samples double mCurrentWavePoint; }; #endif transcend-0.3+dfsg2.orig/Transcend/game/SculptureManager.h0000640000175000017500000003260210302401465022207 0ustar pabspabs/* * Modification History * * 2004-June-21 Jason Rohrer * Created. * * 2004-August-6 Jason Rohrer * Fixed consistency bugs in test for in/out of sculpture status. * * 2004-August-22 Jason Rohrer * Added music parts. * * 2004-August-23 Jason Rohrer * Added function for getting number of pieces in sculpture. * * 2004-August-29 Jason Rohrer * Changed so that only pieces in sculpture are animated. * * 2004-August-31 Jason Rohrer * Added function for getting piece position needed to recenter. * * 2005-August-22 Jason Rohrer * Changed so that isPieceJarred returns true only if jar force is increasing. * Added magnet mode to smooth piece pick-up and drop. */ #ifndef SCULPTURE_MANAGER_INCLUDED #define SCULPTURE_MANAGER_INCLUDED #include "ParameterizedObject.h" #include "ShipBulletManager.h" #include "MusicPart.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/math/geometry/Angle3D.h" /** * A class that manages and draws all sculpture pieces in the environment. * * @author Jason Rohrer. */ class SculptureManager { public: /** * Constructs a manager. * * @param inFirstSculptureTemplate the class to use to draw the first * anmimation frame of all sculpture pieces. * Will be destroyed by this class. * @param inSecondSculptureTemplate the class to use to draw the second * anmimation frame of all sculpture pieces. * Will be destroyed by this class. * @param inSculptureScale the multiplier to scale the template by. * @param inMaxDistanceToBeInSculpture how far a piece can be from * other sculpture pieces to be counted as "in" the sculpture. * @param inAnimationLoopTime the time in seconds of a full * sculpture animation loop. * @param inNumSculpturePieces the number of sculpture pieces. * @param inPieceParameters array of shape parameters, one for each * piece. * Will be destroyed by this class. * @param inPieceStartingPositions array of starting positions, * one for each piece. * Will be destroyed by this class. * @param inPieceStartingRotations array of starting rotations, * one for each piece. * Will be destroyed by this class. * @param inPieceMusicParts array of music parts, * one for each piece. * Will be destroyed by this class. * @param inSculpturePowerUpSpaceFILE the file to read the sculpture * piece power-up mapping from (maps [0,1] parameters into the * 2 parameter ship bullet space). * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading the space * from inSculpturePowerUpSpaceFILE fails. * @param inEnemyBulletManager the manager for enemy bullets. * Must be destroyed by caller after this class is destroyed. * @param inEnemyBulletJarPower the power of enemy bullets. * @param inBossBulletManager the manager for boss bullets. * Must be destroyed by caller after this class is destroyed. * @param inBossBulletJarPower the power of boss bullets. * @param inFriction the force of friction on jarred pieces. * @param inWorldWidth the width of the world (for wrap-around). * @param inWorldHeight the height of the world (for wrap-around). */ SculptureManager( ParameterizedObject *inFirstSculptureTemplate, ParameterizedObject *inSecondSculptureTemplate, double inSculptureScale, double inMaxDistanceToBeInSculpture, double inAnimationLoopTime, int inNumSculpturePieces, double *inPieceShapeParameters, Vector3D **inPieceStartingPositions, Angle3D **inPieceStartingRotations, MusicPart **inPieceMusicParts, FILE *inSculpturePowerUpSpaceFILE, char *outError, ShipBulletManager *inEnemyBulletManager, double inEnemyBulletJarPower, ShipBulletManager *inBossBulletManager, double inBossBulletJarPower, double inFriction, double inWorldWidth, double inWorldHeight ); virtual ~SculptureManager(); /** * Gets the current parameter for close-range ship bullets. * * @return the close-range parameter, in range [0,1]. */ double getCloseRangeBulletParameter(); /** * Gets the current parameter for far-range ship bullets. * * @return the far-range parameter, in range [0,1]. */ double getFarRangeBulletParameter(); /** * Gets the current ship bullet power modifier. * * @return the power modifier, in range [0,1]. */ double getBulletPowerModifier(); /** * Tell this manager that time has passed. * * @param inTimeDeltaInSeconds the amount of time that has passed. */ void passTime( double inTimeDeltaInSeconds ); /** * Gets the sculpture piece that is in a circle. If more * than one piece is in the circle, the closest piece to the center * is returned. Ties are decided in an arbitrary but deterministic * fashion. * * Considers all pieces, even those not in the sculpture. * * @param inCenter the center of the circle. * Must be destroyed by caller. * @param inRadius the radius of the circle. * * @return a non-negative piece handle, or -1 if no piece is in the * circle. */ int getSculpturePieceInCircle( Vector3D *inCenter, double inRadius ); /** * Gets the piece of the sculpture that is closest to a given * point. * * Only considers pieces that are in the sculpture. * * @param inPosition the point to measure distances from. * Must be destroyed by caller. * * @return the position of the closest sculpture piece, * or NULL if the sculpture has no pieces in it. * Must be destroyed by caller. */ Vector3D *getPositionOfClosestSculpturePiece( Vector3D *inPosition ); /** * Gets the number of pieces that are currently connected * together in the sculpture. * * @return the number of pieces. */ int getNumPiecesInSculpture(); /** * Gets the positions for all pieces in the sculpture. * * @param outNumPieces pointer to where the number of pieces * should be returned. * * @return an array of vector positions, one for each piece. * Vectors and array must be destroyed by caller. */ Vector3D **getPiecePositions( int *outNumPieces ); /** * Gets the music parts for all pieces in the sculpture. * * As long as sculpture piece positions have not been * modified with setPiecePosition between calls, * the pieces described by getPiecePositions and getPieceMusicParts * are in the same order. * In other words, the piece at position with index i has its * music part at index i. * * @param outNumPieces pointer to where the number of pieces * should be returned. * * @return an array of music parts, one for each piece. * Array must be destroyed by caller. * Music parts SHOULD NOT be modified or destroyed by caller. */ MusicPart **getPieceMusicParts( int *outNumPieces ); /** * Gets the center of mass of the sculpture. * * If the sculpture is empty, the zero vector is returned. * * @return the center of the sculpture. * Must be destroyed by caller. */ Vector3D *getSculptureCenter(); /** * Gets the position that piece would need to be placed at in * order to recenter the sculpture. * * If the sculpture is already centered, the zero vector * will be returned (since the only way to keep the sculpture * centered when adding a piece would be to add that piece to the * center). * * @return the piece position that would recenter the scupture. * Must be destroyed by caller. */ Vector3D *getPiecePositionNeededToRecenter(); /** * Puts a piece into magnet mode. * In magnet mode, a piece is drawn smoothly toward the positions set * with setPiecePosition until the piece finally hits the set position. * Once the position is hit, the magnet mode turns off. * * @param inPieceHandle the handle of the piece to turn magnet mode * on for. */ void turnMagnetModeOn( int inPieceHandle ); /** * Sets the position of a piece. * * @param inPieceHandle the handle for the piece to move. * @param inNewPosition the new position of the piece. * Must be destroyed by caller. */ void setPiecePosition( int inPieceHandle, Vector3D *inNewPosition ); /** * Check if a piece is currently being jarred by enemy fire. * * @param inPieceHandle the handle for the piece to check. * * @return true if the piece is being jarred or false if it is not. * Returns true only if the jar force is increasing. * Piece may be still moving from the force of a previous bullet * when isPieceJarred returns false. */ char isPieceJarred( int inPieceHandle ); /** * Gets drawable objects for all sculptures in their current * positions/states. * * @return all sculptures as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); protected: ParameterizedObject *mFirstSculpturePieceTemplate; ParameterizedObject *mSecondSculpturePieceTemplate; double mSculptureScale; double mMaxDistanceToBePartOfSculpture; double mAnimationLoopTime; double mCurrentAnimationPosition; double mCurrentAnimationDirection; int mNumSculpturePieces; double *mSculpturePieceParameters; // current position for each piece Vector3D **mCurrentPiecePositions; // flags indicating which pieces are in magnet mode // in magnet mode, pieces move smoothly toward their target // once they hit their target, magnet mode turns off and they // stick to their set position char *mPieceMagnetModes; // position each piece is trying to move toward Vector3D **mCurrentPieceTargetPositions; // velocity at which each piece is moving toward its target double *mCurrentTowardTargetVelocities; Angle3D **mCurrentPieceRotations; MusicPart **mPieceMusicParts; double *mCurrentPieceRotationRates; double *mCurrentPieceRadii; double *mCurrentJarForces; char *mJarForcesIncreasing; int mNumPiecesInSculpture; char *mInSculptureFlags; char *mDelayAnimationStartFlags; char *mDelayAnimationStopFlags; ShipBulletManager *mEnemyBulletManager; double mEnemyBulletJarPower; ShipBulletManager *mBossBulletManager; double mBossBulletJarPower; double mFriction; double mMaxXPosition; double mMinXPosition; double mMaxYPosition; double mMinYPosition; int mNumParameterMapAnchors; double *mParameterMapAnchors; double *mParameterMapCloseRangeValues; double *mParameterMapFarRangeValues; /** * Maps a parameter into the power up space. * * @param inParameter the parameter to map. In range [0,1]. * @param outCloseRangePowerUp pointer to where the close-range * power up should be returned. * @param outFarRangePowerUp pointer to where the far-range * power up should be returned. */ void mapParameters( double inParameter, double *outCloseRangePowerUp, double *outFarRangePowerUp ); /** * Updates all flags indicating whether pieces are in or out of * the sculpture. * * Should be called when a piece changes position. */ void updateInOutStatusOfAllPieces(); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/ParameterizedObject.h0000640000175000017500000000460010106045337022652 0ustar pabspabs/* * Modification History * * 2004-June-14 Jason Rohrer * Created. * * 2004-June-15 Jason Rohrer * Added a function for getting a blended control point. * * 2004-August-9 Jason Rohrer * Made a subclass of ParameterizedSpace. */ #ifndef PARAMETERIZED_OBJECT_INCLUDED #define PARAMETERIZED_OBJECT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/util/SimpleVector.h" #include "ParameterizedSpace.h" #include "DrawableObject.h" #include "ObjectParameterSpaceControlPoint.h" /** * A 1-D space of object control points. * * @author Jason Rohrer. */ class ParameterizedObject : public ParameterizedSpace { public: /** * Constructs an object by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading the object * from inFILE fails. */ ParameterizedObject( FILE *inFILE, char *outError ); /** * Gets drawable objects from this object space. * * @param inParameter the parameter in the range [0,1] to map * into the object space. * @param outRotationRate pointer to where the mapped rotation * rate should be returned. * * @return this object as a collection of drawable objects. * Can return NULL if this space was not properly initialized. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects( double inParameter, double *outRotationRate ); /** * Gets a blended object control point from this object space. * * @param inParameter the parameter in the range [0,1] to map * into the object space. * * @return the blended control point. * Can return NULL if this space was not properly initialized. * Must be destroyed by caller. */ ObjectParameterSpaceControlPoint *getBlendedControlPoint( double inParameter ); protected: // inherit all protected members from ParameterizedSpace }; #endif transcend-0.3+dfsg2.orig/Transcend/game/NamedColorFactory.cpp0000640000175000017500000000471010107122335022632 0ustar pabspabs/* * Modification History * * 2004-June-12 Jason Rohrer * Created. * * 2004-June-14 Jason Rohrer * Changed to allow specification of a directory to read colors from. * * 2004-June-15 Jason Rohrer * Changed to use LevelDirectoryManager to determine where to read colors from. * * 2004-August-12 Jason Rohrer * Optimized Color constructor. */ #include "NamedColorFactory.h" #include "LevelDirectoryManager.h" #include "minorGems/io/file/File.h" #include "minorGems/util/stringUtils.h" #include Color *NamedColorFactory::getColor( char *inColorName ) { // will remain NULL if reading the color fails. Color *color = NULL; File *levelDirectory = LevelDirectoryManager::getLevelDirectory(); File *colorDirectory = levelDirectory->getChildFile( "colors" ); delete levelDirectory; File *colorFile = colorDirectory->getChildFile( inColorName ); delete colorDirectory; if( colorFile != NULL ) { if( colorFile->exists() ) { char *contents = colorFile->readFileContents(); if( contents != NULL ) { SimpleVector *tokens = tokenizeString( contents ); int numTokens = tokens->size(); if( numTokens == 4 ) { float red, green, blue, alpha; int totalNumRead = 0; totalNumRead += sscanf( *( tokens->getElement( 0 ) ), "%f", &red ); totalNumRead += sscanf( *( tokens->getElement( 1 ) ), "%f", &green ); totalNumRead += sscanf( *( tokens->getElement( 2 ) ), "%f", &blue ); totalNumRead += sscanf( *( tokens->getElement( 3 ) ), "%f", &alpha ); // make sure we read all 4 values if( totalNumRead == 4 ) { color = new Color( red, green, blue, alpha, false ); } } for( int i=0; igetElement( i ) ); } delete tokens; delete [] contents; } } delete colorFile; } return color; } transcend-0.3+dfsg2.orig/Transcend/game/LevelDirectoryManager.cpp0000640000175000017500000001155010065645144023521 0ustar pabspabs/* * Modification History * * 2004-June-15 Jason Rohrer * Created. * * 2004-June-17 Jason Rohrer * Added function for reading values from level files. * * 2004-June-21 Jason Rohrer * Added function for reading int values. */ #include "LevelDirectoryManager.h" #include StaticLevelDirectoryFileWrapper LevelDirectoryManager::mFileWrapper; void LevelDirectoryManager::setLevelDirectory( File *inFile ) { if( mFileWrapper.mFile != NULL ) { delete mFileWrapper.mFile; } mFileWrapper.mFile = inFile; } File *LevelDirectoryManager::getLevelDirectory() { if( mFileWrapper.mFile != NULL ) { return mFileWrapper.mFile->copy(); } else { // return default location... level 1 File *levelDirectory = new File( NULL, "levels" ); File *level1Directory = levelDirectory->getChildFile( "001" ); delete levelDirectory; return level1Directory; } } File *LevelDirectoryManager::getLevelFile( char *inFileName, char inPrintErrorMessage ) { File *levelDirectory = getLevelDirectory(); File *levelFile = levelDirectory->getChildFile( inFileName ); if( levelFile == NULL && inPrintErrorMessage ) { char *directoryName = levelDirectory->getFullFileName(); printf( "Error accessing file in directory %s\n", directoryName ); delete [] directoryName; } delete levelDirectory; return levelFile; } FILE *LevelDirectoryManager::getStdStream( char *inFileName, char inPrintErrorMessage ) { File *levelFile = getLevelFile( inFileName, inPrintErrorMessage ); FILE *returnValue; if( levelFile != NULL ) { char *fileName = levelFile->getFullFileName(); returnValue = fopen( fileName, "r" ); if( returnValue == NULL && inPrintErrorMessage ) { printf( "Error opening file %s for reading\n", fileName ); } delete [] fileName; delete levelFile; } else { returnValue = NULL; } return returnValue; } char *LevelDirectoryManager::readFileContents( char *inFileName, char inPrintErrorMessage ) { File *levelFile = getLevelFile( inFileName, inPrintErrorMessage ); char *returnValue; if( levelFile != NULL ) { char *fileContents = levelFile->readFileContents(); if( fileContents == NULL && inPrintErrorMessage ) { char *fileName = levelFile->getFullFileName(); printf( "Error reading contents from file %s\n", fileName ); delete [] fileName; } delete levelFile; // will be NULL if reading from file fails returnValue = fileContents; } else { returnValue = NULL; } return returnValue; } double LevelDirectoryManager::readDoubleFileContents( char *inFileName, char *outError, char inPrintErrorMessage ) { char *fileContents = LevelDirectoryManager::readFileContents( inFileName, inPrintErrorMessage ); double returnValue = 0; if( fileContents != NULL ) { int numRead = sscanf( fileContents, "%lf", &returnValue ); if( numRead != 1 ) { *outError = true; if( inPrintErrorMessage ) { printf( "Error reading double from file %s\n", inFileName ); } } else { *outError = false; } delete [] fileContents; } else { *outError = true; } return returnValue; } int LevelDirectoryManager::readIntFileContents( char *inFileName, char *outError, char inPrintErrorMessage ) { char *fileContents = LevelDirectoryManager::readFileContents( inFileName, inPrintErrorMessage ); int returnValue = 0; if( fileContents != NULL ) { int numRead = sscanf( fileContents, "%d", &returnValue ); if( numRead != 1 ) { *outError = true; if( inPrintErrorMessage ) { printf( "Error reading int from file %s\n", inFileName ); } } else { *outError = false; } delete [] fileContents; } else { *outError = true; } return returnValue; } StaticLevelDirectoryFileWrapper::StaticLevelDirectoryFileWrapper() { mFile = NULL; } StaticLevelDirectoryFileWrapper::~StaticLevelDirectoryFileWrapper() { if( mFile != NULL ) { delete mFile; } } transcend-0.3+dfsg2.orig/Transcend/game/ShipBulletManager.cpp0000640000175000017500000003054510302660571022640 0ustar pabspabs/* * Modification History * * 2004-June-16 Jason Rohrer * Created. * * 2004-June-22 Jason Rohrer * Fixed a memory leak. * * 2004-August-15 Jason Rohrer * Added sound support. * * 2004-August-19 Jason Rohrer * Fixed a memory leak. * * 2004-August-20 Jason Rohrer * Added sound priorities. * * 2004-August-30 Jason Rohrer * Added bullet scaling parameters. * * 2004-October-13 Jason Rohrer * Added function for getting bullet count. * * 2005-August-22 Jason Rohrer * Added smooth fade-out during last tenth of each bullet's life. * Started work on boss damage graphics. * * 2005-August-23 Jason Rohrer * Finished boss damage graphics. */ #include "ShipBulletManager.h" ShipBulletManager::ShipBulletManager( ShipBullet *inBulletTemplate, double inBulletScale, SoundPlayer *inPlayer, BulletSound *inBulletSoundTemplate, double inWorldWidth, double inWorldHeight ) : mBulletTemplate( inBulletTemplate ), mBulletScale( inBulletScale ), mSoundPlayer( inPlayer ), mBulletSoundTemplate( inBulletSoundTemplate ), mCloseRangeParameters( new SimpleVector() ), mFarRangeParameters( new SimpleVector() ), mPowerModifiers( new SimpleVector() ), mCurrentPowers( new SimpleVector() ), mRangesInScreenUnits( new SimpleVector() ), mRangeFractions( new SimpleVector() ), mTimePassed( new SimpleVector() ), mStartingPositions( new SimpleVector() ), mVelocitiesInScreenUnitsPerSecond( new SimpleVector() ), mCurrentPositions( new SimpleVector() ), mCurrentRotations( new SimpleVector() ), mCurrentRotationRates( new SimpleVector() ), mSholdBeDestroyedFlags( new SimpleVector() ) { mMaxXPosition = inWorldWidth / 2; mMinXPosition = -mMaxXPosition; mMaxYPosition = inWorldHeight / 2; mMinYPosition = -mMaxYPosition; } ShipBulletManager::~ShipBulletManager() { delete mBulletTemplate; if( mBulletTemplate != NULL ) { delete mBulletSoundTemplate; } delete mCloseRangeParameters; delete mFarRangeParameters; delete mPowerModifiers; delete mCurrentPowers; delete mRangesInScreenUnits; delete mRangeFractions; delete mTimePassed; int numBullets = mStartingPositions->size(); for( int i=0; igetElement( i ) ); delete *( mVelocitiesInScreenUnitsPerSecond->getElement( i ) ); delete *( mCurrentPositions->getElement( i ) ); delete *( mCurrentRotations->getElement( i ) ); } delete mStartingPositions; delete mVelocitiesInScreenUnitsPerSecond; delete mCurrentPositions; delete mCurrentRotations; delete mCurrentRotationRates; delete mSholdBeDestroyedFlags; } void ShipBulletManager::addBullet( double inCloseRangeParameter, double inFarRangeParameter, double inPowerModifier, double inRangeInScreenUnits, Vector3D *inStartingPosition, Angle3D *inStartingRotation, Vector3D *inVelocityInScreenUnitsPerSecond ) { mCloseRangeParameters->push_back( inCloseRangeParameter ); mFarRangeParameters->push_back( inFarRangeParameter ); mPowerModifiers->push_back( inPowerModifier ); mCurrentPowers->push_back( 0 ); mRangesInScreenUnits->push_back( inRangeInScreenUnits ); mRangeFractions->push_back( 0 ); mTimePassed->push_back( 0 ); mStartingPositions->push_back( inStartingPosition ); mCurrentRotations->push_back( inStartingRotation ); mCurrentRotationRates->push_back( 0 ); mVelocitiesInScreenUnitsPerSecond->push_back( inVelocityInScreenUnitsPerSecond ); mCurrentPositions->push_back( new Vector3D( inStartingPosition ) ); mSholdBeDestroyedFlags->push_back( false ); if( mSoundPlayer != NULL ) { // play the sound PlayableSound *sound = mBulletSoundTemplate->getPlayableSound( inCloseRangeParameter, inFarRangeParameter, mSoundPlayer->getSampleRate() ); // shot sounds are low priority mSoundPlayer->playSoundNow( sound, false, inPowerModifier ); delete sound; } } int ShipBulletManager::getBulletCount() { return mCloseRangeParameters->size(); } double ShipBulletManager::getBulletPowerInCircle( Vector3D *inCircleCenter, double inCircleRadius ) { double powerSum = 0; int numBullets = mCloseRangeParameters->size(); for( int i=0; i *bulletObjects = mBulletTemplate->getDrawableObjects( *( mCloseRangeParameters->getElement( i ) ), *( mFarRangeParameters->getElement( i ) ), *( mRangeFractions->getElement( i ) ), &power, ¤tRotationRate ); int numObjects = bulletObjects->size(); char alreadyHit = false; for( int j=0; jgetElement( j ) ); if( !alreadyHit ) { // check if this part of bullet hits currentObject->scale( mBulletScale ); currentObject->rotate( *( mCurrentRotations->getElement( i ) ) ); currentObject->move( *( mCurrentPositions->getElement( i ) ) ); if( currentObject->isBorderInCircle( inCircleCenter, inCircleRadius ) ) { powerSum += *( mCurrentPowers->getElement( i ) ); alreadyHit = true; } } // otherwise, ignore this part of bullet } for( int j=0; jgetElement( j ) ); delete currentObject; } delete bulletObjects; } return powerSum; } Vector3D **ShipBulletManager::getBulletPositionsInCircle( Vector3D *inCircleCenter, double inCircleRadius, int *outNumBullets ) { SimpleVector *positionsInCircle = new SimpleVector(); int numBullets = mStartingPositions->size(); int i; for( i=0; igetElement( i ) ); if( position->getDistance( inCircleCenter ) <= inCircleRadius ) { positionsInCircle->push_back( new Vector3D( position ) ); } } *outNumBullets = positionsInCircle->size(); Vector3D **returnArray = positionsInCircle->getElementArray(); delete positionsInCircle; return returnArray; } void ShipBulletManager::passTime( double inTimeDeltaInSeconds ) { int numBullets = mStartingPositions->size(); int i; for( i=0; igetElement( i ) ) += inTimeDeltaInSeconds; Angle3D *currentRotation = *( mCurrentRotations->getElement( i ) ); double rotationRate = *( mCurrentRotationRates->getElement( i ) ); currentRotation->mZ += rotationRate * inTimeDeltaInSeconds; // compute new position Vector3D *newPosition = new Vector3D( *( mStartingPositions->getElement( i ) ) ); Vector3D *travelVector = new Vector3D( *( mVelocitiesInScreenUnitsPerSecond->getElement( i ) ) ); travelVector->scale( *( mTimePassed->getElement( i ) ) ); newPosition->add( travelVector ); delete *( mCurrentPositions->getElement( i ) ); *( mCurrentPositions->getElement( i ) ) = newPosition; double distanceTraveled = travelVector->getLength(); delete travelVector; // compute new range fraction double newRangeFraction; double rangeLength = *( mRangesInScreenUnits->getElement( i ) ); Vector3D *velocity = *( mVelocitiesInScreenUnitsPerSecond->getElement( i ) ); if( velocity->getLength() == 0 ) { // treat range as bullet lifetime in seconds double oldRangeFraction = *( mRangeFractions->getElement( i ) ); newRangeFraction = oldRangeFraction + inTimeDeltaInSeconds / rangeLength; } else { // treat range as a distance newRangeFraction = distanceTraveled / rangeLength; } if( newRangeFraction >= 1 ) { // bullet has reached end of range // should be destroyed *( mSholdBeDestroyedFlags->getElement( i ) ) = true; if( newRangeFraction > 1 ) { newRangeFraction = 1; } } *( mRangeFractions->getElement( i ) ) = newRangeFraction; } // walk through vectors and destroy bullets that are flagged // note that our loop range is adjusted as the vector length shrinks for( i=0; isize(); i++ ) { if( *( mSholdBeDestroyedFlags->getElement( i ) ) ) { mCloseRangeParameters->deleteElement( i ); mFarRangeParameters->deleteElement( i ); mPowerModifiers->deleteElement( i ); mRangesInScreenUnits->deleteElement( i ); mRangeFractions->deleteElement( i ); mTimePassed->deleteElement( i ); delete *( mStartingPositions->getElement( i ) ); mStartingPositions->deleteElement( i ); delete *( mCurrentRotations->getElement( i ) ); mCurrentRotations->deleteElement( i ); mCurrentRotationRates->deleteElement( i ); delete *( mVelocitiesInScreenUnitsPerSecond->getElement( i ) ); mVelocitiesInScreenUnitsPerSecond->deleteElement( i ); delete *( mCurrentPositions->getElement( i ) ); mCurrentPositions->deleteElement( i ); mSholdBeDestroyedFlags->deleteElement( i ); } } } SimpleVector *ShipBulletManager::getDrawableObjects() { SimpleVector *returnVector = new SimpleVector(); int numBullets = mCloseRangeParameters->size(); for( int i=0; i *bulletObjects = mBulletTemplate->getDrawableObjects( *( mCloseRangeParameters->getElement( i ) ), *( mFarRangeParameters->getElement( i ) ), *( mRangeFractions->getElement( i ) ), &power, ¤tRotationRate ); double powerModifier = *( mPowerModifiers->getElement( i ) ); modifiedPower = power * powerModifier; double endOfLifeFadeFactor = 1; double rangeFraction = *( mRangeFractions->getElement( i ) ); if( rangeFraction > 0.9 ) { // fade out during last tenth of bullet life endOfLifeFadeFactor = 1.0 - (rangeFraction - 0.9) / 0.1; } double fadeFactor = powerModifier * endOfLifeFadeFactor; *( mCurrentPowers->getElement( i ) ) = modifiedPower; *( mCurrentRotationRates->getElement( i ) ) = currentRotationRate; int numObjects = bulletObjects->size(); for( int j=0; jgetElement( j ) ); currentObject->fade( fadeFactor ); currentObject->scale( mBulletScale ); currentObject->rotate( *( mCurrentRotations->getElement( i ) ) ); currentObject->move( *( mCurrentPositions->getElement( i ) ) ); returnVector->push_back( currentObject ); } delete bulletObjects; } return returnVector; } transcend-0.3+dfsg2.orig/Transcend/game/SoundPlayerActive.cpp0000640000175000017500000001003510077544735022677 0ustar pabspabs/* * Modification History * * 2004-July-17 Jason Rohrer * Created. * * 2004-July-21 Jason Rohrer * Switched to a passive (callback-based) player. Old player saved here. */ #include "SoundPlayerActive.h" #include SoundPlayer::SoundPlayer() : mRealtimeSounds( new SimpleVector() ) { PaError error = OpenAudioStream( &mAudioStream, 44100, paFloat32, ( PABLIO_WRITE | PABLIO_MONO ) ); if( error == paNoError ) { mAudioInitialized = true; } else { mAudioInitialized = false; fprintf( stderr, "An error occured while setting up the sound stream\n" ); fprintf( stderr, "Error number: %d\n", error ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error ) ); } } SoundPlayer::~SoundPlayer() { if( mAudioInitialized ) { PaError error = CloseAudioStream( mAudioStream ); if( error != paNoError ) { fprintf( stderr, "An error occured while shutting down the sound stream\n" ); fprintf( stderr, "Error number: %d\n", error); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( error) ); } } int numSounds = mRealtimeSounds->size(); for( int i=0; igetElement( i ) ); } delete mRealtimeSounds; } void SoundPlayer::playSoundNow( SoundSamples *inSamples ) { mRealtimeSounds->push_back( new SoundSamples( inSamples ) ); } unsigned long SoundPlayer::getNumMusicSamplesNeeded() { return GetAudioStreamWriteable( mAudioStream ); } void SoundPlayer::addMoreMusic( SoundSamples *inSamples ) { SoundSamples *mixingBuffer = new SoundSamples( inSamples ); unsigned long bufferLength = mixingBuffer->mSampleCount; // add each pending realtime sound to the buffer int i = 0; // we may be removing sounds from the buffer as we use them up // i is adjusted inside the while loop while( isize() ) { SoundSamples *realtimeSound = *( mRealtimeSounds->getElement( i ) ); unsigned long soundLength = realtimeSound->mSampleCount; // limit length of this mix based on whichever is shorter, the sound // or the mixing buffer unsigned long mixLength = bufferLength; if( mixLength > soundLength ) { mixLength = soundLength; } for( unsigned long j=0; jmLeftChannel[j] += realtimeSound->mLeftChannel[j]; mixingBuffer->mRightChannel[j] += realtimeSound->mRightChannel[j]; } if( mixLength == soundLength ) { // we have used up all samples of this sound delete realtimeSound; mRealtimeSounds->deleteElement( i ); // don't increment i, since the next element drops into the current // index } else { // trim off the samples that we have used realtimeSound->trim( mixLength ); // increment i to move on to the next sound i++; } } /* float **samples = new float*[ bufferLength ]; unsigned long j; for( j=0; jmLeftChannel[j]; samples[j][1] = mixingBuffer->mRightChannel[j]; } WriteAudioStream( mAudioStream, samples, bufferLength ); for( j=0; jmLeftChannel[j]; } WriteAudioStream( mAudioStream, samples, bufferLength ); delete [] samples; delete mixingBuffer; } transcend-0.3+dfsg2.orig/Transcend/game/ObjectParameterSpaceControlPoint.h0000640000175000017500000002207710114400460025324 0ustar pabspabs/* * Modification History * * 2004-June-12 Jason Rohrer * Created. * * 2004-June-15 Jason Rohrer * Added a copy function. * * 2004-June-18 Jason Rohrer * Changed to use triangles instead of polygons. * * 2004-August-9 Jason Rohrer * Made a subclass of ParameterSpaceControlPoint. * * 2004-August-25 Jason Rohrer * Made readColorFromFile static and public so that other classes can use it. * * 2004-August-26 Jason Rohrer * Added scaling of rotated copies to support spiral shapes. * * 2004-August-27 Jason Rohrer * Added function for writing out to file. * * 2004-August-29 Jason Rohrer * Added a scale factor for the angle of rotated copies. */ #ifndef OBJECT_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #define OBJECT_PARAMETER_SPACE_CONTROL_POINT_INCLUDED #include "minorGems/math/geometry/Vector3D.h" #include "minorGems/graphics/Color.h" #include "minorGems/util/SimpleVector.h" #include "ParameterSpaceControlPoint.h" #include "DrawableObject.h" /** * A control point in a 1-D parameterized object space. * * @author Jason Rohrer. */ class ObjectParameterSpaceControlPoint : public ParameterSpaceControlPoint { public: /** * Constructs a control point. * * @param inNumTriangleVertices the number of triangle vertices. * Must be a multiple of 3. * @param inTriangleVertices the triangle vertices. * This array and the vertices it contains will be * destroyed when this class is destroyed. * @param inTriangleVertexFillColors a fill color for each * triangle vertex. * This array and the colors it contains will be destroyed when * this class is destroyed. * @param inNumBorderVertices the number of vertices. * Must be a multiple of 3. * @param inBorderVertices the border vertices. * This array and the vertices it contains will be * destroyed when this class is destroyed. * @param inBorderVertexColors a color for each * border vertex. * This array and the colors it contains will be destroyed when * this class is destroyed. * @param inBorderWidth the width of the border, in pixels. * @param inNumRotatedCopies the number of rotated copies of the * base vertices to draw (evenly spaced rotations). * Non-integer values will cause angle between copies to * be smaller with an extra copy "fading in" as the fractional * part of inNumRotatedCopies approaches 1. * @param inRotatedCopyScaleFactor the factor to scale each successive * rotated copy by. Values less than one will cause rotated copies * to get progressively smaller, while values larger than * one will cause copies to get larger. * @param inRotatedCopyAngleScaleFactor the factor to scale the * rotation angle by. If inRotatedCopyAngleScaleFactor is 1, the * copies will be evenly spaced around a circle. For values less * than 1, copies will be closer together and will not fill a circle. * For values greater than 1, copies will be spread around a * circle more than once and will overlap. * @param inRotationRate the rate in rotations per second. */ ObjectParameterSpaceControlPoint( int inNumTriangleVertices, Vector3D **inTriangleVertices, Color **inTriangleVertexFillColors, int inNumBorderVertices, Vector3D **inBorderVertices, Color **inBorderVertextColors, double inBorderWidth, double inNumRotatedCopies, double inRotatedCopyScaleFactor, double inRotatedCopyAngleScaleFactor, double inRotationRate ); /** * Constructs a control point by reading values from a text file * stream. * * @param inFILE the open file to read from. * Must be closed by caller. * @param outError pointer to where error flag should be returned. * Destination will be set to true if reading a control point * from inFILE fails. */ ObjectParameterSpaceControlPoint( FILE *inFILE, char *outError ); virtual ~ObjectParameterSpaceControlPoint(); /** * Writes this control point out to a text file stream. * * @param inFILE the open file to write to. * Must be closed by caller. */ void writeToFile( FILE *inFILE ); // implements the ParameterSpaceControlPoint interface ParameterSpaceControlPoint *copy(); ParameterSpaceControlPoint *createLinearBlend( ParameterSpaceControlPoint *inOtherPoint, double inWeightOfOtherPoint ); /** * Gets drawable objects from this control point. * * @return this control point as a collection of drawable objects. * Vector and objects must be destroyed by caller. */ SimpleVector *getDrawableObjects(); /** * Gets the rotation rate of this point. * * @return the rotation rate in rotations per second. */ double getRotationRate(); // These internal members are public only to allow this control // point to be blended with other control points. // These members should not be accessed by other classes. int mNumTriangleVertices; Vector3D **mTriangleVertices; Color **mTriangleVertexFillColors; int mNumBorderVertices; Vector3D **mBorderVertices; Color **mBorderVertexColors; double mBorderWidth; double mNumRotatedCopies; double mRotatedCopyScaleFactor; double mRotatedCopyAngleScaleFactor; double mRotationRate; /** * Reads a color from a text file as either RGBA or as a named * color. * * @param inFILE the file stream to read from. * Must be closed by caller. * * @return the read color, or NULL on an error. * Must be destroyed by caller. */ static Color *readColorFromFile( FILE *inFILE ); /** * Writes a color to a text file as space-delimited RGBA. * * @param inFILE the file stream to read from. * Must be closed by caller. * @param inColor the color to write. * Must be destroyed by caller. */ static void writeColorToFile( FILE *inFILE, Color *inColor ); protected: /** * Makes a deep copy of an array of vertices. * * @param inArray the array to duplicate. * Array and vertices must be destroyed by caller. * @param inNumVertices the number of vertices in the array. * * @return the new array. * Array and vertices must be destroyed by caller. */ Vector3D **duplicateVertextArray( Vector3D **inArray, int inNumVertices ); /** * Makes a deep copy of an array of colors. * * @param inArray the array to duplicate. * Array and colorsmust be destroyed by caller. * @param inNumColors the number of colors in the array. * @param inAlphaMultiplier a factor to multiply the alpha * channel of each color by when making the copy. * Defaults to 1 (no change to alphas in copied array). * * * @return the new array. * Array and colors must be destroyed by caller. */ Color **duplicateColorArray( Color **inArray, int inNumColors, float inAlphaMultiplier = 1 ); /** * Blends two color arrays. * * @param inFirstArray the first array of colors. * Colors and array must be destroyed by caller. * @param inFirstArrayLength the number of colors in the first array. * @param inWeightFirstArray the weight of the first array, in [0,1]. * @param inSecondArray the first array of colors. * Colors and array must be destroyed by caller. * @param inSecondArrayLength the number of colors in the first array. * @param outResultLength pointer to where the length of the resulting * array should be returned. * * @return the blended array. * Colors and array must be destroyed by caller. */ Color **blendColorArrays( Color **inFirstArray, int inFirstArrayLength, double inWeightFirstArray, Color **inSecondArray, int inSecondArrayLength, int *outResultLength ); }; #endif transcend-0.3+dfsg2.orig/Transcend/game/DrawableObject.cpp0000640000175000017500000001420210065534277022143 0ustar pabspabs/* * Modification History * * 2004-June-11 Jason Rohrer * Created. * * 2004-June-16 Jason Rohrer * Added fuctions for modifying object position, rotation, and transparency. * * 2004-June-18 Jason Rohrer * Changed to use triangles instead of polygons. * * 2004-June-20 Jason Rohrer * Added fuction for modifying scale. * Added function for testing circle containment. * * 2004-June-21 Jason Rohrer * Added fuction for getting minimum border distance. */ #include #include #include "DrawableObject.h" DrawableObject::DrawableObject( int inNumTriangleVertices, Vector3D **inTriangleVertices, Color **inTriangleVertexFillColors, int inNumBorderVertices, Vector3D **inBorderVertices, Color **inBorderVertextColors, float inBorderWidth ) : mNumTriangleVertices( inNumTriangleVertices ), mTriangleVertices( inTriangleVertices ), mTriangleVertexFillColors( inTriangleVertexFillColors ), mNumBorderVertices( inNumBorderVertices ), mBorderVertices( inBorderVertices ), mBorderVertexColors( inBorderVertextColors ), mBorderWidth( inBorderWidth ) { } DrawableObject::~DrawableObject() { int i; for( i=0; irotate( inRotation ); } for( i=0; irotate( inRotation ); } } void DrawableObject::move( Vector3D *inPosition ) { int i; for( i=0; iadd( inPosition ); } for( i=0; iadd( inPosition ); } } void DrawableObject::scale( double inScale ) { int i; for( i=0; iscale( inScale ); } for( i=0; iscale( inScale ); } } void DrawableObject::fade( double inAlphaScale ) { int i; for( i=0; ia *= inAlphaScale; } for( i=0; ia *= inAlphaScale; } } char DrawableObject::isBorderInCircle( Vector3D *inCenter, double inRadius ) { for( int i=0; igetDistance( inCenter ) <= inRadius ) { return true; } } // else none inside circle return false; } double DrawableObject::getBorderMaxDistance( Vector3D *inPoint ) { double maxDistance = 0; for( int i=0; igetDistance( inPoint ); if( distance > maxDistance ) { maxDistance = distance; } } return maxDistance; } double DrawableObject::getBorderMinDistance( Vector3D *inPoint ) { double minDistance = DBL_MAX; for( int i=0; igetDistance( inPoint ); if( distance < minDistance ) { minDistance = distance; } } return minDistance; } void DrawableObject::draw( double inScale, Angle3D *inRotation, Vector3D *inPosition ) { Vector3D **worldTriangleVertices = new Vector3D*[ mNumTriangleVertices ]; Vector3D **worldBorderVertices = new Vector3D*[ mNumBorderVertices ]; int i; // transform the vertices // (do this once here before drawing since we draw them twice, // once for the filled polygon and once for the border) for( i=0; iscale( inScale ); worldTriangleVertices[i]->rotate( inRotation ); worldTriangleVertices[i]->add( inPosition ); } for( i=0; iscale( inScale ); worldBorderVertices[i]->rotate( inRotation ); worldBorderVertices[i]->add( inPosition ); } // draw the filled polygon glBegin( GL_TRIANGLES ); for( i=0; ir, mTriangleVertexFillColors[i]->g, mTriangleVertexFillColors[i]->b, mTriangleVertexFillColors[i]->a ); glVertex2d( worldTriangleVertices[i]->mX, worldTriangleVertices[i]->mY ); } glEnd(); // draw the border glLineWidth( mBorderWidth ); glBegin( GL_LINE_LOOP ); for( i=0; ir, mBorderVertexColors[i]->g, mBorderVertexColors[i]->b, mBorderVertexColors[i]->a ); glVertex2d( worldBorderVertices[i]->mX, worldBorderVertices[i]->mY ); } glEnd(); for( i=0; i #A4# transcend-0.3+dfsg2.orig/Transcend/designs/level001/enemyShape0.dia0000640000175000017500000001312310065275721023474 0ustar pabspabs #A4# transcend-0.3+dfsg2.orig/Transcend/designs/level001/enemyExplosionShape0.dia0000640000175000017500000001317710065275721025406 0ustar pabspabs #A4# transcend-0.3+dfsg2.orig/Transcend/designs/level001/enemyExplosionShape1.dia0000640000175000017500000001317710065275721025407 0ustar pabspabs #A4# transcend-0.3+dfsg2.orig/Transcend/Makefile.MacOSX0000640000175000017500000000177710133736155020417 0ustar pabspabs# # Modification History # # 2002-September-9 Jason Rohrer # Created. # # 2003-November-2 Jason Rohrer # Moved minorGems platform prefixes into platform-specific Makefile templates. # ## # The common MacOSX portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## # __ppc__ flag for Crypto++ # paths to GL and GLUT headers PLATFORM_COMPILE_FLAGS = -DBSD -D__ppc__ -D__mac__ -I/System/Library/Frameworks/OpenGL.framework/Headers -I/System/Library/Frameworks/GLUT.framework/Headers # various frameworks to support OpenGL, GLUT, and portaudio PLATFORM_LINK_FLAGS = -framework Cocoa -framework OpenGL -framework GLUT -framework CoreAudio -framework AudioToolbox ${ROOT_PATH}/Transcend/portaudio/lib/libportaudio.a # Nothing special for OS X here GXX=g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix transcend-0.3+dfsg2.orig/Transcend/build/0000750000175000017500000000000010305077131016741 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/unix2dosScript0000750000175000017500000000013710066175660021642 0ustar pabspabs cat "$1" | ./unix2dos > tempUNIX2DOS.txt cp -f tempUNIX2DOS.txt "$1" rm -f tempUNIX2DOS.txttranscend-0.3+dfsg2.orig/Transcend/build/unix2dos.c0000640000175000017500000000035310066175660020674 0ustar pabspabs#include #include int main(void) { while(1) { int c = getchar(); if(c == EOF) exit(0); if(c == '\n') { putchar(015); /* ^M */ putchar(012); /* ^J */ } else { putchar(c); } } exit(0); } transcend-0.3+dfsg2.orig/Transcend/build/makeDistributions0000750000175000017500000000433310133732545022400 0ustar pabspabs#!/bin/sh # # Modification History # # 2004-June-22 Jason Rohrer # Copied from Monolith wxWindows build. # if [ $# -lt 2 ] ; then echo "Usage: $0 release_name unix_platform_name" exit 1 fi rm -rf unix rm -rf windows mkdir windows mkdir unix # work on unix tree first mkdir unix/Transcend mkdir unix/Transcend/levels mkdir unix/Transcend/levels/001 mkdir unix/Transcend/levels/001/colors # these calls will skip CVS directories cp ../levels/001/* unix/Transcend/levels/001/ cp ../levels/001/colors/* unix/Transcend/levels/001/colors/ # remove backup files rm unix/Transcend/levels/001/*~ rm unix/Transcend/levels/001/colors/*~ mkdir unix/Transcend/levels/002 mkdir unix/Transcend/levels/002/colors # these calls will skip CVS directories cp ../levels/002/* unix/Transcend/levels/002/ cp ../levels/002/colors/* unix/Transcend/levels/002/colors/ # remove backup files rm unix/Transcend/levels/002/*~ rm unix/Transcend/levels/002/colors/*~ mkdir unix/Transcend/levels/003 mkdir unix/Transcend/levels/003/colors # these calls will skip CVS directories cp ../levels/003/* unix/Transcend/levels/003/ cp ../levels/003/colors/* unix/Transcend/levels/003/colors/ # remove backup files rm unix/Transcend/levels/003/*~ rm unix/Transcend/levels/003/colors/*~ cp ../doc/how_to_*.txt unix/Transcend/ # duplicate unix tree so far to make windows tree cp -r unix/Transcend windows/ cp ../game/Transcend unix/Transcend/ cp win32/Transcend.exe win32/*.dll windows/Transcend/ cd unix tar cf "Transcend_$1_$2.tar" Transcend gzip "Transcend_$1_$2.tar" # compile unix2dos cd .. g++ -o unix2dos unix2dos.c cp unix2dos windows cp unix2dosScript windows cd windows for file in Transcend/levels/001/* do ./unix2dosScript "$file" done for file in Transcend/levels/001/colors/* do ./unix2dosScript "$file" done for file in Transcend/levels/002/* do ./unix2dosScript "$file" done for file in Transcend/levels/002/colors/* do ./unix2dosScript "$file" done for file in Transcend/levels/003/* do ./unix2dosScript "$file" done for file in Transcend/levels/003/colors/* do ./unix2dosScript "$file" done for file in Transcend/how_to_*.txt do ./unix2dosScript "$file" done zip -r "Transcend_$1_Windows.zip" Transcend transcend-0.3+dfsg2.orig/Transcend/build/source/0000750000175000017500000000000010305077131020241 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/source/runToBuild0000750000175000017500000000072310133740153022260 0ustar pabspabs#!/bin/bash # # Modification History # # 2004-January-15 Jason Rohrer # Added bash path. # Fixed string comparison operator. # cd Transcend chmod u+x ./configure ./configure echo "Building portaudio..." cd portaudio chmod u+x ./configure ./configure make cd .. echo "Building Transcend..." cd game make cd .. cd .. cp Transcend/game/Transcend ./TranscendApp cp -r Transcend/levels . cp Transcend/doc/how_to_*.txt . echo "Run TranscendApp to play." transcend-0.3+dfsg2.orig/Transcend/build/source/exportSrc0000750000175000017500000000023010133733531022155 0ustar pabspabscvs -z3 -d:ext:jcr13@cvs.sf.net:/cvsroot/minorgems export -r HEAD minorGems cvs -z3 -d:ext:jcr13@cvs.sf.net:/cvsroot/transcend export -r HEAD Transcend transcend-0.3+dfsg2.orig/Transcend/build/source/cleanSrc0000750000175000017500000000017510115325457021732 0ustar pabspabsrm -r minorGems/ai rm -r minorGems/bench rm -r minorGems/doc rm -r minorGems/examples rm -r minorGems/temp rm -r minorGems/uitranscend-0.3+dfsg2.orig/Transcend/build/makeDistributionMacOSX0000750000175000017500000000313310133732604023221 0ustar pabspabs#!/bin/sh # # Modification History # # 2004-June-22 Jason Rohrer # Copied from Monolith wxWindows build script. # if [ $# -lt 2 ] ; then echo "Usage: $0 release_name unix_platform_name" exit 1 fi rm -rf mac mkdir mac mkdir mac/Transcend mkdir mac/Transcend/levels mkdir mac/Transcend/levels/001 mkdir mac/Transcend/levels/001/colors # these calls will skip CVS directories cp ../levels/001/* mac/Transcend/levels/001/ cp ../levels/001/colors/* mac/Transcend/levels/001/colors/ # remove backup files rm mac/Transcend/levels/001/*~ rm mac/Transcend/levels/001/colors/*~ mkdir mac/Transcend/levels/002 mkdir mac/Transcend/levels/002/colors # these calls will skip CVS directories cp ../levels/002/* mac/Transcend/levels/002/ cp ../levels/002/colors/* mac/Transcend/levels/002/colors/ # remove backup files rm mac/Transcend/levels/002/*~ rm mac/Transcend/levels/002/colors/*~ mkdir mac/Transcend/levels/003 mkdir mac/Transcend/levels/003/colors # these calls will skip CVS directories cp ../levels/003/* mac/Transcend/levels/003/ cp ../levels/003/colors/* mac/Transcend/levels/003/colors/ # remove backup files rm mac/Transcend/levels/003/*~ rm mac/Transcend/levels/003/colors/*~ cp -r macOSX/Transcend.app mac/Transcend cp ../game/Transcend mac/Transcend/Transcend.app/Contents/MacOS cp ../doc/how_to_*.txt mac/Transcend rm -r mac/Transcend/Transcend.app/CVS rm -r mac/Transcend/Transcend.app/Contents/CVS rm -r mac/Transcend/Transcend.app/Contents/MacOS/CVS rm -r mac/Transcend/Transcend.app/Contents/Resources/CVS cd mac tar cf "Transcend_$1_$2.tar" Transcend gzip "Transcend_$1_$2.tar" transcend-0.3+dfsg2.orig/Transcend/build/win32/0000750000175000017500000000000011476353073017717 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/0000750000175000017500000000000010305077131020073 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/0000750000175000017500000000000010305077131022573 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/0000750000175000017500000000000010305077131024370 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/PkgInfo0000640000175000017500000000001010115307251025636 0ustar pabspabsAPPL????transcend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/Info.plist0000640000175000017500000000202110305075575026346 0ustar pabspabs CFBundleInfoDictionaryVersion 6.0 CFBundleIdentifier org.Transcend CFBundleDevelopmentRegion English CFBundleExecutable Transcend CFBundleIconFile Transcend.icns CFBundleName Transcend CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 0.3 CFBundleShortVersionString 0.3 CFBundleGetInfoString Transcend, version 0.3 CFBundleLongVersionString 0.3 NSHumanReadableCopyright Copyleft 2004 LSRequiresCarbon CSResourcesFileMapped transcend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/Resources/0000750000175000017500000000000010305077131026342 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/Resources/Transcend.rsrc0000640000175000017500000000552710115307251031166 0ustar pabspabs §§°;)This application needs at least a MacPlus"This application needs more memory!This application is out of memory*This application needs at least System 8.6 About this wxWindows Application}This application needs Appearance extension (built in with System 8) - this restriction will be relieved in the final release"ÿÿÿýAboutÉ-Dà01Æ#b""#b1Æ0ààðøü?þ?þ?þ?þ?þüøðàDDðˆp @@€€ðøøððàÀÀ€€D!@€@€@€@€!€Ààp8?€ÿÀÿÀÿÀÿÀÿÀÀ?àðø|>Dàð88< ngc†aÆ`æpv0<øà@ð?ø<<~ÿo†çÇcæá÷pþp~<<üð@D€€€€€€àððððPPPÀÀÀÀÀÀàð?ø?ø?ø?ø?ø?ø?ø?ø DÀ@@À1€Gþ€€þ€@À@ÀÀ€?€ÀÀÀÀ?€þÿÿÿÿÿþÿÀÿÀÿÀÿÀÿ€?€D€`Œ €€Àø€àðüÿÿÿÿÿÿÿÿÿÿÿÿøD@x`üqÎy†|~¸|0l0F00ÀxàüñþûÿÿÏÿÿÿ¾ÿüþxÿxïøÏø‡øøD>~þþ>6b`ÀÀ?ÿÿÿÿ÷óáàÀD€Àà€€€üü€€€àÀ€€ÀàðøÀ?þ?þ?þ?þÀøðàÀ€D€Àà€ˆŒ?þŒˆ€àÀ€€Ààð èÜ?þÿ?þÜ èðàÀ€D Ààp8?—ãá!ð1ø:|<<>??€ Dxpp`HàÀ€€H88xþüøpððéðÇà‡À„Œ>\<<8|üüD @øô *¨ð€8`€>|pàü?ööþüø?ðÀøà AÀ¢ð¥üKÿ×ÿÀñÿÿðàÿü€ÿÿÿ€ÿÿÿ€?ÿþ€ÿÿþ€?ÿúÀÿÿöÿÿî_ÿÿÞÿÿÿ¾Ïÿÿóÿþÿäÿýþá?ûüàO÷øàïðxßà?À€?€à?x><¸ð`AÀãðçüÏÿßÿÀÿÿÿðÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüÿÿÿøÿÿÿðÿÿàÿÿÀÿÿ€ÿÿþüøð`@ ÀðëüŽÿ¯þ—þßþïú¿ç‹æÅÜ0Ø0`À Àðÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿü?øðàÀ€ì oÀÞ½ÖfoÀÞ»îeffoÀâ;»fffnì,¥ffff`á¦ffffн½æfffmÀÿöffff\Àìßfffm ÿü ßffPßü]À ßm ÿÀ uÀ Ðßü mÀ ÿÀÎeÏüÝoÀõ÷ëñ+õøˆ‚VÒÒÒìñøùû^^ý‚ï°ìÒÒÒìñ÷õ­]33^‡^‚ÓÒÒÒÒÒëý‚33¬ÕÒëÒÒÒÒÒÒÒõˆ33_ïÒÒÒÒÒÒÒÒ€û^]]ÕÒÒÒÒÒÒÒÒ€öýÿÖìÒÒÒÒÒÒÒÒ€++õþ÷ôïëÒÒÒÒÒ€öùÿýþö+WþðëÒÒ€öVÿÿ÷~ªö÷Wóì€+Vÿÿ÷1͆ö÷ú+Vÿÿ÷õzë€öVÿÿ÷*«ì€+ÿÿ÷[«ðý÷š STR#RMENU^MBARjcarbvldes‚CURSšICN#Nics#Zics4fics8r€ÿÿ?ÿÿeÿÿm€ÿÿsÿÿ‰ ÿÿŸ ÿÿç ÿÿ/ ÿÿwÿÿ¿ÿÿÿÿOÿÿ—ÿÿßÿÿ'ÿÿoÿÿ·ÿÿÿÿÿGÿÿ€ÿÿ×€ÿÿÛ€ÿÿ€ÿÿ£Simple Alert Messagestranscend-0.3+dfsg2.orig/Transcend/build/macOSX/Transcend.app/Contents/Resources/Transcend.icns0000640000175000017500000012137510115317333031153 0ustar pabspabsicns¢ýit32bíkfd‘kd_jke[gkV^‘kY\‘k`^kfd‘kd_jke[gkV^‘kY\‘k`^kfd‘kd_jke[gkV^‘kY\‘k`^kfd‘kd_jke[gkV^‘kY\‘k`^kfd‘kd_jkRDgkV^‘kY\kf`ZYkfd‘kd_jkM?gkV^„k;gj‡`TV`_^YXkfd‘kd_jkM?gbaQWƒaEF`a‡^SU^ch_]kfd‹kf‚a][‘aF;‘^OU‚^H L^_‡hX[hik`^kc`‹b`‚^[Y^T)9\fgT[€gd6(8agg‡kY\‘k`^kaŒ^c‚fa]fUgkV^kNB#)Z‹kY\‘k`^kfd‘kd_jŽki==gkV^X3% QŒkY\‘k`^kfd‘kd_jŽkg'=gkVS;( kY\‘k`^kfd‘kd_jŽkg'=gkD:*"CkY\‘k`^kfd‘kd_jŽkN&=gkU1*$7ŽkY\‘k`^kfd‘kd_jŽk.&=gŽkX6+$4eŽkY\‘k`^kfd‘kd_jŽk.%‘kY\‘k`^kfd‘kd_jŒke:,#;852/,)#!!k`^kfd‰k]=ƒkd_j‹k6V;2)>u„Žš££žf[G=4,#9w’¡¥©®³·]YLHDB>;741/+)&# Cffk`^kfd‰kiKL‚kd_jŠk5`F;1(Cz‰“ «´œXPE8/%#^kiª¯´µ¶¿NI@=;840-*($"QTafk`^kfdŠkN!Ukd_jŠk3^D;1(IŽ˜§­µ¶SE<1) KUW]l½¹·Î?;52.,.'# :a`Y\‘k`^kfdŠkORkd_jŠk3^C:0'O„“£©¯´·³>4+"@FEKHHa™ÌÜ1.*'$"„h(![[ckkY\‘k`^kfdŠkd8)c€kd_j‰k4gPC:0' $U‘ž¥«±³µÌ²…% ;FC@@=8B`[Ø&"…‘wjebusnkkY\„kd‰^YXkfd‹kB+€kd_j‰k4fKB9/&$,hœ¢¦­´»·¿ßÛo 8=C?<:11B8$J s†›‹€}yp‡w^SU„^\‰ZWVkfd‹k^0€kd_j‰k4fKB9/&;85/'9& 432f{¡œ˜ˆ‰ °·¯QS„Za‰f]\kidg‰k g_43__]Z]‰_5XJA7..™œŸ£tpli\T% !6;:741.8$ +)0AEEXcЍ¨¦«´»½•cW^ƒfi‰kh^Žk`_‰` ^Z39ZZXW‰Z5RI@6,q‡‚~yYOEGY›¿ÂVgfhkkY`‘k^ŽkdeŒk2$+Xb_‚kŸ’k"]SI@6,!,ED@>7/<0 63062/,()0/.-//346@??hÁÏIRU]bkY`‘k^ŽkdeŒkL'!1X_‚k!ÒɈwlk]SI?5+! +DC?;72:5!!3102/+($*+.-.1;;'#)¿Œ:61-@>41.0/+($"%&&'))()/;5{(.5;AGMS`‘k^Žkdek.(!8]ƒk<±Ã ‹paTH>4*  *?@=950,;91.+-,(%!!"##$#$##"$(/32")/64* '9?<84/+75/,**($!#),/25("G758?Qfk^Žkdekg.("R‚k%l§ŒsdWJ>3) "6?;73.*01,))'$ &3443-0€31"%(+.0358'{>)-4;RiŽk^Žkdek+g2+%kklnqrl…¡z[RI=2( "6>;72-)&.)'&$  L€kgVb€k]*#%(+.1479AHŽB"")05X_k^‰kcidek*h>/)![kox‚‰vy˜¡rmhc]V !6;:61,($*'%# .@kgVbkQ&"&*/37;?BH]];$*/7`Œk^‰kL^deŽk']2,$%anwŒ– «¶·¿ÊÔàæ !52950+'"&$" ((ƒkgVb‚kF !%*.26:>G\Ÿ‚(#!$*;c‹k^ˆkhLFDCF^e€k€iˆk&^5/' 8mvŠ• ª°¶·ÇÒÞ× 5184/*&!# #^„kgVbƒk5 $(-27bi‰k^ˆkPI 5Cahii^\^j‡k%`;3*$5ht~‰”¡«¯µ´»ÏÎ: 5173.)% !&Q…kgVb„k%#$+3;EJN‘¨œrokk!# %Sjˆk^‡k jPF&1AFR\[`ˆk"U6.'"Gr}ˆ’Ÿª®´¹³Àa'4152-($4‡kgVb…kb"(098?Kd𱏭‚zmkkW!"bg‡k^‡kTL#&49HNVRNMO[d‡k"W91+& \{†‘Ÿ¨¬²º¸P+40-1,'#4‡kgVb…ka &-391ƒ´´¶·ÀØ‚k1"*g†k^‡kTL'"7;5.EKJIHPd‡k!X=5.)#u…”£¦«±¸LPB40-/+'""ˆkgVb…ka$*17€Í¬lŒÇÙ„kf0†k^‡kjPG'/.9-)*7BGFEGT]i…k!jP91,&t„’¢¥©¯¢J<2>13/,.*&!"ˆkgVb…ka$).4zÃhg…Œrm…kg†k^ˆkPI$4)*()(,05BADSf‡k S<50*#)‚‘¡¤¨®TE;0'3 53/,))%fˆkgVb…k jO"*,2. #9AQ^b‘k^ˆkhLB6('$!%0/1>AQf‡kU?84-& $Eœ £¦eOD;0% A952.+((#‰kgVb†k R(084('7GU`‘k^‰kdI>'!.,2L`j‡kgJ<71*#&=ž¡yYND;0/,?>72.*'&"‰kgVb†k Q%.7;(/?N_‘k^‰kTJ@( !*))3Jj‰kN@;4.'!'>›‚fYND;6303;71-*'%!W‰kgVb†k R"+4;>$&7EZ‘k^‰k 18I1 ,7'+7RŠkQC>81*$(‚šŒqdYM@<841.261,)&# f‰kgVb†k S #)29A@ _0=Uka[WˆkU.7L6$-<)&-?Vdˆk]GA;4.'*˜’|mdLF@<952.,01,)%"Ei‰kgVb†k T#)+25=NWˆ'5GYŽ]XVTˆkK9.4E-;846$2LaˆkbKE?82+,–†w_RGDA=:63/,*(+'$!Mˆ]5]ZNV…]W/%,25:@W‚˜ ->;740,(%!M„k H-H@C?6,-?a‚kN!$'25 …²®¤v0 1Zk^‡kTL42/EXcekk`[\^‹kb_hQJD=6.'  -A9=<851-)&"L…k J1#,+3+":BEkYD+%,+46š±…qL)Xk^‡k^ZLGATgde‘kb_hUNG@92+$/798962.*($ 5b„k!gN=-"86*"!L7!),118;1L|º¬kQ("DhŽk^‰kXUQdkde‘kb_j\RKD=6/(  853774/+)%!V…k fWTRYB<.+((&%)/1&9=B2Ij›ÜkYL*)cŽk^Škf`kkde‘kb_kfVOHA:3,$/742651,*&"V†k€“”³K954/01-22$BFL4Jak£kY`="bŽk^Žkde‘kb_kgYRKE>6/(! 362142.+'$ Ci…k¢¿®¨cD4()-<88* "NUhC%6Nd€kY`J$OŽk^Žkfeike_hi^VOIB<4,%(74€1/,(%!f…k‘§Œ{{W *E@=avw,8Rk^^iE&Žkbkdf‘k`akh`WOGB9/(U !6530-.,*&"f…k`W]$%9SkhYck'¡kdf‘kmltvug[OE95/-)'$!)‰kcVf)*Di„k-0ƒkhYc£k`L k`au—Ÿ©¬¯³·»ÀD<4.&2GC?;62/+'$!&‰kcV-&gi…k/gƒkhYc£kdcF!%#FŠk`zД𡩬¯³·»F>60($1/-+(2/,(%"8ˆkc6#[‡k0„khYc£k dfg<$! #!‡k oyˆ•œ§ª¬¯³·KC;4-/1.-+(('%# F‡kcQ]j‡k1„khYc£kdfkc^&$!&%Lƒk#oltƒŠ–¤§©¬¯²NF?84431/-*)(&$" !'^…kcVfˆk`„khYc£kdf€k Z(&$!"*(k#lgo~…‹‘™¥¦©«®RJC<875320-+*(&%#!(…kcVfkhYc£kdfk3[K)&$"*(Ekkaiy†Œ’££¦¨«VOH@;97542/-,+)'&#! -„kcVfkhYc£kdf‚k3eB+)&$" .,\ctz€‡¡¢£¥ZSLC=;98641//-+*(&$" =ƒkcVfkhYcŽkd__kdfƒk4`T2+)&$"-.Cu{‚ˆ• ¡£p_WMB?=;:86411/.,*(&$#! 8QkcVf…kh‡`^T[Ž`\ZZkdf…k3W0.+)'$" 8?rƒ‹žŸ sc[OCA?><;96331/-,*(&$#!)T€`ZP\…`^‡ZXQVŽZaeekdf†k8RK0.*'%#!@N^žvg]JCA@>=;:75431/.,*(&%#!-VZZUMW…Z]‡daV]Ždh‘k``‡a7]@1.+)'%# $2r›œyk_EDBA?>=;9776421/-+)(&$" 3dd]R_…df‡khYc£k\[ˆZ6S<310.+)'#!"*Qšš|oWIGFDBA@>;::975320.,+('%#!DKcVfkhYc£k``‰a5VN:641/-(&&#  (Aƒ™s0.;IGFDCA><=;976420/-+)'%#! $NVfkhYc£kdf‹k3S;9642-,+(&$!$,e˜‚L .+HGEC@>?=;986531/-+*(&$" )KfkhYc£kdfŒk2OI;96210-+(&$)c•… 1/7EB@A?=<:87532/.,*('$" %FfkhYc£kdfk1cG>;86530-+)(Q…m 30@CA?><;975420.-+)'$7BUfkhYc£kdfŽk0_ND=;:8531.,,r8" .04B@>=;986431/-+(;9742/,)&$! '22><:9868@]€kcVfkhYc£kdf‘k%`]NGEB@><9751.+)'$"  @>==;:M‚kcVfkhYc£kdf‘k$`adOKGFCA?<:631/,*'$"-# BAA@CHƒkcVfkhYc£kdf‘k"`akk[NKHFDA?<9752/-*0žg!GIHE…kcVfkhYc£kdf‘k`a€kWPMLIGEA?=:752/mãë2#!2NL_†kcVfkhYc£kdf‘k`akVSPNLIGDA?=:85uãä7)'$"576XˆkcVfkhYc£kdf‘k`a‚khVTROLJGEC@>ˆkcVfkhYc£kdf‘k`a…kj`\ZXUXZU¹Æ¿¹´¬B>9742BˆkcVfkhYc£kidhki_d…kih`]`eY[À¾·³¾¾A?<:8Fˆkj]Yikjb[f¢kjdg‘k_b‡khirf\`»·¸¹½½¡GEB@>K‰k_VhkdYe„kÚškjdg‘k_b†kmo}ufˆ¥¹¶··¸¸±yJHFCN‰k_VhkdYe„kñškjdg‘k_b…kmn…‚…“»´€³€´¦iNKIK_ˆk_VhkdYe„kíškjdg‘k_b…kooœ¨¤¨ƒ²ƒ¯°°wTQOM[ˆk_VhkdYe„kéškjdg‘k_b…kpª¹¸œƒª…¬˜eWUR^ˆk_VhkdYeƒk·ê§…kedkjdg‘k_b„k“«Òº~mœ¤§ªª©¨¨€©q]ZXaˆk_Vh…kh‡d_V`ƒdáêÈ…d`_kjdg‘k_b„kºßŸŽpm}™Ÿ¢¥§¦¥¦¥¥Že`^`ˆdZSb…db‡_[S[‚_J„wO„_dfkjdgŠkfƒe\^ƒeƒËµsigeƒ‘“”•˜¢¤‚£lfcaˆ_WP]…_b‡e_V`€e T;2<8Q€‡‡~G"‡…oZWSOLJFBE]ds‚ƒT‹‡gm‘‡ql‡wr‘‡xm…Œ‡!XA?=9R†‡Gvw^ZWSOLIDEyV]j|^X‹‡gm‘‡ql‡wr‘‡xm…‹‡#…XB@>:S~}†‡\!%)Va]ZWSTPICnpRXc_*")Q…‚„ƒƒ‚‚di‚‚ƒŠ‡ql‡wr‘‡xm…‹‡9gFCB@]YURS]‰rkc]VQM),c:H]\XUROLIEC?=:7/-/-+(‡ql‡wr‰‡vMƒ‡xm…‹‡6hIFDA>RsrjcS5+AD]YVRNMd‰l\VQLH).EFZ[ZVSPMJGDA>;86-+-\‡ql‡wr‰‡…_a‚‡xm…Ї5tPJGFC?Ppog^N/*^FNZVROKGe|QKH4!9GJZWWTRNKHEB?<964`e|‚‡ql‡wrЇc-l‡xm…Ї3rMKIGD@NlkdUM/ bNPZWSOKD=m~vhD1":LNYUQRNLIFC@=:[}}gm‘‡ql‡wrЇc'i‡xm…Ї3rOLJHE@Mih[RK0D^RVWTPJ=8=uyrY03=RQWSOPwfCA?>yy€‡‡gm‘‡ql‡wrЇ~G8~€‡xm…‰‡4€ZPNLIF?KdaWPJ0!€‡xm…‰‡4~TROMKF>Sd]UNI?()>@PYUG4<84@ukLRbY[WTPLG‰zh_\^`eS_p\`„pk‰h_]‡wr‹‡w>*€‡xm…‰‡4~USQOLNfecZSLGC94YWXYU81:62-AiHXc^ZVMB>9†x]YWPM3*+2X[„ht‰}kg‡‚sz‰‡ r?'Grrmdj‰r5bTRPNLrvk`WokgbH>ea]YE39951-)LRaa]D?88:5‚{dSS21" 'Zxcpƒ}ƒ‰‡€lއkj‰t oh;&'Ihha^‰h5WURPNtŠ„~wq}yuqNAfb^O;6@<84/+?P[OJ:55::5~{f>5#&>'S‡gs‘‡lއed‰h oyf2+2]ykf‰y5ZXVSQkkvŠ…~zurQDfcWD;8B>:51;CMOD@;=<<73UpS1#4HC;r‡‡gs‘‡lއmm‰y ‡‡;0/U‡tmˆ‡6q][YWQOLU^WVV{vsTHg_KA>AB>:61HGOJE@AB>:51-WD92Ab?My‡‡gs‘‡lއsuŒ‡=10Cttm‚‡X^‡6a^ZXVQNLI=455VROFJh\GC@HC?;6@KNOJHDE@<8/($OWTC?VJVlr‡gs‘‡lއsuŒ‡^321Ljm‚‡<(%bvyqa^YWURPMK?5752/,2DVVHEEHD?;7OMTPLKHC>92).9^igFHOUZ\\rgs‘‡lއsu‡4422^m‚‡?NKGKHD@;BSSTQNJFA;3,7IUhecQXVYVXXV^s‘‡lއsu‡6543Mlƒ‡<1'-MTV][XVTQOC5:9631.ACPMJLIEAFVYZXVSRB/<;8530EGSOMMJFA=T][UPLGB>FN[VQLGJPLVSQP`UOJJ`€‡lއsu‡‚87731q‚‡>„[,?JLLIRYYXVT?/>=:742?JUQQOJGBIekhbVYZWUj]YSNID?;6GLMXzVLFFGd„އlއsu‡+‚:9853@‡‡†„‚~O+QhaPQRZZXVA2A><:74*NWTSPKGHl€‡€dx€‡aWRMHD?;72@Fi}MGCCDCov‡l‰‡”Šsu‡*ƒI;:65?x‡ƒ|tooB-W][SNQ\ZWD5?A><96-RYWUQLWe‡€dx‡wYPMJGDB?=;>isNM>?@?>AyŒ‡l‰‡¹—tuއ't<<869I„}ule_P-><^[YF87C@>;81VZYVZXƒ‡€dx‚‡mOHFB?=:86L‚jfs@FC<;:I~‹‡lˆ‡Œ¹²¯¯¢wu€‡€Šˆ‡&u>>:8;:\…~vmf_RI%&=>>C`][I;9EC@=;8YXZ[„‡€dxƒ‡]EA:88520K}b\kZnaCB76P|„‰‡lˆ‡³¶~jk…u€Šœ œ‰‡‡%wE?<9=;11i†ˆ‡l‡‡‰³¯fgiw…z›  žž™ˆ‡‡"hA=;?>=hxoh]RMH>%2Zedb_N@?HGDB?<=j‡‡€dx…‡‚`YNG"&IB-$#'\y…‡‡r43.|‡‡l‡‡­¹fgyww¡—™™}wŸ”‡‡"iC?=@@?>oxpi^TNIF94AafdaPCBBIFDB?=U‡‡€dx…‡‚[XVTRk_5$""g‚‡E/4‚†‡l‡‡ ­¹fw“•£¨““€w|•”‡‡!jDA?BBA@=uqh\VPJFUYCFhfcSFEELIFDAHˆ‡€dx…‡VUQNLffS?2B' g„‡>†‡l‡‡‰³¯y«›“t”·ww{“¢žŠ…‡!…aBADDBB?vri^WQLPrplKMgeVIHGMKIFCJˆ‡€dx…‡€PQMIF`bM4C?2E€……‡‚†‡lˆ‡³¹½§~ih^ˆ¢©”wz‘¦‡‡ cDCFEDDA@sj`YRMvurplJU_NLKJJMKHFƒˆ‡€dx†‡ rKLEAD\WMZ]^p‘‡lˆ‡Œ¹¹¨{aRfoŒ££xŒ¥‡‡eFEHGFFC?Hkb[Tvzvurph>HPONMMPMJH‰‡€dx†‡rE€F:DXWY[]s‘‡l‰‡“¸­{PKR`{˜¥™˜¡—ˆ‡‡TGJIHGFADmd]r~{ywt?8LALRQOOROMK‰‡€dx†‡ q@ABC?>HOOVUh‘‡l‰‡ ‚š·yM79U}š•tަЇZKMLLKJForuƒ„‚tD:0/.0CMKV€UTR„‰‡€dx†‡ zY<089:=FxRPe‡wm_ˆ‡‰‡¹{PRKj¢œžjq©’ˆ‡lNONMMLHz†ˆ†NH3321005KNQXWXWn†‰‡€dx†‡ xXP-*12QsLJRhŽohd[ˆ‡–“v¦h™•‰§ ff€¹—ˆ‡tPQPOONJ„ŠŒdQ7665543329:OUZZYiˆo;okXe…om[RSH*bsvwEEGXŽds|g‡‡ž®†|™¹Ÿ·¶ŸŽ‡˜fj޹—‚‡x‚qf€P OONKŽƒcK96€544€321=UZ][bˆd8aR\…daJMMPM=focpBBEdŽz‚‡l‡‡­¹n{¤¶¹¹´ƒzwf~¬¹ˆ‚qh‚d ^WPPOONNh_JIC€6ƒ5€4C[^nyzWB‚z,&H]m…zKEJIWhCLUZm??Bj‡l‡‡­¹fm‚ˆ­¹„t†’•¤¹¡p‚dr‚xjfTTSSRRQMLKK<;;::9€79Nay‚‡ n`Zc‡‡e11:Wx„‡pM?FDZcJ0@Ylb;>S{‡l‡‡­¹nff€¯˜jy–´¹´ ‹ƒv‚‚‡tmlXWVVURN G@<:>>==<;€:?Nr„‡ _EegC6;@Ke‚‡s[T8B>U_D#)Sk~L:?q‡l‡‡­¹—…x©¦uu‡‡ž ›‹‡tmZYXXU€Q€P H;4>A@??=<€=@€?X„‡!_SSMZADEQHQr[BMPI*58MVZQ7C‡aB7WƒŽ‡l‰‡¦«±“‡su‘‡tm„i]\W€VUU€TH456@DDBACBBAAv…‡ }iS:H>5:>FPSA?IKT"-:JRW\N:‡g`B6}އlЇ™‡‡su‘‡tm‡}_]Y€XW VN<78:CGED€EDDv†‡t7!#N(8@DMF0?APa/?TLPUZc}T‡gsV2|އlއsu‘‡tm‡}`\€[€Z WWXWM=;DLT3/=C^cHa{[OU^y€‡gse7dއlއyt‚‡zn€}€^]\UVXTW[ZZF=>?CGHJIIƒ…‡dHkR]mJYb[*08O^U:TgbJRe‡ooƒ\0އt‡ry‘‡ooqUUVWVHMQWS]\\M@ABCDHKMM€Lƒ…‡}sp>MmSZU>QA@R]?#?€a``JGHIJKQ€RQ{‚‡9=BFKPU[^WC5\lcSZJ;(_KCP‚‡gy‡‡= ‡ryއ [ #)=Z_ff`O€>d€cbaIJKLMOTSЇzC$i^CNW__+!cJAN‚‡gy ‡ƒ…‡ryއ^!#(,`_>=<<>fedMMNOPPQT€VЇzB(v?IOY„~-dH?O‚‡gy ‡Lpny‡†iW %',/!"#?€f egg`OPQRSTVXYYЇz<&{~BJS{‡‡i(cG<ƒ‡gy¢‡+*y‡rbYYa``M" ""$>@BDFfgPQSTTUVWY[\Їzd~‡:Bb„‡D9ƒ‡gy¢‡#)-f‡ †…mo…t\OGDCA@?@ADFHLN>@CEVXYZ[\]e‰‡zd~D;Q…„‡C7ƒ‡gy£‡mX)-00‡ope_RNKHGDCnmllmQ=>ADFKMPSV]`f‰‡zdA5‚„…‡@‚ƒ‡gy£‡ruX€+77_Ї o`ngc]QNKIFEsr€qm,-.00JOQUX[^]Xˆ‡zE0s‡‡?„‡gy£‡ry‚J-39::‡‡ f`ojfbUQOLIGvtstt<,-.//3789;:<>>g‡‡z`t†‡‡=„‡gy£‡ry‡|v.€/001AAiƒ‡ „fdsnieZURNLJx€vL../011256789:<>FN|…‡zd~ˆ‡y„‡gy£‡ry€‡q00€1223?II‡ †jgwrmicYUROMz€xE.012323789:;<>@ABQ…‡zd~‡gy£‡ry‡q]223€4€5(JKe‡‡nk{vqlh]ZWSP|{{^502345459:;<=>@BCDFX„‡zd~‡gy£‡ry‚‡P44€5,66778CRRknzupkb^ZWk~}}W12356766:<=>?@CDEFHIeƒ‡zd~‡gyއzrr‡ryƒ‡4wh<66778899::;KNa~ysohc^Zy€mA34578988<>?@ABDFGHJKQdv‡zd~…‡‡tp^jŽtmii‡ry…‡l€89€:+;;<=:;EWSpsnhc_|ƒ‚e55679:;::<>?ABDEGHIKMNP\p€tk[n…tq‡ifXbŽiu||‡ry†‡d\::€91::;<:;=>@BCDFHIKLNPQS[hiibUd…in‡zu`oŽz‚‘‡jm‡vpJ98899:;+=>>=;=[mh‡w345678:;;??=>A€B'@>Osm„ŠY789:;<=?>>DEFHIJLMOPQSTUWYZrvzd~‡gy£‡jm‰v5g]D@ABBC@AEEFFEDLls‡Œ?A?=>?@ABAAFGHJKLMOPRSUVWY[\^cod~‡gy£‡ry‹‡3eBCCDDABFGGHIHE`zŠqHIGDEABCDCCHIJLMNOQRTUVXY[]^`aad~‡gy£‡ryŒ‡2]VEEFCDGHIJJKIe„ŽOJKLMNGIHFDDJKLMOPQRTUWXZ[]^`_`ac~‡gy£‡ry‡ |QFGEEIJKK€L$`‰‚PKLMNPQSPLKFKMNOPRSTUWXZ\]^^_\iic~‡gy£‡ryއ0t[MFGKKLMNNON^RMNOPRSTUVWNQQPQRTUVWYZ\]]\YZjƒzd~‡gy£‡ry‡eHHLMMNOPPQv\ROPQRSUVWXYZ\ZT€UWXYZ\^ZYT_f‡‡zd~‡gy£‡ry‘‡'UQNNOPPQRSSTPPRSTUWXYZ[\^_`a[Z[Z[\Y[OVb|€‡zd~‡gy£‡ry‘‡%okWPQQRSTTUVQRTUVWXY[\]^_abcdee\][MMUj‚‡zd~‡gy£‡ry‘‡$op{XUSSTUVWWRSUVWXY[\[]_abdefgg\[PNUmƒ‡zd~‡gy£‡ry‘‡"op‡‡kUUVWXXYTUWXYZ[\YGTabdeghieTFNd…‡zd~‡gy£‡ry‘‡op€‡bWWXYZ[VWYZ[\][K>cfghjOIJZˆ‡zd~‡gy£‡ry‘‡op‚‡ ‚[\]^YZ[]^``PI€>hiYDGJr‰‡zd~‡gy£‡ry‘‡opƒ‡ x]_`[\]^`aLI?€>gACEcnqyˆ‡zd~‡gy£‡ry‘‡opƒ‡…‚ka]^_`aMJY>>?'-?Rjorszˆ‡zd~‡gy£‡ry‘‡op…‡„i_`abPE`C?0%#(hlqstu{ˆ‡zd~‡gy£‡s}‡‚nw…‡‚WbcUB\eA7#";=Trstuv|ˆ‡…piƒ‡†wj¢‡„r{‘‡mt†‡W[W?Tge%$0=CDPtuvwx}‰‡sdƒ‡{g}„‡rš‡„r{‘‡mt†‡XS9HeA/*AEEFIavwxz~‰‡sdƒ‡{g}„‡qš‡„r{‘‡mt…‡mY8:C=/ ;@HHGGHOlyz|~„ˆ‡sdƒ‡{g}„‡wš‡„r{‘‡mt…‡c[&",&S=J€K JJKKi{|}~ƒˆ‡sdƒ‡{g}„‡}š‡„r{‘‡mt…‡d)6J`TƒNOOZv~~€„ˆ‡sdƒ‡{g}ƒ‡†„†‡zw‡„r{‘‡mt„‡ a4 ESpa]V‚Q€Rs€…ˆ‡sdƒ…‡‡xo`qƒx‰Š†…xom‡„r{‘‡mt„‡: :Iivtda_[XVUUVUUd€‚ƒ~ˆxi]u…xs‡mfZg‚mˆ¢¦ž„my|‡„r{Ї|ƒzgkƒzf'*\gnzf\[ZZWXYYZ€„…yˆmaWj…ms‡zpar€z ˆ¤´º²ˆ„{z…‡„lrŠzoƒm`cƒmYETc€m^VUUTTWR^_q…†ˆzj^v…z€‡‡{g}‡‡‰•¢§«®ª§¢šˆ•‡ƒeiŠmwƒxfj…xo€x tgcbaa`_N[cc€dzˆ‰ˆ‡‡sdƒ‡{g}‡‡Œ™™¢€¥ –˜•‡ƒjpŠw…ƒ‡mt‰‡ }ssrqponWV‚imŠ‹‰‡‡sdƒ‡{g}‡‡‘–Œ¢€¬œ‡–™•‡„r{‘‡mtˆ‡†~xwwvuts[Zponoor…Ї‡sdƒ‡{g}‡ˆ™’‘¬€³§Ž˜”‡„r{‘‡mtˆ‡ „~}}|zyyx^]u€tuu}Ї‡sdƒ‡{g}‡ˆ™’£‚¹—‚˜–…†’‡„r{‘‡mtˆ‡†„ƒ‚€~~}bazyxwy{€Ž‹‡‡sdƒ‡{g}‡ˆ™’§‚Àš‚˜–€‚…„„‡„r{‘‡mt‹‡†……„ƒ‚ff~}|{€‚Š‹‡‡sdƒ‡{g}‡Œ˜ª‚Æœ‚˜–€}|{‡„r{‘‡mt‡ †ji„ƒ‚€€‡Œ‡‡sdƒ‡{g}‡™•Œ´‚̨…“–ƒ}|{‡„r{‘‡mt‘‡kk€‡†…„ƒ‰Œˆ†‡sdƒ‡{g}‡™•¤„Ó‘™‘}|{‡„r{‘‡mt‘‡kk…‡І‡sdƒ‡{g}‡™•¦„Ú’™‘}|{‡„r{‘‡mt‘‡kk‡sdƒ‡{g}ˆ™”¦ÚßàÚÜàÝ“™‘}|{‡„r{‘‡mt‘‡kk‡sdƒ‡{g}—˜‡‡‰Ï冞䪃˜‘}|{‡„r{‘‡mt‘‡kk‡sdƒ‡{g}—˜‡‡§ÓٛŒ}|{Ϥ–‘Ï­’Ë϶¿Ïˆ£‘ÏŒ™‘ϜϤ–‘Ï­’Ë϶¿Ïˆ£‘ÏŒ™‘ϜϤ–‘Ï­’Ë϶¿Ïˆ£‘ÏŒ™‘ϜϤ–‘Ï­’Ë϶¿Ïˆ£‘ÏŒ™‘ϜϤ–‘Ï­’ËÏ—i¿Ïˆ£‘ÏŒ™Ï¶~wϤ–‘Ï­’ËÏ`¿Ïˆ£„ÏrÇɇrz—‘wqϤ–‘Ï­’ËÏ`¿£¡qƒƒ¡tpžŸ‡‘ls‘¨À“ˆÏ¤–‹Ïµ‚¡‹zž¡pP–’‘ix‚‘o0u‘—‡À„ÀÇϜϔ‹¤™‚‘€r‘€:Kˆ¹¼~•€¼µVBj³¼¾‡ÏŒ™‘ÏœÏw‹‘§‚¸œ†µ¸—-WªÎψ£€Ïo7]µŠÏŒ™‘ÏœÏœŠ‹¸Å‚Ï­’ËÏ©0_¿Ïˆ£Ït8K­‹ÏŒ™‘ϜϤ–‘Ï­’ËŽÏÊl0^¿Ïˆ££O98›ŒÏŒ™‘ϜϤ–‘Ï­’ËŽÏÆ5([¿‰Ï ²žOG?6/"nÎÏŒ™‘ϜϤ–‘Ï­’ˌϾT=4'[¿€Ï²ȂϽZQJA91)-¡ÊÍ϶²¿‹ÏŒ™‘ϜϤ–‘Ï­’ËŒÏ!uF<3&[¿ÏÏÉ~1“ÍÏÏźlSKC:2*"9š¼ÉÏ@1z‹ÏŒ™‘ϜϤ–‘Ï­’ËŒÏ!tE;2%[¿ÏÏ®A3ÆÏË’^UME<4+#/~Ž­ÆÀ‹ÏŒ™‘ϜϤ–‘Ï­’ËŒÏ!tD:1$Z¾Íϱ<¯½¤`XOF>6-#+¸s‚ž»O„‹ÏŒ™‘ϜϤ–‘Ï­’Ë‹Ï#ËqC90#Z¼¿ÍÏDbYQI@=5*!œ§iuŒƒ%iɂȄÇƇ’‚ÆÅɊϜϤ–‘Ï­’Ë‹Ï9LB9/"Y·¹­Ï‘'1XOMKB:Ž„zojcYQK@,)3Jgea_\ZWTRPMKIF77@><:97oŠÏœÏ¤–‘Ï­’Ë‹Ï6KA8.!W²³¤•j'#54I@7.3=}f_XQGA;&X3?VZVTQOLIGEB@><--6420ϜϤ–‰Ï´uƒÏ­’Ë‹Ï6J@7- U¬¬žŽ[''75C:1( =uYHA:4-#66FLNLIGDA?=:8642$$,‚ÅÅϜϤ–‰ÏË”‚Ï­’ËŠÏ5§ZI?6,R¦¦—|D""J435,"Md`4-(12=;AB><:7520.,*(Š»ÆÏœÏ¤–ŠÏ–>¤Ï­’ËŠÏ3¡QH>5*NŸŸO<F3//& 'KVPB ,-2.,510/,*'%#nººŒ™‘ϜϤ–ŠÏ—6žÏ­’ËŠÏ3¡PG=4)K™˜[C6 8,'   &GHB0(&'# *kW!¯®¿ÏÏŒ™‘ϜϤ–ŠÏÁiNÀ€Ï­’ˉÏ4¿jOF<3(HsgJ5.fm]N?RG=4##  XZK?E4?2‰¯ƒƒºÆ‰Ï¿ŽÏ††‰¥ œŒK)%XŒŒwp‰Œ5XNE<1e‚vj^SZOE;$  KQE(&#)uÆÏŒ¥‘ÏŽÏ{x‰Œ š²•=-3}²ƒ‰²[QH>4TNXpf]SI?5 ‚ .@3;4.žÏÏŒ¥‘ÏŽÏŒ‰² ½ÏÏN6/sÏ¡’ˆÏ•^TJ@0% 8894C9/  ƒ  )%M6C¦ÏÏŒ¥‘ÏŽÏšŒÏQ92P«¡’‚χÏg\LC9) -&  € $"=8I™ÏŒ¥‘ÏŽÏšŒÏ‰<5.^’‚Ï'?9—µ¤`UE;2(    ‚  "(/9DS^Œ¥‘ÏŽÏšÏ?81+x’‚Ï~2_LHXOE;2'   € $)27GRXv¥‘ÏŽÏšÏB;4.WƒÏ% HLGE;2'  €   %+;FMQ„»ÏŽÏšÏu>81"aƒÏp"'578>81' €  €‚  !F?A?H}ÀÏŽÏšÏÆA:4%Ÿ‚ÏÃX*AA<33/'  € "€A =+7ABB1ƒ  (k=53>JŠÊŽÏŽÏšÏ+ÆD=6(8ÏÏÎÊÇÅ®CIXL71*&  €Ïˆ°€Ï© Bo.)(2>E °Ï‰Ï›šÏ*É`@:+"6¯Ïʽ¯¥À†8&GB8," 4dψ°Ï‹ %N`3C&29@MµŒÏ‰Ïƒ˜ŽÏ'­C=/%(E¼Ë¿°¢“„O  !'ƒÏˆ°‚Ïq  /]U_®%66.5<^¾‹ÏˆÏ¸ƒ€ÏÅÂňÏ&®G@1(+$lÌÁ²¤•ƒQ3  ­„ψ°ƒÏG +TIPŸyŸy:A18l½Ê‰ÏˆÏ 1“¿ÂÂyhyƇϲSC4,.'dÅ´¦—mD7( "€   ‹…ψ°„Ï% +H;>‚h”ÊÏÏ<=3:œÎˆÏ‡ÏÅ  -HPgfghh†Ì‡Ï”F8/1+#†Á¶¨˜oE7+ "%€ A‡Ïˆ°…ϵ & 3‚¹ÌÏϨ4:9½Æ‡Ï‡Ï4 &H^J`ijhœ‡Ï–J;34.'›·©™nE8+!"€ J‡Ïˆ°…ϵ€ ! #$'Ÿ‚Ï^=KÈ†Ï‡Ï 4 _j€kiœ‡Ï—M?571)"¯«‹UF9,"*2€ ˆÏˆ°…ϵ :!Ÿ„ÏÅ[†Ï‡ÏÅGlmmklnÃ…ÏÌ‚B9;4-%±¬ŒUG9-*F;.€ ˆÏˆ°…ϵ*4+TÄÌ„ÏÎȆψÏ no]QªÎ†ÏˆF<>70) !®ŽVG9-OF:0$  Ĉψ°…Ï Í†  #9J^ ‘ψϸq^O¯‡ÏŠH@A:3,$0mWG:VQD:0% ‰Ïˆ°†Ï  $3C[¥‘ωϞ‚  <ɇÏÂdCE>7/'!iXHW[OD:0‚‰Ïˆ°†Ï   +†Ž´ÅχÏ4   p‚£‚ˆudPJC<5.N8  Q¨´zP‚´!B{›…´. *7Lš%8“χÏ4 /- &u‚ˆ£‚°Ž‚XQKC<6- ‚Ï ¦‰z„ÏÏŠY°„Ïb)`x2f¸Ï‡Ï4 Fw‹€8hŠƒ¬Ä‚Ï¡’YQJC<, !  ‚„Ï „E€x; Yµ‚σ  jŸÁI+A¤Ï‡Ï4N“ÏÏ—jh{‹Ï¡’Á]UNG9*#  ‚…Ï |?.! NhnÏ¢i 8¤Çw$:¢Ï‡Ïx[9²š‘Ï¡’Á`XQE6.'!    J¸„Ï!ÂvH. tC  -9, bÏ€?4vǎωÏN:"žÏš‘Ï¡’Ç€\UB92+%€  ›…Ï ¹„J    /E?F"ÏŒ~J<½ŽÏŠÏ¯†ÏÏš‘Ï¡’Ï´`UE=60)!  œ†Ï²C+   "5 5Lg³ÏŒ¥t6¼ŽÏŽÏš‘Ï¡’ϵaRGA:3,%€  kË…Ï‚%;+H" !3-'  €  Â…Ïšn BBKƒÏÂŒ²¢Ï?3¨Ï…J33C@>7  „ŠÏ´ˆ½Ï-?„„ÏM@ƒÏÂŒ²¢ÏÆ*3<—ÏÊǓƧ{K4,%" ˆ%‰Ï´ˆ½J;hÊ„ÏOCƒÏÂŒ²£Ït6::8Ï–šÁ‘†S@7/( C;0*#ˆ‰Ï´ˆK:ÆÉ…ÏRƃÏÂŒ²£Ï–¢ƒ852DBˆŠÏ–€¤–I=4,%IA6/(  „RˆÏ´T7¬‡ÏT„ÏÂŒ²£Ï –¨Çl:7429A><‡Ï ‰‚¦ž•ŒPC:2*$ME:4,   t‡Ï´¬Í‡ÏV„ÏÂŒ²£Ï–¨Ï¾³<9641-+HF“ƒÏ#ÊŠ‡®¥œ“WI@7/(PF?8" '¯…Ï´ˆ½ˆÏ¸„ÏÂŒ²£Ï–¨€Ï «=;8520-*?PNÏ#ÎŒ´¬£š…OF=4-TJC<" *…Ï´ˆ½ÏÂŒ²£Ï–¨Ï3«‰=:752/,)&OM…ÏÏ”‘¼³ª¡™WMC:2XNH3  4„Ï´ˆ½ÏÂŒ²£Ï–¨‚Ï3Áq?<9741.+(%;XVŽ—Ãº±¨ ^SJ@P\RL2  [ƒÏ´ˆ½ÏÂŒ²ŽÏ³¢¢Ï–¨ƒÏ4µšK><8630-*'$"CGvÁ¸¯§„ZPF``VD%  NŽÏ´ˆ½…χ¦w‘ަ–Ï–¨…Ï3¡CA>;8530-*'.[U£®žbWLdd[A 'Š€¦“s™…¦ž‡†j~ާ¶¶Ï–¨†Ï8“ƒC@841/,)&NWUh]ShgX*  +ƒg„…™‡²¨}›Ž²Ä‘σ‡ª7Ÿ\>842/-*'! HcZlkV  7²²z¤…²»‡ÏÂŒ²£Ïu}ˆ6~N??<9741)')&# 2k`qo? h{´ˆ½ÏÂŒ²£Ïƒ‰ª5‘‚PFDA>;0.30-*&!-^gus"!  |ˆ½ÏÂŒ²£Ï–¨‹Ï3KIFCA53852/,($LpzO !  n½ÏÂŒ²£Ï–¨ŒÏ2qKHE;9=:741.*S|  b½ÏÂŒ²£Ï–¨Ï1¹eMJ?=B?<9740Lo%   @\‡½ÏÂŒ²£Ï–¨ŽÏ0©xYECGEB?<963s@+  DÉ´ˆ½ÏÂŒ²£Ï–¨Ï.‰LHMJGDA>;9gA,  +:ÏÏ´ˆ½ÏÂŒ²£Ï–¨‘Ï'e[ROLJGDA>;9(%#!  <¡€Ï´ˆ½ÏÂŒ²£Ï–¨‘Ï%–gTROLIFDA>-+(&$! c‚Ï´ˆ½ÏÂŒ²£Ï–¨‘Ï$–šµg\TQNLIFC30.,)'%"  (IƒÏ´ˆ½ÏÂŒ²£Ï–¨‘Ï"–šÏÏ\WTQNKI8531/-+(# !-…Ï´ˆ½ÏÂŒ²£Ï–¨‘Ï–š€Ïx\YVSQN=;96420,!" †Ï´ˆ½ÏÂŒ²£Ï–¨‘Ï–šÏh^[YVSCA><:74,'(&#!OˆÏ´ˆ½ÏÂŒ²£Ï–¨‘Ï–š‚ÏÁa^\YHFDB@=:. .,"6‰Ï´ˆ½ÏÂŒ²£Ï–¨‘Ï–š‚ÏΩda^NKIGEC2( 2&)(aˆÏ´ˆ½ÏÂŒ²£Ï–¨‘Ï–šƒÏË‚dSQOLJ846  '21/,^ˆÏ´ˆ½ÏÂŒ²£Ï–¨‘Ï–š…ÏÆyXVUR@5G ::;952bˆÏ´ˆ½ÏÂŒ²£Ï¿™µÏÄ“ª…Ï¿C\ZJ8KN (DA>;8fˆÏÊ¡’ÈÏÍ®“¾¢ÏÈ–¯‘Ï’¡†Ï?IS:KXS(JGDA>j‰Ï§ˆÆÏ¶Œº„Ï1šÏÈ–¯‘Ï’¡†ÏA<-?]:##%$"$;MJGDn‰Ï§ˆÆÏ¶Œº„Ï šÏÈ–¯‘Ï’¡…ÏB'(27,').,)('+FPLJT ˆÏ§ˆÆÏ¶Œº„Ï šÏÈ–¯‘Ï’¡…Ï^CP/98640/-+FVRPMˆÏ§ˆÆÏ¶Œº„Ï šÏÈ–¯‘Ï’¡…Ï_$1naB@=;7542:TYUSˆÏ§ˆÆÏ¶ŒºƒÏVn…ϰ¨ÏÈ–¯‘Ï’¡„Ï•308w„`HEC?=<96V_[X“ˆÏ§ˆÆ…ÏÀ‡¬™zœƒ¬ +…¬–‘ÏÈ–¯‘Ï’¡„ÏX ,5b’£‰‚n[KGDC@>Jba^„ˆ¬Žw¥…¬ ‡‘ƒl…‚‘Tc„‘­´ÏÈ–¯ŠÏ´ƒ°‚Žƒ°•9>l…°}{zxgTOLJHECggdzˆ‘{iŒ…‘Ÿ‡°| €°‰I€ IIª°ÉÏƇ™Š°•ƒ‘rzƒ‘v[ CFBundleInfoDictionaryVersion 6.0 CFBundleIdentifier org.mute.filesharing CFBundleDevelopmentRegion English CFBundleExecutable game CFBundleIconFile game1.icns CFBundleName game1 CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 0.1 CFBundleShortVersionString demo-1 CFBundleGetInfoString game1, version demo-1 CFBundleLongVersionString demo-1 NSHumanReadableCopyright Copyleft 2004 LSRequiresCarbon CSResourcesFileMapped transcend-0.3+dfsg2.orig/Transcend/build/macOSX/game1.app/Contents/Resources/0000750000175000017500000000000010305077131025413 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/game1.app/Contents/Resources/game1.icns0000640000175000017500000004142610066175660027305 0ustar pabspabsicnsCit32ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt8mk@%# !q·äñýÿÿöîç¸v%K˜ÉìôýûñíÖ­dM½üÿÿÿÿÿÿÿÿÿÿÿÿýÕn%B(Ÿòÿÿÿÿÿÿÿÿÿÿÿÿú¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêk7¡ržÿÿè$¨ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ'ØÿÿØ1ûÿÿÿ£nöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöààïÿÿÿÿÿÿÿÿÿÿðPíÿÿÿöe6ÿÿÿÿÓ ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý½h Fáÿÿÿÿÿÿÿürîÿÿÿÿþ›5ÿÿÿÿØ×ÿÿÿÿÿÿÿÿæ¦dJ$'R{¶÷ÿÿÿÿý¬"HÌÿÿÿÿÿÿþjíÿÿÿÿÿÿÊ)þÿÿÿöÛÿÿÿÿÿÿþÄCaÕÿåBhóÿÿÿÿÿüLíÿÿÿÿÿÿÿè4úÿÿÿÿ ÓÿÿÿÿÿÿäNf'ÖÿÿÿÿÿðíÿÿÿÿÿÿÿÿöVøÿÿÿÿªÿÿÿÿÿÿ¹ÐÿÿÿÿÿÀíÿÿÿÿÿÿÿÿÿüyøÿÿÿÿ)Xÿÿÿÿÿÿ•ãÿÿÿÿÿWíÿÿÿÿÿÿÿÿÿÿÿ•÷ÿÿÿÿFéÿÿÿÿÿ *q¤¢p,úÿÿÿÿÛíÿÿÿÿÿÿÿÿÿÿÿÿ¹ ôÿÿÿÿHzÿÿÿÿÿÀkØÿÿÿÿÿÿÿü¶"?‹º»»«|GU}_‚ÿÿÿÿÿVïÿÿÿÿ÷³øÿÿÿÿÿÿÈ íÿÿÿÿKéÿÿÿÿò§ÿÿÿÿÿÿÿÿÿÿÿñD]Þÿÿÿÿÿÿÿÿÿÿÿ¥ åÿÿÿÿ½öÿÿÿÿŽ+ÚÿÿÿÿÿÿÓáÿÿÿÿdTÿÿÿÿÿr1þÿÿÿÿÿÿÿÿÿÿÿÿõ* ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿyÿÿÿÿ÷ûÿÿÿÿZ Ãÿÿÿÿÿÿà!àÿÿÿÿi«ÿÿÿÿäoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅ ËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEøÿÿÿÿW#ÿÿÿÿÿN¡ÿÿÿÿÿÿä!àÿÿÿÿjêÿÿÿÿ{fÿÿÿÿü«vy·úÿÿÿÿý¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFÄÿÿÿÿ–$ÿÿÿÿÿ#þÿÿÿÿÿè%àÿÿÿÿj$ÿÿÿÿý'ýÿÿÿp5øÿÿÿÿ`ÿÿÿÿÿäy/xïÿÿÿÿÿ)„ÿÿÿÿÉ%ÿÿÿÿÿhýÿÿÿÿÿé'äÿÿÿÿi\ÿÿÿÿÚ“ÿÿ³³ÿÿÿÿàÿÿÿÿ¬ 'îÿÿÿúdÿÿÿÿÖ7ÿÿÿÿþaýÿÿÿÿÿï=òÿÿÿÿOuÿÿÿÿ©,5×ÿÿÿÿÿÿÿÿ×Oüÿÿ¡8ÿÿÿÿïKÿÿÿÿô cüÿÿÿÿÿùOöÿÿÿÿH¡ÿÿÿÿ‘°ÿÿÿÿÿÿÿÿÿU?¤~ 7ÿÿÿÿþ^ÿÿÿÿëcüÿÿÿÿÿúm÷ÿÿÿÿG©ÿÿÿÿp“ôÿÿÿÿÿèÿÿÿûNÿÿÿÿñNÿÿÿÿï ^ûÿÿÿÿÿþŽøÿÿÿÿC©ÿÿÿÿ•BöÿÿÿÿÿÿýUÿÿÿû|ÿÿÿÿÔ9ÿÿÿÿø Nøÿÿÿÿÿÿ·øÿÿÿÿ/ÿÿÿÿ­Íÿÿÿÿÿÿö]ÿÿÿÿi ²ÿÿÿÿ·%ÿÿÿÿÿEòÿÿÿÿÿÿÓúÿÿÿÿpÿÿÿÿÝôÿÿÿÿþ¿(ÿÿÿÿôk eÓùðnèÿÿÿÿ$ÿÿÿÿÿ6ñÿÿÿÿÿÿíDüÿÿÿÿRÿÿÿÿýÎÿÿÿõ^ÿÿÿÿÿÿôËÄÁÊØûÿÿÿÿþ,EÿÿÿÿÿHûÿÿÿÿ6/éÿÿÿÿÿÿûx$ýÿÿÿÿúÿÿÿÿyRüÿô;¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV½ÿÿÿÿô ñÿÿÿÿXÕÿÿÿÿÿÿÿ¯,þÿÿÿÿ×ÿÿÿÿà;~äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ8ýÿÿÿÿ¶íÿÿÿÿg¶ÿÿÿÿÿÿÿÝ7,þÿÿÿÿ„ÿÿÿÿÿ_2WÉÿÿÿÿÿÿÿÿÿÿÿÿÿç=ÊÿÿÿÿÿMìÿÿÿÿŒˆþÿÿÿÿÿÿø|2ÿÿÿÿÿüÿÿÿÿè žýÿô\^ÎýÿÿÿÿÿÿÿüÐu zÿÿÿÿÿ×çÿÿÿÿšHôÿÿÿÿÿÿÿÇ5eÿÿÿÿÿ©ÿÿÿÿÿ§Wÿÿÿÿõ Be€‚tP;CüÿÿÿÿÿTÛÿÿÿÿÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿþf‹ÿÿÿÿÿ;<ôÿÿÿÿÿÊÓÿÿÿÿ«„ùÿÿÿÿÿÿÿÿÿÿÿÿ’ÿÿÿÿÿûOnÿÿÿÿý$Eôÿÿÿÿÿù(Óÿÿÿÿ¸7Üÿÿÿÿÿÿÿÿÿÿÿ Ýÿÿÿÿÿ÷RÁÿÿÿŒ{ýÿÿÿÿÿÿjÓÿÿÿÿ¸ šýÿÿÿÿÿÿÿÿÿ.÷ÿÿÿÿÿüz>X2ÚFBÐÿÿÿÿÿÿÿ›Óÿÿÿÿ¸?åÿÿÿÿÿÿÿÿ UüÿÿÿÿÿÿÊ6WÿÿÍmVÇþÿÿÿÿÿÿÿ³Óÿÿÿÿ¬ ¯ÿÿÿÿÿÿÿ"týÿÿÿÿÿÿü¶G ZÑÿÿÿÿÿûܺ‹‚ƒªÍöÿÿÿÿÿÿÿÿÿ¡Ôÿÿÿÿmùÿÿÿÿÿhûÿÿÿÿÿÿÿÿﺆxaz‰ÀôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøpÍÿÿÿÿš5èÿÿÿÿKíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐ/¥ÿÿÿÿƒÃÿÿó¯ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäi5úÿÿÿFnµZCÉþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ S¹òÿÿÿÿÿÿÿÿÿÿûÀ] püÿÕ7žèÿÿÿÿÿÿÿÿÿÿÿâ„>z²õÿÿÿí [E +q§öÿÿÿð¥r,6ÿÿý.nÿÿÿ$Hÿÿÿ]®ÿÿÿ[ºÿÿÿÚ6ýÿÿÿÕ,üÿÿÿÿeÉÿÿÿÿÿV¢ÿÿÿÿÿÝ_ÿÿÿÿÿÿÙ÷ÿÿÿÿÿÿZèÿÿÿÿÿÿÿ\‹ÿÿÿÿÿÿÿÌ•ÿÿÿÿÿÿÿÿÚîÿÿÿÿÿÿÿÿ1+úÿÿÿÿÿÿÿÿÿ`iÿÿÿÿÿÿÿÿÿ¥°ÿÿÿÿÿÿÿÿÿÿÚ¨ÿÿÿÿÿÿÿÿÿÈñÿÿÿÿÿÿøâÿÿÿ2ÇÑÐÿÿÿÿÿþzœÿøýÿÿÿŒ ºÿå»ÿÿû]œÿÿÿN$ tÿÿàŠÿÿÿMÿÿàÿÿñ 7ÿÿìÈÿÿÙ+þÿó òÿÿ¦øÿÿ#(ýÿÿoëÿÿVgÿÿÿ/Çÿÿ fÀöÿÿÿÿÿÿÿÿü×u J©ªªŸsWtranscend-0.3+dfsg2.orig/Transcend/build/macOSX/game1.app/Contents/Resources/game1.rsrc0000640000175000017500000000552710066175660027324 0ustar pabspabs §§°;)This application needs at least a MacPlus"This application needs more memory!This application is out of memory*This application needs at least System 8.6 About this wxWindows Application}This application needs Appearance extension (built in with System 8) - this restriction will be relieved in the final release"ÿÿÿýAboutÉ-Dà01Æ#b""#b1Æ0ààðøü?þ?þ?þ?þ?þüøðàDDðˆp @@€€ðøøððàÀÀ€€D!@€@€@€@€!€Ààp8?€ÿÀÿÀÿÀÿÀÿÀÀ?àðø|>Dàð88< ngc†aÆ`æpv0<øà@ð?ø<<~ÿo†çÇcæá÷pþp~<<üð@D€€€€€€àððððPPPÀÀÀÀÀÀàð?ø?ø?ø?ø?ø?ø?ø?ø DÀ@@À1€Gþ€€þ€@À@ÀÀ€?€ÀÀÀÀ?€þÿÿÿÿÿþÿÀÿÀÿÀÿÀÿ€?€D€`Œ €€Àø€àðüÿÿÿÿÿÿÿÿÿÿÿÿøD@x`üqÎy†|~¸|0l0F00ÀxàüñþûÿÿÏÿÿÿ¾ÿüþxÿxïøÏø‡øøD>~þþ>6b`ÀÀ?ÿÿÿÿ÷óáàÀD€Àà€€€üü€€€àÀ€€ÀàðøÀ?þ?þ?þ?þÀøðàÀ€D€Àà€ˆŒ?þŒˆ€àÀ€€Ààð èÜ?þÿ?þÜ èðàÀ€D Ààp8?—ãá!ð1ø:|<<>??€ Dxpp`HàÀ€€H88xþüøpððéðÇà‡À„Œ>\<<8|üüD @øô *¨ð€8`€>|pàü?ööþüø?ðÀøà AÀ¢ð¥üKÿ×ÿÀñÿÿðàÿü€ÿÿÿ€ÿÿÿ€?ÿþ€ÿÿþ€?ÿúÀÿÿöÿÿî_ÿÿÞÿÿÿ¾Ïÿÿóÿþÿäÿýþá?ûüàO÷øàïðxßà?À€?€à?x><¸ð`AÀãðçüÏÿßÿÀÿÿÿðÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüÿÿÿøÿÿÿðÿÿàÿÿÀÿÿ€ÿÿþüøð`@ ÀðëüŽÿ¯þ—þßþïú¿ç‹æÅÜ0Ø0`À Àðÿüÿÿÿÿÿÿÿÿÿÿÿÿÿþÿü?øðàÀ€ì oÀÞ½ÖfoÀÞ»îeffoÀâ;»fffnì,¥ffff`á¦ffffн½æfffmÀÿöffff\Àìßfffm ÿü ßffPßü]À ßm ÿÀ uÀ Ðßü mÀ ÿÀÎeÏüÝoÀõ÷ëñ+õøˆ‚VÒÒÒìñøùû^^ý‚ï°ìÒÒÒìñ÷õ­]33^‡^‚ÓÒÒÒÒÒëý‚33¬ÕÒëÒÒÒÒÒÒÒõˆ33_ïÒÒÒÒÒÒÒÒ€û^]]ÕÒÒÒÒÒÒÒÒ€öýÿÖìÒÒÒÒÒÒÒÒ€++õþ÷ôïëÒÒÒÒÒ€öùÿýþö+WþðëÒÒ€öVÿÿ÷~ªö÷Wóì€+Vÿÿ÷1͆ö÷ú+Vÿÿ÷õzë€öVÿÿ÷*«ì€+ÿÿ÷[«ðý÷š STR#RMENU^MBARjcarbvldes‚CURSšICN#Nics#Zics4fics8r€ÿÿ?ÿÿeÿÿm€ÿÿsÿÿ‰ ÿÿŸ ÿÿç ÿÿ/ ÿÿwÿÿ¿ÿÿÿÿOÿÿ—ÿÿßÿÿ'ÿÿoÿÿ·ÿÿÿÿÿGÿÿ€ÿÿ×€ÿÿÛ€ÿÿ€ÿÿ£Simple Alert Messagestranscend-0.3+dfsg2.orig/Transcend/build/macOSX/game1.app/Contents/MacOS/0000750000175000017500000000000010305077131024403 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/build/macOSX/game1.app/Contents/MacOS/dummy.txt0000640000175000017500000000012510066175660026310 0ustar pabspabsA placeholder file so that "cvs export" will include this otherwise empty directory.transcend-0.3+dfsg2.orig/Transcend/doc/0000750000175000017500000000000010305077141016410 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/changeLog.txt0000640000175000017500000000460010305076521021042 0ustar pabspabsVersion 0.3 2005-August-30 --Fixed bugs that caused post-explosion fade factors to become negative, which in turn caused boss explosions to linger on some platforms. --Fixed zoom behavior when strafing. --Added fade-in when enemies are created to avoid a visual pop-in. --Added smooth turning when enemies switch targets. --Changed so that pieces can be picked up while they are moving as a result of enemy fire. This makes pick up less confusing and frustrating. --Made bullet fade-out smoothly at end of range to avoid visual pop. --Doubled piece pick-up radius to make picking up pieces easier. --Fixed jerky piece pick-up and drop. --Added mini-explosion graphics to indicate when damage is being done to boss. These graphics also change color to indicate the boss' health. Version 0.1 2004-September-1 --Initial release Version 0.1.1-IGF-1 2004-September-3 (Final submission for first round of IGF 2005) --Added fades to beginning and end of each sound effect to reduce clicks. --Changed enemy logic so that they back off after they knock your ship back to the center. --Made grid coloring in relation to boss position smoother and more detailed. --Added grid coloring in relation to ship position. --Improved logic that determines whether a piece is in or out of the collage. Version 0.1.2 2004-September-26 --Added a missing dependency to the GNU/Linux X86 makefile. --Shortened Mac download file name to make Internet Explorer happy. Version 0.2-IGF-2 2004-October-15 --Added strafe keys ('S' and 'F'). --Switched grab/drop key to 'D' to improve key layout (in between new strafe keys). --Parameterized grid size so that it can vary from level to level. --Added a bullet limit that makes space-bar mashing ineffective. --Made starting bullet shape much smaller in all levels to make its weakness more obvious. --Added a portal object that appears after the boss is dead. If you delay passing through the portal, you can explore the music Elements before moving on to the next level without being bothered by enemies. --Added a zoom-in effect at the start of each level to give a quick overview of the level layout. --Fixed a bug that resulted in keys getting "stuck" down. --Fixed a bugs in the frame timing code. This fix results in a smoother framerate and fixes a bug that could cause enemies to move far off-screen. --Added a new level. transcend-0.3+dfsg2.orig/Transcend/doc/moondance/0000750000175000017500000000000010305077135020356 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/moondance/esrb.doc0000640000175000017500000037100010304351001021764 0ustar pabspabsÐÏࡱá>þÿ òôþÿÿÿìóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áq` ø¿È¿bjbjqPqP 4ø::n5%ÿÿÿÿÿÿ¤°°°°°°°ÄLoLoLo8„oLÐoüÄOŠ2Øq°ˆt"ªtªtªt‰u‘w4Åxœºˆ¼ˆ¼ˆ¼ˆ¼ˆ¼ˆ¼ˆ$‹hé àˆ)°ay…u‰uayayàˆ°°ªtªtÛ Šã|ã|ã|ayp°ªt°ªtºˆã|ayºˆã|ã|ʰ°€ªtÌq  ˆù0˜ÅLoÑyâ^€\Š0OŠê,‰ŽÑzº‰Ž€‰Ž°€Hayayã|ayayayayayàˆàˆ‹|XayayayOŠayayayayÄÄÄDAFD)ÄÄÄFÄÄݰ°°°°ÿÿÿÿ ESRB Qualitative Information Instructions on Completing Sections 4-11: In answering the following qualitative content questions, please include a full description of all pertinent content. Remember that pertinent content can be found in any material included with the product (including bonus material, hidden software, easter eggs, cheat codes, advertisements and product placements, etc.) In all cases, the most extreme instance of any pertinent content must be specifically detailed. All pertinent content described in this Submission Form must also be incorporated in the Videotape Submission. When asked to describe specific instances of pertinent content, please describe: (1) the sights and sounds of specific content, including but not limited to sound effects, changes in soundtrack, camera work, graphical embellishments, slow motion, or cut scenes; and (2) the context in which the specific content occurs, meaning what else is going on in the game at that time. Please give clear, thorough, and specific descriptions of all requested content. Do not provide promotional or marketing language; this language is not relevant for rating purposes. For example: Correct: "There are 11 levels to play as the hero Omega battles and defeats the Evil Forces controlling him. Based on the famous comic character, the hero uses hand-to-hand combat as well as an arsenal of weaponry and superpowers to complete each mission in the game. This game features several different styles of game play, including: Action- revolves around the hero fighting enemies in fierce battles. Puzzle Solving- requires the player to figure out mission puzzles, such as unlocking hidden doors and getting into a security system allowing the hero safe passage. Stealth- allows the player to avoid involving the hero in outright confrontation, and instead use a variety of stealth moves to knock out and evade enemy soldiers. Some missions will be impossible to complete without using the stealth skills."Incorrect: "Product is a thrilling first-person shooter experience that has to be SEEN to be believed. Nowhere else will you find such AWESOME gameplay and tension!"  Please note that examples given below are only some of the potential sources of pertinent content. If the product has content that seems to match the question being asked, or that is similar to the examples given, be sure to include it even if it is not listed as an example. 4. OVERALL GAME CONTEXT Please provide a description of the theme, setting, plot, and objective(s) of the game. Provide any information that will be helpful in describing the overall context:  FORMTEXT       5. VIOLENCE Violence Depicted: Includes depictions involving injury, damage, death, or destruction of the following: Please place an  X in the correct space. FrequentlyOccasionallyNeverHumansAnimalsCreatures/RobotsVehiclesObjects Describe specific instances of the above forms of violence, and the context in which they occur:  FORMTEXT       To what extent can the player control the depictions of violence?  FORMTEXT       Describe how violent acts are rewarded in the game, i.e., is the player rewarded for successfully completing, avoiding, or preventing acts of violence, aggression, injury, or death? Include specific instances of such rewards, and the context in which they occur:  FORMTEXT       Violent Sound Effects: Includes all sounds associated with violence, injury, pain, or death. Please place an  X in the correct space. FrequentlyOccasionallyNeverViolent sound effects heard or depicted Describe specific instances of these sound effects, and the context in which they occur:  FORMTEXT       Non-adversaries: Includes characters who are non-threatening or non-aggressive to the player, such as bystanders, pedestrians, and teammates. Please place an “X” in the correct space. FrequentlyOccasionallyNeverPlayer can injure, damage, kill, or destroy non-adversaries Describe specific instances of violence against non-adversaries, and the context in which they occur:  FORMTEXT       What consequences, penalties, or rewards exist for committing violence against non-adversaries?  FORMTEXT       Blood Effects: Includes all blood-like fluids, regardless of color or how they are depicted, such as puffs, splatters, pooling, or bloodstains. Please place an “X” in the correct space. FrequentlyOccasionallyNeverBlood effects depicted Describe specific instances of blood effects, the context in which they occur, and how long they stay visible:  FORMTEXT       Gore: Includes the mutilation of any organism (e.g., humans, animals, aliens, or other creatures) by dismemberment, decapitation, burning, or other means, and the depiction of burnt body parts, non-identifiable body parts or "gibs", guts, or severed limbs. Please place an  X in the correct space. FrequentlyOccasionallyNeverGore depicted Describe specific instances of gore, the context in which they occur, and how long they stay visible:  FORMTEXT       Post-Mortem Damage: Includes any damage that can affect the body of a dead organism (including humans, animals, aliens, or other creatures) by shooting, burning, or other means, utilizing blood effects, gore effects, rag doll physics, etc. Please place an “X” in the correct space. FrequentlyOccasionallyNeverPost-mortem damage depicted Describe specific instances of post-mortem damage, and the context in which they occur:  FORMTEXT       Other: Please indicate whether the following themes, information, or actions are present. Please place an  X in the correct space. FrequentlyOccasionallyNeverCrude or vulgar actsActs of discrimination or racismSexual violence, rape, or attempted rapeSuicide Torture Describe specific instances of all other pertinent content indicated above, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of the other pertinent content indicated above?  FORMTEXT       Please provide any other information that will be helpful in evaluating the overall level of violence in the product:  FORMTEXT       6. SEXUALITY Suggestive Material: Includes anything that could be considered sexually suggestive, including attire, dialogue, sounds, behavior, references, or exaggerated body parts. Please place an “X” in the correct space. FrequentlyOccasionallyNeverSexually suggestive material depicted Describe specific instances of sexually suggestive material, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of sexually suggestive behavior?  FORMTEXT       Sexual Activity: Includes depiction of or reference to any sexual act, including all forms of sexual intercourse, whether explicit or implicit. Please place an “X” in the correct space. FrequentlyOccasionallyNeverSexual activity depicted Describe specific instances of sexual activity (indicating precisely what is seen, heard, or said) and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of sexual activity?  FORMTEXT       Nudity: Includes any full or partially obscured view of male or female genitalia, buttocks, breasts and nipples. Please place an  X in the correct space. FrequentlyOccasionallyNeverNudity depicted Describe specific instances of nudity, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of nudity?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of sexuality in the product.  FORMTEXT       7. CONTROLLED SUBSTANCES Tobacco: Includes any use of or reference to any tobacco product, including smoking and chewing tobacco. Please place an “X” in the correct space. FrequentlyOccasionallyNeverTobacco depicted Describe specific instances of tobacco, and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to tobacco?  FORMTEXT       Alcohol: Includes any use of or reference to any alcoholic product, or other liquid substance with similar properties. Please place an  X in the c™ ¢ 23ÙÚÏÚKkwŽ’ª²´ÈÊÌÖØâü"ÎÐ"$ÌÎÐÚÜhj~€‚ŒŽ¦¨¼¾÷óï÷óëóëó÷óëóïëãïÛïÐÛÂÛïã·ï²ª¢žïÛï“ÛÂÛïÛïˆÛÂÛïÛï}j%h46ÌUj±h46ÌUj=h46ÌUh{Hh{Hh46Ì6h{Hh{H6 h{H6hqNh46ÌB*phÿjh46ÌUmHnHuj_h46ÌUjh46ÌUhqNh46Ì5hù#Sh46Ìh(“h(“h(“51HIZ [ ¬ ­ g h Ö ™ ¢ ì 3ÚÏÚuvýýýýýýýýýýýý÷îØØØ÷î÷ & F ÆÐLÿ„$If^„gdù#S $Ifgd(“$If|¿Æ¿ýývwxŽ‘’ª²Üâü"΄‚‚‚‚‚‚‚‚‚‚‚‚‚‚zkd$$If–lÖÖ0”ÿ4$ \ tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÖÿÿÖÿÿÖÿÿ4Ö4Ö laöÎÐ$&<Vbdrýý÷ëëëG÷£kdÓ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H$IfrtvxzŠŒŽóóóMGóóó$If¥kdd$$If–l4Ö”ªÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H’´¶¸º[UIII $$Ifa$gd{H$If£kdù$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4º¼ÎÐÒÔ[UIII $$Ifa$gd{H$If£kdŠ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4ÔÖæèêì[UIII $$Ifa$gd{H$If£kd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4ìîô¶àäh’–¦Ð[YYYYYYYYY£kd¬$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 ¾ÀÊÌÔ”è€8:NPR\^fˆCDmnÎ89CD  ÜÞòôö6 G ‚ ô õ ÿ !" " """."&$z$Þ$²%´%È%÷é÷åÚåÒÎå÷åÃ÷é÷åÚåÎÒ»Îå÷å°÷é÷å÷å¥÷é÷åÚåÒÎå÷åš÷é÷åÚåÒÎå÷åj[ h46ÌUjÅh46ÌUjQh46ÌUhÄ„h{H6j»h46ÌUh{Hh{Hh{H6hqNh46ÌB*phÿh46Ìjh46ÌUmHnHujh46ÌU8ÐÔ’”èê&ýýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H &(xz|~[RFFF $$Ifa$gd{H $Ifgd{H£kd™$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4~€†8bfˆCDn[YYYYYYYY£kd*$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 noz‡ŽÊËÌöêêêFöêê£kd/$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{HÌÍÎÑ7ÜóOMMMMMM£kdÀ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H6  G H S ` f ýýýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H f g ~  € [RFFF $$Ifa$gd{H $Ifgd{H£kd9 $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 ‚ … ô """"."$$&$[YYYYYYYY£kdÊ $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 &$z$|$’$¬$¸$º$Ö$úñåååAñ£kdÏ $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{Hgd{HÖ$Ø$Ú$Ü$Þ$æ$²%Ü%à%óóóOMMMM£kd` $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{HÈ%Ê%Ì%Ö%Ø%à%&à&á& 'L'¨'©'³'´'( ( ((("($(Ð($)l*P+R+f+h+j+t+v+„,†,š,œ,ž,¨,ª,ž- -´-¶-¸-ôìÞìÚÏÚËÃËÚ»·¬»ž»ÚšÏÚÃËÚ»·„»ž»Ú»·y»ž»Ú»·n»jOhnLÚUjÙhnLÚUjchnLÚUhÄ„h46ÌB*phÿhÄ„jhnLÚUmHnHuj‡ hnLÚUhnLÚjhnLÚUh{Hh{H6h{HhqNh46ÌB*phÿh46Ìjh46ÌUmHnHujh46ÌUjñ h46ÌU+à%&à&á& ' ''$'*'ýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H*'+'G'H'I'J'[RFFF $$Ifa$gd{H $Ifgd{H£kde $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4J'K'L'P'¨'((($(Î(Ð([YYYYYYYYY£kdö $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 Ð($)&)<)V)b)d)Ž)úñåååAñ£kdý $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{Hgd{HŽ))’)”)–)Ø)Ú)Ü)Þ)óóóOFóóó $Ifgd{H£kdŽ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{HÞ)à)2*4*6*8*[RFFF $$Ifa$gd{H $Ifgd{H£kd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf48*:*L*N*P*R*[RFFF $$Ifa$gd{H $Ifgd{H£kd°$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4R*T*d*f*h*j*[RFFF $$Ifa$gd{H $Ifgd{H£kdA$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4j*l*n*P+z+~+„,®,²,ž-È-[YYYYYYYYY£kdÒ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 ¸-Â-Ä-Ì-æ-.ž.Ÿ.É./y/z/„/…/0 0 0ü0þ0111 1"1*1L1'2Q2Ž233%3&34 4 4â4ä4ø4ú4ü4555 5ô5H6²6R7T7h7j7l7v7x7<8òêæÞÓæÏÇÏæêøêòêæêíêòêæÓæÇÏæêâêòêæê×êòêæÓæÇÏæêÃŒêòêæjhnLÚUjkhnLÚUjõhnLÚUj]hnLÚUjçhnLÚUhnLÚh{Hh{H6h{HhqNh46ÌB*phÿhqNh46Ì5h46ÌjhnLÚUjhnLÚUmHnHu7È-Ì-æ-.ž.Ÿ.É.Ê.Õ.â.è.ýýýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H è.é.////[RFFF $$Ifa$gd{H $Ifgd{H£kdÅ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4///y/00ü0&1*1L1&2[YYYYYYYYY£kdV$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 &2'2Q2R2]2j2p2q2ýøïããã?£kdÓ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{Hgd{Hq2Š2‹2Œ22Ž2’23öêêêFDD£kdd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{H344â4 55 5ò5ô5H6J6`6z6†6ýýýýýýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H †6ˆ6¨6ª6¬6®6[RFFF $$Ifa$gd{H $Ifgd{H£kdá$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4®6°6²6º6R7|7€7<8f8j8x8[YYYYYYYYY£kdr$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 <8>8R8T8V8`8b8j8x8h9j9~9€9‚9Œ9Ž9–9È9Ú9N:x:®:û:ü:;;< < <¨<ª<¾<À<Â<Ì<Î<Ö<è<Æ=È=>˜˜†˜(™*™>™÷óè÷Ú÷ÖËÖÿ´Ã¦ÃÖžËÖ–’Öÿ‡Ã¦ÃÖÿ|æÃÖËÖw–u–’ÖÿU hÄ„6jýh,f©Uj‡h,f©Uh{Hh{Hh{H6hqNh46Ì5jh,f©UmHnHujïh,f©Uh,f©jh,f©UhqNh46ÌB*phÿh46ÌjhnLÚUmHnHujyhnLÚUhnLÚjhnLÚU.x8z8h9’9–9È9Ú9M:N:x:y:„:‘:—:ýýýýýýýýøïããã $$Ifa$gd{H $Ifgd{Hgd{H —:˜:©:ª:«:¬:[RFFF $$Ifa$gd{H $Ifgd{H£kde$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4¬:­:®:û:<<¨<Ò<Ö<è<Ä=[YYYYYYYYY£kdö$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 Ä=Æ=È=˜˜4˜N˜Z˜ýøøïããã $$Ifa$gd{H $Ifgd{Hgd{Horrect space. FrequentlyOccasionallyNeverAlcohol depicted Describe specific instances of alcohol, and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to alcohol?  FORMTEXT       Real or Fictional Drugs: Includes use of or reference to any real-world or fictional substance with perception-altering qualities, whether prescribed, over-the-counter, fake, or illegal. Any form of drug paraphernalia should also be listed below. Please place an  X in the correct space. FrequentlyOccasionallyNeverDrugs depicted Describe specific instances of drugs (indicating which specific drugs are depicted) and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to drugs?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of controlled substances in the product.  FORMTEXT       8. LANGUAGE Dialogue: Includes all language or other form of communication that is potentially offensive, rough, impolite, profane, or otherwise objectionable, including but not limited to swearing, discriminatory remarks, blasphemy, and racial slurs (including relatively mild terms such as "damn" and "hell"). This includes language that can be heard, read, or seen in the background and have double or vernacular meanings. NOTE: ESRB recommends including scripts or dialogue sheets if the product contains pertinent language. Pertinent language must be highlighted in these materials. Please place an  X in the correct space. FrequentlyOccasionallyNeverPertinent dialogue depicted Describe specific instances of pertinent language (indicating the specific words or phrases used and their frequency) and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree and frequency of pertinent language? Indicate any specific words or phrases that can be directly or indirectly triggered by the player:  FORMTEXT       Soundtracks: Includes all language in the soundtrack that is potentially offensive, rough, impolite, profane, or otherwise objectionable, including references to sexuality, references to controlled substances, swearing, discriminatory remarks, blasphemy, and racial slurs. This includes words that are edited out of the recording (i.e., "bleeped" out). NOTE: If the soundtrack included any pertinent language, please include a copy of the actual soundtrack and a complete lyric sheet. The lyric sheet must correspond to the order of songs on the soundtrack, and all pertinent content must be highlighted for review. Please place an  X in the correct space. FrequentlyOccasionallyNeverPertinent language Describe specific instances of pertinent language (indicating the specific words or phrases used and their frequency) and the context in which they occur:  FORMTEXT       Edited language (e.g., bleeped, scrambled, etc.): Describe specific instances of edited language (indicating the specific words or phrases edited and their frequency) and the context in which they occur: (*Note that ESRB needs to review all edits.)  FORMTEXT       List any songs on the soundtrack that have previously been labeled with a parental advisory or other similar warning:  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of pertinent language in the product.  FORMTEXT       9. HUMOR Pertinent Humor: Includes gross, scatological, sexual, or racial references and slapstick used for comedic effect. Please place an  X in the correct space. FrequentlyOccasionallyNeverSlapstickBodily functions depicted, overheard, described, or mentionedSexual humorRacial humorOther pertinent humor Describe specific instances of pertinent humor (indicating the specific words or phrases used and their frequency), and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree and frequency of pertinent humor?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of pertinent humor in the product:  FORMTEXT       10. GAMBLING Gambling: Includes any of the following features related to real or simulated gambling: Please place an  X in the correct space. FrequentlyOccasionallyNeverGambling advice or instructionPlayer can participate in gambling, card, or casino gamesPlayer can spend or wager real moneyPlayer can win or redeem real money or prizes Describe specific instances of gambling games, information, or references and the context in which they occur:  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of gambling in the product.  FORMTEXT           PAGE  PAGE 1 Z˜\˜~˜€˜‚˜„˜[RFFF $$Ifa$gd{H $Ifgd{H£kds$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4„˜†˜Ž˜(™R™V™ê™ššJš œ[YYYYYYYYY£kd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 >™@™B™L™N™ê™ì™ššššššJš œ`œÈœÄÆÚÜÞèꂞ„ž˜žšžœž¦ž¨ž°ž¾žÆŸÈŸÜŸÞŸàŸêŸìŸôŸ    ’¤ôìÞìÚìÖËìÞìÚÀÚ¸´Ú¬¨¬¬Ú¬¨„¬¬ÚÀÚ¬¨y¬¬ÚqÀÚhqNh46Ì5jh}Ujh}Ujh}UmHnHuj£h}Uh}jh}Uh{Hh{Hh{H6hqNh46ÌB*phÿj h,f©Uh,f©h46Ìjh,f©UmHnHujh,f©Uj•h,f©U+ œ œ`œbœxœ’œžœ œýøïããã?£kd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{Hgd{H œ¾œÀœÂœÄœÆœÈœÐœöêêêFDD£kd$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 $$Ifa$gd{H $Ifgd{HМÄîò‚ž¬ž°ž¾žÀžÆŸðŸôŸ    H£J£¤’¤æ¤è¤þ¤¥$¥ýýýýýýýýýýýýýýýýýøïããã $$Ifa$gdÖ@ª $IfgdÖ@ªgd{û’¤æ¤f¥¤¦¦¦º¦¼¦¾¦È¦Ê¦V¨X¨l¨n¨p¨z¨|¨„¨ž¨X­¬­®Z¯\¯p¯r¯t¯~¯€¯ˆ¯ì¯†±ˆ±œ±ž± ±ª±¬±°²²²Æ²È²Ê²Ô²Ö²Þ²ì²î³ð³´´´´´÷óïçãØçÊçïçã¿çÊçï´ï÷óïçã©çÊçï´ïçãžçÊçïçã“çÊçï´ïçãˆçÊçj—$h}Uj!$h}Uj«#h}Uj5#h}UhqNh46ÌB*phÿj!h}Ujh}UmHnHuj'!h}Uh}jh}Uh46Ìh{ûh{Hh{û65$¥&¥^¥`¥b¥d¥[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kd $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4d¥f¥n¥¤¦Î¦Ò¦V¨€¨„¨ž¨F«[YYYYYYYYY£kd– $$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 F«H«V­X­¬­®­Ä­Þ­ê­ýýýøïããã $$Ifa$gdÖ@ª $IfgdÖ@ªgd{ûê­ì­®®®®[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kd"$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4®®"®Z¯„¯ˆ¯ì¯ô¯*±,±†±[YYYYYYYYY£kd¤"$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 †±º±¾±Â±°²Ú²Þ²ì²î²î³´´.´P´µµjµlµ‚µœµ¨µýýýýýýýýýýýýýýýøïããã $$Ifa$gdÖ@ª $IfgdÖ@ªgd{û´´.´P´µjµ„¶†¶Â¶ü·þ·¸¸¸ ¸"¸â¸ä¸ø¸ú¸ü¸¹¹¹¹ºº0º2º4º>º@ºHºbºvº»j»ª»æ»ð»"½$½&½<½$¾&¾:¾<¾>¾H¾J¾R¾`¾N¿P¿üôéüáÝÕÝüÍɾͰÍüÍɥͰÍüéüÍɚͰÍüôéüáÝ–Ý–ÝüÉüÍɋͰÍüéüÍjª,h}UhÖ@ªj_)h}Ujé(h}Ujh}UmHnHujs(h}Uh}jh}Uh{ûh{û5h{ûh{Hh{û6hqNh46ÌB*phÿhqNh46Ì5h46Ì6¨µªµ¾µÀµÂµÄµ[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kd %$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4ĵƵB¶D¶F¶H¶[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdž%$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4H¶J¶d¶f¶h¶j¶[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kd/&$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4j¶l¶†¶ˆ¶Š¶Œ¶[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdÀ&$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4Œ¶Ž¶º¶¼¶¾¶À¶[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdQ'$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4À¶Â¶Ê¶ü·&¸*¸â¸ ¹¹¹ ¹[YYYYYYYYY£kdâ'$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 ¹ºDºHºbºvº»»j»l»‚»œ»¨»ýýýýýýýøïããã $$Ifa$gdÖ@ª $IfgdÖ@ªgd{û ¨»ª»è»ê»ì»î»[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdÕ)$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4î»ð»d¼f¼h¼j¼[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdf*$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4j¼l¼¶¼¸¼º¼¼¼[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kd÷*$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4¼¼¾¼½½½ ½[RFFF $$Ifa$gdÖ@ª $IfgdÖ@ª£kdˆ+$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 ½"½$½>½F½$¾N¾R¾`¾b¾N¿[YYYYYYYYY£kd,$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laöf4 P¿d¿f¿h¿r¿t¿|¿~¿‚¿„¿ˆ¿Š¿Ž¿¿”¿–¿¢¿¤¿¦¿¨¿ª¿¬¿¸¿º¿¼¿¾¿À¿Â¿Æ¿È¿üñéÛé×ÏËÏËÏËÏËÁ»Á»·Ë¦™¦Š¦™·Ë×hù#S0JCJaJmHnHuhù#Shù#S0JCJaJ!jhù#Shù#S0JCJUaJhù#S hù#S0Jjhù#S0JUh0’jh0’Uh46Ìjh}UmHnHujh}Uj -h}Uh}N¿x¿|¿€¿‚¿†¿ˆ¿Œ¿Ž¿’¿”¿¦¿¨¿ª¿À¿Â¿Ä¿Æ¿È¿ýýýýýýýýýýñèýñèýýý„h]„hgdù#S „øÿ„&`#$gd0’21h:p(“°Ð/ °à=!°"°#$%°°Ð°Ð Ð]$$If–!vh5Ö 5Ö\ #v #v\ :V–l tàö6ö5Ö 5Ö\ tDÿÿÿÿText5$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4“$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4”ª tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4tDÿÿÿÿText4tDÿÿÿÿText1tDÿÿÿÿText2$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4tDÿÿÿÿText3$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4tDÿÿÿÿText6tDÿÿÿÿText7$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4tDÿÿÿÿText8$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4tDÿÿÿÿText9$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText10$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText11vDÿÿÿÿText12vDÿÿÿÿText13$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText14vDÿÿÿÿText15$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText16vDÿÿÿÿText17$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText18vDÿÿÿÿText19vDÿÿÿÿText20$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText21vDÿÿÿÿText22$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText23vDÿÿÿÿText24$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText25vDÿÿÿÿText26vDÿÿÿÿText27$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText28vDÿÿÿÿText29$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText30vDÿÿÿÿText31vDÿÿÿÿText32vDÿÿÿÿText33$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText34vDÿÿÿÿText35vDÿÿÿÿText36$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4$$If–!vh5Ö|5Öƒ5Ö½5Ö #v|#vƒ#v½#v :V–l4 tàö6ö5Ö|5Öƒ5Ö½5Ö f4vDÿÿÿÿText37vDÿÿÿÿText38†œ@@ñÿ@ NormalCJ_HaJmH sH tH DA@òÿ¡D Default Paragraph FontVi@óÿ³V  Table Normal :V ö4Ö4Ö laö (k@ôÿÁ(No List jš@³ój (“ Table Grid7:VÖ0ÿÿÿÿÿÿ4 @4 ù#SFooter  ÆàÀ!.)@¢. ù#S Page Number4@"4 ù#SHeader  ÆàÀ!”5øÿÿÿÿHIZ[¬­ghÖ™¢ì3ÚÏÚuvwx Ž ‘ ’ ª Y n q ~ ‘ ç è    + 1 2 9 : ; < = E F G H I Z [ \ ] ^ g h i j k s t u v w z Û ð ò 4 I K S h j É Ê ô õ  <=>?@Cœ±³ÄCDnoz‡ŽÊËÌÍÎÑ7QS³ÍÏÑàabŒ˜¥«¬ÃÄÅÆÇÊ9SUW]XYƒ„œ¢£±²³´µ¹46J&'QR]jpqŽ‘’–î bcŽ™¦¬­ÂÃÄÅÆçèéêë!"#$%-./012£¸º=RTÊßáÄÅÐÝãä    t‰‹ÿ'§¨ÒÓÞëñò   ›°².08¡¢ÌÍØåëìüýþÿQfhÆÛÝäå\qsŒ•õö ! , 9 ? @ Q R S T U V £ ¸ º !!!$!’!“!”!¾!¿!Ê!×!Ý!Þ!ï!ð!ñ!ò!ó!÷!D"Y"["¥"º"¼"Õ"µ#¶#à#á#ì#ù#ÿ#$$$$$$$$’$§$©$ñ$%%%%“%¨%ª%¶%À%T'U'ø'ù'#($(/(<(B(C(_(`(a(b(c(g()))Û)ð)ò)ÿ)S+T+[,\,†,‡,’,Ÿ,¥,¦,¹,º,»,¼,½,Á,]-r-t-¦-ª-E.F.s...‘.///&/'/§/¼/¾/Ç/Ø/:0;0e0f0q0~0„0…000‘0’0“0Ñ0Ò0Ó0Ô0Õ0â0ã0ä0å0æ0ó0ô0õ0ö0÷0 111111®1Ã1Å1!26282?2@2½2Ò2Ô2á2ë2:3;3e3f3q3~3„3…3¤3¥3¦3§3¨3â3ã3ä3å3æ3 4 4 444=4>4?4@4A4B4O4S4Â4×4Ù4à4á4W5l5n5p5q5s5t5v5w5y5z5ƒ5„5…55‘5’5•5˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€Щ0€€Щ 0€€Ð©0€€Ð©0€€Ð ©0€€Ð©0€€Ð©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€¨ ©0€€¨ ©0€€¨ ©0€€¨ ™0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€Ð ©@0€€Ð ©@0€€Ð ©0€€Ð ™@0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€¨ ©0€€¨ ©0€€¨ ©0€€¨ ™0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€¨ ©0€€¨ ©0€€¨ ©0€€¨ ™0€€¬ ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€¨ ©0€€¨ ©0€€¨ ©0€€¨ ™0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©@0€€¨ ©@0€€¨ ©@0€€¨ ©@0€€¨ ™@0€€¬ ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ©0€€Ð ©0€€Ð ©0€€Ð ©0€€Ð ™0€€Ô ˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€IÈ00Iˆ00 IÈ00Iˆ00 IÈ00Iˆ00 IÈ00Iˆ00 IÈ00IÈ00€IÈ00€˜@0€€˜@0€€IÈ00 Iˆ00˜ HIZ[¬­ghÖ™ìÏuvwx ’ ª Y n q ~ ‘ ç z Û ð ò 4 I K S h j É Cœ±³ÄDÑ7QS³ÍÏÑàaÊ9SUW]X¹46J&–î bÂçèêë!"#$%-./012£¸º=RTÊßáî™ÅÐÝãä  t‰‹ÿ'§ÓÞëñò ›°².08¡ÍØåëìQfhÆÛÝäå\qsŒ•õ! , 9 ? @ £ ¸ º !!!$!’!¿!Ê!×!Ý!Þ!÷!D"Y"["¥"º"¼"Õ"µ#á#ì#ù#ÿ#$$$’$§$©$ñ$%%%%“%¨%ª%¶%À%T'U'ø'$(/(<(B(C(g()))Û)ð)ò)ÿ)S+T+[,‡,’,Ÿ,¥,¦,Á,]-r-t-¦-ª-E.F.s...‘.///&/'/§/¼/¾/Ç/Ø/:0f0q0~0„0…000‘0’0“0Ñ0Ò0Ó0Ô0Õ0â0ã0ä0å0æ0ó0ô0õ0ö0÷0 1111®1Ã1Å1!26282?2@2½2Ò2Ô2á2ë2:3f3q3~3„3…3¤3¥3¦3§3¨3â3ã3ä3å3æ3 4 4 444=4@4A4B4O4S4Â4×4Ù4à4á4W5l5•5Kˆ00PIˆ00€Iˆ00€Iˆ00€Iˆ00€Iˆ00€Iˆ00€Iˆ00€Iˆ00€Iˆ00€KÈ00€Kˆ0 0I KÈ00 Kˆ0 0I Iˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00€Iˆ00€Kˆ00€Kˆ00€Iˆ00€Iˆ00€Iˆ00€Kˆ00€Iˆ00€Iˆ00€Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Kˆ00 Iˆ00 Kˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Kˆ00 Kˆ00 Kˆ00 Iˆ00 KÈ00 KÈ00 +Fq6rt‚ IÈ00ì/ )Fq6rt‚ƒÖ KÈ00 @4p”à  IÈ00l0 @4p”à $,ÿÿ KÈ00 @1ÿÿ Kˆ0[0¾\šIÈ0 0 @1ÿÿ IÈ00 @00ÿÿ IÈ0 0 IÈ0 0 @00ÿÿ Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ0p0¯qLšIˆ0p0® Iˆ0p0­ Iˆ0p0« Iˆ00 Iˆ0u0« Iˆ0u0«vØšIˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Iˆ0†0Ÿ Iˆ0†0Ÿ‡´šIˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Kˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Kˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« KÈ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Kˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Iˆ0 11 Iˆ0 10 Iˆ0 1/ Iˆ0 1- Iˆ00 Iˆ01- Iˆ01- Iˆ01- Iˆ01- Iˆ00 Iˆ01- Iˆ01- Iˆ01- Iˆ01- Iˆ00 Iˆ01- Iˆ01- Iˆ01- Iˆ01- Iˆ00 Iˆ01- Iˆ01-T%šIˆ00 Kˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 KÈ0p0¯qšIÈ0p0® IÈ0p0­ IÈ0p0« IÈ00 Iˆ051 Iˆ051 Iˆ051 Iˆ051 Iˆ00 Iˆ0:1 Iˆ0:1 Iˆ0:1 Iˆ0:1 Iˆ00 Iˆ0?1 Iˆ0?1 Iˆ0?1 Iˆ0?1 Iˆ00 Iˆ0D1 Iˆ0D1E|)šIˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00 Iˆ00 Iˆ00 Iˆ00 Kˆ00 Iˆ00  $$$'¾È%¸-<8>™’¤´P¿È¿)4>GbfmzvÎrºÔìÐ&~nÌf &$Ö$à%*'J'Ð(Ž)Þ)8*R*j*È-è./&2q23†6®6x8—:¬:Ä=Z˜„˜ œ œÐœ$¥d¥F«ê­®†±¨µÄµH¶j¶Œ¶À¶ ¹¨»î»j¼¼¼ ½N¿È¿ "#$%&'(*+,-./012356789:;<=?@ABCDEFHIJK`acdeghijklnopqrstuvwxy{Æ¿!Y e k Û ç í 4 @ F S _ e œ¨®8DJ³¿Å9EK+1îú£¯µ=IOÊÖÜt€†ÿ ›§­%+Q]cÆÒØ\hn£ ¯ µ !!!D"P"V"¥"±"·"’$ž$¤$ñ$ý$%“%Ÿ%¥%)))Û)ç)í)]-i-o-s..….///§/³/¹/®1º1À1!2-232½2É2Ï2Â4Î4Ô4W5c5i5”5Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€  '!•!ÿ•€ð8ð@ñÿÿÿ€€€÷ð’ðð0ð( ð ððB ðS ð¿Ëÿ ?ð”5ÿÿ&Text5Text4Text1Text2Text3Text6Text7Text8Text9Text10Text11Text12Text13Text14Text15Text16Text17Text18Text19Text20Text21Text22Text23Text24Text25Text26Text27Text28Text29Text30Text31Text32Text33Text34Text35Text36Text37Text38Y Û 4 S œ9´:î£=Êtÿ›QÆ\£ !D"¥"’$ñ$“%)Û)]-s./§/®1!2½2Â4W5•5  !"#$%l î G f ¯KÆL2¶P݇®,dÙo¶ !W"¸"¥$%¦%)î)p-†./º/Á142Ð2Õ4j5•5CI9=n5n5p5p5q5q5s5t5v5w5y5z5’5•5CIloksØÛ]W—'¥8 •ô$!‘!â&4'ÿ)+Ø/90"3/3n5n5p5p5q5q5s5t5v5w5y5z5’5•533333333333333I™¢ÏÚ’ ª  9 = w õ oŽ7S³ÑÇ9W„µR’ ŽÆ2ÅÓÍ! V ¿!ó!á#$$(c(‡,½,s.‘.f0“0Õ01f3¨3n5n5p5p5q5q5s5t5v5w5y5z5’5•5n5n5p5p5q5q5s5t5v5w5y5z5’5•5ª;¬etD€pÿÿÿÿÿÿÿÿÿh„Є˜þÆÐ^„Ð`„˜þCJOJQJaJo(‡hˆH·ðh„ „˜þÆ ^„ `„˜þOJQJ^Jo(‡hˆHoh„p„˜þÆp^„p`„˜þOJQJo(‡hˆH§ðh„@ „˜þÆ@ ^„@ `„˜þOJQJo(‡hˆH·ðh„„˜þÆ^„`„˜þOJQJ^Jo(‡hˆHoh„à„˜þÆà^„à`„˜þOJQJo(‡hˆH§ðh„°„˜þư^„°`„˜þOJQJo(‡hˆH·ðh„€„˜þÆ€^„€`„˜þOJQJ^Jo(‡hˆHoh„P„˜þÆP^„P`„˜þOJQJo(‡hˆH§ðª;¬eÿÿÿÿÿÿÿÿÆhÄÅ         å }{HqNù#SÄ„0’(“,f©Ö@ª46ÌnLÚ{û™Ïvw   + 1 2 9 : ; < = E F G H I Z [ \ ] ^ g h i j k s t u v w ô õ  <=>?@noz‡ŽÊËÌÍÎŒ˜¥«¬ÃÄÅÆÇƒ„œ¢£±²³´µQR]jpqޑޙ¦¬­ÂÃÄÅÆçèéêë!"#$%-./01ÄÅÐÝãä    ÒÓÞëñò   ÌÍØåëìüýþÿ ! , 9 ? @ Q R S T U ¾!¿!Ê!×!Ý!Þ!ï!ð!ñ!ò!ó!à#á#ì#ù#ÿ#$$$$$$#($(/(<(B(C(_(`(a(b(c(†,‡,’,Ÿ,¥,¦,¹,º,»,¼,½,e0f0q0~0„0…000‘0’0“0Ñ0Ò0Ó0Ô0Õ0â0ã0ä0å0æ0ó0ô0õ0ö0÷0 11111e3f3q3~3„3…3¤3¥3¦3§3¨3â3ã3ä3å3æ3 4 4 444=4>4?4@4A4•5–žžžžž–ž–ž–ž–ž–ž–žžžžž–ž–ž–ž–ž–ž–ž–ž–ž–žžžžž–žžžž–ÿ@ ,e3A4¨Rñe3 e3”ÿð$ EEFFFûûûû¨¨¨¨° °!”5`@``````8@``@@`"`$`L@`(`*`,`\@`0`d@`4`6`8`t@`<`˜ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿG‡z €ÿTimes New Roman5€Symbol3& ‡z €ÿArial?5 ‡z €ÿCourier New;€Wingdings"1ˆðÐhø˜F(˜f ùu-`ùu-`‘#ð´´4dS5S5 2ƒQðÜHXðÿ?äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(“2ÿÿ)Instructions on Completing Sections 4-11: Alienware Sborchers þÿà…ŸòùOh«‘+'³Ù0˜˜ÌØìø (4 T ` lx€ˆä,Instructions on Completing Sections 4-11: AlienwareNormal Sborchers10Microsoft Office Word@Àhx@ƒ—Å@8Ø0˜Åùu-þÿÕÍÕœ.“—+,ù®0 hp|„Œ” œ¤¬´ ¼ òä`S5æ *Instructions on Completing Sections 4-11: Title  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|þÿÿÿ~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“þÿÿÿ•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛþÿÿÿÝÞßàáâãþÿÿÿåæçèéêëþÿÿÿýÿÿÿýÿÿÿïþÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀFàüù0˜Åñ€Data ÿÿÿÿÿÿÿÿÿÿÿÿ}–-1Tableÿÿÿÿ”™ŽWordDocumentÿÿÿÿ4øSummaryInformation(ÿÿÿÿÿÿÿÿÿÿÿÿÜDocumentSummaryInformation8ÿÿÿÿÿÿÿÿäCompObjÿÿÿÿÿÿÿÿÿÿÿÿqÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ ÿÿÿÿ ÀFMicrosoft Office Word Document MSWordDocWord.Document.8ô9²qRoot Entryÿÿÿÿÿÿÿÿ ÀF`ØÒ„`˜Å÷@Data ÿÿÿÿÿÿÿÿÿÿÿÿ}–-1Tableÿÿÿÿ”™ŽWordDocumentÿÿÿÿ4ø‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“þÿÿÿ•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛþÿÿÿÝÞßàáâãþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöýÿÿÿþÿÿÿþÿÿÿþÿÿÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ SummaryInformation(ÿÿÿÿÿÿÿÿÿÿÿÿÜDocumentSummaryInformation8ÿÿÿÿÿÿÿÿˆCompObjÿÿÿÿÿÿÿÿÿÿÿÿqÿÿÿÿÿÿÿÿÿÿÿÿþÿ ÿÿÿÿ ÀFMicrosoft Office Word Document MSWordDocWord.Document.8ô9²qþÿÕÍÕœ.“—+,ù®DÕÍÕœ.“—+,ù®T hp|„Œ” œ¤¬´ ¼ òä`S5æ *Instructions on Completing Sections 4-11: Title4 $€,°transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/0000750000175000017500000000000010305077141022546 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen6.png0000640000175000017500000022527010304371203024625 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ  •ÉÎ IDATxœì½y`ÇöÿTUÏ€@ÜH€m0§/$ˆÄ÷‰íøvìÝ@²ÙÝl²IÞlììnüÆñfã$oŽÝdsüâ$›$tocÛ`ãûĘ˜÷- I3SßßÕ]jµ¤ÑÝ3ÝÒ§UQq›:PpÏ)ŽÊÊnTd¯¯^³>î.jîÜ«5Öí¬OS,ëþÝEóÞã•ݽx2ïi¾gÅÈûß¼Ûuÿý¿P\|V ü{æÍ»]v¸'âåÄw·Ì»CšïéùþæßíÒ^>gä‘ÝÈç¼|ÕÖ>]T4£¸ø,uuËr^>¾´‘GWÖ÷¨ÁÝ;Ù|š^>ËþÝ;óÞý.JéÜ«›w»2oäƒeÞU ¯²;Üýéå³kÞê3”ß=2ïv¥oäû³×r„ò©]$@pOS:”Ïùî²Ã@yù­ë9#ß§âùÁ=3Ò¡|ÊF>›þ=3æ½Ç›¦pßñÝ­¡f¦º&¸æÝ!Íwÿy_™w»z4òâ{Ì»])W× ÿnWjÕ5554 àŽ\uM7Å;lX÷I(ï[¸£«‘W¬Ü3/GuMâ^>k|ÏŠyWÊÕP&%Oãš`™÷>åˆkü@yßÊ>ïªà56®(cæ]+…Êìä3Y„{#AÜÁøg´}ÊÓÉ‹¸&@|oÞ»+»qŸÍ{w=ðÀÃŠŠ¦ùß¿76®PRr]v‡añÍ{UœÓ:ß•âPÞoC¯ L¸Hù~ w­lQ>@|Wù cL?âgÊû„ï0o¾h½Q> ùŒ‰Ù?âšÌÌ»Êîé(+qM€à®UZzƒÆzCà þLlüw˜q‰õ%K^é~Â’%¯ ¸ùÕÞÔc(ï÷"Ÿh@ÕP¦lÞ•r¡|)ó®ƒø@PÞWš;÷*Eù%K^éNùLç3 ¦ÃÂû|´JÙ*ßL-® –yO“ïve ®©«{@yù-]ß]9øn—î~Hl|eÞ»Ë÷¹s¯R_æü{R^~íÚk×nÌöXRkóSkÈ~ãÒDä"Üaóòù~wtóò™V0¥<€%K^Ù°a'2Ì÷@˜w¥ššGfÍ:wÖ¬sðP>3J*®Q'd½qÂrÿ­(×$.MùìÆ5>7ïZŠò îȸŒkS D¥¼Öö¿eµµO¨¨¸Ýõ+{Ê÷'óîP.”O\K–¼rÞyg>øà(æ]I%ï*«ÉÜ{“?» ûî`Õ5éË•&e‚{:êÊ¿ØÐð¢×w .Ü‘›_í®ø“þ‹küyukræ=޼žzõR6ïZ™§|@• ¾÷ón—+݆]‘ÿç«sÊ’Uj¡ü1ï•–^_Zz½:öñ6ïÈLþ ¾';Ô,†òþ‡»Cÿþïÿ@qñ9þ÷ï™7ïÝ•x(,¸§oÞ{”æ»&¾+ :ß=÷ïýîðc\ãS-^üxqñ9ÅÅç Ê'¦~¹sˆGp‡ ë.Æ5A‡;rù{úÊJ eàÌ»’#®ñ'åý`Þµú åƒeÞ=•#®P¡ü’%«êÞeÌ[¾÷oón—ByÊ1­š å“UÿXU_¿ /Ì»]nQ>Xæ½7y˜¿îpu´^‡ò3ïqÊf2¿—w|ùʼw—#”–y¯¯_ ¬ì³»£îIåò+””ÌqLHõ‚ïqß¾\>¸ýVd3òKÕ•]T‚;|¶äÕçp‡3”&»ƒIJ™‡;2R`ãsyÅ÷`™w×Õ5®qñASú¹¸&Y)Ê75mijÚ’í±@šò Æ5ýƼû|&@|÷z¨šïéß"€æý1••w%|~ÖâšÚÚ'TTÜ‘áû¦&+¨éÜóÈÏ)MVÌ{wYpgJK{Åw€øîðÈ¿çàn—¾¸qŸ•,Ü᳸Æÿ*/¿Ec½®î&6>;œqÍŠ††ÝÏ ÜQ.Ï„ªªîIŸò3ï©)+5”A4ï:ˆwP>kà ˆJKçhóÞå¡>Í;¼à{μ÷&å3sÓl)ónW.”OVšò¾2òþ1ïõHù~fÞá:ßû=¶Ò—¦|RF~€˜w‡2Ó‡2ÐæÝ!ÿÇ5¾’ò îAQ"æ®ó}íÚ kÖ¬Ï𞮩©¦f @Ùúœ‘T\SS³ Á=MóîÍÈ?¡6nuQÁ‚{"òO\ã[óî¢üúõÛ×¯ßží±¸,7ëgª«—8™7o®[w]55KTUey„‰T×(ò‚Ûpïzeî.nÌ,¾Ç7ï=oò=óÕ5A»’ÊgXg9’¯SšÍ;¼à»bºõ>¤¼OஇòÁ‚;¼ä»u}×(ß¿án=«ÓÂg’òAä»ÊjìA)Ÿ8Üáb>c‡»:ÐÇÝ}}ÖE_eHý¦†Òk¸Ã†u/âšþ§¬ÔPîJJæh¬+”ï.×ü»ƒïÝ¿ÕÛw3¯8Cͺìp¯ªº'gÞãÞ«î)xù`Þ»]$CqM ùn—æ»OŒ|cãËJJ®Ið|wøÞ'1ý×øîZ ¾/^ü(€ÊÊŒn›r\3ùn]Ê[Ê××? ¬ì&/.îºâÀ]ÉfáYÖÛI&Ë÷ ­oòy\ã7©ʵk7¬]»!Ûcñ»*+ïTdO*®°pG—ÊgëêžuåšýX]ãš•YI²p‡+þ=YGœÅ¸&æ]©ºz)ºNèûÜÅgżw@¢qMmí**\+ÂñZîòÝvYîåå7»uÍ~fÞÒ|ϼ‘OîHß¿§@L»‘Ïyùør¥±A”u¸ÃfäÑ•õA—GpP^~³"»[F>hp ÄáÖWf×Ë'¨tý{:Ž8á|àÌû¼y] »‹}(]—ønWœP>Xæ½®îiåå·y|—N¸§ãåÈ÷÷ã¶Ã=^>5óŽ4ý{šÄÌ…òI)µÆPMÍ£D>‚;r5”IJy¤áåÜ””\g÷ò® Ëm¥åß]tÄ^‡òA7ïv9j(31¦¸ª©y@U•ø®ååsæ=›¦Ê(¾Ûåu(Ÿ²yG:|w˜â À¼y%n]‚;à»’O(ïg¸k)ÊëÉê߸ur”°pWò4®Éß½#fuu£:pñêšî¾gx¤d_Øì†ò€»Öw¾óSÅÅg‚ïY„»5€DCù`Áð]É #ŸÜáÃý=æÍ+Q ®®nÔ¬OG‚{ òm(ï7-^üDQÑÙÅÅg¨­}B59Å‘#”Ïî`\”Gp‡-”w«º&M¸#5ÿž™¸Ã÷tè ¾§óÂf>® VKzѨyW;Üýéå³nÞŠ×˼{w»ÜŠkÒç»ïü»–6òHÃËîi*(•òY‘î**îÔX÷§‘÷[ÿ»ô«k”Õ5©yùôáŽü{Væ*SåÄw_Ø „òÁ5ïi¾ûÇÈ×Ö>  ¢Â/æÝ.G(Ÿ3ï}*åP~ñݺur”˜p×ò”òâ{¸+ù*®ñ3ܵå™U”ã{Ÿ²(ÏJJ®Mìü—$xr%—Ïd·Ð0©¸&@p÷HÞÅ5‚{"rÄ5þLl|%5õÚÔ´µ©ik¶Ç’¨²wtilð’bw¹w$åßýSEžˆ‘ß½~aÝz ÜÕ®Üz×>•ݸ&æ]Éaáá{Ÿ]¾ki¸ÇÁ·‹|÷ïüjõYC ¸g@vê5Y¸ÃæåkkŸT=„sŠ£²²›4ÖëëŸSq¼å¸()¹V»7#ï"Ü‘8ß««òƒy×J¿ºÆÊا"åS»H°Ì{ʲÅ5™£|àÌ».ªqP>kÊ'R ã}"ïDâštHÿ®Õc eμÇÑÀY•‚y·«¢â½ûGÎË'"My¿ù††”–Þít‘6ò°QÞ]óŽó÷@Óaá}>Z¥,Ni¤Ê˼§Éw»4ܽÛï©¶ö)·{t}å0ïÝe‡{ÖCyÂÝ!‡…Ï(ßw­oûA€fÍš9o^i¶ÇÒ·²þÚ&Nù w-ï( ¸#¾+ù„òà»ÒƒþÀÌ™“s|ïYÕÕ ŽGüLyÿ¼°‰TÊ«süÐŽ¸Oyw%{Jã"åÄ÷ᮥ)ŸÄîÝSøLÔÏø‡A‰kÞ¼Rõêê†îÐÏÉ¡>CùÁÝSyÊ÷c¸ÃÇ¡¼?Õc(ŸŽ‚=¿j—â¸&»ƒòYV/òágÿ¨¡ôμkå¦^“U×Ê噹iàÌ»&»ƒòé\9^>ãCÅ‘ƒïÝ¿ÕÛw3/Ÿ¿°ŽP>Xæ=|·+ÍP¾›÷Ð] >ë°zWpùÞý[½}·OõÿîöÇsqM"r¥R>+Ê0Üaóò9#ŸˆÊÊnÒX÷ÔÈ÷¸£K¥ü˪ãXRêÕ¿ûÜc:ŸïŽÓ”²ååƒõÂþë¿þ'€Y³Î „Ï<ßµR˜zhæÝ!Íw×|€àŽ„kÞípO¼©dÏ|ƒ„»ãü¤žâ¢ôÚv7ï~¦üâŨ¬¼+‹cHœòîJv ï"åÄ÷d4iÊ'ˆøÀó=Y¸;ž˜ÚsSV€^Xt-›ñÉvÞ½Ép×J$”Ïñ]Ë]Ê÷c¸Ûžø²:è“ò=äïÁbPÊÊ…òñå˜V n(ŸyõÊ îÏäÜ”•}ÖÊg¬ÀÆJ±+ŽÆzŸ¡|°çWS6ïJ®”ïoœþl_ã+ó®•«¡LJʧv‘@™÷•HcwÖ’’k¡¼“ïý€AÉÊç•òYQüšÈþQ)ŸõX)4óŽòò[2vGMùfäS”ƒòÝOè’¿ îiš÷8×tý²Úk›`Í{6zíSþ4ïÝeYx³ú;Ç÷øJ!”ohx@ié^É=¥iÞ{¹æËêÀÊ•ï^ÀÝ~e%¯ïÝ€]WMÍUU‰vµÌ"åƒw­ï|ç犋Ïö?ß³w-åYYYçåæÝ!My?yšw»ì”WpÏ©7q³f§Bxÿ“+óæÝ®d×C @ón—wqMMÍ£UUÛ¼;䇸Æÿp×R”ojÚÖÔ´-ÛcIH™7ïØ¢EŠ˜fþ ¿áC*eî%R) ¾»kÞ{º¾É÷ôç]kjPUuwºcÊ”â›÷žÎÏÚÔk€økÆ•u–#ù:¥Éß»÷óÛnZ¾â»Rœ×*@p¯®^`Þ<¯à®äVuM°øž,ÜmOÌ4åƒwX|WY=¨ñ!å³wôØ&éDï´hQ=‘¿àŽÊ»(G\“Zb,¸§£ÜÔk|Ùá ´ôFu†òDè}£<>Å5*Gš?ß_|×r¼V9ó_)Ç5Áâ{Êæ½ÛuL¾{gäëê–(/¿Õ£ë».ßí²øÎ|Òh¬¡a%€ÒÒL›wÄç»’âŸÃ]Ëaás|¯d),¸Ã=¾£‹…g·¥A‡‚Å÷8pWjhx^¯Î.å³w$Âw¥,R>(p׺ï¾Ìž]ä¾g}][â¡ü@†»íšOÙ:”¹Fù`Á ð]Iµ FV ¾£ñ„ÌNr‹ïj´Ì6©ïgÊgïJ‰ù`ñÝ ¸Û.þ´:pñýîZY¤|vᎤø®¤ŠX”2@ù`Á]ì« Œîò ܵâP>Xp‡Ç|·náåƒÅ÷dᮤÌR¾¡a€ÒÒ9»£CIïß4o^™Æºõ9¡Û»Ñ@k_“¦úÍ’×ÚÚ'òî°a½¶öiÍúd,¸HmO»ÒÒ4Ö^°ãÞ;eîHÁ¿Û¥ùî‘‘_´¨Àüùå^\Ü Åù´á‡ij»üfÞír„ò4ïO¨¨¸3S·ë„{²^>X|WíàÓ܃;cqMàù ñê®§(Áâ{ŸQ’â?Ã]Ë¢¼9“¾gî¶û&Mù`Á.ñ]ÉkÊûîHm{\£ç–‚÷D”‹k’’ZµvíÆµk7f{,PEÅmöĦÏó2ÜaúqOàŽôý»]šïéùÀÁ=Ùyà,Æ50ïJ ß»øl™÷nÃè{ê5X|wîvyaäýÃ÷tý»]šn‹Õ»ëå}®Š|r WUÕÝë55ª8Þ‡ò Ü‘ÀÔk°àî©ôÔkCà /¦AÿÀîúw- ⻛þ]kþü2›—¯óâþQšú¹P>)i/ï7#¿xñDþ‚;º…ò ÷Á‚{ÆTZzƒÆzCË©yy_Áùw»4ßôã4ï® ØëP>èæ½ë p÷ƒ—_¼ø ••þâ»] îzeuPøžóî†{²F~Àñ]-||d¸+y×îH€ïÖi¾ ¼ÿá®uÿý¿P\|VŽï}*YÊû îÈ ß•1òÁâ»w£õÂȨeq‚p·ŸÍP>@pwXxøÞÅgîJö”&>å}wd’ïJq(,¸Ãû»Hù~ wÛ³CùÀñ]%ò*…Wò'å³w­DŒ¼?ùîÉüji.ZTè©× ¼åj(“RVj( wåå·j¬ÛYŸSw•–^¯ÈÞÛ¼«?áŽÌûw-G(Ÿ3ïq”f(?Ì{׋d.”.ßíÒ|÷‘÷y·«·¸Æ·|Ï´ך?¿Üîå³5ŒÔ”áw£\ eRr¬‡òîF‹?PÐáÖëê–å¼|i#›—÷-ܰvj#œÝAÜ{ïýfÏ.š?¿"“÷½âŠj¯½6/Ù'fñÓF²¡ü@3ïÝ®ù(ÆXeå].^ViñâÇTV~Îõ+»®ø|×òI(ïOóîååõ’(¿òaPVF°hQ­ã‘ŒQþ²Ëþà7¾”Ô³²%%×îð†ïjjÕ#.R¾ÿÁ]+»qM à®õ½ïýÀÌ™S}Êw"ê@`0gxŠïŠévÖ{Mù9sµ´´†BüÕWÿ1ñgeîZ‰ùñÝ#¸k-^ü˜>NŸòýîZuuË”9-/¿Åýaõ®úú甕ݔɛ¦&‡…‡ÿ\<FX›÷S8ÕŠÖŒÝÞwu »ûzw aäÈ!ŒE¯ºêÿóôFI‡ò½U× îKòî*+ïÒX_¼ø1;îsêQ¶Pþ™ººg2sÓÁ]«´tŽÆzCà ÇûDó«ƒ1X›÷L"¾»4å-ªõˆò·ÞºH<÷Ü|ÆbŒÅ|Ö¢EµùÁ¼kåj(“’ƒò©]d ˜w¥òò[´yÏ$å!eÞõt«ƒòYVW9ëgò7C´ ¥ÍžÞÛaÞ²y×)oÌ0€¤øîO9ªkåƒfÞQUuOÆî¨)Ÿ3ò‰ÈAyïnDóOŒ|Ïõ‘ ñšÑ|'38ž.rÄ5.RÞ0È0cQÆ¢‰<%þ»QÖ•«¡LV©Å5Ǽ;¤)Ÿ3òèfÞòO\ÓÇú¦f4ë©×áîâ“Å¥†{ú„--­ÐÐ0Àu×ýÀÊ•ÿ–ÈÝ} w‡î»ï³gùß¿gÞ¼wWâS¯‚;Üæ»–î.N½˼Çç»í´N¸geêµõMùȆaêø8Ž»u×péb(/„0ûA>ãТEõ³fÍœ=»¹P>1¹ÊûMÁÞ„òýîðA(ŸD‚ã8®ŒüHŒLó®éØá4k(,¨°pa皦ë¯ÿ€_| þƒbÞíûdxs¨dåóîæ{w# óîÜrËˈï‰Ã½ÛW¨JÊÒÒëÜV/J¢?ÁŒPGqôޤ|Ë4q™f(o7ïJŒIÆdjƒñ››Iù<”'B–ºõª\ eRêêåŸMí"‚{:²ù• +3sÓäúÏŒÄHmÞàÈaö`H )åJùžøoŠ5Xæ½GõY)ŸUW/0ožÌ»R•ò9óG¶©×gS¦| ”²yW*-½N›÷ P¾±ñåTú‹Â¨Ñ­ŽáPRÏu—]CùÅ}žÿ•¯Ôûã»4œáœ8÷™LIñw‚õU¥¼oá®Õ•ògw0IŠÔÖöVyùÍê )Êón—ƒòžÞ+õþ‘£1z Æ8„Cqн!%-›‘_ŸòºìÝ®8þ½˜w»|×øPŠòëÖ}´nÝGÙKBª­} @EÅíY¹{yùÍIQ>XpOÓ¼;¤)ßа²±Ñ}Ê76¾ Wú¿Â!]CYˆÂ8gzK;Üçϯt|÷›ß¬ðË_V9¿õÖ°lÙOº]-HpoÞ»Ëë½¼ãÞÚïæÝ®îæÝÏ)Mvùn—†»&~wˆïî½ë•Wê KJ\›zU|w¡ÿûXŒ-@:Þýû°¯ÇÓ<ÂeéÜN¢ÍŸ_©±ÞÝË÷hÞ,[öÆäm·}»Ûw²ó97% wø,®ñ¿*+?§±¾xñãþLlüwؼ|oF>@p÷T¥¥×i¬76ºãåÜKJ®qmBjó¾{ݺl|U•Í !ǃÊëÇ…`BôÀwôT¯žØýC@R ¼VÍ»&»ƒòYVp”Z(ï7ygÞµJJܧ<Õ«hÃ8{±w/öè4œ¦÷ȼ†aoÎi—B³vñ}Ä„À~Ôó=¿š‚y·K—Ö¨ß}X&ï7)ÄkÆWæÝ.íâõïåå7çÌ{RˆWpol|©¤äÚ.¢Í;<ÚŸo&¨ƒOðÉ'øÄ‹[(…D8$Bÿð…/÷v‚6à‡8|ø@o§9Jà‚yw(35”ÕÕK ¨ywÈWqoá®eŸzUpŠ2`ÞÒ^¾±ñ¥ÆÆ—Ò¹”Wû¯NÀmÞ¹ô×ðf¢Ò†!B!ïSˆŠkTdo6öÉ”á~Åÿ›ìSÒWšæÝ![\ÓX]ÝèÊ5µª«—˜7o®»—Í¢rqM²R”ojÚÚÔ´5ÛcI\Ù™‡Óæ=)ÊÛÍ;¼Þ_ûtœ~:NpxÈáÝØíîÅ¿þß áaFûÇ>ôPýé§OøÝïþE}ÙñŒc©ÿ^}õ¯®¼òaW\Q­öt͌܅»R×PÞeÄH‰g/šòÙ2òþ7ïvÕÕ=;sæ´¢¢éêëŸó¹—ohx@ié Y¹{IɵIQÞwxÍw‹ÕŽnsÏ=Ÿ° »vb§[W6DHùwƒ÷~õ_ãiÀ0ÛÔ«ÃÈ?òȯ… ’’¯%kÞ¯½ö§×\óßêøµ×þ^·ºæÍ+™7¯îù`™÷‚u_Å5þWYÙM:|÷-å³ w-å“z®ç|'""šˆ‰1Q=²;w`GúW!C„  !£÷ˆæg?k‚ýû¿w)£TvÊ'k᯿þûsæüP¯ZõW_ý'¡BÎr¯´hQ@îšw‡âáM\ÓÿÔ­†ò‰ Ü4pæ¶¢å³6¬ HS¾7#ßݼÃõú‡.\ `Á“§“0 €²ðÛ±} ¦¤sqC„b``Ô[ €k"µ‹×¿3&wïÞ\Ö§y¿é¦û¥4ॗº|n0¼}9³ íâõïú‰«ß›w»tV`ñâ'*+ïtqlýR ñÚÅû¤¨Æ'æÝ¡’’kÜÕï}Øxèßp×:gNÆdÛ±ýc|œòõ a„DÈ0Ì)Öï{¨çÓzYÖ„®•ò{öìŠßEòŽ;¾uÛm÷Ýr‹¹È‹/>¸bÅÿuœ³p¸×wµhQ€Œí«ã ìP>qUV~N‘}ñâ'¼3ò6ïù?®ñ‰z å{4ïðÚ¿ÇÑLÙŽí¶a€i˜–ÔÓð퇄sa/ÁtñNýæ7K8Ç×¾Ï9*Äÿìg¿íí„»ïþº”B¿>÷Üz;3êá{or$ò ùeÞª¬¼SÁ½¶ö nzù`Á=i#¯~Ï¢‘÷§y·Kg5°àÞ£¼òï½™w»¦`ÊTLUÇ[±u ¶$~}CB„ !ÌIÃ0DÉ·\ˆ¾Æ… kF>jÔpt åKKÿùž{¾¦ŽŸ|ò¿Ÿ~úçÏ<ãlSc×Ò¥ó ee‹ÿARP†Í»CI…òÁ‚»ª¬¼SG4ŠòSñÍ»]:”Ï–‘÷?ܵ”—_¿~Çúõ;z<ÁóùÕ>5 Ó´yOñšé!aÂÂ0¸ñ³þÇ~ÎþðˆøÊW>ŸÈ¿ô¥ªiÓ&¿ôÒ«êË+¯¼õšk¦ IDATŠ+Lgô裿~üñ_&:°Þã W”]¸+õ׸ƻõ¨w*ó^[û„+”–yOîZ¹¸&A56¾4sæ™3gNÐØø²ÃË{Â÷DÌ»CÓ1}f،͛°©ÏóÐ na†apÃ0ŒP×ùÍQ»pa € ª8çxùåWV¬0)ë­—-]ú›Ä á0Â}”ã÷õYC,óžf:Ÿq‹òý[Ýj(—gà¦2ïv•”\£Ãw;å³ïßíRˆ° ›>B¯·õý‡CªøÝÂ0¸še5ï²3çŒó$¬ôo¼òƯ«ãW_]öÚkO!žòußÊEùÁ¼;”«¡LJÚÈ# Ê÷{ónWWÊgñ’£Š¦;åÝç{ æÝ®³pÖÙ8[oÄÆ ØÐýÃJÞ… !ôZ§‡l6 ¨©yÜ0ؾàÜ%¹ÛhMóàꫯºâŠËÿüçß/\ø°únœný)b¡P&Jhü#G\cSæÌ{¯rP>ù ¦sµ[*+»©¬ì³êë—{Gù€šw‡ì”çÉîO_iÂ]ëlœ}ÎQÇÝop!Te$†w#d!¸9Ëš¬yððÃÿñ;ì­ÛpoÒke]—Í»]])¿$»ƒ „R åkkŸPQq‡‡#sOišw‡âá1僢ø%ðŠòÀÂ…µ ºIy·t.Î=çXõMhÒ«XFð!„ª¢° /44<%ï³ÍnÞã«·ÆÝå‘÷9ܵå×®]¿víúl%Qe·Ío׸æÉø' î^¨¬ì³vÊ»xåþaÞ⺭cúˆw˼;¤  Më°náÏ .74Ü 2á.„àÆ¢_Ô'‚'w­Dâ¯ëgü¯êêÆY³Î›5k&€êê%9/ß§ºÆ5} >@r×¼Û¥)ï–‘Ü“X¿º`AÅ‚HÏÈßùõ·ýD^jÏíS31³Eê8"::DÔŒe íÜ­ßÍrÉ„ÊÞSPü-áM>ón×¼ysuøîgÊûgŽŠŠ;•1¯­}²GÊ˼{w­×$wØëgâaR>‰Š¥»/ÚÊžü`F²OL\E(*F±à†¢ƒG„B•HªâU%)Œ]‡öpÎîºëÖ8—JÁ¼Û'”÷.„K[”ÏÚ°‚#ïÞ(Ÿ“]ޏ&5Êμ'®.&WyXaKÒ—c¬ä[ïº0®Þ5L &ò…àí¢½C´ a›B‚où0»ôÊÿû¡è¡z·î²hQ-@Á2ïiÊWW/Qû¯úDþ1ïvUTÜÑò9óGi‡òAªGJܼ£Çú÷ *T†¾páâ)¯N;sÒDÆÀ+¿ï½$Æ›¤ ! n Ã9‚‹6ÑÖ&Útþ¾óÐNÁÙô ñ>F¤iÞê×xZïgÅïKc3òKý@yÂ]ËAùì&ye˜©…ò Ï(-½ÑÑeO½†Ôzš´OÄëiUwÆÀªþõ}G©õÔ^V3BŒ.F!„­âT+o1„ap®ªiâ^Æå¿|ޏ&Îkéܲ¹ŸO¢$Ü£}ɳ¥yóæêÍWkj–ÖÔdŸò>—¢üºu›Ö­ë{Q·OTW÷ €òò[²r÷®qÍóñOÜ“2ïQ¤Ó|ï­0Æ^6S~ß{\Qàœ1à/ÿ’ˆžùÓKf·Hh¦~c'Ø Ø· é§¢›¿p]O£­°`Á<Ge×¢E‹?ø`€_üâ^ûã_œ÷¥˜ŒÊ˜¬©Ot¿`Á=ÙîðîUUYØnÛçæÝ®îæÝç)Mvù®U_¿\¢¬¬W|‹ïɉô'°ùî^ÞQé€;gøÒý$>š>%¸Ü\èžb*s%Fâ#wïÛÙŽöñgœ.=íèä5ÜÌŸ_©úØè¸æ«ÿõúâWC<¤ºÕ{wë`©ªêEöœ‘ïK `=†ò>”Oà3®¹@}ýó=ù`Á=5%TD¸`Ae‚qMW¸30&þñ»º0R€ÁÕ ª!„\èß9Bƒ1Œq.Nˆæ“â„[7MVÓ§Ÿ9}ú™Þ^õÎW¿øu™}rD¸Ç&Æ=ª›w-mÞkj–ÖÔ<âò°zQ Ì{—V3å³ mÞ{£|P”‚yGRýÅ4嵑ᅠ©ú§ÚàÁÆ8ÃWX“Ô°zÔKÕo«šH!¸µ¾É0„\ñ^pƆ`ð>Fιx¬þ™·ð–~zÌ»’LöÑêMÆä anÄ¢Ò0B!nüïÂ?x}÷Ì+e¸+i# ¦æ‘ŒQ> bÝ÷žÔ”¯­}J½øDþ1ïv••ÝØòÁ¼#…þ‘}yåßípg`œákÿ‘.âÄÕ"UU)8çæ±\ Œ !ÆŠ‚±|,€ñ™QÓëÖ­^«öþ64?è0ƒ‹XTÊX¢“ºÁ2ﮨªêžª*³S¿§ˆy¯«{š1TTÜÖãwmFÞ”÷'ܵì”WpR3ïHm>…øo}ëßÌž]ìø®êê¥án%6``ßüϵ¿üÞ¬î¨$„ÑY÷¢= 37ç[³u5clÖ´ Ìïp à\ðÞzo.{æÙ˜áµyÿÙÿC2&ˆXHˆ˜”ŒqÊÏÖÚÒ"W‰|ü-¼ƒ÷4Í»C ñÚÅk⻥Á=©ÜFÁ=XMƒ³%…øø€¢¢äöͪR¬÷KqÿÂ…‹gÏž¥à¾paª(WúÓÎ ¬Ü9€oýçÚ{Z—âXW‹WM©ä‚sÁÔ^¬¬k.o¼ýøšKp‰zúæ‚ͯãõÔnݧ~óÐïõŸ¿3Ì\&DÈ0B† áü¡Ã~ý§_¨““j(?e7ò6®©«[°òòžÍ»]·k¬gËÈûܼÛU_ÿüÌ™ÓÜž÷¿—ol\  ¤¤‡jÀ>Õw}d²'ïv¸«EC_þÙçZ9ÆÀÌç§÷%{ÓwÝÀ@ˆˆAyzwÓ;Œá³/ˆÁü.Ÿ¾£H%ïÓL@ ]‰+Sø©{Ôÿþt¡”2FRJ)eLýgïñ# 34?¦$“òûÿõ]õÍ÷îF~ ›÷î²ÁUUÝæÕdÞëê–(/×c£Gi¾gØÈ‹ï°Œ¼îþÌâÓ;RóïŽiÕ ªôZPÅú>á΀ïü8¹þ±«ûÈLÛµCWÿå‚sþî¦w|æÜKçþ a[Hz9.¿—«ãWñj ?¸CÕ¿lXø‹zaUjš,Í_LYxeÞU×3ýĶ ñ«¼]¦XUõy›—´¦æQïîÕ?d7òóò…;€ÒÒ5ÖýoäSP*þ=N`íåßÙVîz™ëCÿ63Á›®yr+cdfí¶ÅMj¹Ó›ë_piÑåæƒ °Îݲekˈ#ŽäýU¼ªŒüÕ¸:Ù_©á7ÅH𮤔$)fyøØ¾cû 4fèhåècRþÛþ¥ûEìpŸ?¿2hæ½À¼y¥™¹†{jF>PæÝ\Úáîµ—.ßíjhx^1Å?ÆÒ4ïHÁ¿Çoò®ü®]»vîÜïý´‡í÷z”à\5œá]:Wy·*#3=;‚q. !ø¶mÛ9ãݧUu>³ «^ÆËI½ÿáÙG¿L#$BæîÞæNj±•·>kt7ïvumlàÇ-Vü£ªª»ÙsF>e,”ïp‡-ŸihxAu”ìJÚ¿'¸‰Ç_|JíŽÇ¦N™ @p†®pg:ÉaøÞ½çšÏœ>¶:@n|v'”WgD` u`ºøW>|ÀUç_mº{«ºfóæÍœ}öYç]?¥Çq®Â*eä\‹>ÊžýëJåÙ‰dL™ÎÝÅ,O[öoÎ/È/ŒÉ˜:ëÿñ•ø¿÷Þï˜=»8þ=ÃæÝ. wÆPY™—”yw¿ó¢ÅwÖ[©eÊ ÜÑßµìpÏ¢—Oß¼#Y¾'¾CÓ7ÿs-vîÜÉÀô©“Ñ;ÜU±Íÿý—stœ[Ú±™MœˆÍˆß´|uH]V{0xiõJ×^x­ýq6oÚDÀ9眰³¯›g´/á%º=¿š+j_—RIiÆ2DælªõXL}‡b{Žì¨ ‚$I2“ô•ïþ]ü—K½°Œuþl~¦|á®USó¨õj±ÊÊx»¨îõõÏ(+s¿­nmíS:Ðt‘òâ{‚p×Ò”Ï â];R®ìS¿üÞ,ÎÙÔ)“§L9“3lß±cçÎqà΀ÿrSkñ§£´§ºö×µfMUE¤àÖ1çœs®fùÔ㜛Žàœ1õÁy?浸V™÷X±+ìßzmÉ{«ßQ;C .T¤–V nnf3ó÷¿ÛÃ9?}Ô™†šY5B¡„{ÎÌŸ_¡±¾hQm.±‰£ªª»µy_¼ø±ìÆÿª¨¸]c½¶öéÚÚ§Ó¿f?†;€ÒÒÙ×$Á÷d·WUnAp6eÊdµŠuçŽÛ·ï0+â»Â]üb¼ˆÅž›Ìœ®/%,jsf²›ë ^-[³¾4¿µyÓÆÙÌ™3Õ×Û^ý[ŸÖæýE¼ø^xûñµo=ö¡ÂPA¿ú‚¢sXÃjyfaì>¼“1~æØ©†™Ë«n }ðÝñÂ:(Ÿà«1ùÁ¼kUVÞ­ÌûâÅõHù@™÷çæ…yת¨¸ÍuÊ÷oióžIÊ»eÞ‘8ßSØ;›1ffîœM2ÅŠh°cÇöíïè„;cÜ:ç¿=í›±ˆ ¹Ë¾´–›§Ê¸[¿qÁ9þíå ÜtéÍ ëæ÷8gœq¦ü½²ù í¸1sæ`€öeì€Ø@P72¬Ïfy“ìöM£ÄŽÛ8çÓÆe™}Ãà†ÁÅ¿JÒ¢)Ÿ3ò}Jç3½Q>'»”Oí"ýÛ¼Û¥<èå½ÊgüìÿÁÖ(˜16}Ú”iÓ¦¨v4Û?Þn.s˜•ՀĨ¯Yð‹Ê‚çO°{ÕÑ&ÓÍ/ìÔÖ~^=¸qã&Æx‘eÞ¹‚%ú“6-ßZøÜ¤‰l¢J{ðý\®PÎ “ðLÜ$½Á ƒ[÷nfŒÏ˜pŽÑ¹Ó·Â~ #ã¿qú-®ñ•y·«²ò®î””y_°²²›2vGMùT|€ö´sa¨ʧ=¤^å¢yG‚|OÁ¼›WçŒ[pç ªNrú´)S§Må ¼}û¶5ÜÛ–¯’±ã¡+óq&Ù¯ ËU odÌÿ23z·*#a~—qÎùÆqΊ‹Š¸u–z|ÏÛ‡ãvÓÊ]Wì°î!&ò‰gð3â÷óý*ÙW^^ƒÛv|ýhÏÆøy‹ìqÕÊ8Ýúr¡|âêJùdz;˜@(µ¸&ååµ™—Ú¨Ooꔦº†ò/ºrMOå¡WWg]áÎTSøiÓ§©îoûøãmÛ8˜çŒ8$q\:â·ãþO‚s¦ËÞy—Ô…q""‚-‘çælïúôÞÆùñ«ÛºêMv«³ \œ!ÎPOÞÏ÷äÔ· Ê !¸X¿sc¼èÌÙú­P^7îùZ¼¥%‰¿q&ÊOŸn˜z ßšw‡å×­ûhݺ²=–„ä.ƒ’U²¡|€àî‘lFþEw)ï®yGâûó%eÞ÷½~rüåÃÔñ?Ý`¯™Qp‡-–ùxÛ6ÆX¿cH¨eðÅaN$ 9Qè£,4tîCßP—3kˆ:Øÿv³:xtå#îžcÖQÐÔ´„¢YEª%ógÑÇPw½yHÕ=‘$ë ý•´þ·Gî@RŽ“ôïo~À¬)çY«X¥U3IRJºíæ¸øÂ*i¾;j(;¦ í²‰±Å¹z }îJ]Í;c wfm4q•f:ì®,¸³ŠŠ^ñ ¾{ýÆi‡{iéõi^Íu¸#µþÀ=jë {m=ùáµ­D ¹ƒóléhëîŒaÆŒi lÛ¶máÐ) m q¤¼h¤±6òö—?3ÿ‡Ö¶*b»8_ÝtÐÀ¸Ï pð½bÄÀ‰‘²êVy4\-{:ðÁÉÂóÍ7ž½ï•ÎÕ'b IŒ¤$Fj프$"FLB2b“Ø$)é¾g?ÛG’&°ñomx‡1vÁôOK’$9“’qÉ‹1¦¾ä“€rþü …x{?ƒæ£˜毜iÌïÀ9Óñ‘ûˆ”€ÊÊ;,^ü¬ÝŽ|¾siÖe%òËjk—èNùÜíRLW”W¿§OywÕ‡ã1ßt£°&umºYãÂEx˜¡V…¾ðÊÕóQ?qu¨‚HUÏÁÀÐüÒ»²ý QÌ8IN48$8$'9¹e*D˜;‹]4º³ë;€ÆeK6þîËæðþú×E¾ð…ùú^‡>l`ï?vöPûWŸHJIIm×MoþÏþ•éÚ÷Ðn’Xß´ÀµçÌÑËXÕyÒZ×J’æ,¸,…6qÙƒš;~ô/ ò—Âðß·À7ù,óÞ#Ê­ÝìüÔ$Ý·»)¾+Ù)Ÿã{oÒ^>5Ä{aÞ‘”qáëÂ6©¨ËºMv2@5õŒ™5äКV×_Y°òÕƒjÕ) 4 a}þ~…ÚÌ 02 Aˆu´E:¢4XˆØø!âP­eçÎzÿý#^0Ú´å„úeî¸ô®Ãk[aKoìû©<¼¦Up. Œ¤$.ˆ$‘ ’“è FL2©¸Ï$ˆ‘òõrÇêí``Œw^Ñ~:@$Çcc’¤äÄ%“’qNR2™øk›štå?º¯bøÇQ„¼™R J¥D¯ÒÚ©ß}ƒxçÞ{>‘bº¢¼öò9¸Ç‘öò©yOþ‰Åóï¿ýÞŸ7Æg¶Ð⪑–ajÙŽê¨eßjC׉ó —8¼¶Uñråk?™Ý­Ÿ‰Ð¶s_¬£ù؈9¤E;Ú9‘ Ùvx"€â‚a8{–Ú-äü Fª'5<Ý ìöRõOäýÕ«pá…ôúî:´¦…$,ßn3ð&ÓÉÈK+ §ï¼ÀÕ³®’D{änõÔ r‚6ñ¦÷—òÊŠ‹z¼µ+æ]kï¬i ³˜dRŽû k]óïÕÕõæÍ+s낞*‘mŒÔb}ÆÈîÞI•Ùù§[aoR|×½3r|¯BùÆÆJJz«KYNÿþ»þÙ¡0N?)¸[0Æ4æ0wúeÌ4éVºÝù\=¥IÀþ·š!ªÃãÇu;0Æ"s£Ž¶]bÑVcœQ@3Ìf_!jnû(ÖÑ®ž¸öàÉÙX‹³gáƒÕÇ>uÁHꉈöþê÷\xá…D8´¦Uÿ]ŒÃzÔú°U¨˜†ˆH2âDĈ$#°1bËßZοî¢ëúA¤Bù=´gÛGãÙx’Œ———]êJÚuþ¹à¡?ˤl9qœblÿý|ÈwûÔt|80#ø¾qEÅíj"1‹†´¡áE€ù-®íQÊËßÿ/ÏÈöpúVvë‘’ 彃;ãû÷þÐ0B!Õ)E„ kS²1™:ÿÑY WFÜnÿÉ:Á c ×\Qðòë‡@`:¿a 2Ûi™Y¼ªŸ„eë ùá³Â# ´öÀîYç˜O]óÁ±YŸ!¥cÖ0Ôêù²ºiÖ÷zà\Ý"IX^^25ÑJ$H‚žyã9ÎØõŸù,‘dDĈK)‰1Ð$š¸›öh?í'Èñ4ÁÞ)Ì!ÍûÇÍüñ±:x$‹ÉæãÇÁp²åäc‹êæÏ/OóÁ2ïuuËc‰ðZM$*¾h)f¶TW·L“=@)MÕ•ò+JK=ÁwŸW_v*Ö6 !8?vüg¼`ìØÎ¢r®ÊÍ­EFÌüM0Á87{˜'1ÕØ«à¢üÖýu›»[™ ä0k%Õž©ˆ?%eDäq@Œ©þ¿`ÆŒqª- °ü!#Z'‚ â-olk‹}qn%€÷Wà‚ /èš41»“kÝ9u rjäÔþÈq=¬):!ÍÀn¾ün=ë[ê#؈áløIœd`ͬ¹…µœun‰Õþ'ºK~:Úô™OK&Fþ Q Æeˆ1ƒ†¯ŽvD:>ºò¦5kšÖ¬iê¾zâZ»¶ ÀìÙIo¦˜y¥ÀâⳊ‹ÏjjÚ ©ikSÓÖÌøSÖZÄ‘z‰ÊËoµ½\[šš¶Ÿ•í¡9•]óîPQÑÔ¢¢©ë×oW¿ŠŠ¦:NðÔ¼àLGàªfDÀñŽCGÚZ6\%&”[†¹Í©:@`ԙĨÆ"¬éÏ-4rÛΩ LFZT¼åââŒ0fh!©Ô›Ç‰±æ#GZO:~¨9‘±ùÞëGÉš51ªÇª°ú»Ku@8´¶õðÚV5+ëÐØ †^˜?îÓÃl-øS«žb`·_}{g‹J«Ùîq¦ª†&ò‰ÅDÎyû]G—b©GfJQŠòŒ1("("bCF…ŒÙ²gØðaÚ¼/ZT—Ú-‚eÞÍ1y•—ߢͻêÐë½RjVäxã,/¿U«oå_Ú¼74¬hhXÿdwÅþã[?0ŒPÈ"¡C-‡#‘ˆ!ØÐ!Cã\õþbìÜ3Š ³®Ù)×¶P³sŠUX}¸8çã/¦«à_yã ¬6]RBëž#‘¶“áÑÜ$1ˆIó`ıŒèð±V06fø`•û[;ä½zÛs./ºëT[k8ÜQ8®$#FÓÏÉ´wg]M|ç׎:Kôàx¯yéóp†;¯½«kí¤%©§^uý$‘¤™·MQ|'Ð\ÌUWsѼxÙ5 TpßFRM«rë@ýõÇ¿À›[aã{RqM°àîÖ¶õõϪ¿Þµ‚Qÿ³õ=Åù`TW·L½\>I·|eÞ»Ë÷ÒÒ9^›wœ˜Ëiï UèB#B£O9Qylqoúdý†]Mëv¬Ag4ÎÝ6tVoYþ.7a¸ò²±fÁ; ÊÞkUb†ðÄ#óKóÓ‚éÇ €”ÑÂ!a0ööÆ'‰è‚i7:Õ bíía"FŒ@غ±yˆ“›×ŸÜ´þdgíK¦^6éÝÔ?ýþy¡Ï_½e—#‡—·öpîQiDc#]ü3{ïÊ¢<4ú[cM·®~ï2"dDPDȈïì=a7ò){yŸËLËÒ–nÏ[_¿\ÁÂؼ;d3òϨiŒ,ÊçpPZ:G¿¯+¸{-öà½?¡ji 6”çÜ(((07FHBÛöm¶šøšÝÚ/9ïR®¶¶ÐÎ]íyjõèÉ0‡Ö´@D¯½uXqºs–  õ“c­'Â#™År ¨0E2F±ýð‰V6fØ`.c X¹MmØTvüÄq¡P^ÞQcÂêŠzÆUIš3Á8wÖðžMýÏßþ¶ÃÔ?öú#œs½x f_³P²K ¥½Œ’hÆ §Ù_b÷—W½B ß]ýÛ4ÿÀÞºæ6F4î«Ã %—’ÅLçn~)%IFrlÃxµK îñ½|°Ì{}ýspÛq[pW^ÞµõG-cñN Îjh¸gÑÈûŸïv=øàÿ˜9sЧþ]\sÙœC'Û[;:8gCx˜3>fÌhÁj:U0Áç#Ç9~üè PµÃ{íùäàî]ûwíÚ·cÊiSÌÝ’S3¬œ±ü38u ¢H»{Ï)ÂëèÐzàcƒ˜v5Œ˜š_ ƒÕçSyá“Qbüo­{£ÑèŒÊ2”q ‰œ<ÑòÁyÜ^ýc•ù€€ûÛìkß¿¿ýÀ¾¶Âqƒtˆ{û]vü(V¿K/×à¯{ª!ÖNŸŸs}J6ÿŒpþƒZÿéœ`åêÀú4ÂclÔÔ|ûK<3gbæ²Ïhž|²©ÏU¾ví]’ñ‰C”!ÆåjZ1Ž˜:`ˆ²Áï¬çyÃðÅoØŸ>{vñìÙÅkÖ4ˆ?ï iU:ëM/*šÞÔ´ @SÓÇEEÓÒ¿f…;€ââÅÅ3Ô¤kSÓ–Ì—Q î + FŽ ¦^gÎtN½º"~à„Yâ2~ÄHFf½£‚#cŒ¬¢E¨éTÆfM;ÿ‚]tögìø¥÷W¾øÞ‹ËßZn=—8ðn‹ú® c.¿dLg9$Æ8c­{O0°ðpaÎ×cÄt ³¦nÍ1H@máAaN;–—————§îÛÚÒúÉžãzBÕËtÖnªh͇Ç×|xìÃŽ®^øÚ#gÊa Þ™Þ,~¢@é­%éÍšÎôfÜÅùã/6á²á‚ÛšYZ³=v©\¸°æâ—(ó^‡ºZ¤ÒãwÍ?ý6ÆC…7Zå0*Š¡(™ §ˆ ˆ‹‡Ê¯†®ë¡—äüùåʼ÷×˼{w”•ݨ̻K;+™yH¨ØTK›w?Ä5þWIÉmÞ=ŠkØÃ/9´}[H„ZZZ nŒ3Ö0Bj‰“0 Cæ>¢Â0„Á…ÈÂ0Ô®B!^ù`”çS  îºî.Îyá§óÍ)V€ˆÞ|û°}Ù*Zöèh>ÆÌµ¢¢¡ƒGêÕ®Gš[‰hl~o~¼`çŸs'šO# püœj=E špú²&Øãê4ôêˆE›6<ŧ…°PѬÍ{žãœ•ßaÒ­Ï)Ù}ož´7®9ãªQÝ_å…ã0ˆ ß½@êÈZaP‰$ªàW\£„ÐÐ IDAT_¢Ó+‡0)™$&c,&9‹Åô#<&™õÈ„U›`E¯«œz‹kÄ÷Œ-µàÎzƒ‘—””\ëæ°¼T:ËìpÏ@b8óŽnìß™»ïÌþ?ùîµ6ŸÜ(;ÖzÈ0:Énp«3­~Ɔ•¹›üóo=¯ùÍÀÂcw\r—É÷wŽèÆ ñG¶ü @8¤Êf`Eð„¼Á#:W¼nnÃØ!y¯m{ž.Ÿz]Û áyCóîÝ bÆæokë)¡Ž±êAåÙAfE'¬9["D›>O a[cá7bÛcŸ)¾]ïâÏØ:š™?“>3kÈþ7š%I"œvÅpÇK¼paÍ]ß¾wxÁp„Á onP‹Zë³Íü>ÿœ–ßøwŒè´’<.‰IÉdŒK‰qiÂˤäŠûRòöözx¾U¬ÊîÈøké¹¢|r±i°øîÊš¯Œ…òý€ï0o¢Å-Ê›|W‹*ï:aµ–ÕFvaè²HmÞ­cª,Ò°wV$O®zRó½õpDµj,½½ôíwލв¯¹íä‰p>ÓÁ 3;ƒ+P’Šà·´0`ô¼7¶=Ï /Ÿ6‡FìÛ BÁ„ Öûµ¶ž Â’PXP`¹u»'‘õ›Ižâ“ õ"¼µz5c¸øŠy]ÖæZ!W\:Æ|Õ®³Ô5‘í3?eümclâ„ðè03<· @ jÔHâ#þ™›þ $ϸ+Yøf1âc1É$1б˜ÔÜg1b$y,6~ÅóÆ˜ x.¡.Šòº%Ã|o+º@DÛæÍlð™Øšh[…l­²ªÜc‰þ;ll|@IÉ5ÞÊ]¹¸¦×kÊ÷¸k©I¥ô)ß…ïjÑü~ò×aYx…u#ä »­l¦K‹1aîbgŽût>€ßþ诺?סCíÊË_\|GË–¶ã'BC³–!Y‘¢ð áÌòÜ im°ñ“Wº|ê\D9g4¼öïßÇ@cÇW?ZOµàFûØÂBíà-v@ÇÆ-;ÅÏ ¼õÑû씼dô8¯¸Óï[™=¬j}Íú«/‹n ;ÛYDo¯yß7å ΢#ÇìÅ`5ƒ?Pjõ3/À‚îBOÞò &åé·ç1)¹Œ±q’ˆÅ˜$N’Åb\JH›‘i#/'nZÏ$JÌûîû.€Ù³‹3Ö øø…W±~b½eƒ'%¾9Iv ÉVªy¤>-ù@†»ý‚V¥|ºkì Ü‘ð_ZMù4ψ¨{G”¿ü|±¶ðB!Ãòï&Í ÃAv.l^wç…Ÿ@GðµOÔ)¾«‰ÌH[4‰\0åFiUFZ| ¦š) =Õ `ãžWtùÔ ^«ìþÀþ} ǃ8d^­°ðTë)j'`ìØ¼«t¨½i>ÉxsûŒáâ³.äŸÄÀ9Å:A’¦gdsþ¤z£¬KÑœ+ ?5Ôñâ:4¾äææCÑã?ý](|tÚ/ïbƒehÔp b„E¿ýŽå¿€/è+¼óOòÎÊ3nl‹_$—1&‰Å$£‹W¶Ýz„Û¾uæŽÕðt²ÞìÐPU57‘'¦¦ƒ—ß!¢í"ÚÆ7…ÿtH†çoby“°)¡Ñú¤E¥.ˆ¼46®PRru†Æ”ž¼ëÆ£Ö ÁUÄ‹ïÉ:’ô)ß3ß•ê~ýˆž\)п۰nhç.¬}¨9çã>mn–¤«à x÷Ý#{sÍãí'Û`œ !I^8é³;‘4ÔL ¬:vÓ¿ïYàÊé7ÄCQÃð5µ{ðÀ> ÇËC‡@QFhmm…Ñ¡®3¶`¬jiоi»Œµˆ3Ä›ÞaŒ]rÖEØîVð0[¤ïÜiÛ©ó¤Þ=:»¬m|r)Ýp‹ZxûãïÌTgu_°ºïê¹'´Ÿz臡ðÑpøØ¤?|™ –ÆÈÑÌÆßÎcbO”ÑÑÏízû¡å»ßxéŒ1Šñ˜¿Hb1É-²ÃVóÎe V8Ã$q)'ï{žèFÌéÓf3„w6«ÇßÕDkuu£}UÕç“ÿ{Õ‡ö_uuàÈê!¿9‘T¿ýÃF6d"6öÍwŸÀ]ÉB£ûÔðýQtv‘ù9êæ–»¶%cÄ$R-@/}ô$À®;ûVMV1( à ;| Ã­à¬`´c……öŸoèpãÀÁC ÚL°Žw޽Ç.=çbÛ|o x˜Î™ Ø"xfªæ\-Û"m±ÕË©d䘨dÓo&¶wH¢3'Ÿ)%$HJH¢‡ð)ïÏþçqÿ߇ÂG :VxúïÄ‘SâЩÐ+Û!ˆƒðѬ?ÓFrÜ4"–¯»Ê˜«U¥dǹí[Œ$7Éná^Ʀµ½8¡Ùqnñ‰áˆ±a)C1’y…#B–µ3„p¬õBlÙ§ÿ§²òsIýÝrhûí_çÑŽØþ7E,šÿãqŒ$'Ɉ4å‡þËZ>ô 4õÁw?ÇÙ«¬¦×˜;÷ª%K^QYVÂÊd«dEyõo[·…H\îZ)P>¡ýùnÿÒg,¯^¥ºÂ¨äCqO5óU_‘fž.\ïi1‡Úëºõp‹U®¨4ᢉsôë'§¨ ì>¾‹:ù €$1Fàc‚ˆ9F2ÂG²±tèHÇÁƒ‘±¡ ­<Å,Ël>-;†€ãûO¼³ï>ˆ]rî¥d&GÌü1n(ßßB›špV‘Ó³ëKYÿÝkÀqD#±VÂÐ|Úòüvã,0L<9FV‰'À€üè䘫ÛZ¢´çÚ@m2b±“F䬉|_»8µ?vÙûwýøÕa_–tL¶0)GÈ|®bŒãÄXŒ™_JÆ%c’ñƒd1b<Ƙd\JH&%ëm»¾ðÆuCf}æØQ~ ùÌMßø™!NŽ# Ñl-Sj¿‰±A’… a9,,ObØ-kþϱߤF=¸sб« [¶$ò—Gië]÷  ýÀ{#¾'I â1’Œ$'ƈ3’`HdsAWúÌx$²+¬/Yòj¶‡“œ2Ü_9wÕ°³¾þÙ?ÐTRrB|cãÊD&ö‘ ×ZYû:·&WMÛÎ {ÛH!8gºP’s! .0gíük/îo>ØDzŬ:`$I‚dxˆbý''vJX€M{ÞpöiM>I¿3ðÁÑØ)Fœñ\m»ÁˆîP0át«Úæ›ZDÅðÖ{u Ø¥³.Vï\í1e¹xq°"Dg鿦m·Òv5Þ²ƒˆboÕ# ÄÀd„ËDGʶhÞ ™·“DI’$‘Jo$áØÛÚ[:ØäÓˆu ò1uÄݺP<Å÷·E·ÄÌH{ûèÄ¿ð˜,ù?:œáRÂΨõZ§iÍõá …¨‹·ŠõÂüZ°‚#ßþaH4[|oFKáô:ÑrŠŸ<Å·‹cíìp;ØAü¨Œ„½dIŸ+×›ü„Ç"§v¬ÑèèûÆ0’\JF˜=8“ê˜9òo‰a§ãƒ^‡ ¸cɒר$OÉçÃÎú&VöÎûñ),¸Ã³BÞDj·ø<ÜMW̽蒻χU˜H€ÚË W{x–ÕöW·³ý2ÛΘ¿¢¡!fÕ;¤îåK!1hxþø‘ùãap"ÈhlëþOvûdçñ]Ûwï„Ù¯FÕÌó1BáÐÁÖ¨ˆ@¯°T½›]tæ í‘D`L0&ˆÊh4¦Cfͽmá«™=1"¢?y­o>ÁŠ¶Ñ¤!4>ŸB‚Œœ Þs‚ & r¢csç'tÆ­Þjjo†ÂkΓÌhýˆÈlæË¥d$ñ‹~7 GbaÝò7ù‘QÃ"£dDì›xq4Œ¯/¡ˆa}×ÜßÃü2ª»s[r ª‡F4°jffnycÄiƒ‡ÿüû¡ÿúŸŽÈH$¯#’ ŽQ8FaIa‰P †d !ɈI ®nYo=FÖ|ù7ë¾ôß'w¼6õ‘ÍÚòÖÈoŽ“.#F,jÄ"‚:u2bÄ""2bȈ ÖkšÔõ/Q¤:ÍÍ{•Æú’%«Ô‡*ëpPVv³­9ósY‰»òn•†ÆzcãKšõ¥Âw¥ÏÜÙY$Nfúa­,êŒ4z–ÜqD7³~S¿ÈÚáCQUEóCL&8&O*:#ÿ´‰Ã'@\ùlß½ëãÝ;?ÞµKßwlA!T'¼¶z €ËfÝsá™×«G:"#Ô²*Î$±hDF"’$¢#†ÊX[6èwIDÛ6ÐÇ÷íZÇD;Ú¦Îçæ#ÄÉ0B!N!nì>;:š;¶¼Ñ¶ùmýƒ+¼«~“ È1X2A’“DüŸ®ùËyéhÏ‹DÑhØB¶Éq3V¼á᎙çDŒØÑH³ŒÙaÈ—Q!;¬Í›:¬ó; ³|TœðrÃȉyD\üÏ:"y‘H^$’‹ ŠÉp !©~qC2ƒIŸ;øSõÄúúgÿ?üêïßöê˜/ ³vq²5Öaȃ"ܤ|Ä¢|ÄQ³!šŒñó÷¥K_cŒÍ{e?‰´té»çžËÕ—vÊ«Zøœz“¦|}ýs=R>X潡áE€¼[‚WRrm|ʧÎwÞ6óS·œ tâÃd«ãK»ömo%ðPHí¶ ç¶*2"³áXhì°X¾AflMjÀާˆ€¡˜tÚä3'Lž<áÌɧM’dκnÛµ{ëá=[ï!`lA:øÚêF—~ꞎã-ƆpurGt„*"g08ê}%“Ñ(E‡å#Ú¶lÞȶnT?QËÙç·Ì˜}jÆ2"a0($`p2Œg2$x8L!A±PtÔ„XûI§¶¼gí;ÛÙ»ÀˆOMå…¿ÞüúoîZñ«Ë‹Å„P¤}p4ŽFÃÖÞ{\FD¬ÃÑ1bJ” â!PˆQD4‹6[=#¹ì0(bP„[¦˜Ëˆ ¨Ñ¾÷ D|ö‚wg¼¸èü ‹% üfQG$¯#2$“añ,$aH’BH)$…ÊËoµmk·Àûߪ^ý?Ù´*tyÓ˜/Ȉ82ü\ ³‰ßÒú4åE,bÈaõ¼4bAqç}¨ÇÍFæÎ½ZM46®²Š)}¡ººgæ“ ˜”4¾{£|Nv9(oÿVZ|W*þìôó®ŸÀÜõ©¿èzûßñ1³†¨½\pÕ—gÄ­¤éå‰344fhhl~kk+Þüð1’tíÅãÆ‘¤#ÍG‘Ç¡fDÕÓɧOš|Ú¤)§M´ `ë–5[7¯9zä“6<‰D.ûÔ\kùtC"D䈈N’Œ3ƒ1CMD£1PK„6mPoí· É—2,ca)ñØÿÏÞ{ÇYQ^ñÿŸóGÜã€Ûø—ü~9ê‘ùŽô´­´­ØÖl)XšL- -Œd·Ëß~öìé³gŸê<»á陿ݿ~Qó–e…åû/7½å Kõâô[ˆ#;ŽÿÓÒÏ-Ÿòÿæ{>ÿüwÞäÏû3¾>{á…å{îÄÏükWësÏ-=¬(¸ÙìÙ§~šòG óþÕÕGê¢üÁŽüçÒGjeeÀê ¹A'ô®xw„$þñ$f]€æõñü9§À²e>î·-j`9)Õf†¯c3¤@s³ãkc˜(1ýºu8¢$ü•a¡tya÷Ëé’È/¬ªØ`gÝZ"ê‘]¾cÛj"QRØ÷àÍ)ý,"‡³Mn÷_ˆ €æ¨¯§áTÊ•a&ÚÛ×ljNÿçÄ[Ëí¬JöKÃH%´ ¿Ìq°µ6šY¬™=KšN2^¹!X6òO·úÄÅdsÈÕ9ÿž«÷:©®fÌA³•kC¹ƒJO¿îÛ][½qáŸI)O˜,ÔÎc¯,~åÁÍÏl ¬¢ª­UÇ„V9:K¥«P¬”‚Ðbkýe£J_ÁexìÀÔå!ÎûÁ6àÅß©Y7Õ=úRMa´×õA°Gì1+"¤ ”Rf²¹²«Næª_¾”çîñ’Éì™aÁiu2Ãbí‘å KY™Ú•‚‰Ykf"‚5|I‘&JÏÿËçÐäØÿ‡¯ê#Þ‡ûsÏ-økLÔú¢úÖ~Iæ£ü`¸ÿ?û÷vÁÇûp÷?ÿ+¾zï>1 “¤Ažß1`j1ž¿'8 Žèœje&އòìMïËéSq×똅ŸðCì‡à9pÞÁÒò.Ad<8 ù"ŸT‡£S‰´\^wþ;âô{ƒ>I«ãû•|oÕ<;:v̬Êë5k­¹ºagúLŒÒÞe|ÐY]dIŽ -„' 0Ã3 ¡†ª Á’I€„†P$„)qÎ&«=0Ȥ´Ê’¬Yhe2³fòö£–klF~òZÐÒÌÎòZ;”²œ¤ šž­Ø:x£SŸý>ÐÄY?˜aÚsj2ìœ^E·Üàÿõ!<ä?§Ê&ìÎÑY¤5+&RZkUÞûohnGa鸩­ È-òËçß¡/º½­¹¦úžTïïÀÊ ¥  ¡]e)+ïxž»ÍÒ^gdgëX4F,"­NfØŽs±ÞÇß~dÔß¿Ù÷©çwκÀW»³ÍL"ýh×Jÿ•ÿþ ýKø°²_\IDçœ3î?néçguR~ÉׂøÃî]æSþ¦›0lØg+7ûºŠ›¢sÞ5Í÷'žxúß'øl«¬ŒšÔæåuw6B²ÀE$x{KåFHªñK!2ÀH†òÛʧ÷ Wãé?5Ÿx˜B–`…Âòlò ¤*B}cÒß/…Bï¼÷wÖäª×Ty†i‘[Ã>à'^«<ˆ+–—úÓÒ×á:‰Œ‘Ú(rðÇí¼·4¤/akŸòCžþ%øÁ÷O}o÷¸ï¥hð@ ä)ejWÜùÎÞKH©ž§e+×—´SÈΠͱŽX¤Õ S)˜@»’X0k¡5˜‚Y“Oy­Áú3ãï/¼ð€#î«:眱Ÿ—NÊ/9 +.V6oÞ]d?²¢4_—ùcÉÀg•Á:`' Áâ-Ÿ^±þÃøä³*ÜãS2³ðö‹ £¥àöo £LÒîõæÄÚì‹1yy/þ¥búµ»œ`ßÚ—ÜE<âÌËs _ô¾ec„L P§#Eg|í;ì&’ÒHd‡3Ú::Z¢±ÜŒPz—ƒ¦u5  ,[þ ˆ&™ øÑf¦°´u^/” °k¯<ÚU]韬OI™'²„Š¢óÝ̱ê5V0 f@±fô/ëÏé|¥t²Ree•fä´„ñFühÓ,{9+bÄP¿ó@HMÓ{.[Ò°ç¦cï»ÀÊg·–о£{ 55óæ¯ÜT³kqªïqÌÞÃ55mJz„TFÑ3ê²îxøýl\?¯kƒkp €ð}­Ð®`üqëÁÇùÜ[ºÖ§Üe%p0céšÉ/zë¯ÚRõx{ÿóÒÊI5—µýܚ뜓.Z³£?5 ë•ëˆ{Âè‡l(‡]Ã:ká'ˆÓÄ'b±ïô3ú®X‰©ex÷` üÿw"3ÿÊz§þêê¦)ÎûÁöépÍáIù¯Ñy?ÄD—óþÄ÷Aÿ +•8÷¨Ïܳèý—B¹¦çš»öJ¼=·æúÇSwv¾7Ð=¹¿; ;7Tu븳pYw|t³¤0.söýèVXÈÿèqaÙxþIÒªÓEi¡}J<Î@0t’žVZk& Ñ%šô¥ñÐ`殼(­f±tóëŽwagF*p;’ ʇܞH0£OqYŸâ}JúwvWuÕ®½••ûë+÷×ûy§­õ;Á^ Ü¿••U•••bŸJþÜ1ý»;áÏ/5>6¯þñ'ê¦?PzÚïJN¿£øô›{}{ÁeÃg=âÌ ®Ú£Ok¬jØ|ÿ½+ŸÙ V}ÇÈ3ßüƒËÁï³&[Ó¡þ»’RI™Uô ".µ+Õ¶íë>}_¾yÍߺz! °ÁÚØª‡dÇ8”X;šÐÜÀi::èxA(§ nm<°)£î˜Õ=·â¹àÖ…¹Ž#6”œ‹°&[Ãòe0ŸT¤Ŏ…ϬQÂl¸ò€³í¬ŸjÉeï?­]ƒÝ´ŽS¹†v v$§"µ+Ó™ö +:÷Ü ÿ~öÏ®%çœ3æ¿Ûý` Äüù‹çÏ_üï·ÿÿ•‚òON½¾ùµ}­#Á€K/ýf—ó~(å߈R‰ïó‰î)ǃåJßxtÌ{÷zŽ]uç²x{î¾­¿õ‚A/ôÂÁT^YûYç'½P¦ó7”7r’FüØI z<;±Aç]’NwBZSæOªv•rtÚ‰;éÿ´î¬q ³wU¬áNU%–lzÀäñv¥œú;ºŽã¿dB ´%ºóÁPZ< ´¸¬OñN—°3UÕ6<ÿöÒšü¸ÿ€eeÊ|'­ª²ªª² @ÕÎ êß¿ßÍ×¾å†Á Ÿ @ØFØF† ™6–ß5þòã.üƒ—gýà¥Ù½/¹±iWGôƒ¡5`&È™Fž7ÎÕŦbó[¥ içl¦¨Kí.Ú]øÂ¿]ÙµAýµç6Î9@fh R-¶JïX­·­Š>pJûý§·ÞwÖÖ9ã±ö .8€Ûq@NáÁy? áÀYÉ€4FßÚ'«{ž†Üún763`f¸žé*“]ƒ‚ug^•¡\©ÈTRJáHáX¤ hG*G²OyWª.Ê{iÊÂ÷Ï®ö¨;/¸à„.ç½³KÔ—bG¢ó~ˆuQ~Þ¼7+Ê>Î;>]Ÿ  îÂ5÷ODÄDÔDÓrô. –{a±#‡ØsVû›¼>íA/dõ¾tDŸ!?CGû=±ßmŸ2Õ0[½U£¡­:wÿÖH÷+Çd.G‹+Ö¶²C|îÏ·nŠø!ø®¢]Õµ‰x¤EGÑ$Ú“5ïTmy.Ùš;¼×™ùÁµÿ òZz-Øï¨Áš˜…_{@kã…mB¸ÎgÚV›m¶Zf{¯Wþ `]Ñ72BVW ¤‹¿³_‚fPÕRXT àÅWø±Úëuî·°’»Þ^2|¸¸ê¥ Ù ÕÎÊg·­š_±úU;—ß[³¹-U‰êŽÄîhbW$±+Ú¡{z‰Ýñõóßü üApÚ–åÁꬋ÷„/N\Ý›=¤2ruŽ©s, Y™"nöyM…¥m½‡è@PÃp Ð4¤aÿ°ŽêázÛée0a<ÊÊV<¸bÍ_×ød@ÆÞ•5 1í¥}ÆÜûJ‹0Ë iïì ­øªvå¤ó˜È<ààË(œÐ…õùóßùb}ù#îŸÇfÏ>¥ 뇛/ÿµÛgë#/½ô›O<ñ÷³Ÿ¹!ò ¡`‹0X:jT)GÀQJtä±çÅ&e…>Ü nîìîsæóæ¥+ïxsÇ?ëK~’A¦C¦‹M¹N7Û‚EL[V¶[%9ªª# ])<­¦Ž[jñÑ_(ë‚Î*6ØYSHE=)ÁJ§u¾R†Éwô³¬P{*Þ’HäÚAJ‹çyÙú—I`ÊÐSºæfNŸu“NzÓžšŠÎBaé'1¸´W™¯ÁIF[»êZ‚¡³{¡µ&¦zgPãÖŠ?¥Ýü'žxjÔ¨‘]Ëø´ø.èo~ÍEFÅcpÖŠ• ¥GÎùÎŽ…·ulÛÞ¶e±[:”I’;W·…R™Eÿ¹ÝRŒY!I%ª[TªC%:Ìh‡iî÷ÈZ°æ aºfVŽd7ºzé EOÙYíÉ~yœ"³ É´°C=÷î®>zl4·ÊÔZš[«Ø“Ú0¨±nãÈÂX“]—=Q-}wð1cЊ ±û½=T2±/Z“ãn™;Ìm+D±ñ^†Þ5摹Ý0W°&&bÖŠ‰…¡œž^5U¬móh6AA+#Þ'-¢­nFØêª ­ëƒü#%,ƒ—^ú ³Îñ…ÙG¼÷/Ê1<²àþù'Q}ÄûpŸ7ïͯő?Üœw|výÈ© Ø€ —[d =yeo#fq›àˆÐQâšO<…•§<·ßIÕÍí!£xÎcVÜÿasãü¡ã:ö/º#£oñè «^ñRºª¦§ÓØ1ù{óR»¦PÄÑeÅÒêí;"CQ8”A Þ°µ ©ÀÐúƒõó:ö¨Y ®²ó~N,·91¹V¿»þ%A4eäL¡Hìn~!˜;6jSºº0—ö,‹8 YfÀß ÕÑÎZw¾¤«"0óYÚ¾5ט½ºu fº¿ìÛ!”ßóÊp§Á)Ú´ff™ôrÑoBavÏ”ÔÑ;öÖNì>i, ¹sUs î¬n¡¾©‘H!‘ZvËÇNcKßá F¬ÝNîÓ…ÛŽ­w~,¥'¤+¥Ò“Â]1Wr21´±Òp„² vˆS¤â±CìÒî!S´’ЖÖBiƒß߬éi+£wϼAÝ™Œˆ `Ü 'Á›·sÁ:‘túŒéö$ÆÝèÿ¨w-îxÿ!}â‚—º~é¢k|ÊkùærȤ‹¦öŸô¬©¢†ŠN"Ò84Ù{`lâïëx4áW‘LS^31¼ àŸßàì³þ߯öWa lpæ™Ã¿ìùìðí!ÈÄ÷ÿZ!ÓåÂÅ”? ùþIÿ}ôë”ÛÑz÷CÚó²{Ý®”gÍÂ0-6„6DÖ²÷ëFœ®–³AtLSÐ Ç;âÄ"Úê8#d ­¿ˆz_±}Eï>5|‚ÌŸÿö¬YÓþ‹ƒApÿ_löìSºy|U”? áŽ|/+Óõ{Ùˆæ·ß×ÊS®ÇQW)/¶ùíy¹½naA®Ê#S´ ¼MKª{©=µüÏwO˜óØ®çT>tcÙÕ¿ňÝÝQ«ÛL™a3³b“ˆJF”ÕíyqÓ[…Ã,mï~¹áv¤ÖÌgXÖÑ$W·¨¯ˆûa­4À‚ˆ™ М¹ûQËépÍpW{ëw×½(S†Ÿ¥SIr¼ªö:&ò•ÜÙÁUk..,“u:ð]IPð™f0â&v6ï)±óüÇÃqv‹Ð~Kb-Îf­­‚ºJº6wÏ­úþYÃ÷7i6öÝ÷ž†©Ét–¬`–LhHiíÕÕÖjZË“º¼—á°»pcyÿè€ož @™km€BÉc®ÂüËžaŠ  õªêwêiH¤׈©êvš^¼¶ÏØç=ØÒ1GÆ=)]H‚AÒÈäøwŠÙ!îš]Í!ƒ]¡ pJ°0…vˆMÁ&éîh •)X½7½tVÞ¼À€ŸÜ⇭·Ý^¿µyç[[ûœF†×\ýf%UÉøÑ £tʰìŒCgÿNúóœO>"žž#\‡—’ž]ME²t›$¤uÐ s¼#A¬£ŽGšFG,ÐÙg¶*÷°² 6tæ™C¿²3vRþíùóßfÆìÙÿ åûåíŸ ×¼`öì“¿¸owĘñï|À£4Z Ç$­ çâ_'Ͱc3äǰ ®<5ÖzñQ·xP ä´ "ƒ‚Eûï œ£•  ï5s+ºqÛ÷·ë‘ÝFŽéÑo]Å2ml~c[Ñc̦µ+ßí52ÿ2íÆ*C œÖÑøÏdB·×8~ÕÜP5ÃÔÌ/);E’éIU?w‰-ˆ™sŒP«[²öy)DIï1»Z÷qZá+Þ¤ìßo(œ¸rIß!GçÛuµíö/D† ¶íMµÌ“` %˜¹°d 4…jÿÖZð ¥…b ±ïmfíÃa6‰B‘};ܨr•ÙÞwŠ—$¯ sï»öÑ5K¶üf6€µ+=bÎzz+´wñÛ_ÿ- 9ð”‘h|…§C+ÿþúL¼ë½­‰ØòP* ¸Úóäk"á4ó1¦ÝÑ“¶Ù¹ýöªïÿ ÐHøË’§„ YKânà&Í.³mh"&Ch‡ª_k’ZKmÈb±aà”Í^›hczœîûiî/»†Åà›¹uæýaÄNß3ïeÿÍiï’µo?Ë$¾áÌFãÖ&óhõ‰¥“dfØ$ –ˆ¸”tEÒ£˜Ò #Û<ÒøÃúÝW .G´BÄœèH0ëX"#%øþui7gÍš6þÛæÍ{àÏI®gŸ}  /<‚¾$ë¤ü[þç—‡øÃÓy‡¿òÊ+<òÈ#þªš.j†RFÐ1ÂŽpdÀ3®´øÔðPÁL̽†,ËŒü©1ó%M¥…)%•›ˆ{Þ¶šØsm×±½$¼¼°çª×XG­?®  ›+Ñ”o^žs¯TXÏúK¸ —‘y€ü0#oãÐS"ûø®0þ[ú£_?ž¬iê=¨‡H¥"Îb¯¢­¤ ùVaIrÈ/Ÿ°ðò¿ éIé éIéJáMØ}˜*N½Z³(³˜]b—Øܨ´§µK°ÍÚ'¥f©XØf„Y ±Û+ppÿc?`†› 7á&«ÿ2Rq¥CÙƒ6—ÏŒDɽ/.mtOa’ É ™øø]Sž¸'=šœ?#•ÜðƒÑÚ–¾—{¦Ûa:1#7âIÝØ.;ŽI¶ÝÞÕV[°Æe.Sðîu¯ŸuÖ!Uz;{ùå­Î8cð×ø|¾ûËÿ^Ï>û€#î_FnªOy"ÌšõSþ°…;¹woÖxôÑGºVe]znÑwÏèyÅô’oŸÔç[Çõ¿drtÕÖŒžYM5†§‚‘Ö!ÑèD¢­noöþýYu YÜÖP]3xç»UÂB°¨W¨[DCc¼Ù  Ëæºê¼!ÇæÅëëAÂ0ŠwÕÆ’ù…F`Ï2 £hÊi¡LcíÚuÚ $9¨] i ì¬Z _¿£Œ !C¦ ™{[vµÅ£­©X[*VQñ!•ö' J³K²Y¹¬ü ›+Xœ•M©$¥âœŠ»lù¥«l®£…ÅB C v›MSâU:º'U=N5å¥ò[îhʽ£–Û¯§ËÐZê@^"¡tRÃÈ©:7E:7äÙÊ ï[m=êý‰µ» €ïÏ0jÔÈQ£FîhÞlì­«ßÕ>rÆÄšµVûþÍEå=Ü«´fÕn3ÐX4æ¼ü0˜]yô7ºm}ôµÔþÆêí%M-¥¡‚’Q·\UýÁ ®ßÛS¸å/þ x樊×ú½îëq[ï&ÛOý‰R¥íÕCÚö m«.©Ö€JZMOz‘õR¦eE_¼¯phGáìUõdpù÷ÇïÁ„’~Xà]¼¾§Ðc~z~ÍÒm©¦ÂâÉ< Og÷îÙãÌK{NZûÑV@ìy¯V5ï63̆j™hþK^É.$Rˆ§êÞjIEùƒF½ÛŠÇ>&cáî=ûYIgW¾v¤è~<‘(›”Ÿ“o -[·²Iæ·´÷+è••_d5}¼Qн—57¹ƒ½rDhçfä5…5kֺќ=õ€ ¢Eoþ@Ù°) †ãtž™¨Üþ‘!åˆÓäåØÄ,”r gfi#Ö–F¹%Ø)f×È8G¸Äðñ•ºm‚Ù×oôûîzþ¡y­DôƒÐuÝ¡UPA*¥4H™¶ç°i`!;ä~× xN`ßr=pè‡cgV¯8¤‰èê?¾¼~ýÇæ¤>mïEÆ=:#o#<ÕÊ©~ñw7<õ÷ú•[ŠËÍx{yí¦Ê“GTWÈ°ÌÆð¢½ÊsGÿü†CnÁ›ÿ‰¤wLÕmÒ¦-3åÇú5 ­…bÉZh-éÙ7í°ZŠ/î0šIiRš×Dà.½–=À%{ê̽çZ€Ûn(¸7{í‘5÷/Êé·yÐÃK¢çìàž%âë^ËSê7„”'´+\W¸^ãܱ¿qÐmF*fÄF<žJFL8¹Á­åxg €%ÇÝ+˜;Ûm³ïÈÇ;’{=Ûx¾ò¥—>î,ý€3Ïö_Žî/Í^ye€™3~Ý_ä€Í›÷–?Ø?=øÿœ÷ƒmþü·º–ÿw_þpvÞñyê×Ï=_# `+²,E–†E–v¬ÁEò/Û›jm«škÛ) ˜ó™™‰ †”ïYÁáÌhÓ*Ìàh»ÊÌ2ûî¤có{—Ù;vY¾ÚeíšuzZ±ÝY¡ 3ª«7oÛò>€ò!ǰ›ê* Ù';ÿ AB7öt„­æ¶T“+ó EfŠ44±²rGbÙ®V‚¢Mf#edd“G̬Óɰ¥ndÀeÓedüxj†¼0»hÁÎu;WŸ£YÛ¤•« ©`(ÖÚ"ͤ˜…bX\[²X «¼pÿØá›Í?Öbp®¿ùà ¸æÿ$àŠ?ÝôÄÏ96k;*"¯Ç;%ÌB@{u«÷lyáGp­PAϪU~gÔûQ³5Y>cäŽgþ ·tfð˜>Ÿ¾/§<õ½·.zXà¹n–OvñJËàÓÏyÂò`xûU·YRf²Üê%I5 one‹pÕOàÁ¶<\ýƪÉ|òÂW¸·J8?»î§•/mÙñµg|Rß­=}ÏŠßû¸AH&÷¼µ‰R¹nkuDj-ÚS"ኸKÅMbuÑÔÌ©…ã—þÀ’ ÷ Ìš@®`ÎLgùrò 6X°`óW9‡ùùì°›!˜=ûä¯^4ò…›ßeûß÷×þÍgºOyÿó Ø>ö)ÿý÷åìíŸ7%Ü?ÝÎü3ù®ÉÒdi²™¬ ÒF²Ã’ÐRÑ "##KÆÛÜ µ‡QmÏî1°‰ *Îé?vàô\Ûžx—£Æ®^³VU·nh©ˆ&ûA3ؾù=ƒ†NîQ‚ÎÌ"ðâwž‚@t¤™Ð€â&Wg ʕĀa³ãjaˆM¤„‡bJ¨"Q "FÄé ¿¼ãŽÊõ‹Þ€;€ü¥3`Ø:ÀŒE ÂcCCxš´ ÍÒcÒLž‘[eî}jÌé[‰´õÆ~aåâ'·A„±sÑjì7ýX? …ÓŸxâïÆò MÒvž—jkªÈ–còm߀” £bÑ&&1ìØÁ)»àœ•Ïl!iŽ5àÓ7/6²›ëÎyX³ÔZh9ÿɦ"Ë#ÓRõú„<)¼ìÜ€JHŽVm…¼ðª*aæð•7Á#(báRý²Pè=m\àVÜÊ`qý}ÛHsè 3BÑYþyWÞùzVéºòsGìyþRnï'"‘DéjÒ$\‡©GH¶'Lšy¢ï¹{ï,5m!ŽJ„|î@êýöŠÕ‚hŠ!¦•)J ÍB±PZ(M‘jSAä”¶0kÍPÌcûÎ ¾î†5I‘­Èj­nTlf (Sd±´4Ùu;Ú49“j hÚ¸x+ö´piÁ7ÿzת»^ öš*öµÖ´æéyò½W|?Í÷äˆ> l˜óVÑ-ßñ„å’EÉöèI'á éÆãmRºÙ9A):e6Ò“Ò+Z¾Å5 óŸ1ø5x¨^ø.<*;ÿ$(ö{4A èy.X»mË<~¿0Ôº9£nçÛþú6±3è¼£L"™ØûòÒÄ–JmO{‚ÕÎå±öP¨lÀÓŒ;ƒKw¼Ü­ =.µ;dtœ <Òž`OÀëâ;!Í÷ì )ááïŸpÞ6Ÿï¾FÄ/\¸ÀŒ‡ãdÚ§í¦›bưaeG„ÿþÕó½Ë|¾ûËŸ“ò‡¿ó€øê)0M˜Ú—1§ÜœÒöoLwÓdïâ»å$-'a; ÓIX3žg{õщì\K)…$…Ñºí©ˆji EÙ½‚PÖGëÛî¸"|ýn‚¤‰¬nYñH=åÁJGѲ›‰(•P–¾ýÇ;ßOlZ¶ò9A8ö¸KÀlØ1h†1X£5îy—ÿ³åÀódeE‰Xª—®ÎèÂÓÂU:;SHOFR“жàk—S[«¬Ö:£G·ü³Ó‚¼›ˆÎûãö¢Æg5ê“ýÆöf? GšïZ(-ÚwJ ‘Ó·UûtoH*&•Rãj~§‡OPd+aµîiT03û—õ¼æ ±â¯y¬±‚V㯜äŸkõíTîÜ•psˆiec³V¿ò^KM{ +§ßÐioä­WwݪÕÿÜ`ÌÙ¥’å}h‹wó„í¸R':ꦜ-…+¤'…†µI鎉¾è±?s ËØˆ‰=ßÍ(èáÎÿØGdf·<ø8ƒ¯*µÃæöù«áixŠ”†bòt¤y”³YxÞG¯×–5iCxåe}„J ¯£½y|íÖ}`]2°Àç{ÕÊd¸`k¸‡“Ü/Þ˜+ ·á#UÐÊ<&ÑÅ÷üÇ_ïú]ÿ·Å_ye»_ÏkæÌψY}©öê«»œ~z߯ø¼ÿùšƒ¸Ã™ò_#Ü»ÌÏ*ðíßSþˆ€;=0˜›Sq~k©fS±©´é±¥´å)«2s–“´Ý¤å$­yW-Ù·Ç÷m+‘ÇàH«Kì ’º£„"?sêÍÌæ®  ÷¾ë2fÍéøýµÁkçN(>Æ08»dÜÒ_ç M<óX?‰)P)@亚V %>úðI3öBwP ÛÌr(]命‡Ž“gÏm¾sBŸ¿e™I…‘aœò$³dMÌ`?¢¢@“ +5[>ÜÕž}LÂèÕ-?·Þ¿7tÓKÏ€oÿã>fqÒðî‹67,©ªŸ\ÖS³TDÑ ÁD9ý[5CšXLJ4ƒ?(ü) '|Ù±öÍ}À 9ßYñä&hß+ÆøïŽïºôko½[1û¯ßYýÛ [6ool{ÙSº—ëc÷½þ¡¿ÍÇ7ß/Øvû ª?\Y2fôªù•cï<ͬÝßœ9œIfY9+ß~÷”{Dʃ$Žö!‘›Åt6£ÁWF¬LÝ–h›ufÊ3•i¥¢Q"âŒnk©L#«Ä„`H?uJtfþ¦”cšÙuåý•ÔY­Ôd(ïîÛæ¥ IDATýºvæXžDP¬EÁI“Ëo¸ÀŽ»n³vn‡!;ŽT‰3צWr¦'Ìœú…íû fëýûO´ û/*ëΜ9È—¯,\Xù•‡J»iÕkà OA§„æ°ÅpG'Ó}Êÿ×E +3ÐàÅ‹rŽí¯aj6µ6´¶”¶´2•²”²KÜ×]²=ÓÞ¶³ØÎP”?°o(BˆDDGêZrWt@€0mÌÌ’V<–ÇÝ»}ëΪædüm{ëžf“ ÀO:B´®`W[]5ÛYó‡=IDÇL¼X“ã²RKH×  ù®c .ºkk+ϸ£ö7'—üâ ÍD`ÖÌ`3•pâMV(ǃfÒ¤M;ybòÃíé6ÜD©8„My«æW²ëhÙµïœo]Eýõü^òâƒZ‹©Ã{.Ý\÷^UÝÄ%[(•ÈÎ,ޤZ3™™Yi(R®ŽŠx<[AÖ¯>¶¸wl»…'¬ü{º»áøË?‘ܿ‘£94Tðš<»Ç˜)“Vµn°|ÅZäÎýÕ•>¾ù~›ñÒ.éAµ/ï>èhµu·0(#ßmøÀ?ÚÔ7oXvÒ¬‰±$v‰ÁàÚ²BzRxyo+ÈFÑMGRF–özA,$"ñÞ|x$Ê{1™ZM“Ôd0jû\4¹Ïì³Þ9÷âüioÍù…øƒ+Ðô[ü6yÏ ~­ˆmwÝégžÖɱy‘|aºŠæzĮ̂[°¤ÇcÖ^ûøQ\ö?ŽZ_˜èÏs.\¸“§Ÿþ¥L^{mvÚjS~xØ!Iù>Ó»("þð±.Êû ÿ4åçñ[S L~jÉó˵65›ž²´¶<ÏTÊr=Ûs-ϵ7¯ê®ay°¹¬{NžB al¯ßçÅS d0•`²ƒæ¨Þã‰HxçŠáƒ¿u_2± ±õ¡ŸpïºÖºv²¬3nŸ¸þѵ̚µ~wW:uñâçœ0ã»ï¼üg8æ˜Kqº„$X»Z =ò𴌳ïÐìD‰Q”•Û6ïšÒŸ¿æ¥<s‘0¸ §o e»R)×Ì߯.i$BxÒE¾Z&ºm_8«1Vù±› ·—LZ0{À¤ïÿPÝâ[I RR¾è¶kLŸûŽR´zÿ&Ýj•eôJ04³Òì·_ÕÔÔ¦€D*S*;×IĽ•:ZC«qßò‰KÞü,kÃÝ‹!ÄÈ[o°åµí»ž{³g¿ðè›/pÿ÷ÿ obø’KÒ­}¾o©É÷”çï FZW=²zü‡fx~püBxñD›”^vv@JwPÛßT’>Üw“1mìó–žðý M0þêñ[ï¾ @ñ¥W´ÿ¶;Z˜>ß™LM‹ZM™Lq/ÖÚ¸É1S¸èø£˜o‰ÅZ[&Oĉ!U%Øm·&W.{ï¢Þ¸èÆ…áJÓ¦' O®´”0\ixó[ L"×ùçk¾¶>ßú²Ã&¯½V à´Óz©gù¢ì_± ”ÇaãȶUÏþU¸æˆâûÆcùîåR×”kmje*m*eyž¥:ɾñ­"E¶‚Ý÷˜ü¥mBC "áÆS„ ÑQý‡t4Å…LÙ‚H.ýÞÄ—ÜŠ:áì`÷¹û÷7¥šô®y?ûÖ¥÷l,™w"ƒÊº3+ÖzU,4l|åíe~Ÿl!hâ¤Kº°Á/î‡×[ç:û>“ε ,KGž»@ñUÿT0¨{YFŽ«Ùç%¼pØVj˜f<3ÙH@æ1³ýU﬒”´ØÛ7wDr_¼åŒ‰ßý#ÙFèZÅ)®4)ÞÿÝ÷Nº÷=¯²f½Úc„Õè¼fÅP ÍZkhjiÓZ%t–ÊÊwqWim˜š1ÜŽùÝìO\ï†Wa›ï\!†ßqîÖwŠØó =gÕR£~õ=+ŸÙ `›—n²áS~Á÷ŸV Âö¤í Û+è?ÀøŒ=øØLþµ^,Þ*¥—”Â-<®,oºL“4àN=êµ¥S¯êqr’‹ úéM¢õ-vF+l:@ÚÁl\{ËuËSL†°2˜dá c!$s𠱨çºz«Þ“ÒëÆõ~á‡ñ×LXôã…éJÆ'-Ož4Óœßo¢ðœª3NÄÔöèÕWwùAùÓOÿRüë×^Ûà´ÓŠ¿ŒƒáöÔ…x”?lùîÛ!”?‚à€"»ÊøÅ”úõP¥-íuÁÝR®ýÄ_K@‚ev³{ å[R);)@€¨^÷ÌÂB)¤=#ûÛ‰‚ÒB!„†¢µ&¶ä{£úž}«0L+h ðæ'o|Í"ࣛsÞž»ÿž•DôîÖ–t¸aÓ·ßef"qü‰ç‰p~²9NN%‰´ð;AnÿÃñYçÞo†“ÐœŠ™É€«P€s¸¾öå[Š®_’Õ»”PÛQÇ^Ì ÝaŶiƲT9Çœ[·+žj®v£±À€¼øž󓽟ºþ[ã/DÚfQëj’hï3R)¡5)EJ‹÷~À´Ûž]ß¶‹Á#³z2´†bÖšµhkUZ'‘©²ò’±˜R,2‚ŠY±aehŠŒ‰·‰Ú—`››ïz B ýõy[—ööØó†œÚÃ-yËÕV>» À¸ ËŸ|òé®û”û'l%l¥;ê¥í–Ëúm«Mbuʲ?|;ßúcax9Ù¿üÀ¶Ç¬Œ]lWCăy FþEÓôÆg uU‹àÄ:3„™-¿ÕTÃË0m˜ kýÍ‹”K#n=ß̰6þ|€á¿¹ „ ÷>Ѱz_m}Uuv¯Ø ¦–tÎÕ‹|ïAd÷¤éù½4=axÒt›ÜÛѧG¸üÙ[ðEØ«¯îîJcøb½ì×_¯0}z¯/ð˜_ªù-Aþcc¿ÃÁ‘?ÌáÞe”ïÒØ|î¹%Fè†ÛçïÿÉ#înkÞ[U`I, E¼º $™‚~¡^£Š¤ÄØQè„0„aH’Ù½K¤ÌR³$ÖÌJƒµf"Ö¬‰N&)×u“¢ò¹Ÿ ¿nY¦a-£ýœëC¯?„ãÙ3AÚµÖŠA8þÄs™ÁÑ]¶E^ƒ ¥²üÆ|í:>ã¬ûY³×aËPÒ¹ÂQX“Re|ãɆßoÜø~¨›ç#®­~N£r{õ›<µí×Sq¯ng‡Î‰j®YUÛM­´”VÚqJiò“ÔJ²/u×D{ö=ï72O¼}ó… ôë í5Ã2º3X3k¤»t³Ð^2Ê`Ø6“òg€×:mGeå2ᣛVÒšüÞÄXl{¯? Ÿ”v6GÞrõÆ›ïÛxó}Ãoû!Ié¯ô=wŸò±öva{9A#7G¼Šh’T”XÕ^7[°×íç<ùä3øéèAmƒã±ÑYa£šd*˜#á1£õ™W˜r&>~ËÁCÁ÷Ü}Ê;ÛŸmZÙ!˜†„aÖÌ9L}!ðˆ¨ Ìëï{†˜§=óóU¾S®¬ç¯YÄàç1<Æ x}ëDz)’ž^Òe]ücLŽÞqþÂM•½tïÿ8šO?½:£(¯½¶÷´ÓJþÇdGÒ´êç„; Ê]ó®G ÜÑŸ¹ùæ?:ôÈPÇ º_ùã3»‰… Kh‘ÚÝÀd€œâÀRa?zã}B’¤Fvq C )…BJ!£umYPZmr!)H8ÍDÖG¿8¡üâßl{êç“»¢#÷YPÙÇ]®Þ=7Ñëâ‡ZV†xuñG¦Î8@ 2ƒT§³L;þ4#tÆDš‚X†R¼¿ÍÐ+®×ì²ìÌ»öÁÙÅ·½i‡2‡Û±½•yy=½¬>Å´{øèFžP´óõm¦µ9¯ÌÞò¬»àÆsN¾üö¤ “Ò¹n­}Æ)­íÞí‘D® (€zÕc¿,øá`,ÒP€¢h«fîžÊ if2ÉcÖ¬=f¥õ¸¬„––’VûöÆú}–]6xô¬qåS»|'6Ýt7€awüdõó»Œ9ïÑäç.¸OØž°½ì‚ ´•xÂö¬è¨èëÏìHN-{)€K.ùƪ£.¤7Ìý3C­—çf*a…´Tl²ãh˜Ú̱r3ŽñG‡‡ÊÇaÛŽ+a™0,˜–•“±ñ'þûëh€•Øô£»kwì-î„vÓKò3šeg…­bN[¼½l•c…V:oEÖ‰3«>Ñ¿555O~ýî¼ \ò 8™®—ÈŽ$­€4ð(•Ô¶„™Ep…í-ýÆï5ä Ï\›þÛBÀ†%­ ¦í¤†ññ—‚1âÁŸð;¡¯¾÷å¦ÍõBÈQ×^˜®Ð :Âïèw7àÍÿ‡½ë޳¢ÈºçVuxo›a†!gI æœÖ,‚#渺Iås׸º»ê®»îêš@]³`DTD’(f1‹Š¤!IžÄĺ«îýþè7Ã0€ Aa¿ïüúýúUW×ôë9ïÌ©[÷6¬R:,qp 0ù€B®ÖŽm0a õ9~ŒãZ[Sôûbí.¨ûûòï»T‡éŽo>³å÷ A#Ç|âÄåm ;ÿ\IÞ7›3ÚF!??!çîXäÞ„µ imï^¼ 2…g}ÄqÓçþñRf¿Ý.ù'OýesrŸ?üêI‹ò„Å/ikÓž™-[a‘†ZS_ÔW+J+’Û ÂuùÅ”(Q…íŠü\[áÚTMæàÛÞê²Wg@lMFD@TØ­­[œ#q•‡:·þ;­h3oÐíN8¸cbõ—ª¶žY3+fÅV£H77vÑ[©q×e+õ¡Ø9Sç´És!¼¤Á¦ò;‹HrÔE%7ÏŠµ)9¢OùªóW¯H‘£™N} ýø\K1U³?†A}©Ã8¿U«>ºÍí`Llµ;{–€D`9Âf033ÄÊžüy n¬¸ïæ9©r¶Â"eY;B–a™X`…¬pŠÁBüæ¼tÕbnï/òÀe»³ë@I¨$|åêQš(R³ “Ïz˜Ø*6¹qÖbj«jj³êü1eR‹½3§ÌŠñäïöÛíÐ×,”°ŽGÚ7ÕéXH Oû¡Š…:f”ꘙvÎÍøf8|˜OÃÓð¸Ú+Œ{G’BÖ'Ãú$€/†OPrÌÁûϺînpT +š—°&V—ß^}Ý?«o…د´ U¶ý<ñ]ñ]0ƒÙ­[ !òp,`Qð§’ üs+;c«<Ùƒujï“&­˜4iåft2iÒ*€Ž;®å—ñö‰Ñ£ßhÈ#6ïô³ÏØÄ¶ùþ?šÐ"éÌ3n¢õæ¥q·Dâ}È#TiUÿ—ûÙzÅÝãÇN»z¯a—45ú~ø¯ :nAÂ-nç•´±ÌÌQÉi¶Éz•ŸffMuyy5y…’×*ô5 ƒm~×, RÏ_°@µò‰¼ò9ßvÙåË¡­—Üo;æ³ï°¯ØwŒ"£•%¨d­J×Y«-kfŽøŽþõ»©×®7ožlYU2•µO]ØéßóX¤k°ÛšY—/ïvTûéúÝèû˜b;_y#ì¼Ê…©åo}*Æ>3ô¬ÓõåÈï*ùíá(0üï¾fAªûna~¡X ¶Â65™î_=õÿð¥aCDÄjG‡A ÄËÄÌi!+ÄAÒ° K;yGŽ{tCɼê3—rÍJ Œkñ;±ÑÖªFŠ/ÈGûÉšêúšwçÏæÍ˜á>ýUç¿Êàýè”ÝÍ£:ˆØTÌhªÓ~DîFû¡öŒæ2-åJÊUÜL»¯âÍ;Ëš“;\úðÙÿs³«ì®ÃoŽúüâ78 v9ç཯9uç{þLξö°ì~Î^6e¦ßý6XÀÔg‚úª’üâ"¯5<-ž®¶åI‰9,[ ¿U¦‘Ü ‰›ÛÄ®^aVÏ(?èÄÕûm¥%vhï“&­ú±§Ñ¥ßi˧ šXþùç'n;–ßAÅ{ 4±ü /LÝ~X¾‰Ü¨0]XÐÞ?øƒ¿÷q]S‹•ÃÎ^6ìb£K œ¢¶ÂÌ,Ì„“ 6“2~bnÒßÇrÂxq>“ E Xb †mÍê•ëçª ©é¼[ŠòüžùÁã‹$†wý;{Zb®õ5{›"KĀάvš(óm`ÜtÚ /ü(œü?Á¤ßÛÐu¾$}×B; Ô½ð‡œ¡V-.ïV_AÅyù%y+Ô›\[оÀR vBÔÌÈø­öl?xù±%ݵäwÀø—`‘s¨ìße+˜! Ά¹K$ä¥1Á[éNE:HÎG:•ȃ²BQÍV+da“B6¨7AC¨}å:í˜ݸžYÄ ?6á7i/p¼Œãf$ % ›7Pb•e­bÖlˆmAn¼ 7‡Ø(± µÕÝO^éš`Ưïóà'þƒ_ ¥R¹äH}V¹{FùVQ­Öµ*fTÜè¸ÑÑN̼uû¢©]SUG͹òFâ°ï}·øâ‰/¾xü{üæ(™úºdY¥M6 ÎÐb%¬á&|· FÆ::Yl ‹¹€,¯¶å«¹Â©¯Ã81jÎì°@˜¥ûÜ;ÚÛêoÔíõ‹­õÄw\ûHƒOšT6yrù&ž5yr9@´ÙZÃØ¦xñÅwˆ0dÈa[¥·æBþÿµü,&@3f»bùj×ãóz¹³éuÕ°ʆíZÕ×)l“åv¶UËVW~_S¾ vÙ¬zNUåÅWs¦œ#ÍF"`–‚hÙOâù“Ó±UuY(/NËf½¾Ø.ßó¤}{ÛqŸËÏ “êpS/•ªÎüyçˆÐ­¯­ëXW[ϱ®ó–f)^©dƒkêëlC–¤(Ç“¹çOÊ¿pB0é÷!›´äHÛÐ1!@ë†J2¡0Ð@åÖ¬j˜ùî‡l,“¯së¿~hÂÌ' Ká!½îêÖó¼'ÿîåuÕ÷i»ka»™ãÛO{,ÿ³©¥ûæwíֵ͡;•Üs ³Û,×3Ë×Ïݶß²±ÜM<,±uL©uá Û$‡µFÀÚW”©XUÔ–™ÿvÝ?›îö”3œrÁƒŽ6nãBkó;[Å6bybVlµ|Nanh³Ë¾ËfVU.X Ѿþt@Ý«ËëV.QµÕ%}NL&ýÜ>cf€X«NN—,ƒÈ’`úä7§è¶oöúì`Æ%Öõêe­Œìaaˆxõ”á®äÏYT·s PˆLµ‰2ÎÄÚŃU•F˜Zw2&Œþ úËïÿšªª=.ÕY{€»fÓíKÞ«¤™Ï zó®¦Ïå­A÷hßFþ»²V‰ÑÂ$ÅÛúêšxL)Wk¶æðV<ÔJŸ}ËÎQD9±8±0ôrüL5+‡ã®ÁQ:NLDJˆˆ•Dsˆ¬QÎ[½'SQݛʾg~üàgözHÓx¼\OÄ×¹ž­¯õ]³È•ß;Œ¡$ÙÌÍ kaİ ËÅTÖÛd<í@W˜rM¶ fiÝ Á{¸_Z ¿zöDé“Jþ(w\d¹S¦T{lñ†ZîP¶ ^|ñ=€Î8ãЭÞs ¯fËIy‡#÷ïÍÑ«Ùæ]áðÝÄ"æ-:ÀÜñ¿O×» Ž&W (ÀUµJéLsÀñ0  „$Ìä [!°"I/ž»GÞWe}Cc÷£¼ çL¬[Öß‹7„É5š´ÿ·Ø@}sþñT~}e%LÂO|[ó+Ð …œˆoL<H2b…‚ ðŠë`Œ‚WtùKUœ®N~˜_ýuÅ…¯öî\Þ¦O s[w/iÓ'¯xÏësLñ^Ù7ï'hWÉ•¤ß#†~CåC§¼ü‹/Ö܉SK€ 7ýÀ‰'žÐâ6íþÄefž¿"óÖ·Ouàó¬ ^Ã.)Û)'±¨¡ayzu‰·5,Êqã­]+,lEÑâáôª Ëàqþ‚SNYrwà@#£LÐüÒJ,‰ÑlU¤Ü­%6Š9kÚˆQ–¹qí›dPÓåÊ•ËïÍ,ß÷Ü~·¡¸>ມ¢jw " ‚V:¦H)ú„(— !‘®ø*U¦rûö€ý¯8 ùx¼\/,»¤]wU½LjVùˆCb  ˆ ÙF›u*Ä‚ö «ªh%TÞ6¢x¬âCȱIÔÏ‘­Ëµ´0eJå”)U{lËŒóS¦¬ԺǷO¼øâûqÆ!oº¹8ûì?qtÍv‚M'÷&œyæÑÑ5›ºak¡…xà`x߈Ùí1EA*¾ôƒKƒTΓ¯kEB€à£N‘ËÐDðó‰…¤ éºZ ù튭X]z€Š{žªY^®vášû¿yûÉ’ªº?žÎmåšd&º¤9²£ eöo’ Õ—!“qí¼¥Lš¡kM;(Å ñ\×KÔEuóDq!"“‚b¤Ä’¯óù˜ž¹ûºüÓ¶æýt­GÚ! ónm–ÎX5|¯]ž àŠWŽû¯_%kÓ^aB„]§!Y/§NXòÀ^dYÞˆj³‘0‰~O_1ë¬ûH†zömüw%£‹gT{nݘ¶×ˆN±ØPˆüöùÂÂ᪠¡’.†Mjy•…•(ˆ–Á£Ã…gçu‚ x€¸p(»~s†Ä¨FsF³Qljª•oyñèe"7®|ã\»2V¶Sˉ€Š¡h­1 pHÅHœ-^•Ë0Y5©:LÊqU"¶ÿÐ7t+¦^?Œ<Ö¨m{®[ZXQN6dÅT_­r !P¡(bRÂÖÓ6•4ëä$|cZSk­L­(G…V¦í!OMüžsÕ×Ñ_Jë‚y[G¼·À±ÇO™R`Ê”j":昂foîHâý§ùK£IÈc XþÿÈ×C£?!˯Kîˆô;ÞšSqIû"¾HìÁE %š”Ò¬©L¨ÈçX`i¤H+>õwƒšº«½ûìLU˜:êŠðþwÜŸrò<“RN²¢Òãdf·ã»˜ƒ:‹¨Y¿iÍßÝúpP=I 4êëc­âÊÒš’å!ŠHAi†‚›“0å‚á@´iH)‘œŽùêÚwìœûÆL"Kg¸w²+^½°ç7^?aHß#þóægÖsnNQn›ÎJ8¿²B8mòr¥õ{µðØ4þÓÈÞÿ°²®xoŽ·æŽ¼Mäëþÿ“·ûP¶¡}Oúè+Òú¸®OO‡“ÚîalîÞ]ºM¯°~k˜e¡¶=¬5©%â¬aÁVÀ,ö©Êwé”Õï.¼¸§S5+®:«ý½£¢+*k5[b£9ìMþ{vº5zIb•ͶÇ#&wr¡ , ÒqÓ¸ˆ”@PLä(×QÚˆâ‰ò*^,3ŽkµƒLPÜÀÇ|v߆îÀÔ¡³3i&mI HÂz—¹AŸ@]% 0e‡ÐMŽ‚Í„ÖTRE+¢åšÖ°|â–O¢o&èûÌf>웆c-0eJ5€7Þ¨è˜co¼Q³Ýo¿xé¥:ýôƒšËµ`ùÿn¦Þ ñÞ§7±üO)ä›à „~úc9m@Dîÿa49¤DÛ4”¥…„‘z:a’‹®»rÆC#»ÿæÒæÝ…Õ«ýyóœ¯ÇJNŒrÊ Íë IDAT¸¼v±—ãuëRûÑŠOÿg·¼,iµ(ÕÏùçC¬â¬sD‹òzuk»Se¯fX âhZ"B"6嚀E„ Ä‹3ªŠ¼pé²Z-Òþ®¹ „¥_É«÷êÉ?~´zßËïŸöÛ£ÞûEÿ“úÌœðy²r5s¡WPØ&Á]ÊÆEдòÑG”È;ïoU³ñ]|™„†CƒÐ w=«?€Êû®UŸùî5WQwÍ!ŽZæ¨úv=•eX¸³kÔH—¼Ø‘ª)剫ƒò±ÿ•EÁ £A4cÜùvƒ6òß%š”Í×>¼háåý³FMç½j˜ü¢’5CÖªfÓªš™Ø6ÔV+ß$rs4›(€²ío©V©ÌÎŽ£XÕÆ I(’‚¦Fýn‡TLGL¨|:ctÇ%m;ìuTÇò—àqyów÷jIe7¥,).ÿÂOpCÇEO»ÅO`¿ eó\®S°a2¡\SSïj7ÌÍó`Ì(¡ÖšL%­¡œÊûÞ3C‹Ž7¨…é­ø”ÿ0Ž=¶Y~ÇÔ©u;–xÿYF»y¦üÿñÞM,¿M…üzÅ;0ÄDü[ïµr5g@ÆjM¤8;»É-ÐGîãïsæUÑ™$a‹¾ðï_ä,œ»¸ì·m’rXå$<[KqëÆSžª?jÒ¿ã¬h{³#ä2»uí&àLrá¼²°{IŸb°!P6*'bv°˜Ðdg¼~®0,g”ä¹’\¬ÃjÛÞ¾sö9çšðæ{¥¯Wýmè®—Üöýgs:÷ÛEL Ë=}2/§8ZW¬ÚæÛUŸ|[õþb²!IHbÎúëÓ§—,zæ"_ϹA©PSØÖ]MÖ|4mœ²FqœxÍ\´ä[-¢b‡±<¥lC®=mgòèÈ ¯}ž÷a}¸ÿõ·¹K«¾\zµöÃà| _?åkÖK ³Ø{g,¸ú NpÀa m %«[?<èoä[%V7‹œÉjùˆîÅÛŽwL’BB™þ¤•¥tŒ[ Ñq"&A´hè˜SõŸ:«]ãäv‹w9tÏû/°òÒÓ´{ì¥u 7/¾W­‘í¬4“¶J ´ÕjjÎ~'©Ü"tßßÖ½*^hœ¼t}¹&7χY+#|“É^B%Z™ÖO`a½ùÛV³o‘`ä‘JéÑc<šÛ%^zé#€N?}ƒ6Ú6Å2åw8rßBñÞgžyTÄï£G¿ `ÈŸHË;0Düëþ8,J±U0„ 9ûðŠ™´í´·ÛDîßþç^ôÿíUÍûš{ôÄ®ŸtîÑaâ*ÿX•Ëeu‹Ý¸ß¶[k““:ûí@;em4i”w?@ò”äÁWõC¾¶5©Ì÷ó¤wØÂ HÀL–†åA¼˜@P ˆiˆV¤W$I@ÁþòÕR„å92=“¬°É{‡îõ»«Êæ,iÛ¥[ª*I*ÜïwÃÞ¿ðà›Þ€£D”H W¯l,"’Eéüù*,ÚeðcÚ ¾yéw»œx•v,ܸrÃÕs<¥7Ç›ýþÈ“¯µ ÈtË¿N‰^ÝuŸ0Ìé°ø[Ø6ö‚ä<"ÌÃÿD¿»¿_æÑ9© ›;ìpq‰ïÂGÓvçwå,|ûû ÿ|\Ç0HkèFýNl#*'»–9ÓPSM>äÆ%@‚Ô>õJ7Ä$üQU¢©úºÎšÂl ¨€SO¤R9rqut­ò_Ö@Éãc[<"ožuŸò¬ö„´%ÍJ­avq½˜ü'©²çW]zzÛÆï†”m£B£a`صöhý í_˜D,€væ6ü ¿~%G±Á…`ÛS§ÖõèáEí"9¿»4?{ŒÏ«]³uÉ=B¤Ü#~=úÍ­Hñïˆü*O]qcƒV+m‚ˆ®tÏOÛ=JQPÓsßvo|ʹ.çxÕGô¶q_‚z!XÊËýE®–TO>ÛõâÇJý‹zö]*ðù~àR\ à1<¶©ÏøÖÃ1Ç$ÐÈïo¼Q-’5p¶7¼üòt€N;í€7ÝÆØ¨]³Ã‰÷m‡ˆÖG~ó§òV.=gÕ`Gƒ Vø®cg³ïß·®oݹMoýó¬=N)ÞÿÒ£sÖufÌJ+­zôMA)·c»ðû¢µO¹ñ¤ŠóÂ{®¨{ì+/løzÏ¡~‰"ÑM¢esÈ"ÔŽgÒ鯦‚ã«eX¹a=…(çÎ1S£•@9PÒæŒ]ýê¥qñ6¼4•ŠOó’¹;]8}áœÉKߺùÐý~{ç'ÿ¹öØ»?Ôò»´–’^E¼ †!ÌÜ4ø‘#Ÿðˉç2JÁ|ȯÖ^øÎð+¸h(ÈøÝarÏ5Ý—>ç¶««Ý½O›>ç|—òY”¯)F‘x‡ÉÔñ§ó€ýjϽ}ôåËF”zçõìõý³ sÏ€8´¹;km+GÖöJïþð —gyMGa3bs3[†%†Ävxe"`aöÕÀ”ÎK‰Ê%%`‹ŠÌMiëÆqÅoùKv> á·Ç5鬳Ö䃭¾è( Žx³éÈ´ãï!•Ǥ¬Êºí 1»DüN“c É„%­¼x`åųµ³í¦Q¼¯Ùz|ôBSUX÷»úæÏÒ#xüþKüR Oà‰-|Ê7S§Ö8úèüèe¤Üßx£À”)«·ËXÉŸ[½¯ÿšÊm!Þ[`È£š„<¶Œå@¼p`aÁ#üšã.Ç=óà+øÑz&‚C³'ß¼Çñ•²ânö·* ïå77ï… céìn¡¿¥áWWí´èƒú'›xÊäâì… |ÝevE¢Ô£eÂ5ÑCšÓ•Ì\“¬Mª¼ ˜”“Î5)Yžl§üv&tóiuN,ƒö]„ˆ@—YRPsWô=5ºÇë“çÝï˜÷¾™1âéW,ÙóüɳŸÐ÷˜»>T®¶Õ‹Wåwic­-î’[¶¸nÕâÚ¾ëÜ ²"«`¬"{ô•ÿž:ü¿¸ìR*àx¼ ¢Šž™Ë 1 •£$GQ#&ä ù 4ëLæ\vÙi=ôòC½ü›gJ½3{öX6zqSZŠ÷µå¼#fDþû§ýnT±ÈŸ±}Ò#(‰ÆIYQPˆR!4G !É'Ä5HA¬¾¡Á:.)WœqcÎ={xuÛ èwöÀQ£&5 ¨D§Æñ>íÈFf×Vi!m•nÒìÍ©6›_Í¡2!€â''V_|LõÅÇ>ùÆïÿ~Â^·ik¦U£È ×Œç¢Tœî7uzj#åþKüÀ%¸ds(þ€[dcí[R»î;ÇS`Ê”ÕQ$eo³=`̘OˆèÔS÷û¹²ÖC¹£Óý6B“ÇæfuÆÆÈ€Ë ÅãQ@(P( $H„„B‚æ}ùç9oÿµ(g‚ *ʃfËÖ›ðÈ/­aË*ÚÌØcî¨sÚMyÝËI¹ñ”OùAC¼ê{_5øºÁÓ ¾Núºž(T0CduÍ*'Ñ8éUÝk»2·÷òx GœkEE¶]ç(3ð¯ö-órªëV|÷—ô==e_—ìÓáÜ=öc‚´­f¶ÇüûÇ‘6»vi½[7Ô,ZÁÂVlQ§˜@¾zc%Åû…žàäWΡÀªÐRh)c)´‡ÞvüÕ7¿ûèc§Ü|ÍÎÉ޲ê3BŒ(Šƒüì>|Š&˜ºø"¿ùÍiÆðý÷¿ˆJݶm»Îã8A³l¡ã„Ž:n¨ÝP»¡k¤ÄüR#Ø»û¿ú?²sÎ}ÔÊæ'nüW„§¸½óyƒóeƒ3#©¿IÒÃ,±„S“–ö­†V±'*Îï§ÞÜcÙ·ç*k”5šÄû /¼±|ð.šƒüïxûw+e” •2Z­B¥ŒÒFk£µÕ:T:zjm´6J‡ZBj\“¥$XüÓ˜x€ì\úDïY#x3j½¯×ç-ð8˜ýb\|.Úhûæ°ä" \·1‘{Yr-¹h&Þ[ I¼O™R9yråƶÀ˜1ŸÛ¹7a‡ÎCùˆ÷æ2ä¨&ñ±üÖ…e G!Ä1‚¯È§Ü,‰5)w¥÷_¯€žWü«éÈÈ‹ß×N.Ï Ÿ  ´zïÃCÚŽ›c>=±úŽ«~`q<¬ïº_©M.‹òI­TÇ@QdÎ8™*$DW¯XBI‘b¿ˆÙã0;Ay­}ÏKKP#±øƒ4ébr@Z(þ9¼ÃW}ÑNÐ^˜WÌ­Œ‹5ZTÇØ’•‹3µaa«Â(F%¯[Ûš…ËV}1«x·^,,"äèæäA…¶™„çhç”k®WA8Ï;·ÿÊûUw$ˆ#Ú(ĈbŸÈ2| uÑå°Ù¦)äË¥îàž>‹4ivi¦ß]Éè05w%ѹߩ5,ðt)¢ MÀœ³¡S¹m° 9Zćæ©þâÄ ÑÎò/Ïhj¶xꬳ(Ü ÀkÇÿ=w—Ûò‹|å19¬&ÇR£xo …Œ\šF¯&ëÛh ›ÖÜ&F¼SáAõ4õ”=ƒ¦°™]Wÿ‡ømJ0ñÇ­WzO\Œ‹\„‹2#7å,V®N®âµ.’à|w£­#å+Ñ˜Þ ZûÿØ"Š¿é¦aú÷ïõsg»F¤ÜG~ëG ùŠw "` ²[¡‹>û«pL$^þŽ‚Ìþ×µ& ››ï£¯œ¤ômû2³e¶ÐÚ›µ¶õäJ lá]ã¦Öë7€F°Ó±½Ó¡=AÚWNi7²kW¸éåQ¬LXäç‹ &?ä¸Ŭ­h¶:+ãtdjlÚ^Ö¿!³¬!µ,¹jú"˜Þû”CV}±?K^1ouzñ<ÇSym vêÝ)Éáö‰ªªŠU_Î)[TH÷CvbHùWßU|ùmA;W9ºlNË_ìã/£€# ¯ƒÀª€#Q¿Ó„Ñh؈ÓA‘r|À‡bK>¨mñâù$k¨‘߯ºê kå®»FÀØR§u;§¸]ûñ“´j'Ôkky×:hŒûþO)·þ”jŒ(ÝDr€¿|ï…©çûÕ¿{bZ|G|½òÛ3 8dCZ{}lQÂUÖçýù#¥L2Y·F³«F‘îå„Êɪx¥Òa£¢7Z›¢®FþLös— ¡¦@®ïÃ`Osïî ÷ðÛûŸWÿXrð$žÑþ¸à|œÿƒÍ‘¾­•%÷ ’À%–ð—’xçÀ•LÞ¡Ó7^²õØc‹ Ȧ¬Ùô<”[cÆ|Щ§î»ñ¦?7ž~Bÿþ=#rß!òPþÄ⽚h½‰è· Œæü¾|Á_–|~KŒ%^6þC™ýþ4 jºhøP ³Ó•Ù ·coxIihÚ³Ì&â÷?þ`þ‚_\xáyQÓ_…œÖÆ’«àžR…/jE¬°6äðZÝ¿ö®rûSK®åZJ‰#m<Û±½éÐá°(†â$bUñ`U.¯dkX“U*Vä3 J~xµr@È“UŸÅtFÛ}FkýW—5Ø,Ÿ__Ä.µŽ²yYN$òD¬@–._6wî;›¼d‡6åb4±ü†„ü&’;Õ™M«êÕª˜käɈÄDâU£&ª ½ï?³ÅÒ–»D!è2ô!Óî~PihMÊöXùÖZkÙ~üÙtåÚ&ñžÅïªîñœäڿ᡺ñË«²å÷r‹t}à¦Ó~ºšb‡XC¬VÕ«=u•«Oer@Ž'‰v%^ë}€l1êò¤M´ó­1.­v|ˆ[Ël­ˆÉÏÏmס@D¤tþüÒÒR¶\^áiÝÚ5¼÷kŸŽžß|hG¼õ6Š÷Ðî2퉬ÛŠ ù"‘ň´!Wh[L>-Ÿq)¥4µäPÀ7ž†rë­Ïg_¿Wª ÿõáýîNr‚õóû‚ã¶E€êùƒ!ЯM6 »vœû¨ÒøG•±NE{fîÝ£fXAÂM$­ŒÒ&Ëò‘W£ŒVáºÓ­šM‹„—Þ¾hŒƒ0Þ­UüÃm5Q<€sqî9hùµg•gUãÔ IøÆãGÇÊcõ£×¬Ð&J8 `Ò¤•›Wðÿ~šâP›íA¼·ÀV±kFÎAHE¹F$&ðEbÂ1áXç>PAºY»Peƒ™¡5"ñÞÛQy¬};÷ݪ¹sf“ËûðԺנéÕx÷ݵ’”¥ù•jI¹^±J\°K÷8çXÌ:š]USáÆ VÝñ·«¿o³×Ò Õ± }ñâé5B6¯['­.O 8¿Ä«žù#5E{we¶Qlfk™…ME¹ÏÖ´mÓ¦k·ÎÑ\ëü FÅ”ƒ S÷ôªgžüä©YkÝ”(„&` yîþçÍï71‰(1Ét*†ÿÁ*ĶE()¤Vͺ˜‘"‘ hÊ`ŒÓ,\ï£Rß1qã§Ú ´¤¯\â„ÉöoZ÷Ä!Þ# Ÿ Cí_øW­K£„×l4·üKcЉÐÅCZ›DSp"[&•ªO¥êµ•¶YfoæÑ;êf?쫯~àä“÷À3¥xf›ä}lÂ3xæY<ퟳ›¿µ–C^[ÂLHbêõÙͦšÍÀqǵmª2qâŠÍëdSðÊ+_tÊ){m»Kl-´ï-°CØ5ÛZØ5Ëoºx¢D!ÁHŒ%IøØ»£UŽr’X9ì,…°ÃЧ|üØÊrh'ó²ò¬ò¬òYiX¹vã<ïpüæh¬J±uIbÖQ¬•uk‡8æq,æå»,òë!ð/'ô5È­\Z$`eؚʴÍ+qk¾ûÖ¡Lá^ݘيå°Ì‹Þ]¸º6*¬+]`±Ì]ºtîÒ¥óêêÕ–A¼07Ö*O:åTŒzüãG¾lãÁïýI6Òï‘¿¤øÄe±«äÈòä!~¦Bº,ü‰ÔäÂ+ñ»RªbvõºúÀßþvŽ1øãŸ[sèÓR•Û)÷ʯ7ðLÆÝbó½ ®WL!š93Žˆ†8Íç~[âÕRÿÓÕ~»Ö;ϼÏÌJ…‰„“H8‘!Q¼ÖÍ]øPk£9P&ëϬ!÷ŸÏâÙçð€³qöY8+:ØR¯+ᛉ÷õ´ÿñ8î¸vQ À‰WLœ¸|KºZ/^}õ+":å”=·zÏ? Ö1å'ýŒƒÙÅ{s¬ÍòÓ~Ô¹ !¤&gFÓAZ7ïA´þå«çoÉŠw å²òXùV{ÖØ HöÙÿù ]Gï…ÓöÇE£PL°Ä,ìÀjbW±Câ ËõZ±VÕ+†<öäÄ®º @qѪlTe\[•fؼÖn¦.íHàè:fkٲ؃w=Iǃ È@fcMÈÖ¶ .*X#b-Aª®2UW/Ê/OŪG?±â†³ÖÜ—€)`X„V…–V!SÈæLâ÷cê“îä÷†ÍGV̺J©hæK‘âp½úÀí·ŸcŒ\{í³k}UªâcÌôlÆ Ö=åG‹÷'!uyú&ˆ‚hˆB”X¹–x½Ôo[B@¯OŸÕŽQŽI8‰„V:+äUÖ¨±úÝè ü°?%žGöÙ;g–ݵ§%·øšo×¼ý¥ ôm”ð} )L½sÍêªâk¾erËïÚRölªå=qⲉ—maokc+—ßÛvøañÞk³üÏCñÛ9¹7!bù™3Μ¹pÓÏZK¿G[lÌ*Hõ‘M/S5ì dè˜Yc¯Óä@kt¯yYyVû¢÷>°ŠÀ-Œì,ÚƒvC1PŠ­Q*t‡Ø%vH²ZEÌn±"7W³´þc³~X‘䬯›Ûµƒs‹œtmÒ.úÖÑuþÎ=­0‹µÌ˧ÍiXVMÄ¢Œ¨2[kBk-[Ë‚Pk´Ê÷Ž©ÚÊâýU%»3§ç—]?8º…FV¬V¥ÐªÐv\1çí¼ZŠaßIïÝþýêùç‘RDJEÿ9b~€Cï¼ó\cðûß7£øoK)Ö%waõºú}3ÉàYPz<5´IÅ—Üw­²A§©ëñÐÖÂÄR﫯¤M÷·GwŸø’ÖV;&QàDâ=®O¥T£–w8Ô&ƒŸI¼7Çóx~F¡1xæ œ±ÖÛÍ%üÚâ= ¡ù Ø~àÀÑþ„ K'LXºå}¾úê×|òî[ÞնƦ“{Î>{àÙg`Ô¨I£FýœB~;ÇèÑoõë×­_¿îFž¶)Z^£`ˆ#ñ.ñ³|‘x_ðÆJƒÒ”CÊcå±r­ò9¤”òm÷®`ÖçÏ|8 ¾FkÊ’{Ð —/þ£eHäÏh²Z±C¬)ògÊgµ.ñlËœ r>ŽêcԭΈbk?l F`™íÊi+ßY-Ê@ Gú]ȇlŒe³xñbf֚Ƒ U¾×*ßX¾õa:qÄÉ3?ȯºn€½¿¸C…6by Xܱn¼Ó¦ó扭Ÿ,Gˆ[}mDë ÿ¿0¦Ùº§³K©kï»ýÀ~¼XBj žqØè ™3ëâÍR·u[tzyl4¿šH8ù 'rfÒé†tº^kãp ÌO—«}£x/4™-kQü÷‚$ККÄÔ'Z¦F`ò˜¶ZZà;Ø1ÚŸ0aÉ„ K¶VÏÿ­ˆ(?-Ëï(â½9† 9¢É|ß(Ëgý ³ú=÷Ñ?é Ùû¥Û£·ë†®ÔïÝAi(Zƒ4:/ñ;ù¬}«|«bÌö$7Ì|Ž" hEhE"†uÈ:d5‰ƒˆÜëªûÖTôÍ)r/ﳞ$àÓ§®|:Þ¹Kùâ¶)ÎúÐqêuŸÞ–¹üÝ2(#ʈ² &P 3õ†ÙæwÌ5ÖZa½¨NİAÄì…ù^«|W#Ó›¸¨ çÌwòk¯9,yÍ~Ù@øÐvË{¹³ë¶o­3jŽ+> Þ_•¾ážöðÇ+¥H)‡ÃuCJšcøðsÃP~ó›gÖ:ºN é-ïY¼4 ¡ôyì‚HÂ+¶1gZ`Z©;³Þ-j×ᙉ훬t¨u˜HèDÂQ:TÚ¦ÒõÚ?»xoŽh²ôE¼àtœ~N˾‘l¶­-ÞÑA8ðPå€hÊò8›\Ðk$÷„£| c×ïÏ´ÀñoâˆÉ–Á"ìFÌN 5½ªz‹¨ô’Š?íݰïí§¯{Þ¼aÖÄ%¡}N}¹˜m( ßãT¦¶¬H”m„Œ(Q¡ ˆ…¬ˆ¶ÂÖ 3[†e1,ÖAà £%ã4²|«|¿û…ùmÓs?[xÔ‹»­­©éÑg´×©­;®®?ÔP9ô˹ãêßþó÷Ùj'×'ô•ßÍ>¾ûF’H<úèyA ^øôzßÝ â=ÂÄÙ±ëƒ' ´#ŽÜ(¦—:s“NA‡Vÿ|¿ð–³,¯Bÿ†% ýòÅ7Ìž½*û,ýÜXøÀ Ln×+&G/_ÂK/#[qÐääœ"'›xëˆw]¯˜Ìä.|à‡ªïn  Êæ?xýõE¯¿¾hÏ;v&@ƒ÷ßF£ÚŠØBñÞM,ÿÿBK0ù,¿&~¦àç»ACÇ·ÖÌûi (,h=rfÚ|<žœ¬3£|Ñ>««˜™3í×ïÍÀì2+JÕ÷JÕôfV–µe}˵:\¿À$„aºhöÔöåS&§W—ê¼ÞVÏ] …"gÆY!+Ê€¡P`XL:“æØÒzmZG7bydœF¢ïú»‚]º´oõÈ{¯út«8†m0ˆû c Ÿ¿Òuó;ßõ+Rêö«nøûÉV«9唡§œrÕàÁØP#Gžo­œw^Ë Ï­Fî€X,Ž­ÄlŽ~oÂç¥:¿#‘ä_÷i"騰¾¢ºOŸ¶}û¶ðê«ßŒûÍVófcÝiÒê^½Fvúø¹âwÎH(+3¿Xtä!gv­ïÔ©ºWˤWÛ¢iŽAƒº|×hÿõ×¾þúF!ÆŽ `ðà~ÛnHÛ9¶]ó SœyfËô$;.ÖËòQüŒ‚!7hpƒfñyÃûj3ßœz¨Ò93Ú!rEy™3)ò8–¯•ÏJ¯wÒpƒ`&kÜt}OeY[Q,ê—K§SÞó¾‹×m¿`ØÕ áʆñ«| êºôcóõ|ÙegfñÝ2!+dF`ˆ„HVİؒ’Ö"YñÎl]d#+ƒ ËòrOý²w§’ý'Ì~å»FŽ|väÈg×RôS0¼2› ºßyN#ù·ßþãáníÖ¦©å‰'^süñ×­·“0¤0ÜÆ}¿;¡ìuïazc¿ÇW¥jAZåvʹ|FüÒo]N8ùäÝ› â±c¿ˆégAÓ4éÒ#ŽXyÀ»gcNb••¯à•[¿~À™•‡D+vß}å,="û[aɳ[oŠuC8þønÇß-Ú?~Á´ÜRÍl]ñÞkÛ5“Gš¼å}îXäþ£’J¶`ù¬~×Ë—»AC«OÞXÓðÊÙ0ˆ½8-ïÊAÑ” Î{6,RÅXù\úáµû#øýÂë?g aXÖÌÊŠbÑ×Ý¥c/·E&“&d“Ϊò÷©éÙOkáÙ¯2Û}]È»öµ WÊ…,Ã2V–ÉtÏk-Ä ëWˆ+b­X†1,ÖÂhŽd\Éhd"–ÏÒ=2Ždìâ·ÜT]S®…õ²¼µ°Ž”ëæ"‘‚R¤”6m2¯¼2ì•Wî;6›‘màÀ ¸iÀ€¿4ïä…ÎC9õÔ5~ëŠ÷±¸‡zKô{f–ªÜΉ_ëÙLÓ¢›“OÞuðà]£ý±c¿Û úñˆ4ø¢AkMÞÒ¥¦N­ða›ñ9wÉZ«@ 4ÿ´Ó¶µ~oŽãï~üñÝŒ?üøõ7  “NÚå§Ï–`Û‘{¶ËïؼÒM,¯žzê9„D!{AËÅ5„Îd¡4ÈQ‰Qo+ä2yL>‡H“ϱ|­b6Zî$a(á&iC0Ã2Ù(ÿ ëuJ=2R‡™Ý»jÝößß÷[…p©ê^³K¿ª¾»i-}ýgV±Ð|ÐÀ%²[o¶pÓeP¡À L”wLÀ X¥Ý¸ÒH;ÝŠ‰üwëH…~ºÒhÑDûh­ÿZ€o/9öâ /<÷ ³I¬Z°|”?'ûbòÏïtÛ‰Q¼¶k1ék¯ýûõ×ïhzyÌ1·}ômG™ R3æcpÒI Kß|6!ZÕÍÔ[¨ß›0«”– ­;¾=ü…ï Ü?²Æ›5nÜì­s¹áó믟1tèÌË.[WƒΛעñXŒ+¯^0·e ¯HûÏ:ôóë¯ß¶ÃmÄ 'ôˆvÆ/?¾…økZuSpÖY¢¢6›âw,ñ¾%2ä ÕPWŸW¸žàëkgà öÔ­A.“Ãä Eó«>+ŸÉgŠq”‹¦ERÜ€ea ±`ÑVôÍ'w`Ž·ëJ*\u”úFiQZ”†Ò¢<&ÅUŒÒü‹!+xÃ,‹®«ÌªxXFã+akÅ‚×)w¨ÜA…c*dÜFŠ˜]KÄò7Ѝ¹«•—®]õ‹Ó´`ùìbÉÚf¿oΡPúüõ"Ò&XwUçĉÿœ<ùï“'ÿµéÈá‡ßqÄw7î‚0ÄÀOm ñ!×ÊÊùïkc̘Ϧ<øÊ†rœžãÆÍyíµ9[ë¢-ðÎðáÜqÇÇ·ÞÚt¤…o=#ûTGâ½ù´ê8ÛúË/š÷ÖÜ»ÿøÖ[?¸ãŽw†ßF#o 'ìt ;Eû¯½6ïµ×戾O:iÝ"’Û~ñÞM,ÿ_/ä7»n_œ .8'«×Ç,ôçzü%‘{ïS¦¤y‘~ƒ ùŽŸïpÌ.üô&í€-m"¹`˜X¦Û‡tRñÂ1Ãô~éŸëm¯Ö._ {ìbh–nÉç­Ûù€Õõ¶Õ'/«ý.b8dØÊîÛ/ßSÕUaQ¾e6žçŠX+bafé¼b,1` Ø(evõwï­Àî]Kíïò«÷=ºðÓ©"ŠT<Öˆ÷FxžŸiIÑÛ o¼‘­^{øáw8ì°a"N&C‰Dî&ÞÃÍÁ¬yÔ³§³µôû&ओv0nܯ½6 OÜ:u|^3ÆI¥œTÊYû¸9Ð÷z6ÉW»éÓ7ÚU»Þ_zt6BΛÖ>²{0š}½ùØc&7ñøñ§žºU¿^œpBO¹¿öÚÜÅvÿqÖY"rþmÒõ?Œÿ;â=‚pÁç4Ñz¤›ƒÐp92g ÒäY?Ï!Ÿ) j( 2†6Ígf0ãÎóÛ ÇErt˜Öï+ï;'¹jVºß1Ê­e§ê²K«\aa†c¡yÀŇk­ö¬e‰Ø¹¹ )£jfc:ìb»ía»ïj»÷·=úÛîýܶí¢L“€„ÖÞÏ¿ê³ÜK?Ï=û«Ä·Ó_¿ß|HË?ôÐk+V” ¼ÿZÃ>×÷Ü]nÚ§…?³!¼ýöuÓ¦e£käÔS÷ÙhË“Nêsâ‰}¢ý×^Û¶%›Ç°wš¶&Jl]ñÞ¦NiÚßòDc[‚Oìu≽æÎ­œ;·Jd*ƒÿÜøéÅ{s´°k6ªåw,rßrñŽæ1ËM,ÿÔSÏ­Åòé^°p/‰Ä{I>+ß’o£\’ÊÁ’Oj7Q‹3žuŽ3ÇŠŸ¼É S;½w½5Œ£ŒrDiQŽP4µë±òü¿ìwœUºþŸÓÕ3CRóªèš• »kF„‘œA˜ øÛ½»÷ºzM`Ĉ»‹bZê›ï.03Ý=CÎÉÁ¬ë.AÌ¢ `™îª:¿?N×™šêTU]áÔÌ<>ZÓ]uêL1|çé÷¼ç}e¨2$äF#5@9û|E*ÄWûU( Y‰«ûÕƒR<|êñ²ªÈŠ¬È²¢È²,ËJ5_<ÞxPzå ôú!éÍCÒÛ‡B›C[CN u<9Tt29^"Çóú¯¹¦ê˜cN 4 `Ö¬êY³tK¯om‡ŒŽŸ¼-™Þµ¿~ý/¾xݯ~Õ%(¦mÞ¼×j©EܨQg1ó¾dÉÙ“FòΜºlqÍšÉw¦S/dÙ 7ænž¿/Þ~ÖYGuV7$0ÞÍyI—#AyÑäÜ‘º'Åhä¯ I&g †LÃo}"£‰ÒÂ.)RI‘úÅîaÁ÷P2MÔëê›^W(T…ÐQiÇÂ{§HñC§,ûSÚ“¿~z\Ó·ï4]8 $Q)ŒS?¯ (DÛ`µ{Ûw  t£Æýz¨rf…„éîrB¡ ÐϾþì³½;eE–Õ„ÌŸ³ Ë[ïã?ïcëûä£8ù$Íï-U }ôq“'Wi«™òE…!Ä©L•k¯í¸qcŽÎ¢"ˆRjÏ]Žu;X²äÜÙß6ÄHOŸ;ׯµ§Gk‘Ó¿7~Cßáз¶ghR£F̓ãc¾iùkÞ ÒåWE"«RO–ywJiöêü{>OvÍV ¡€ñÍM*é ²¢’)L$5Ž [“ôRTªª4Š?#A¨Ú±ë‘‰Lé„™uy=éyl T@C…4T¤„ˆU†Ô"¯¼ôÆ1òç(“ÝU(GÅÃÝã]TªªTÝùÍ—ŸíÝ£¨Š¬ÊŠù2[¤ª’ªJ&O®ÒQ¾ÞÝA~Ô=l¥j®{˪ŽkîÜ׌IÎ3ÓjäÈÓyÒÈÒ¥ÛÚ¯ŸQ©››Ö¬9`à@S gÿãofS$}‹ƒßàà×8°×ÖLÓkñâíF:‹}9zô¹l ÀÂ…[2^Ö.M:#ŸžòSæYö”Ož\9xÎoBꮽŸïÚó9ú_NÚaÉ΂.aR¤¢ƒ²çýilÓS¨! M„LÄg(°àéKä¸ßL '¿¾.í™ß?=XÙÿn¼O1Ë™!…4ÄSï URH Q¨š¦"ÂÄ)%{»›P%|W¨¢(òÑ:ºó‘*U)¥;¿ùfç×ßïü:MKj‘©£|2ºå\¤[<±…м4räi,ûÀÒ¥6 oôÃ¥'¨¤à²ªûrŸJB!„Òüð_Zz»B ÏïjáÆöâÀüàV£íÑ£Ïcù¦ n^°`“Kw±*¡Ì»^ååƒËË“&S¾mšwd¯rü‰GâQ¡•ª»÷~ñå•gAAÁŸ? E4T¤-2ÃJK†Íñ}Ñ3—@‚ AÂñƒáÌ‹èwÂDIT Óã__¬í›¥¤P )¡"õܲƒDU^›™¦pÚQçñí1ÇÈT¢ Y¥²¬ÊŠªÕé°n]:«4™AóüóõO?þW‹©jXU é-¢[©+Õ™(óþ @ǿԑÑFŒ8•í×_¶ì³ü뤧†Vr›wöÙPjaØ3…hŒºª MÙ»fýð%~øûmva5˜wƒx š 6-Xàpi«î\zÊ3¸EšwdãûøKVVŽ;ñ¨ãO:ê@bÿÁÄ~5‚ÒAùn÷,, ƒ0hŽ¢¸zÑD(¼ûó‚ÄÁ®o¦ï{èé>a¢$®ê'I eÙ÷¡BJ •PQ2þ*R¡öGÊ/–Ÿ~ÇÌ›åSºË!IQlAUVdENÙ©ðÈNán“ßøOÄyĸC'§ º_U¥•+›ú–!)'åwäyÏ¢#’…·–-Ûi»çÑÏfÎL]0 Ó€h.€T€p!ÂE(èТwkŠŠ M Û¿ û¾À>‡»ôÓ‹`™Û•]Œò[¶|°eKºÖCm@ø>ðB„„e„(_}»û°nëÖñë_üX–Ãm_³°;Iß!…¦qó|‡LH‚d޼“¦w%) ÓPG¯Y* ÚÖ*J ))¢¡B…©Ùº‰7Þ­Üý$ù¤eENȲ¬È²’PEV Uþõ¯'Üxc²ÿÃŒ‘éÓkï½×¬ã¦T¢TÊrBö”Ó€ª¾þe&\îøÈÇŸÌë¤/_þ…Õ.Õ—Ü?R|÷š5s2ŒÆ’,Ù˜z­M,~!±@qá¨â¬Fžkßçø~'¾ÏÝÜ#»y×kìØÞ¬‘ÓüùoÏŸÿ¯œç;.ñÍ»^‘Ȫ=NïÙótÑè*Á½¼³æùV’ dÈ¡BuEÑÇ–¼wÜ m‡~(@‚îÛÿý÷öIBa)’aÕß Ã 'œœ©]‘üÌùRHQ†÷•$[MÂ…”0Ä©¡B…¯ñ~¸á+CÑfÞù^SENÈŠœP²"'³#•„,˲vÕ”)¥·ß^Æ/¿óÎê©SÓWÓ+mp&USN5ȼ×׿P7àÎ5|øI¼çÑòå»W¬øÒÌU}nO.°§úîuëšrÂ=6þ=òqøÈz5Ä—°ƒ«ŠF(cö{ûþ3|÷)¾K¿˜lî\¼•ÊüùoÍŸÿ¦ù Û¦ÊÊóà»°”wîHÏ÷Ëz",C’Q à7gu½eÙQ¿á :|Ñî©[÷‡ï—$ì;°/T ÁKjÉP,².?JòŽ0QB!B$0¬³h )lŽ¿“"úñ«7KÔXè³Èïxhj2!Re!šDBNÈjBn¹sÚ´òûï¯øÍo’ßãM7U_w]¶Î”†Yò»Ù Ê‹'–UÍhذñ.Õ+VäX´ìwà üXŸ<Ã#ïVÍ;ZV8Ї³ÿ7Ä7Ä³ãÆ ì`šò¾ýß~Œo?Â7ùn7î'ãÆ%«¤Í›÷æ¼yoä9 μàø–”_ñ²V¤t|+X²³6በ1¾èö:í¡;¤0~øáûì  ¬ÆYŸåüEn9­îüçu„9÷ -D*Òˆ¦•õ|gè<[‘é¾Çî’eY‘ 9‘PdYN$dù÷Ï?–öÖ?\õøãÉËõ«9×\3»ª*MÙ/“þ+mP>@æ½®n¥t„+<»ã°aÇz€+¾Z¹rOÚsüò—üxùüˆJ ‡\=-—Umð}ðÕ“R¸|~$õ.èp:vEÇ#¼Ð´hmSrWÔ cí|“ß|ˆo>X[·Í»^ãÆýlܸä.âyó^·7H›§|4ºZÊ»aÞ‘žïš·ÌebD‡ŒnG C “öË·N ÉñFßäÈ´Èí?݇¤¢/Ù¦YRÖ7Š8ÛùïC=zÂ"'Þš2àÎôÛ§HÈ E‘r\Vsonzꩪ瞛ȿ,-5nœò€åB²-)_kõò6¨¡C“5ôW®Ü»rå×ú·†Tµø‹Î”·nƒï©£îD§nètÔZé¥5  î0np›j:7~Œ¯w`ïv{—¸úê Ùvâ¹s_›;7wÕ{ ´y7ˆ·åŠòŽËnOµ…Û%Ò¥Û“ìvD×n‡KaHvò‘SQ:@FsQâ¤mOV¯ S)L‰DMÖ­|ô¹‡eEV9!Ë s;Kÿò—‰ÿüç¤êêÉìË‘#g < @߾ϫjxýú²^QŒòÿùÏæÿügK ÀÖսВ’¾¾Ü}èÐc‡ 9†¯\ùͪUßH-õÅW 9‘]»vÍ÷®]Óç¹§¦Hf+.ÖùèÕÒKÜáê!-P~í ¸êªäVì}{¶aÍ¢ÊW_}1;˜;÷•¹s_¶7H& îfTV6HOygâ’y@²¤»å 9KŽ7~ùû‰¦.‰x—o]`Û퓈9üâ*)¡éJšäž'«‡Ðà Õ.JçBµS‘Ú©ð“7¦%â‡%â]¾ûÇÒõì²éÒ.È`Þõºöç×)Š¢Påï³ÿjuF#GΊÇC– дqã/s_“A©æ}òä Û£¹­ººõJJúù=¬\ù ! ƒ§áòâÅ‹tìxZæ¼õUÆ þ±Ç›ú"gÔ(s2-5Å r–¬(¨¶nÁ’ÿÜ((]ûÂGP|j¦Šcí4çcÛ(u2Ù)X|ÏnÞSÅùî}»m÷àŽ,þ}Ö¬Z€ä µ "'[÷…$œtì‘a5ŽüÇ_IAŠ“/'jQxóNRHQ¨&£4Eêî­7Ja°Š’5U÷†éÿ÷\B˶jä.Y2yÕª¤—Ïî\“'Wp¬ÏšU;kV$ÿ1W,¶ŽR*Ü Òmðà#¬ZõÝêÕßÞeD>tèÆ&KÁ™|à`V¢á"„‹†žÃm˜ÒW[ñå|i­«íøñ—9€úú—êë7æiÁÚCI¸SRH¥0ؾV¦a$>ܶ &Ì;Ó?æüŸ=¾3­[79Ï`̼s²·¤¼ˆˆMܼ¯^½oõêu&˜ygŒæ2Ï÷<áε ËÑ`øáG1 Gœœz‹Ì à‘™œÚ½ »7a÷°ËìÎ&nÞëê6ÔÕ½höFmU†p7”wÕ¼#ß Ê¦ ;¤ð¸_ô €5Ež»{†²­Uj’ò*92 " ˆ’"ú͇×ñFN’‰Æ%ÕZ±°š¨pIŠ“'WLž\`öìÈìÙ¢P>khii¿'bÔàÁ] :‚¯^ý‹¹³ÿ2:ëÒI'¥…ûI'¤Ó)¸s1ĉâ‘GLF×îèzŠ3Cïz»Þƹ77M˜Ð‡g=ÕÕ­g¡6«jõæ]¯–”_ãØ´|R~þId„$(ß5†uë«9w÷¤—ŽìI _ ’ÿˆ-|ú=R¨²Zð’ö_ebyX‘/ZéVIqg•ý'C<’”·\5ÁYÅb/”÷bPƒ>hÐaìxÍšæBF©ˆÏiÞ‡;Ó24,Õ(? Å£PŒ#OÅ‘§.Þ˜8Xt²óžI_¼…ÏßÄç9ö7•”ô-)¹’Çbëò½i§|4ºÆ=Ê»mÞ‘–ïÌ;Ó;¡‚ã+~VS›ZX ×\ÑÓ@v„ Y…K–‘ði4qľÝÿÃ#3’„î“$zZ÷™Á ®I“Ê'Mâ”÷ñmhÊSƒu8°ó‡&>ü0Á'l@|v¾»w®%hX¢Q~4t¿/»†n§ã¨3¸Çço`çëØùZ–SJJú±u”X¬‘Ōڔy7¨¬l ;p•ò®*MþŒe¾3õÿ·#O.XÎ÷I“ʲŸé¸¢ÑµÊÊx|_{bí÷Â’FÊ|=÷¶mi2Ï9ç¸wƒF£øë½ °ñèt,Þûž1òjr¾~òe™æÀÂn¬XGXZ îpšï\îÙ—%y`Þ‘Êw›ppΙê'Ÿ(§žUðn¶F¹)Ÿ]Ï–›ö}yës‰¦._ïQåÄa:œ ÿöÑ®'¾ð÷vôJö¬Î“I“JžTF±hš][eeL_½úþÉcРÃ9¾·nmÑ©Gðî/Þþðe“Ž>º0_óõFíy7_¾ë_?%ÍÆãXl-¥ Y2ƒÅw—àÎå å½;œ‰¿3mÛ:±{A†6Ù\ùf×Ìy/:ü¸ׇÂ4¤Ei, š&M*ãæ}öìØìÙ1n®r8;q´zõ>nØ Ô…¯^ý=Ô¤g¼‡;Ó¯Ì^¨aýj_t+Çœ‹cϳ—üžFŸlÀ'ðq‹šÒÒì¯8]¶ÕQ;Ü b¸¦…·oÞ­Kw«^^ñ#Eƶþ֩㉉x:íþÃŽ-ê¾Òò–%ÏäȃåpwÛȫ٠K„çé4†·X¸¦©i}ÂÌÎ;á9Ü/Þ€õgbp'ÀÜLFžk7ëÜ”w܆½þãþ|àht5MžCˡò×ÛùžIz¸Û°Až™wèùî%ܹl†kFŸ?¸ÿ½©íÜéÄx¼‹ÜtØ‘ONpâšÙnL29øl5Ê“I“Jò-U^þ;É_«V}]F|º¾gäjjÚà„N°k×.øáÜSùÎ4ŬNE}NÊ3ízp"nŠÓ’K,‘ÈJ”¯¨Þ÷œÒ(O¬.Sµ!¾k·¶FùY³jÆþqJ§Â‚Ï~»XŽoêÒmæNXçsNaZ9þ`gÏŽ„•Až8ÑIÊG"+¡kR,¾X9¶©5çi ñLâÀi‚fäÔ™¤<€/Þrl=öôA"‘åúôí|Ï)–‰ÓÉ^œï>ÂÉ|¸¦¹*΀³‡?}è…D¼KÇ›~Õ©[è˜ ]Ÿ¨E¹÷`çÌI6uñ‘È úé"kåÊo ÒÍäù/¿üò×_ ñàÎU¢QžQó”ðùùòœ1äгç™à»ïŸ89â‘‹òÃN®¯æ';[^×n/:t¿u †¤6YèØ*4qb #ûœ9usæÔç?`$²ÜY+WîTópO«B IDATÀ FvíÚuåʽ+Wîuon¶U‡nÞËÓ®»fÒ‰ᤋqÒ%ùÏaÝ_tê÷ƒNýþ„Þ¨­]Æ5íÊ¢²²ëzÖ‹ B)õݼ”%\“¦¤åegÅÉß<¹Ãá¡#Þ´byÜ—7vΜ:^gxâÄ ¶Ç©­] ¢b˜3ÓrY¬é/lFëÖ­cMM=°X3/1ïžLšwƒÊ5/_kÉÈs}Êê[öïë^пÿE týúdÃ/º\,¦—÷ݼ”%\ã½y‡8þ]/k9”¯l' zô7¯µÖÉœš8±„c}Μz{^¾¶v@ƒ÷+¾¢”Z‚;€þýû«ªÚ¿ÿ!CŽ2äh>” t@ ë•(®²ä噺_Žî—£{Ÿ´Éï™Ô°ŽÁ=YG¾_¿‹úõ»À~xíſ܇m‹,OÃu‰µËZoäEðòäŸÿd³Ü‘!(Ÿ±}ï3ã‡Tµè¶øÐQ>­|ùT¤ÁLœ8ÞÒ…µµKTTŒpaR΋µÞf ü,iõêÕƒµØÎ³bÅW 6F3#{æ]¯J-»@µ=/Ïõñúìþ½aÝ«„2¾OxñÅ7Øñ•}Pœ;.¯™8!‘Ó Ay_Ì;²÷÷Då ûiK¿úzî™êÇŸ(§œY°mkšwýQ¯9s沓”¯­] ¢b¤‹srN+Vì0tèñ¶®]1t¨qý¶`áÞÚÛ)åÏw¦*-\3'OÄ3}ÔÊ÷††WZÜ™÷47¼É~=о}–|ýc©†vïîؽ@˜¿KësæÌå¬Ï¤šš%”î`¶kJÒöô:ô8nÞ—/ßef)r îªÑÀÌû$O¶®1èÇÅ8í*žünRWö½ðÊ+“½¼7lxkÃÆÀÖzl­ÇÖº|§ÔÅ–^·nýhëÖ|™@ü;‚ÖÐNœõjwBHUUƦ 55‹TVz2hOË—`ذ¹6þ.Ò6ìÄ<‡rîzMÒÂ5ø§#^Ðð×ß´¸ÿÅ&+7lx‹}Ù·ÏOÒœßËÝxo Ì;S,Æ5ÍÆÔK$¾3bêY/CS%ß™ª«ç±ƒTÊ×Ô,PY9Úë9ÙÒ²e; ~RÎ3óÑòå_0NQJó¹—K|gšÌ½ø¿¼)ßbõeÇ &o6l|‹qëŠ>¤?¿wÞ­˜Ó)p|/--…†Éëý«Â*-.…¥<ë±'Úz5GÌɺ–-û ÀðáiÚÝ9®åË?×þiPwtî\ÿOóòy">ãêúö¥@ŽÄÊ,JJ(íÓ炌矟WWK½ wÝ‹ÞQ>¨|׿Å$åÅä;“Fy ªj\uõBUUAû§†ïîíMw²pÕûzÃw¦ŸkK¯·EyS©Sï-Î·ÙøÒ¿ˆözŸË/È–hÁ56&©WÐù®½•¤|>ˆŸ>}ºþËûï¿_ÿåŒ3Eç»™X§¼ïˆî\ÕÕóµCú±>NƼ–.ýÀˆ§xkö«…Rjòî^ÂéçºpÍ_-RÞZjì¶…Yâ6/½”ÌNîsùù¹ãø?ù¹¥y2µ¸ëÎÉ‹ò<0´Ì8á¹:’T*¼7Ëöò€;×w> W¯sÁ÷¥K?0bÄ©~M`Ù²Oy¸&û4¼‡;×é–^ÿbŽòö÷=¼;?SÜæ¥—Þ& ô²Ë.ÈÆwýë?û¥™{î0Çwä®yàé}àØ—Ó§O×ó Îw« •þ†kÄwƒ…'„‚_ºô##FäÝ:ßi|ÂÂ5Yfâ#ß™~©yù?çB¼3›Ú¶²-u-xýòËom[ée—ö¶PøìÂÿÎr«ñÝ$Üuç3Êæ›ÚøÎ5cÆL€Žq‡¸|·…â åwh|¯ª ºzö;Ÿˆ™H³dɇFŽ<Íï‰$Å~ÙPJS§ä;ܹþ[óòÊLy‡7-o‰xýÊ+ÿP\zio+…-‹®5 ߊᮻ0ù—eñŒïc†ßÿ“‹›Ã4 îcGÞo Âþ&«š<¹‚c}Ö¬ÚÔÜù6.=ÜTU忥KŠ'PýžC³¸y_²äƒ%K>ðw2™ÄÍûµ(¾6Ý~(ç+Rô,EÏ2ô,ãÉï—]vÁe—ÏŽ_}uÓ«¯m²0ÚÏãõçðú³ŽM/*--fdÅ8ë3馘0î~UÅË É I¸º#G"NõïN¥{ãåƒkÞ ª©YÄ>!гÝIGœ* îÀÈ‘gˆ9Õku5åŸÓyy*mªföüÕW7±ÐÖ¥÷Û¿·<á­·ÞÙÑmLë6ï-iþ›Êäåßǹ¯é iã!¼¾éQãFßoB¢‰6µJÿ®—ÁËû;”îÐmtª©YÂŠÒø«Å‹·T4bryúÈ‘§³ã%Kvø;™Lú#þ¨aý:ÍÈ{WN®wΟˆó']ziïK/é àµ×6¿öÚf#ùͼú4^}Êé)Š(nä‘ÃËSUEòO²ôMÄ‘ˆÓx‰¸ë«.íÿœ5«–™Sgv«1ïzÕÔ,!ìqùXTrñâ÷Œu¶_0¯'žxRzÖYGŠ<Ûë´¥×>µ·Â§¿Ù×þ8óò—\ÜÈíßßzëPú³Ÿžcx”âò)žÙäˆyO3½—gÁ÷±ÃïolDã!úÒ›5üŽxâM4Ñ„x“xñWqÉgräAƒû<¤«RIÌåÄ—Þ‹m0zô9ÞßÚª/Þ®RVmôèsýœPf]â=_~ à¸ãŽzƹò5V‰¬øñw+YŽÍÅõÌÂ÷7ßz‡?ûé99fûÜêÇ÷a”|×F6.½2¾vã!¬ù ¸ò¶¦F © ñ&oBØñyˆ,bå`¡9Y…;4Ç›´yNy±œGVQhŸ3/Þ`Ñ¢w:zôy>Ï+EWÔÞ àåŠÇ܈b<ååˡCY¿ßw üÌÏòñ¥G“¸¿âægKîÁÖy¬†S^U±þ•G(0°ø¶¦F$âô‡}(($‰8â¢Åg¼dnþ^>X¿!lð‹ñ¥Q–—{ÑãiÑ¢wÈÇT¥ "1¾SJÇŒéáÓ¼ÒKy¿QË¡ôñ\‘È  ,/‚7ÿ¤÷éo¾µÀ…?=×tb¥îõ¾wyù]¸ ÷–7jðÎ;ë\=úþQ?÷ý/¿­©‘65¢©ˆ7¡©‰¶òõÕ,š<¹œ£yÖ¬ˆ÷­OùÀ@EÅpnÞ#‘Ìv¹ª|м{¬´S=ú\öËiáÂ- ÚYNtC†eÕ§ÑÀÈ~3Šoο¦¼]ñ–î‘ÈÊÈû§â¢kS“ßíèÅxq^üC‰$ýÒkÇÃ’p¿ªïm‰â $H$è·_ÓxM"ùwí0‡»ù»·ónwâ^²â˜1½\ßA-Zô.€,Ñö… ·°_;ö|§–F™ÒfnÖåP>ágP~%?./’ÜÐtÚûV6Fe}½ß½.ÍÜ3óÎÅ‚ïE…“a÷&Ä‘ôï‡h¼((hç{ÊÌÌÁ÷©Z’ƒpçÒþ)Ç[×/X° Àر½Ö%™Œ#-XÌþ;öOæ•F9s"oѲk<.å¡ß°úêÓòâ»þõ✳÷|Ÿ>}zAAƒŠooU p»fÛiÓ!Äicñì| —Ù)/Úl³Ë=¾3E£«4ÏGÊÊæ9# &×¼.Ü Àêòé‚ÿ¦IÜÐqã~æÎÔÒÈꆦ[5/ÿˆ”7Xxd);óòãÄmØÁ›‘zïùþþÄêf‚âª+ooj¤‡xp†~ÿ5m<„ƃh<„Ÿ-hç{Fe¢¼˜³M«9sæ˜8Ñ-¸sE£«u”·ÿƒ>þÛ¼··6´pácÆô´qíüùÿâAù«¯¾Èé©eo·êmZv <§¼¾”˜!(Ÿñž‰¼}ýÀ‡ÌOÕ{¸ô"µ3)Eß>7P¥ƒ’HÒ¼ñ =xqè€ÚxœuéFˆÐ¿iöì€I“Å¥ò‚;<ä;S4º†QÞâçÏÿ€qã~êð´\#+Àóæ½ÉŒüÕW_ìмÒ+Ÿj·ë–^ö„òiëDZXïÙ8Ó±õØA3sÎÖ{¾?òÈ#‡0jè}‡¡ñ e|?t€²ÿÞpO@,¶í|7)†uB’+|÷î\ÑèZ”·Ú¬`þü·xµ°­… 73ÆEàyó^gF~üøKó-UŽ”š¹C ×<ä>â³ÔŽDV±S«ú/Îà@܆ y,õ¾DÞÜ{ïCF¸³ñ Æ÷ƒIÿþ?·Jú3C³gÇ<žœ^€;´dùÿüg˦M[‰¡#V»ZŠ›÷Xì…œ5N¹æÍ{ƒR5p_°`¥ÎÀ7ïsç¾:wî+ŽŒé¸f¢™÷»P<ÍÍLùìEÞ9Ö#‘Uœõuå4\9 WÞíÌÌVNÅŠ)XÑ\îÆ/¸åBY.¤ T%Y\LQ¨¢á€ÌšeG“&•z<ËÙ³£&M*óø¾öÄf«—È3Ÿ3g.€‰Çû8‡Xì”/-íŸåÌyó^‡Žt‚Ë¥ Ÿ¹s_eAù .wjLÇëDÞ¥kôú[¼¼É&ÖŒ<׺íû÷”bûGÃ'¾OŸ>ýÀn……öýù¡d|†VüÒw°øŒÞÂ{Iù òÍVÏzç/ܹb±­ëXÿLçÌû€ñã/ñlV¶åv†O}ýË,(?aÂyå^à»uK¯RÞj‡&›”gj¸?¾oßñ)€³Î8  þŒå»ç¡>ĬÙÓ4:ƒRô½¸bÄøôeÞ|AnŽ¿{LùàÂÝðbêë¾K(¾3ÅbëX\«¤¤Ÿá­¹s_àRôÙY-Xð6€±cÝM﩯‰g×””\i{·‹¼ß£-½þÆ ÄÛk¿ÇÏ"¦ee¶¶Ú½po¾|ç¯ð¢ÛÔûz:€?ô >6!íi›_•©’²¾Ê)ï6â[ßõoez×{ w¦ººõ˜—×c‹ǿ̟iY‘—>uu UÎÝJ+Ï:xÜ«El¦çGù|Ú«F£Í±x›”°f3|ß¾ýgyrÆ_ #Ÿ·9š>}:€aƒïk<ˆk˜1#MÛä÷þ¥Pš!ÆmÊ·¸ëO`òý›–ïLuu/Ð(ß·¾þeFœÝ“/é›uuëÙ¿P«ˆ÷®CpŸ®!À¶(ïHïlòyï¦^}g¾|׿>úÏyM&EŒïCÞwIàŽ;V˜9³Å£ûø]…­»¦Üp¬Ïžó7Á&š4©Œc=uÖK w%%Wró^_¿‘YT§dZ>Lµ¤¤#{,Ö‹½`ò*/áàA4pó>ÅúTв¬l03ïÑèêhtµý=ŒA33%¿oßþ)ÜMjѯ°ð—Xø_öç“"JÁà@U‹TµHÿî®Õ‚"RPD ‹HŽüw7Œ|+3ïiÏ·t‰Sî=ø` ¥ô¼óº—”ôõ{.94þ›Æ»Ð¯ Äb <(Ÿ½„Çp7hºæåï3mä1ïzéáîXe¤•SAé{Û?% gyr^ õcÿ/Ÿ‰<øàƒâñ.ªZ4cÆõ¦N}Àã'?ïÝ¥R*…šO+)ßºá®¿Š… ¼ÌG ßëê6´|!¯µD·5oÞ<¨%]ÑèZö1" ¶üå;ÓƒåïÍEyÇáÎÅ)ï`ñ»Xlí»ŸË—ïüõqÿ´=“{îyBU‹¥PQ {ìš›o~À“O^¸ÿ[J)TTÅþ}²Ùý«Ne×´¾k×zš,¸Cã;sîÚZ"ÐréU w®ht53ò©XîL¿Ñ6¸;3åÝã;“ƒ”7nhZ~“c…®žmoJwÜñE)TÕ"U-TÕ3(Å£3{©*¨ UÅýеúy2«íÀ]7ˆGùHÁâ;[e5 \[zñn¿ŠDV14PJ+*†ò×Åá;ÓouK¯ÓR(ï6Ü™œ ×dܰºô8PRL¨±1±)S"ªZ¸sg÷nÝ:>ÿ\J¡ªØóÕw;t±SÆ6³Ú ßµ¡Ü¥|°àŽ |×½E‘.SÞ‰¼÷*YÉ« WT  î\¿Ór(ïj‰xoøÎ”'åMU#Xr3qöe‰µLo\üÙg§PJŽ;nÿŸþtùW_~×¹“-¾3YeV›…»6 ‹áš`ñ%ÂgÁ·–)ì§y @콪­]®ÏíïLi^þN4À[¸si”·Ü«ÀZµ™Å×:·a¥uæç9jÔ6€tíºWU þüüEyÕ4Ï,v¦÷%nlË¥ßFnù9sêLœ˜~›€ÊÉwýi-)éïúœ2(@{¯¦M{@¯^g Ëwë‚òÝ#wÂs¾3E£kØIÊçUJlѯ+L_67çÝþû¿¿üî»EE» »¨ªý=Ì0+X|wû£†ƒ”ÜYYjó»uêêÖ±ŸP{8óT€àÎ"3z‰Lù™(Þóå7Ž;®Ûm>uäˆG.Ê;V'rá9·@Q>?Ë}&Lø @}ýÉ?ÿù;ŽÕϬ ÁÝ‹Ù:® ß­Â]€––z·w&@{kõ‘w=ëŤ<‹Ì|Zþ0 ×ø…x˜3òÎמÿÿäÅw~BÅBÃØuuë¬Xq!€¿ý-ø{ª21«ïÙï¥eÊ—X½òþ¨¶ô:UHÊ»[ä}Þä|ù®?¡j 4¾/[v9U 9ß¿É@ùv¸›¸irýÄ*âƒÅ÷|àn$ÿqrª¾~#€ü‹ôz ,i3µµËØ8”O]V}LC<€)‚…küºÕŸÏPµ¦ï&nmòÁ‚;’í>`µi_†¡ØÏ·#£¥ª¾~€ D¯š ïñƒò™ÒfžÐ–^oÀÈ(+èC‡¦úJÛ|ßµk/@O8¾[óë“VºÛõ¶Ûîлw@ðÝ÷ñÈEù¶ wý€LÎR^¿±V|™Ìy„ò9s"¯yù›ü¦z‚ô|çš5Øa¾[…»î˜ímúù(p|gL׳^LÊûw.ò”5_ÎpŽw‹yÊí"ïÎRÞ½"ÀÔ"6ÿíå­þÄbkµ·>|òËw€[ù3V¥Ë®ñ.]$ pgü8Õ׋!QQÊÊ1ó‰¬b¿u Ü=PEÅ0ŽõÚÚåzÜ %nÞÿŒâ¿8‘`cãÇ ´tÇz,ö‚~Ï r¶¾˜Móž:“Û^>¸|O}+Ó»¾È—Ö 9¥ƒ{‹Î¥>.X•ÇšÜ Ay¹#ïÍÁŸuK¯ÿ•‡—Ïó×¼{ªÓÊŒy‡€|×åÔhYnáq8ÈžræDŠ®‰DV(/šóLïÅ;—(/®%t#”|éÀ‰0 O¬þ…zùkþ¯ÚÒ«=Ä;õÎÊ›„;仸tò‚;LoX„ò"Ë¡‡K´iåc{ÕHd94›ü›õå3Üß4ÊÿÂ"åÑqÊ»„x¯ùî.] ׈ïV74ù®aŸèÝÞ,ã”î¹çY={ž.øo#&ßÛg³ßÜLÙŸ˜º¿ë ü?s”wiÆ%Ê›‡;Äç»~|¦üïÒŠáÞòBÏ›nˆïzZ1‰LyßáÎÅŸ[–ÇåûÌÿiFž×d¥¼««ëºp qjßœ%¾;?ã.'M*i5õX¼‘.»fîœ9¹Ûò:¢ÚÚe ܹÊˇrNE"+R¡ß.ƒøËô¸|‡;€Ÿ£›÷Y>•¯PZz•.Á¦w§±-Kp‡#þÝc;œgP¾-˜÷–ƒ4ÃÝm/ÏꙈÐD"§Ò.˜q¦¾Hó®W¦p|×k–æå'¥y/Scõp·çå­Âùûwïqɽ|»‘7£‰Çs¬»jäkk—4pϤœÎÔ‰ wdøè#ÜLÖ°>ÅÕ~zùbŽuG¼¼åëß}´Ã6‚òmͼ§Œ™ä»F^X ¥*g†ù…Dˆ«=1žc#ß¹æè‚ò•hðq_›6Â6Ì;òäûìÙõ&Mò3áÚ<åw¸ÖÄÃ¥pMMÍ••#ÐU™ÌàÔQžÚÛà“¿w.-é 1ùÎT­Q^ŠÞ _÷-[¢¼×|î\f‚òâ»ÛEÞ¥|Ðྰ°!3YÁöCù‚øñݰ¥¢ºx¦§¾:ÀqÇv+÷¯ LåíÁ­†ïLY(ß÷t7r†ò55‹TVŽr`N.Ë*Üõ²(^fîhy׳^@ʳȌZö;¢E”Ê„ Ê°¬Z^>„§úzATnÞc(®eéuë#æˆlúwÁ»Ä‚òíæÝÄ}“|Ÿ8ñjKÖÔ,PY9Úù99-Ûæ]/—Ф§»QPÍ{Ú·2½ë½Ò·ßÓŠ”MðÕÈCkÉTZÚ?ó{þ]p¸£å~(QËçŠ%Žõ9sæ™÷ò‚;ÊúSæ'CùÜ|‡kÊž©7òÂzùR4” @=Šëý3òJKûó ƒ{>²ìßŇ»Aêi'³ÕÃ=»—¯®^ ªjŒësrBnNp¯§]«1ï©§™9Ó=å̉¬×•¯¹Úo/ÿÀÿУǩ¶ý{+ç{°zÚ‰ólÍ„kÄw÷ªâ¸® ß­nhŠDV²…Dï{{™OxŸ«…kŒó‰ò©æÝõüqdRêi'à³ÍBùêꪪÆz='[r»p‚FyêÈ]Z1ܵ«š»hyIy«šæk^~¬ˆ×GÞ9ëÝÍAY”v¶v;bbÙŠ—{Ì©Lášñݳª8ìFL¶o ¸#¿jSÞönÕš—í!åÓ.«Ú ¼…õÕ`Á=“xçÒ9sêÅYzî&N¼:u鵺z>@w/UQ1œc]ÏúÖª7߃ ä·Nà1åƒõlõp7¼˜úºï—¬!ÒÍJÀÙf’7<¢ÑÕü8Ê»]ä}¹VmNP>Ÿjiƒòô_Jy.·å­ªªj·ó©Ñ¿$&.uAù¥l†íʤ²²AëÑèj=îÍ˃ÃÐÀ±¾2¿ |ž¥fÒåsø÷`L8šäãvP>XÏ6­yO=Éw//&ß¹ôp¯¨!ølõò¥ý‡»U#ïq‡¦•ZÄf-#Ÿ'ßuã4‡k²ñ=X‚ œâ œ~{¶9ù®?ÍÌ™î)(¸4Xxñ' _Û«Z¥¼/í÷VéZ´By§à®p=€«=—½”éù-Ã5­äAÙI¸£E¸fÛã걂w#**FlÞ¼cóæ~ÏÅ”üím)\ãWoÕÁhàæ}-Š_ð/’…kBæÌ™› ¯ÀL÷Äû`¤æ,n IDAT}P6¬gkî\üd¿(ÕÖ.íÕëÌ^½ÎD{PÞ„ Ay'“EƒÐÀÍ»Ä;nÞ¹Bº>ö§l¿Ò?¨@?+oTU5–0ðŒòµµKó®3òìXXÊûkÞõâ”F×0Ÿn_æÝ h¸ PÜ™òîÁ,&ÕŸË`z¦– ílR>XÏÖ†y×Ë@yǦÕ*d%µ¤|+Üòê¬tF>=åÑUš‘_‡âõž‡kZ¬¯jÀ",K7( ò…˜îVïÛ¦øÞr¨$ß]*LV[»@EÅH7wCY– 앯qOâ˜÷Téá^V6Póžªuº¥×+5è»jÞaÈgþtÓ¦w7mÚ¦U·oWzÙ Ê·Y¸C‡õö [.ù®ç»I#ߦÌ{ÊPI¾WT Ë´Tݼ·<¡î¾{ù,9‘¯jÊ\ì„—·ÌwýÉæÏwV€;×wü@ïÞçVU‰ÚÊ"áΤ·ðÙ)ß–á® Ølá¥|€àÓV¡|Μ÷×u^/ÌòÉøŒ%¸C·ôڮɮêêy½{ŸËRN««çUW·?«ª¬ñޚÂ5n>«¨ƱÞfÃ5æ74•• àX÷+\cfCÓÅhàæý­üÂ5Iÿn•ï\Þ‡k‚eÞЙs×Ã]L/ï»y7¨¦f!û´ZY9:å­¶nÞSÆw,\ ón{·j4º–-é—–zjä­nXå|ÿ™-#ÏêGÚ„;Rr(Û½¼^z¸³~,ª‘'Bí[ÖùE55‹Z¾IY@ȃªÅz#ßf½¼yq#‹½‹½`c„3þí¡‡l®÷8U'’9÷ÿ xŠ Ð+åsô×ÎGÎ.½ îVyío¸&æ)% /º‹÷½_ ã;SvÊ·Y¸3•—…û%)!I I’»t!’î¹ r^ûØc¸õÖ[3àxàMÚ–Wd¥¼óþK_¤,XtÎS6Ì8÷ò¢e°ˆ)ÆtÆz‘cñ¾ÃÓå#‘VÃ5mA&€¤OïܹC($ñ±ƒã»Qä½7lF1€-(&@t”wÑ¿ë•gP>X¿ò ¶0¾3yUþ%`æÝ€rÍÑs%Eà»^Ü˧R¾-›wÆ÷ººBÃëZPž,XpJ—.]UµPQ UµðŸÿ4Ž0ût÷žÇÜvÛm©ã»ÝÄc‹¶ôz^ â=â;“mʈïŽDÒ½¤|Ðù®½µ˜ˆCyÑàΔ)\Ófù^Z‹xðÁåŠR$Ë…ŠR¤(…ŠR$Ëß~›P”BY~x7ÆwIjüûß;è©M2ùêë4Q·áÎõ޶ôz®Žòžò¶‚ò‚;])å”w¹{àá®°˜‹@y1ùÎd |›…û³Ï>ûí·…‰DQçÎ †õf¾ué<5ž q‰ÙµçQè¢4^Âk›®Ñ«?|g2® ßÝËqt)\ ¾[]Dõ7\¸sÝsϳzö@\Í^¯ªDZ^]=_ûv¥ÇzMÍb}ÜÆUî\ååC8Ö#‘•©Ðo•’¤8¥Top™m‡ – ) %*ˆŠWÔw·ÒwßÁ»ï@iiqii1€K/ÝM€5^ KT’Ñs;€™3gÂo¸CÿÎeÊͼ{š·®ÁØ3àmÁ¼·„Á•¢t· w€øžvYUwq¼¼ãæýþçÝÓN[K§ž|ýøRw B)UоkÁÆ#Ž €VÛ‡P‚óÎC,ÖpÂá}¬|­ ðPŸ‹ÄãHÈäã/èé§_â;ßý÷ï\úR”Á2§ÞoJâ^¾ºzƒu»²¨²r”ÎË/qïF‚{&¼¼¿“qO̼ƒÒÁ–-ز‰‡e(U••BQ@Tfêé¾] TJ …B¥ç¥¥Åa2zÐ¥^ÿ÷âwÞ…%ú£c¦øè£×ýý!ß™å7mzwóæm„T«V@qn‰òmͼsUVŽd潦fIm­‹”„²çDrÊ‹®qܼßpÃë’ÔÔ¹³ªâÓª–¡¤ù¿)*…B‰¢*Àþˈ¯),¡ Œ¡W^|ö°„ãšJ)eQ%ßTWÏ×õ<=ÐìoEªª±–( ¸»$Ÿ©­u˜ò­À¼Ô2(¿Ê—98÷›oÞ(Iñgž¹¥‡",’Bep§IˆE÷G¥û¿P P(8÷üfr†%„CK(¾täÅ=K¨a1È*Æ,Ò)ˉ‚”‹1PÞßÉ8%÷ ËTVެ¨p˜òÁ‚»ù M-Ã5«ü¢¼ƒ ‡ãápœ !\€ovk1ÚìÐÓµ%ÖdX†( J‹¸|8D¥0 K”óN›ôå·Ù³ïOÇ5EQÔ‡~ØëïP'áøn ¦ò¾M+â”ÏdädÞkjÄÕªaÎS>²±[µ¼|Hyù`írï(ï¸y¿ãŽå’ÔôØcW`É3! D.¡ *‹¶'³hX¨$@JìÿB9çâæ’Á¾)sÿÑpá¥(Â!J)K”ôEÂñ=­tˉyAÌ»Aö‚òmVÊÛ$XæÝ¶ÊËë)ïïdì)n ‡›ØqS"ìMÄ›—R¡‚PJbˆOþayý€$)iÞ Ô°„mŸþ ”žqòu;†O;ñ.JéŽ/×Õ­÷ãÛ)?&ˆé}yÅ,“ï\z¸WU šy÷aë©Æwb•Ôâ»S¥f8ß9ñ—ãæý¾ûb|°ÀSO=ÕÔ„³O»±c!:Ñ¢Ú±½¯¶nYYBÑø¥ B(Âv9tî@AqΘd}±/6%š„¥ElRßÿBÚµç¹Á9§\Ÿ—É„*<üðꪞyf%%ýœúvÌ(þKœpàpGË |MÍB'éŒüR†l3 Ü”ÞÈG£Áðò¬bŒþ‰€0ÿ!xÿM¹Gß0TM»dfØC %r2JC èâïlOSXÂÇ_‘O¾’¾øêÙ¡?9ë:îëëæ€RÊSëêÖ{éåâ»ybú®î\Œò›7oÛ²å½@ä›úeÞ™**FrR›¡|°àîlH}¸&u˜òŽ›÷3þÇï»oróK4™IFiB !èÑ¿€/®BŸQi§€B¡â½ºƒl yQT¨jrà0‹Ø„ÀþÜu×]ÞÿenÞ=C¼(|·ALñ³kQMÍÂ^½ÎéÕëvÜîåsª¢b„žòþNÆ)¹T¸¼|pY§üjGÆt£ýž>òÎE¸×vmM8o`! ¸‡´-N|[“–K“aÇçdÛg!Uªà³]ÏJ!\yÁua [q• …èÜêæ;–”ôc”÷ÆÈ‹Âw{ò%‡2@æ]¯ÊÊ1••cر°”÷×¼Ä)ŸÉÈ˼»ª²²$å£ÑÕNQÞA=ñÄápÓí·7×£”RÐâˆOy¦Î‡“NGªJ•ù÷wô*ˆJ7¯O¼µ^Q¢ªPT¢hÄg‰4’)D¥$ ’fázè!vŽÞÈ»Jy!øž'1Å Ê (ÆqNvå}›Vpd)\#¬¼éàQV6ˆäCy7Ìû”)S(¥?þ8…B@šƒïhvñû>ˆ`ÛV;"*ˆL¡Ð‡–OTªžRÈÈ®¢(PU¨*‘$ JÃ!„´ŒII¢,V3¯& â= ¼|wDÞå««$pæÝ Ny¡Œ¼Pæ]/C¸†Q¾Ý¼§UYÙ G(︦NJ)}ì±ÇØ—Éä÷P‹ø ?)‘i§®„¨4¤$ t9;,^¨(PÄààø¡¸LCä“]Ï`ׅ̕Ã4JfL6ÿ a^5 å*a¤ü‹Ž×þçG:îàµáB:`€² ‘bÞ3À”å4$,Ü Ò,|2c.|÷«ýžîú¹.qÞ¼ëÅø.'Â8ó¤;âÈ#QTH;¢ûo=êìÌù„EèA ~"56‘Æ&ÒØ„CqÒGcö'q wøt×Sª‚ñý¯oŠƒÕ‚Ë$.#!#‘ ¬õǘ ̘1À´iÓRçÆù^Rr¥S߯þðaލe¸ÆÉ >Á‚»µ‡k¬Šyù-[vlÙò¾³?·­O/Ÿó|·áàÖ[o¥”ÊÊ ”P‚ƃˆ7"Ñ„DJ_ìIþ…)Jdzvy'E&²Y¢@U (HÈ”Ä ŠŠŠv|ü€[¦ÜÔý§á°¶ÝI’h˜ L %s%1¿Ó¦M£”þîw¿KÇz]Ý‹NyyŸý»ÛÄä|Ïÿƒ{Nóžö|K—8¥ ˜w¦ÚÚe†W**†û23¤w6ç{#ïß™~ûÛ߆Hdz»OéX„ŽE´CíT„ŽE´cݳ?T¦aÐ7!á¬kðÍöÄÁïé¡8³ð´± qÄã!¹(ôñO¸é¦›ø-Þ}YI$`ž(dT9Üï¾ûîLÓsÐÈûéß= f›Ý¬oîú“kj±Ú^íÊ¢ŠŠáëµµËR¡/‚;Z,½®a7È3¸èÔ©U”oèsf>ÙJ(ˆË$!ƒ(ÍæýÛm‰Þ+ haH‘¡*”ùwE!Š‚÷>0ÂÀ¹—K¬MXK” H]T›${Z ÏTRr%#{þF^„øŒ»²ZA7­gÞí©e¸Æ#ÊѼs²(ïÛ´‚ –ášô”÷F„)$@Q¾ BðñW!Y&² YAB!T¦D¡iÇ+:±è9Ë“!B²‚„ EU‰¢@RÕ´QóúJçK’”Ì•d«¬!)õÄôr$\ãß=&f«¬ ›I6Ì»^••£yáÆv#oFœòByqÌ»^ee¹Iç”÷Ò¼CKž9úènP”o ðÑ—!YI®‚&dÒé¥}D¦Ì¿'’¯CŽCQ ¨u ©2…(2T T9 fº×O¯’.â!øpˆ.Žä¶ðLÜÈÃ.å[¿×+gÝ´j#æÝ NyW| Í»Aâ‡kÄ‘ò¾Ì!DpôQÝ@¨¬|ËN ‘eÈ2XX2•/=L–WH‚­¬ªDU±oÓ¡P<|˜¤*tÇž§Œ'×d¿×eCCa‰†´tÉ3fB²„àõjIù Ö¾GKg;%‰ÙºåiÞ ÒyÇ),¸›‘8á1Í»AŒò[·~¸u뇞Ýôñǧ”^xÞ-„ ÂÑGu{qóßw|ú¨¬@–!+Dfu€eJdfÞ‰,CN–ó.+PXü]9<fÁŠÜ›ý¾— õE¤$‰*2±Z J”ß`žòmË¿sµÖp³pgj×0e7ïzéÂ5Ëkk—»>³îLÑèš=NëÑã4x”ç5#Ï¿2tç·ØþÉ£ÜÅ™•î½ôHfç2I(x#Ý¿¯QQСS'•bëŽßKCp ÛìjFÅcÈ¿¶ÏÛÎjU%%}ÙIÊûÀwqqkjxä¶×˼›‡;—ÎÈûCù)%(¿ÖÕÛ5JîV½h@€wÞy;¥ô½IÈÉäwÆz™a[–Ø/€N:²š3…E”Rzê=G…J?¹{™»B ‹ÈŠ:›“/)é«§|–3ëë7¶Qÿ®W«É¡tüäj¸¦õ©¢bXEÅ0vìåƒeÞ¡[VmIyŸ…ÐgH‹ !”RšŒÏÈôÃ‹Ž’HÈ`뮲Y!ŠB•²jbªnIõ”éǰ2Â9oýÈ#Bî¸ãŽ¡%y} œòÙ¼×|Ó§Í¡sª¾«e¸f1³áVÕêÍ»^Ê;6­Vª²²eeD£kÝ üÔ©S {eÓ“O>ù¤þõ;ï¼3Â;–’‘`Ë­,(/“„ŒƒeYF§ÎTT%Û?}À”)SØåÝw,Q°óÖ/Ÿpe ×Ô×o„ÇûWALÍÂ' Œ>[.Ì{º›&án•ÔmŠï-‡JòßY×¼§;¡îŒøNiÍlþè÷„P(tã7êßzøá‡œyòá0-PFD Â(ƒ*ñx¼ !&M ²í“|gÚyËî“~|¦û>úè£n»í6¿&=ÜKJú2¸O˜pE{|Æ(Þðhóæ÷U`Äá2>fTY9Š1Ú’‘o³p‡ë‘ÈòH¤ÝËgSYÙŽug½üÀ±¸å–[n¾ùfÏ<óŒþ­;ï¼ÀŽÏfYsñ& QTÈ T•¼ûñã”RÜœôûã?ÿß]™îKˆå´“ÒåÜ“wôÌ¿WW/PUåg‘B“bSÕKði³P8›ø1f¸gw°à§ùÎÅá^^oMæ=åü$Ü5òžyæBÈÿþïÿê_dUÚÏé~W8LÃa¨J¼@¢‡u.`êÔ©æoĪÏ[ºÄž|°ÀyçuŸ0áŠv¾§‘~ªzÖ‹9yßá®›IîpM°øîܹ"‘åìSWyùм‡ ÜawÃj4º–ÙßÒR')ÿ‡?ü9ëë®»Ž¿˜Dü)w…%ªÊñp‡u)Øøö“4!ÿpØa]¬’ú‰'ÒÄs—Þ¼ ÕÕ <Úî†S_Aâð) åÛឪHd?·òâ{>Õb1fä €ÒÒ«œšÒ³Ï>Ëÿë_ÿš¿8}útaéH•„PªRJiH¢wM»ÕÒø¿ÿýïÜrË-NM8“xä‡áÓÒ\°Ä½¼h¿¨Dƒ;€ÊÊQŒãÁ¢¹_bLg”gÿµAùÁ=O1狽Àÿë寿þzÏ?ÿüÿøG4¨9zïÞ½ ù›‚ð‘„R=O½%žÀI?ò¹'R&éáΤ¹sk7o~ÀæÍïmÞü^¯^ç8~Wј˜EÙ§Ú»÷9úgÕ»·óÏʪØ|zõ:ÛP¯^g÷êuöæÍÛlÞ¼}óæí½z,Ü{cÞ¹zö<£gÏ3¶lyÀ–-ï÷ìy†¥Ë·lù€ âÊä•#¥Äzôøq?Þºõ#[·~ԣǙÛE]ôæ›oò/¯¿þú={ðÍ7;C’ÜóôÛ~tÌ¥EEèÒ„ û™–IŸ|òIBˆæýw>pÞyÝù+Íñw—Ú;î0=[A‚òš÷T1¬’üIkç{NñˆI# óîFHæâáh¸†«®n€;^Ðó´iá0-”–P¦}†çH>dXç?ù†ñŽË`Þ™Œë«ŽS>@|·:Uƒò€;×´iOèÕë¬ŠŠ‘~Ï%·|„;“ù |€à×êsÄÃQÊ3¸—”ô‡VË·÷éw„iXBAýF-<Ë!šø±!ÅÞ%™â;í¹ÜŠán¸ÐÞµù(@|¯­]bxEpÊûÎw&3F>@|w»È»³”×ɵÆî}Ú´p˜†%¼ùÞL=Ä L'„°üzo”îÈ’ÿo |G‹p ©ªò¸‚;4¾3¦ëY/&å;W$²BË¡4B<@p‡WM<4ʳ›bÛã¤òš‹WB@ @¿õVkI5N)Ü‘sS>”o#po9Hò#›Û”ßõp7¼˜úºïÐ8Τ§y€øîq‡¦X¬ØC|Z¸3ýîw¿K$R¨è’w o†Ê>ßÑŒx‹@ik|׆JVUtñ‚;2ð]ÿV¦w}‘€pç2P>@p‡ç|g²Mù,|çZÃðR›sPYàóõ x1X“Xi›p×¹!®P8@|Ïwý L¾S^d¸sé)€ðݸ3qÄÃ4åÍÀ]eç»Ùúb–Ú;TW/H àî’¸yw¼Nz€ànF#9Ökk—ÔÖ.õw>⫼|Hyù-[>`9ïíÊ®ÒÒbŽõX¬Aû´jMp‡úbzZe¢ s¯Þ¬4æ)·?g˜y\VG Üsš÷tç#\œVÆ»À¼3,<Ävñ>šwƒÌ„kÄ÷œp‡þ9»qî55‹q÷s†áqµžGVᎤ— ¶vi»‘Ï)fäÙq$²2Yåï|Ä÷ò±XC,¶.õ„ÁݤlÖw¼gë–#-ªƒeÞm‹›w/)8óÎÉÞ’òÂ!>] Ì;—.\³.-å!3æù×7Ä‚eÞá.­®Të. Lýæ=Ý Ípw;b\¾·|+É÷òòÁžÎ)ƒXÑvÇ+¶;%=ÜKKû˼›ä{¾ý›q¦mJöÂ5‚»Sª¨¡÷òîݨ¶v9@‚wè°‰¬ÐË‹¦ÒÒþ¥¥ýÙ1ƒ{Pdîp¶Ó]w=  W¯³Å!ˆëÐÁ´¦žGŽ˜÷”1“|wÜȳ†¨.uCu\&sÞõp÷ËË nÞ zàèÑãÔ@øwó|w¬ÿjMÍ"VíAysjùÑÇlóRÁåÜáSP^4™ßÐT^>XïåÝVðUW·®GS{ô8• îåÍÃúw½#v6)Ðq‰`Þ jE=\á»n|w–F™oD%@æÝönÕHdëiWVæ‘–y×GÞõpÐË[‚;œòïb¶孊㻦fqp½¼Ûp‡1(¿Œ­‹¶+‹¸‘FWG£«ýŒ€2,«–”ôçÇuuëý™“srÆ¿gqĶÓE\’€æ]/=Üy£»vóžávI¸Û3òmÁ¼ë¥‡{YÙ |ç”íFA5ï)o%ù^RÒÏÃe”UóGü{vb µ»Gp¸¨¬¥÷òþNÆ’<†;€ŠŠáŒìíFÞŒÊÊq¬·y¦ì9‘ëuuëêå[_Í¢öpU1ʳΥ~ÏEtqón‰òmͼsqÊ»® –yÏ©’’~zÊû8æNìo²@ð1\#¾y×+Õ¼‹¥ñÞ¼§›C3ܳGlÚ,Üõr)\ ¾[ÝÐäc¸ÆÜ‘§·Ög+þà‹ á1Ÿ˜p‡.\ƒ–¬oWZÂ5ŽxùV w3\“—Ïgõϰh{¦o<ónx,Yr(ý• |׫¶v™–Ci4é2ï«àÉ% îyz Ü‘_)1=Ü=ðò¶Í;òñïy¦vw!Ñ/ñ'VS³¸¦ÆØ±Ú/ÕÖ.ˆPpG‹ ürôveQK/¿ÆßÉx R’ïÞyñánÐ=÷< gÏ3ÊËE÷ï~™w½ôpÏîåÛ¬yo9¬óF>OóÑø®ß‘[´n¸kW5[x/),¾§šwa)/ܹ̄kÄw·‹¼;Hùüáñˆ)~F k"<°n^••#õáo"6Á‚;Wyù0ŽõHdy{Ä&§ÊÊ2²G£kÇ j‡»^¢…k¼Ø¿jC-½} ÙIDATŽä}ͼ/AÜ@yǦÕ*Ä8ÎÉÞ’ò+"‘¾Í,EB™w.]P~mZÊ·‹«¤äÊü)ïˆy‡U¾{LÌöJ«â”wÕÈÔ¼d ¼¿“_ee¹Iç”o7ï™Ô’ò¼¹iª¬Åß}tÄVƒòmʼgþv<9=X|7˜÷ ç$ù^^>Ô‹9eœ†ˆæ=U ë„€Á£ï¹nä{II_3ç;eÞaÉ¿×Ô,ÉÙIÎ=Y × înH®aûKR°ànRë>†k‚weeÊÊlÝúÑ;ï|D,/ù {gs¬×ÕmÈéå„;ÌûwÇ ¦m™1òÁâ»ÛÏVã;Éʃ»óÞòüf¸{ìåÄw±˜1 _Z*®‹÷—ïºiä6òmïL­¦¡7¶¶v ÏÌɇÎÁâ»U¸ë.ôšòÁ‚;4¾3¦ëY/ å;WÊ; w˜ŒÏˆw´éJ;ª¨™‹ê`Á=•—ÕGlüŒ€ÒÃðãXìߦY ×ä)Sþ]@¾sº¡/–ÃÝ*©ƒÅwÛæ=eœì£Eص[ؼ§¼•ä{iéUžÎ)ƒD3ïzéá^RÒ×qó3þ]d¸£e¥³ ‰­U¼Eµ%#,¸;(‘_ɺm´qe;tXÅ^ðÝË‹ w%%}¹—gpw\9ü»àp7hÚ´'ôêu–håjÓÊ÷g«‡{vpîN™÷–c6ÃÝA/ßšÌ{ËÓü7ò‚ó]¯¬pÞyÝõï­‡ï©æ]pÊ òlÍ„k‚Åw7ப¢b8Çzmí²Tè "Aàµ#‘Ui½|°àîªJK‹Ù2ò‚» µ Ê¿T_ÿRÎKŒ|–y·-_Â5z¶ŒãœìÊû6­€¨¼|pNÊEî™w.WÃ5ÂÊv5‚ ®àæ='åñ™@1ÈX‡—K¯z¶-øžúV¦w}‘8æ=Uz¸——œy÷€ïº{5ÃÝFÄ&@æÝ©R3î™Â5-ü{€ä”<Ë”гÍwýë"‡kÄ‘ÞËG£«ýŒUy wèÂ5b±uÞÜ4К0¡#{&#ßìß ¸³Pé`Œ”‘ƒõl³ñÝp“^^dónн÷þ@Ïž§—• ò{.¦ä1ß[Þz;(-íoæü6hÞ[ŽÙ w½—$ßÝ›ªS}0R† г5wÃù–.qP‚{ªyœò>Â]›À:~œò‚;Üá»6²‘òÉøL€äªéƒaPë~¶|éµ¶vymír¿§#ºÊÊq¬G£«…Øøw¥¥ý9Ö[M¸Æ=¸C®± ^~¤¸L»S¿-ȪyçÒå½£|àÌ;'»ò¾M+â”ÅÖ¥¥|°Ì»ÒS>„Ön0mË@y{ƒèÙÚ†;SEÅ°ŠŠaÚPí^>·8åE3ò"˜wƒôF>¸^ÞUón£|(X)ÞãÒ^·£6+åÝ»QpÍ»A׈ C¸†Q¾Ý¼gW¨½ÆˆÙ ×´ón§|mírÖm£]Y$TP^@ó®—žòuuë}‹5yiÞ¹B={žÉŽÄOgö—Vƒò‚»KâF>q˜ò­Æ¼ë%BP^p¸s1ÊoÝúñÖ­Ÿ´{Ó,ÒçG ‘ΜIBᲕõ´sÖ¼§J÷ü;o´J¸§»(+èü´2+(|G:ó^RÒÏ—™˜‘/æúý«í5FÌ«½§%•—ãXwÜË·JéŒüšht77 ܹJJúq¬×Õ­3bãÜ‘©¿‡h5F„Å¥îúé ;áT¹mÞ ÒàNìð¶`Þ[Ò w·½|€øÎ8n0ìz¸ åå}ä{úü÷ö#&ôžvÃ:/‰¬`°nW•• äXwÕÈî™dðòþN†ËG¸#K>¦ÿßÞ¹#' t™ÉQRr…¤H rc÷¹3 ‡H™ð (HK±Œÿ+[ÒîÚzU #4.^^¤Ø’°(¯E—Ï´‹ïwƒ-wb/-Þc>ü"äù½5Þ[¯¼,¼~x~5-ÊÓQw¦£Ü (¾Œ©)-¯Hî°CÞoË+’;!‹ò¼r‡Á~·aY”çu+ºÎ´“soÜ{ô­Èï!â½:¾ÏEy]r§Ä{ózì­,{5­4ù¢/×È{–¶zxo{,ŸäÞöE~,¯Èï®r·>xÃb*ž]îàzþª–Ó8Yè?ÓNÞ½w¬ÓrM¢ºõz7ˆ"¹O!ËÞÑìey3®_nýnz¹F``öC9Ó®ë‚Èà›aÌó¥Ò¨m½¦x'|ïÃïyîfjE~ïÕAžrÚòâ\ûÝÆzñ·À8Íà™vi›šNuëõÂ;­ŸNWzË+’»/LÈCÈ–"w˜Òï†qª+ÞfËþì˜ðx¯±ÛýÀzýZî¹ ÀïÕ9<åÞßòºäî%Þc†jùYùñhy]r‡ gÚ9}Ê ºäÞŒw±–— we¹F‘ßCÈÝÜóÖ«¹Ã”õ™‹]‚ñÛHüÖ«,Šbc´~<^ÒŠÍ yþ‰fïZ®Q$÷ÐÌ{ëÕ[¿ÛLY‚ÐïÓg³å5Æ{-Øm¹ËiyQñnÓµ\£ÈïAã½úE(wüOù·qƒˆŠwðØï6)N餖w"µ¼&äÁjyErIuëõ^–wÞùx!H¿\ãtiñÞ: âý&Ì Þ[¯A[^l¼×@¹¯V÷#©ð{´xo|ïSîô–—ïðtô擜‰Òíržž†ü~ÿ ‡Ã7÷tDƒNŸA‡íw't¦®?5ð­ëÓÏNŠ%Þ… %Þ‘óùØn?xgB+ÞÇ!0ÞàÅp8å½úO‰IEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen2.png0000640000175000017500000035457610304371203024635 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ;–ô‰7 IDATxœì½gd×uçù?ç>“®l—kSÕ¾ÑÝ040‚H‰AIEJ+¯àLŒv÷Ã~ØØˆÝؘ/ŠÍÆîj#¤‰Ø‰•fDŠ”D¤D3¢H‘hA€0Ýh´wÕ®¼OóÞ½çì‡û^VvuUwµ¯x";ãåË›îuÖïýóÎ=—þ芻•¢inÏ×Ü]|'W‰ÓŸP)0cB%€P¨ “@¿“ €4Û !UiÙ ‚ÀlÀLZà4—ç»"  ñ@ªä7DI ¢$ Ñÿãɹìµ @ñ?~£D ¢à£ú5À›Ç.‰(;aU¦ þîéq2ŽŒp d„P@`"¾ìºå&ƒ˜‰‰È6¶í}ò*0m0€í½#þ¨(  fÓ¹éF{gû8–CKaÊaJQj¢F¡’ªqîÉR1 âZ" &&ø×%"&"1~s~ñÅ\¤¢¦ƒûÊÖ9'Ή›8)qÊlÚÛJÌ|ñpÕÕ40t$Î:gsÎY†hAG˜¸3ÜÌÄÌÄĆ˜™Ž¼¹pÍ/LoW`l*¹î¯Ú ±±§à7.Ž×oì:gFLwô¯4`K_À¹ÑÚ=ÿŒ[Ê~ãĹkÿ_¼m#¸ÛoækÎ#^Uïö{Y1<‡1kAЊ¤P‚߯PÊÿA³ ¨Çms' Ù½ ò·a¶Í¨‚§­§^Ú̘@Õ ª¤J U%¨*HþÔŸcLUÿ.¡ÄŸLœsâüÉq©>!U¤³“q¥Ë² Hɱ ‡¤ RR² g‡B÷>ðáë:z6a" vŸ÷g%¨÷t•F(bê.Ï€ˆ˜‰˜‰Ëódøãr1 "Mv?›|gñÉCsÍ×b‰ 0ݺ¿bSQ‘ZR‹Ámåäÿ' ûO0D Òü2>l6lIA)ˆTDI¬e¨ðÞ}åë@ü­ õ=…=…Fü[&<Öwn)ï,Ÿþâ—Z;ú½ ÷…ºÜÕ·³L¼6ûñL}ÏYRP)r…¾¨Í%YÊv6º×øi¦úýCT‰·Îò”õ[; x#(¨€D3…®hªx…*„‹w/óÿüé:<ýûlìeþGþÕ§à”4ÓïÛGΫ’(©²SRÄ~áèpPé2B##l„B!v>òÈïÝ’بó†ð¢ !©À£…’à`&¢+¶¹\ À âZC¼xo%û‰VÙ„ˆ¨p`poÉyñîÜøiú˜¹£ÒÎl˜ÍùÃH"Ûî‹Ä¹¦~wΪªa®g˜¨Cw213³!böBž¶œWZã–‹÷Ö¸1!ß9} ÀtçÀJÖ‘xoƒeÿ'ö3!e¬ ýîÃc½\àr±Ö(O€B¼3c@rÙުͽ2Ïd6µ*tdRÜ‹|ò¥í³$ )çOmÚx…«.·0”m@Aê*(šBÞÿ*Pý×ÿþ§§ ‚BTü [—B@Bä$U!€UÊÄAY›Ú·÷m½;„6Žá@D”ù&ž8 DÃj B ¸s ¾ˆòÌx!"Ff¼P©³_)™Z"™!âc‡.ƒ{lbqðpß¶·l%ïê&rDùo‘Eù&8uh~ÛÞ’·|”H‰ÇÎ=›åtk­p|Ž·Û½ ¨@™T¼Çýû*.?Áà6ÃÀÅñºGüê…|×ôESW°^á¾%ƒ»ßþâ—ÄÝç»ïMÛ}¡.žï¥W× â3ˆÀ™» $'; ´qµ˜3¹)ƒÜ±Aîâl›ÕGã†$;=BÎûúøAªªÞ*É6 þÇBæd“*)qœý2pþ§`ÅQ¦úJ !£†Õ¨*GEEðý÷ŽÚÝNÝÂØÉJÏ6bÿÃ%¨ƒQ6Ë”ULa³ÿì›yÄÓ¼Ipj!û’ý`6l@TK”XKESÉø#WPµœhŠ€û*Î9Qq*2q†»‡¤š Ô4š²·GÀŸ”H³÷D=›ãâÞM p=wi<ÖÉÃ] ÊôÀ¾ÊëW¼™ÛM¯fSoÀ…±kQ~ ;Ÿ7‹^Í–2~&ä[âf¥ÙMƸûX¨‹菱Kñ]~‡^Yø˜’ÊBª¤(Ñ"n¢ä3žŠº’§A3½½è—g;ª¦tÎÁ›4íÍ|£fšüÁÄZˆƒ8§Î©s*Ϊ“Å›âTò›já~ë³dbp QÈ?ð1'Y~QE ¨3ê bá,$…¤$iwgw×€Q»0~Úˆ3b—gK—²KoÕÁgW¹ØV(h¸€]û+*"ê<ÜU€…d‚ȵºàËä;R4ˆqÌ ûܱ?áP‘âü~˜ÆBå妕¯""¢**ª*î«4Ÿív‹÷ÖhŠwOù•¢{êS]›V°ŽÅ{ Í›ÍÔëÏâîë÷•¢Z÷RÌÕÆÝò ræÉh‹B_E>׺¨ÐÑ’€]4pÔïÜ0 !ËÓ’,¦F3J€SÉÒªùÝþLAÙ­¦E‚>*A`¢ê¿ÿÓò;8q"`ìÀÊKÊñFÄúÁÔ·ÑõTWçÀôôÈÂøi!SîÙ (©ŠQasó^Mkœžèð»ê­–:˜@\.`ª%jxÑù髳:*aG%03Ÿöv”¬UÕ4ƒˆŒ_⎞œÅ*Ý[¤*ŽH½3C‹?©²ðëø›ó;ï))H‰”I…zêã—Š½ÕÂô;’žŸT;PžzŸ2«H&áYUXò§»“p÷á•û¦Þ…ü†Éó&º7¯ô o ¸ûh ù][ÊŽ¿í…üÝäû²â½5<Ö½Š¿‹ˆW‚T-@\æ ñhº1©/¨e§*QËNxôÍyœËFë³¢ü„‘Y4¹O9ÊUõ}®ìbhIífýßÿŸ’?t}à ë,+D@°2iªÂÐìBbóÇ©™ü6u?ÕÕÙ73=â Õ‰3å ƒ”¥ƒo™W³l¿”ÉϽC’Õ>ú‚DbCÒ¬–9˜Û 3ó©çû®Íí3óvja‘M»÷•­sª=v¤*2q.ܰ%e¶DÆ;3DKùn8ÛÁÍd=} *P êÎ4;³ „@`%Ñ»ý#óÂXÝó}Soá Ä¿•™eãĹÏ÷]ƒåãoïÒš»Æ÷k½Õ†”b.Æ  vÇ)ÿrý£”(sÞ—-‚̺jk¤ø1 "¤a±¥J ¢ŠQ—‘}ñ š9qßk£ŒÝ¾î^¨•ãÍaÈMøÖÇ’ªu–,`GP°@Ä)AT™„U šf–P@Y-A;;z3Ó£Õñ³Â¦¼aˆTEÅ©’·Ëém4/Y™K¦î< …J":TUJŒ2e.;€|Žò´LüQ\>Lõò{›ÿPÅâ“XqNœg}ÝŸ8'šB©SXRGê š–Äbì;F¬Qk$íêè5jºêÄY#)‹5.5Þ”·)Ém<Ѷ—ƒ#§í‹kŽÖ ó•p°csÀñó³þfg[¸o›ŠˆŠŠØÔµuÖUäø+65ØX26ïY!ÿe„7L^Â3Á—Øçºˆˆzzç¼Ïã±ÔãÌÖWQQõ·ïh\W4)¿¹¯Ð3~ªWû:Õ'Q=Ù=åoûÛZ“qwø¾zñÞŒZC¼x/F\ŒîÐÛ~)ùˆtQòébñ:A¼êü¿3ã~¾ß¥â¬ÈИ‹œa;¿Ò|òÓdçî:Zﳯ+ÜëoAá9±V¬k%ub8ë«#8‚%uPK°€%8 µ„”5eµF-kÚÙÑÛÙÞcÄV'‡k“ÃF¬ˆ5’—’܉™Æ¯.Wšr϶ €á‘3ó©ïňŠ1ù„ªM­unæyÃmdRfËÈŠg–Õï^Â>4oˆÌâ„Ýì2>Þ®"áÅ÷º$^è|±™eõ¬ã¤í팠wM¼·FS¼lˆèª#שx¿®hŠ÷·'âïßoîÍhú3…;‚x%¸†SØã;WèšÓdñÞųBs'š÷¾ï¾¦{g&Û "GH –`)àêH©…ÚL¿Ãz¯†Åu¶mèlÛˆ­M«N3.mÄ·åïäšpøÞ6GN_&êïÛWQ‘ùjºPMEÝñá!<´í;#%õì&v3ÛZÍ÷+ø“Ÿ­ØäSNyßjÇW6‰göIÎÅGšÅ‘ªkÅœiFߨi@p6Uu%ỏ™òö¦[!~}øïWF=¹íBþÇîW\*J g^Hî¿gÞHNóŒ³Y?1QQ@u¨Ja³=ÌbC+»±èœgFËe÷^±qŽË{5Î¥ÎYkÅÚæ¤{q.3g„àˆà°€cX" X†MFŸgM½Eãµæ×ŠFCïÛU9x|ñ÷ï-;çòÙFB;H5©¥. ´cá£&eæ¶ó ãí¤Z0•t ßsÙni4¦9啨·sREÅ Rˆ2WЬ*k îý£§Œômoî9?ZßÜWðˆ??Z_§p÷õ%¾Ü ıá÷ÝCåcgßâ¥5wŽï·îͨ'â%|Ò­E¼KLlHÔC¾µÏŒ·nHµ¥P€B¶5àZÄ9àÏ‹ºCÅyLù6ZwjÖæ Ÿu©l>¤õ±MéîËí¨>²Ë˜îÀJ,P!ˆ‚YHI|“ ˆm\‚*‹ Õ¾¬JN‰•½yt&nC6?P'éÃÿ›©¬Ülöº¢½˜­^C¼<>¿g¨²g¨rôì<€wì+;'Yõ¦Håå$!gD]bI4·?.œ9ÿ.b´÷-6Z/…‰¹ì…Î\Zر¹¼csÙã£Ù‚¦Y%©äûWÊÈTw'æ15C£èS¡eßé݉þÑ“Fúv,Ùï\¹¯»¸Up÷ᱞ«x:vöN·—¸c±^ý™Ö¨'âɇ‡·æ¯í‡øe°‚òÙ¢ÔÒ`q ©Âw#ðwAÊgã+r¤¹q²l:ô ¯¦µ8ry‹fI6{löã5+ˆl¹öNœ8Gì8tˆ‰œ œØ MG%Ñô"7­‘(·©Œ*©žŠÛòT/TAlÚ:ÌÏŒ.ÌŽÞäÑn+º ¸ûh$ÚHò3§ˆHÖ‡`ˆƒäë`§Ü0‘sJŽT÷ ©o»¥:€öh©²  ‚<ÅJX̯æCÄD}ã*¢Ö¨5•‹ÈìüïäÛd,Þ«iV‚þ,šâ}÷Påê#×oÜ!¾ß&ñÞMñ~K¯N•!ùô©äª»™bUÊ,™–É,×Êçb´x…­È^âÂc ²[AŸ'\óªÇJæqÙ“L=¸Ñ—EZgÖ‘²BNØ©qB¼x¥ÌŽÉBäˆ2Gžá1 †2äXÜZ˲º(üüŸhŠ÷›Güê#µHóÁb+eVf0k`ãø4ˆ\´ 0št€ihð|¥`<âÅ$“SÉ#÷_¦dƒAÎü×Íû†ÜltÙR?:ÛÛÆ•RNÖff瓵“_9è¥þ¥â½›û Ö‰źò·V¼·Æ±³ ^¼ïª¼%)'ø~àf.|Rts”_tG{¼k3§º8³)¯¢ÉF爧ó1]ˆ[ÙEU®W«voRžZÎ ¸âÞìÕQ؃Â=(îFqJ»á»¨sX”íNœUkʼnXÎÉÎ䘅I‰²öÁLÂäˆ,HŽa-• ô¨#k#ùÏ„K¢ÒÑWnï°0;¶07~‡º­˜[x¦šæçòæ<£mCgÀÌ`£AÊqC@J¬j@d¢Ø±s¬R4•bÀÀÑ3Uï|1ëغiñæë‡«%ÙòTy¹¤_[¤¿}LEÄ1œñ'˜øÔ·<6^:Nª—úw^s乑š7ß·ô×>åoÜ›Ñôgvo}«!~íö»áh¤êá”Øq俇_Vï}S³bÝ·nÏvy®U)ëéÞ”çt1ÖþÆ’©¿JWéLКxõ ßpFA‚¬=Š?ç4{Ñ(¬8VR!Îä6u_ª‡@ýZbÂì;ð¤ò{X@Ê$`¥ ÒV%£0@ (îþ„yñoªþó»? ÜÞ·0; :7QjÛ°úC}½p06Sí*•ºJ¥©jUò²¿ö†Ÿ§$…ÚîܬUve á t§Ø¹wæôÑîJ!ðÒÚû/|üÃѳ_I„†v VÒT“'¼qÒîßÖìH)C•Ê%3[Õ…†t”Dã#a2hïº;v á_õf~Õß<7RópßÒ_\w¹Ö[™ŠßZñˆ?væ-âÈßv¾ß1ñÞ‰WñE¸nÊ3´Éâ«5†¼l>ÅbÙńé¥}æ³ù RÕÖ"šÓ‰’ƒ È|Ñ‹ƒÌiF^‡æbL¤`ßÒÃa¥ä…2#”5SrNBL ‘*åu@PÇÄ pvÚ"‚0è8ÚX©ŒF«íùU¨ö¿^R=?xó˜•Û{Tç&®ñͨÎ~•+_—TîX”©Ëâ¡SnÏ ìÙ7‘$™9fS¨;Mc-6$ï!‚9`×}ÕÓ‡ÚºÚBÿò½úÇ?ù§zù¹—1·k0Sp=m¥Ôb¦V=tZÞ³9O·‚*¥Ð9Ë¢=tœ: Ù‘¸»ækoºxÀ…{V°î>š*~°¿`xíQþˆ÷Ö8vfÞó}ÏÖÊÑ·âo/ßï Ü›‘Xõ|JWøÀˆÈ÷f§ËChm ‰|¿fë4‰äNë²M#(´§¾Œ±@*”2Éè¾xè´UŠb€HÙ§²xê± ^Çw¾ox¾äS¦ÑHœsÖæsĬ{yþyW-ÝÛÚ49vö{·[M…£XKæî̼ÆwxS_A «ï­1œ;òCýÅ»…øµM¯æž­•#ë ñ·…ïkM¼·FêÔó=0ÔŠø›…þ*Ä{½6ËÄLxbàK/\øX³H­ÏÙ½Èñ¦]³Té+”¬:#Ô"Û3¾{‹B¬QUp˜-+%PñÎ ”Avº•XüòƒäÓ³P*O̪’B/l¹ç†×‰(”:<ßµ¹¸ØvõÁýýéűàÍáýû†ƒÎùëûî5&0a`LH„g?GŸø-Ÿ\5V`LiPÌßD¬–²cŠâËVÝ[üiáÞälúô§ëà”œo°ü ý¥³m¢U¹¯k‹³éácØ·û»‰Bc_>ü`(ßñI¡ƒçÞ¼¥›ú ¸~¸ûðþÌPq¨¿¨w¶´fíˆ÷fxå~ÏÖÊ=[+Öåo½êXËp÷az޹–¿î×;oÅ—a_e@m~ @T¨„Ñ⌒'6}é‰MÏeŇ— Mšëåš=k)ºxórç½ièm„|MæÌ&q~þ~~“Cp¨€˜@)ôÿ>Þ³8 Tĉüæ3¿O“³ÑÄŒ*&ºÚ¦ßñQ…üûë;Ð-Q(¶{ñ¢Ú¨Ï_¥¤Ê˜€Œvµ»4{¿daÉõâ†Â7ðeeN-YGÒ(K£ fNŽ”šÝc@Y Í×¾úzð¾ ÞÀ^^LðZç©M³Î)ÜëåÿÓ÷ÿÝx§¦A­A¸7£‰õ{ÖÏ4¨·Bÿ™‹&Í—ôôq]}½ë ÓñüZ6üÄΕ<9øœæ.|nU]Žã—ݼŒûô˜-¾ÇrüY'Ö©oìœ8OíœïàH9R ”ý÷ot«f݃EÜ'Ÿù_æw\Úðh›í¬ˆïÍ•›éÍ ØÐÝ `¡æ’úŠ ˆÙÔÇ*pðÒÔ}N\Ö:'»vNÄ_[qï~ |å{êÈ:z‡þž&¥#ÑWNU€lfòúÈVñÞh Ñ’ÁýÅ_7Ö¹ÔÙFbëIjźêAœ IDAT?ùÔ=ò g;rô»v¼„qÆa…QDYr5 ÞI³óZìöËm_±ÖöuÇÙ‘ZV]“—É¿ãÈ™ù#—kù5·˜ïk_¼·FSÈãŠr6ª:›®æy®ï'ÞøçæöüÌ€«ƒ?¹õ¹'¶=K£…¼ÀrW_]ÓzX*çßgKï±Eï_‹ˆ‘¼Ë˜¨ujM¨(àâßý¤MTü`ùøGþ›4µfvîoþ¿?MÚ %‰‡žUQUàÌÁgWylWЏØ*’Æ‚OZ, (Úß}hdrÿrÊ]œ8¥ÔªýòQbVpUk¢T»\R–4K„xÃ]ê§8µ¸(I‚¤e•‡»uÖŠ³Î½ªÿ¯«U¬8@¬¸‰ ï°k÷ËAqÆQÅA…Q6=* Þ®UÇ—ÆÖáƒ=;xïJnÜ›Ñôg†®‘aº™XËâ½5šB~ï¶k˜w=n%ß×Ü}xñîá¾$E¶z ¿däñ_MlF®Ù© Êm=«yž'v<«-ÆúRŽç o:õ9Ö©UïCñ>W~·-ZqÒ왟B˜L¨êÿüÝ’×õþú™_þ gmqrBÒm%RUÕhðIQøe¤UUÏøÒªŽìQ.ÍÓªñÒd©±{òÂv6Á– %ŠìhµÞ×sxd|ïevSÎzÿ¶yÒ/ŸjÀ¦aƒ†5jKÚä»/bÊÓ ß·l4DOœÉÌ™§>ÉiNöÔÙ×Ó¿võòö¹O7k›:ÿ(€]{_ â(ˆã0Œ‚0 Â(Œâïþ(ËJDrÔßÞØzöTÏ Þw'^¬%†s!?4Pº”_/p÷±(䷵ݳ†)ÏÚ+A{åmTHse80¼HùUò}avôéW¾ô±Cßô7¿þ\⪉[˜;331Œ–&‹«‰';¿­úý Ê«¶¤a[±ÞâØŠ÷Úò£®ü.[Ê-xq"&3gð?ü×Bý¿òK¿öѧ?i]j•4‘ÔBœæ¬4^  ‘Å.–×墰Ðb»GqÙ—¥Im åÙ[ºº(¶£™¾Þ##£{Z$ü¢÷´ÿÞ__dÃŽc§d…`‹j3'áÔK»}‰¤ -.Ü”&HsñþsŸRqÖ9ëœßp’k­³§˜8ó°*z·þ´X,a†ÞvÃ8nš3âXãøVVd­ùJ‘+Æ-ï­qöR&\n«_qÏÖJóGÿšEü¢d½Iį_ñÞ´h\sø¬Óãqúð Ííä;$"ódôâáí¯üãÉ_L¥–Jupó{ü€ö®M×ý¶„¡fšêT­.Š÷¥§«:6ªŠw¹òöäQÈ1q„ólèÄy[é—?ø k­M­M]çèˆ8+.…(D [žU6ä)ŸåwUOøòu¨¢™m¶é"8ØÆŽ,ÇÖ€‘K».wiÄŠ(Y§nË£C)ÀÆ)9¥}=ï‡+Nw~Ã?›òRÿ½»Ý¤)F&²ïªµÞ–É2«&¿)â~ÿñW²IC][_ MK…B)ã( ã ŒƒpQ±Ç1âçÛÞ²Ø~æ5R==ôÀJn+Ü}œ½Tõ”¿…B~}‰÷Ö8rzîÈé9¬U!Ïfçíì¼ÅMù·Ü}´ ùb±•+3"YVŽ]ZÜùzõY8ÿOÃÕÃ¯Ž¼Ö~îTkÓSçtöníØp#%…Þr1ˆ bUèÕAU«’䫳겅4zùÍ¥~Îîò«˜Hï¯HÄ9•§?ô«¿ü¡_s6“®}—ÎIšŠM%I!"P›ºl ifhþãáz%|¹` —‰÷Ö£¢§¼M6m0&`p –·š8¥³›N¹°£Õ¢q>Á dÛ: ŒAÅŠ²©WàŠm=YîDýD§¬QpyùÇ>ešKÔZg޼ i¼£ð¾/íññÝ;QD™0£0Œ …Ø»ðA Ñ Ensöqû™×œÚúŽ«Œ¹é’™ÕÆ-òëîK %=â±ö„ü¢~÷ˆÇõ ùõ÷«GSÈ3-#áÅ%‡^{@h¡«ï°³/niÂ-SÏN˜|ù‹³~нò ׌'{¿¬ùb†"Aè€À7…_™ã­Ž.ñsÿáÏÛÞé*N܇>ðŒõᬳÖ9+.—º4gI¤´å½,ª"‰sÖ©B™aŒ—ð8uàVùqÊ`¡~¯Gf͹œM8L`<åM¡a 6ÁÀ–ÓFÎmk-¤çDUN¾a‰&“ðÐ’=¡)DJP146;®ïûÐköÔYëÜÁs/IRØÑþÐÉŸìÌà¾ç Ž‚(˦’ aÂ0.J…B±æú}ÿŽR©¨§/Þ^Bekˆ­{ .ÞNñÞM!¿u ´õ&(ÇÎI·*–­‚o ù½ÛÚÖNÞõ²”âÍ ùõלÍä…¼ª0 _žwM$¢´Úšúû¹±³0="é--tDÔɧ¿þ™Sÿø™ã_úÌ÷ÿêžÿ«ß¿î÷—;ïn ‘µ&†ÐA¿@›M®ås?g©´_®dþƒ?ÿQ_(ïrÄ]<£âÔYuÖ%iiÓ»X„DHÅ?Aj³4ùÚS׎UÂ݇¯; "l6†ÙÄï4…Ædåe6¦𠀑á!_ dsÞŠ8qåfQv ½[ï‡Æãü²æ›$D¢VñžŠµÎ¦’eV%ÄÆ§~œ›ûî;„ÿ®‚(£BÅaƒC_ù½Éø^*k©|{µãô+€žÜöàJî0Ü›Ñò7†øm›ÊN¯+ñ~•h ù½Û×â—©ŸYòå ½| ʯGñ¾ú©ªÖe)Ö&âOøGÕT5 Ózuì„‘FÜ8À‘ØHÙ0õß½cÔtu®×£Õd´šŒ–ƒñ ~ýïþðÕ¿ùÃÕ¿Ã'6})c±ê²îÁrÖ)¾ß™>·Löµ…㺘€Uø¾ê‚ÊSïmê1ç5»µbNö\<¥Îf”KSu ùêÊ©@S§‰UU ‰êÉ_¹‘£­0AÄAU41)ÖM±Á&0AÐ74 `ôô 'Nüµ—ógZ°"ƒÃÆZû‹}ÿ¯SçÔ9ðȧuÎJöæÐÉã“'zÍ©ènû8ƒ( â8Œã0ÊT¼‰â оüwúÅÏ[=€RIK¥ÛÈ÷§~Jª'·½óö½ÄÍÄ™KÕ3M!¿ñ:(¿á~Í)¬‡OÏöB~{Û]§üòõ‘M!à*ˆ_p¿®(–»¬ÓùÙIø<`ESAúú‹¸z%}•MÝD’F*±pÁýñÓÓi,CbH¤.‚ E"‘@~ò·ðã/üÁ>÷«yéE¹-è3ÄGÌû¦×…;€Ö‰Q¢¸œø­ŽMôøCfbˆXo;[GÎq&Þœ…©°oE¯NE²s„ªB«  :uàkWù¥‚ÑU‹÷Öˆ ŸbU€M0Pû`Pª]Úöçl˜¾m猞Úl3 Þ5§¹–;L¡O”L¡±ÚÀ©o4›HÊÎAYà"w.uΊ{óèðÄўܠ»=ì}ç)˜0ˆã ŽÂ0 Ã(ã0ŒL…QôÕ¿Ëü»ñé@OgÔÕE•ÊmÕï×ð06õHï‚xo3M!=ˆ__±úþ‡O­ !5y>»`Ñ¢âýÍõ×ÛgFUž~åËÓ宩 ž"‚¬‘$B½Ÿ^W; š'ݸé„/¹ÿþÒƒ¡q¤âT ÄBBqa§ÎBHŸÿüï=ù鿸úë>9øÜ §?žMqò=Èÿ+(b˜̸p¨Ý¤G©µ;Mëô(߬†Þ{¿XøVdÐ@YÔ¼câ´C¨*FE!NØdnÚ´÷² ©#uŠ@!ªäKó+qh ‚çO” @õúá€MÅìDÔ&ê Þ©Kùë“•¸$êNöÃFOlìÛ=bÄAØ_;b£äÄÃe3Ž|ý»9âÁO³MëœsîÈ዇»{ C~pï#gCœw53  0&øÆ,pÙ”·ñéäw>uuèØøíê?³óÔËNlx¥›z .ŒßM¸ûÈTüÆÒÖ%N_\fþZ3Ö£x¿®ðˆ÷*ž€7sâßɸöü¦&Ö—ùõ(Þo¤‰˜  ÛϾêíçâÅ#¦Vç«îŸ¾)½Qoç-žã´ëäˤWƒûŒ39Ö·­,ä×#Üo¬¹XSÈï»B~UIÔ+…üz„ûjÖ÷¸2Tõ«ïø•_<öBñÍÏèí´Ó r¿ñÉß¾éLRšOçæ’9IlJµÕ,%± T,„Õ À*NAÎâ‰}võ/ýÄög_˜yȧ2![–/kÞ®P• Ì8ž´,ˆw, híyôWÚ8Gª“¥óv•¢ö=³gTCø~“ÊA¡[E˜ÂûvÄrXîÛõ£×?G*$NØTT AT™T†•8dŘkŹ`7#Þ}¼ô#<òÞ@I"Rµ¢:`?àà.ÒO'ôØÀ¾t´Àè‘Þ¾}!/á‹íáì´#6L¿7Waõ (Zßø×9'nüõŵGîùÐD¦Üƒ¼±1Ïÿ?©bû`†ˆ£¥uîýw™¢÷pïíŒÆ¦oÙÛ×\5{íˆ÷Öðˆß¶±äu!¿.âf:GzÄïÛÞæ'…<ýÑÿtõ£›Þ§¿Þ|Pýögôá±¹1Š~ÔÓVGRÕFMóv~õÇîƒ3ÉÜ\2§ÇæÓêp[òH#pN…s`ªˆƒ8rVŸø¹Ï_ïK¿0õ4²±3.3òrd°ffÚ±_n[á:†‹áƨ Ô0²ñ×ÊFùƒ gUŒJ(bhÈ…>¥H9“½–Ãrï.k¢4ˆ~ôÚ_‹‰Æ!2ܼÀ0&³ÿ¡ù7\kÈÍý?f¤9›É9QuêÄÉ…ÆIC± g)Ǿ{' Ml¼?>ä¬Mßøf §£}LJç£8ž:]<ù÷®Ã6IœMOú¿>[vPÄ÷(['hTŒX8¢²X8GÎê“þÍ ¼íçÇŸ&d=Ë[i¾”ïù9 œ²ñ‡7ö˜&Ôå ñj5üKµ3âŒj(¨Tè"‡H)¶Ý˜œµ–úv9¦&²Aüò+ŸÍÉp+å‰wÜûAïÒPÞà–ðÝǃïc§"Îå]œ¹0TmàœÑsY›­¾ûfL <M]àù©ÆÈ£‰é*€G~[¦‡Kam¸‡lšº49ù÷ÑÄt À†Î"€OÏâ0à€ÀS'£€ÍÙW$00~Eïaˆó#«ýD½]™Ks3ˆßsâEGw>z•1{ .®a¾ûh¥üÛî͸“”¿nç´ wäüÚV¸¯¦«ûé#‹„¹íŸ£—6¶ƒ8º¨B”Tèÿ>BŠ@1ª§‘acH«‹UBq¡“ÐÝÜ_ÿ(iØjÁÃA°WìÉ/¶#H;‚S¦\©µ]ZÈzGªØ Œ™1aŸ®Ï[” Ä©8ß]†TH„áBª,6«¥Q÷ÐÿýàC¿é½"ͺYútêзj iµhna¼ú1ÆpUÄûBÉí{:·3Ú|Ü=Øá-—¬Û%‚!˜Š9© eøIª'ÿ>šœN&ƒû¯ÌÏœ¯Lœ*ž(Lœ*œú Nþ$Cù|ÍMͺÑI·z¸›J<Ù{»¢Þ®íZ ×˜f°^àŽñ®¯¸å‹ó5±¾ûŠÅoU˜Gá__×âˆÌU]’j²¿$éšž€f˜Ð\»YUÅ_íÄÖÙ³õøßèîÛé ³c¡ktn|éh£²ÕZwAÿ¯‡:m §‚ÚFˆ S«”HØ»Á,Œ'÷}ñÆÞöÙ¹/Äå+åñíT@‚–k"ÅkA8ÎaR4Åg¥ù´<—ÖK‘_‡”8°à“8¿|¶*CɺU`+ JÛâbG£±P«ÏÇ¥Ne£Äbï븷o`ßè¥#fbñÌøÙ[v —ðaÀ7ðk Àþv¸w9pè²SÅ¥³::Œþ­†8sÒAQ¹Ø¾P«¢msFâRca\ä°ÌG½ÓÄH×Ð\Ï»úê³é¥˜¶¶Ôh ¶` ÜûhbbcB">óÌœW HEÕÊM}««uW.€ÊÅàzÍÜsüÇÝõ#¸û˜žO§çÓÞÎØ0‰bznU·×BlèŒLÌܲœ €ñéd|:éíŠýelºqíÇÜP\ß$Õ%…’sUç%|[ɬY¯f‰3CÌêÄÙÄ/’·Rꛯ9uõüd¥gÈ]|£6ú|ÌlÂÞÆ–$œšÿÜë=œÔ»ã%6Bc„hA„õßx·t"@ F¬Î·že‰Öì\•]{±ú¢ÑÀZßsº+ `zÇQt]œ›é«ˆB Dª6WØ©á¸K5_tª$¬NÈ0ÄdŠÞ1 ‰S^üŶÿpôÀ×I/Ëz–• æfŒøg>Öëúõo^fsü¾Ø÷Xe‚¨ ô\›˜ªOCƒöbeôÕJßCÕ¶Ífæuí f~$¢h> EG]úò† h\ºˆûîýg‘®Êþ¶¹ú~cLr˜§æÒøZ džútÈY2:K„°*A³ _nù?ÏU|ìUüØÔªþŒ÷û€Ã»ßs=lĶ¥ÔŠoá¹mSy]X4·ueíC§f½„ß¿½ýЩÙÛñ×á¿_¥ ¾­düŸ÷ü£üJ¶»³‰ª4{ž\'þ×T©«'R|ßGÝ…}ÉB^@r.þÖ T&ÆfØÎpr±ÓÍkýQ à·É‰ =Þ~µ¹?Wob?þ/Øusƒ8 R°ñ43®Þ ÿ!7B˜A¶ôL‚˜þÑ$ŠÔLô·àß/œwŽÕ±lV 9îS ECÑP)EÔóóŽ#ËÑäÌ„å°Ô»= "kb,¿†Å‰Cß*DS°qçcÍžï¸ÄüÀg¿’x¾×ë¨7Ðh৯_ö­Û÷„i)‹'22:=u´5í¥"€¾GêaO8:F”НÕëm;~©<0ÿ—zÛÔÔÀ¥ ;RÛ½ù©Š£Ä;O}1ø˜ô"¸ ê‘Ý7CIAªù]D|Ëx‚~õ+K¿W™KC›¼6â÷ý!€Ã{Þ»Ò€u'Þ}´ÖÒlËšµLùÛ ÷Öð”WÒ7OÞbGþ:üïÌ4Òe<Ö$Õ(dÑój.sfZ‚ÙˆXçRc–VWß®©K‡N`/±¯¼¹/"7Æ}£2äB̶G †©:{Õ@ 4PY€v…mŸºá·ís”g¦ÿ†ØØÈR–`Ê“p™3Rrá0Y/š9DJµRØ6ohŸK~CΣÍÀ!ïUE ".A9óg¼]SÞª`%ÓhT•8,u*±2 ´,q׌Í[vu÷m¿tátgÏÖæÎÔjj5 8 8Z…]óþÇ õ:}í› ‡Êñ“²}ˆ­ƒsèîäÎ6žœÎ'ŽžÕ‰aôl3 "&¥J¡k&Ï"I4ŽÌÂ… ¼KSÔ»/*'¯¡>¸ÿ/l•™lÕ€öGá‹3úXÊá|ÚWØå1tš÷³i.Xë÷¨ól>Ài¶ ¢ßcÕµŒwîĉ¥ž{µîªuW.þR]yùÙ}G@Àá=ï[iÀ[î¦çÒζ@g[´f½šÛáÌ,cÓÞ®„Þ®xü¦Ë®Zcµú}•SX+%ƒ5£â¯Y3“&5×\KhIœxýK©Ôip:g&Ͼg{A~º¡gªUµ>Zoí flbȵ±#8R(Eo|Î?É œë(à gžA#s›‰ˆ Ç„&0‹23«œ!~ÀÕ0SîA¨™Š7à@õƒ£õ·O! œ%±,Ψ⌈áhsSÂýO9Š,GÓ³“)GÅ [­‰Ó ²&ò.ü’ðÅ3+åW}—1Њ-‚€7ïyìÊ»š±qCÀʼnõÄ÷«TÁoÛTö_øµÖøŽ‰÷ÖØ·£ÍC·HȯÊ_}‚ùª«”L¥h¯Äv·b5‘aTLêóÚ\\\fj1‘!(ó/¾s'3G»ª‡„|ÿõ£5Cb‚p£ë´b²Ôð¯J]'â›p = ŸGÌ.¼z଻$Pj>à1Wú1×䛨û «ª’ŸåúëÎHbä”ås Jú ~”?C¸ÚUC…mê¯Rië蛞œŸe“ZTÀÉïÉŽ'e8#´óçåä·ÑÕqÒéÞ‚‡ç~©oâ³àc°êæS†©/ Ýÿà_Œv¿ôòï¸ô|¤pJdz¬«:ß‹âà›G‰SPBÜ€iPP'Sç Ž NQ•ÃGUWíxÐýn‹QƒŸ{ïùïþpó•Äû3}Ýq_wŒ+(ÍëîWÓ|QÍöM嵃ø»wožœÛ¿£ Àþm·ñ«òg®âÌ\—y57TAqK"0D´ØÉ}¥0AälbÓú•^|WÿÞ™±C•‹ÇÎ|ÿÞûœìÝ $¤¯}÷Ùžs lƒ©S+ „j°5þMÿ$7Ü”äù§¢Ítzî‰0—TÇAD¤~^fæÏx¯fÐ…çxÉʹEbÅ] ÒŠ¡i!E0ã¢Y—Võme„TH’YIf¢®”0JÜHjJÚ•XÙ1h±úèšpoFj¿Kλ›7”Ó„®9ËqfNç«Z­k!bpf‰ëÔY9‹ÎÌ®Açvmw'E’°[·ÑæÎîv{0H'¨Τ•ʘˆ©W7;MìLêm=NMŒíëì4¥BX*D¥¸€©šîÂt¦û¥ýœ",*R¥ œ‚bK&•$ºP;q~~¸ß f5©â¶nœ¾¸ü¢ê 5W.ÊÅ y¶Ûø{í}|¥?°¡àÒzƒû5§°NÏ¥ÓsiW[Ôµf¼š;æÌ\¾¸v±´ææìškû37Ü\¬RÌ’®WùU~›" Àê3õꌊ+—ý5¾úÕã_™x}ÚÈ'ý·&+áçþñ/j¨ÿé'«¿û%~ˆJbÔˆuFuȹ€4€ŽÄ=w“áùɧ¹Å™i^P9C²O«rž_mæZDÕff5€ ÀFMþw÷N»¹„$õ6g,;"1³ý%qÆ»4*Æ CæD)Ú÷?YЉ¦g'-G… CWfYWÏ÷fxOù/¼®bÀT-Ónù8²µ°} ™³îÿôù¥ù±1ÖyONÀæ'ȉ\ú>íÙýÕêBØÓ{áÞûþ–þ,Ç$D2;»Yúvuš—Á)þo|!J“Âð¹wUÚ¯ñ…Ùþî“ù,â,õŠÌ±Ñ7éëR« ^Ѥ„¤øÈÀReÅî»Ê6Uü5áŽõÉ÷ëêOÐ¬Ž¿»B~÷Ö €cÿ?yïÙ•y~çÜûÞKïÊ{oX4EÓd“ìn6©ö"ÕŽ­–&4£I«‰Yí†fµ«Ð*v5;313šØÙÕJ!­¤ÕhÔFdKjoØì&Ùô®Èby‹B9€‚OûÞ½÷ì73‘åQU P̨Hd>&È_~ùÝsÏ™só~‘¼‘ÇÍe5×àûMvŽô¯dÌ-â¯î^¥ü¨¨mîzôõ¯åÎì?¯‚g&ôu·$lb¶1›ßÚ1 s6ÌÉ¿|!UVæNDNÁÖøî4Œ8YêXrS|ñüÏ $_ÏxÄí§t5ðm什€7¹¨j™»†Ò¢þÃ=¶B.&“«"ž­a—pæ´!Ëlù|OÖ9vN9«È¬c+dÁÁm¿?žK8Hµ¯0*4:Ltä?QÜܽ²iå?‘˜R@Õ×ÒÇ>VrD¨VüW‡¦¢vÅŸÔ!D8ÌG_µÖÁ98‡LÎ-yH9çν$÷ÞýGJWº»Þ ƒ¢ÊZÇaj˜4$pN«MNÿVç¬ÕC½©‰â²ŽUXÛûýÖ+Ÿ{U«o?0UKS è÷öHœA%MIŠMtûòÿÓ½g IDAT Þ>|Å¡ðˆ'¬ï…±Édߦ¯tØB„;®“Ÿ'p¯kËš&ØwìF(?»|÷šcÊßß'‡=âŸ8ø\îÌþÓ+ï;ò·#¤ ,%wÜ6dEg­ÎÊïìn*³©(SáäJ;媠×ÞÑ ´~°cºì.Ò‹çg‚C\wî\÷ï& àˆ`ÏéZ‚)ó^¿åM.N™wá?| `+pr‰G<Û„œa›XÝt*!Kdy ''V9§È­°sdAVøt*+ÖšÛþ q\ø ý®È5r¿ ÿáGF«]ÀU¸³ïH\#;“ªd'ªVUW—©ÿˆò›T…Œ•wžÎà S¹³ÿO´.é  p’d²¹‘Éñq¢hcR½ÇNÝ·ÅØMbí¹CãºÖÄbýçãKbÄZX×ûÊŠ+ý «7îòn °ÈÞþIN"²[}Ûªd÷‰+îTðÀ™W½íׯžü{]«—d}"yüÌ\S~¾ñÀ–µÕE×@üÕø>ƒmßç ñ7 ÷ºòã"®©uñäsÿÝ]C#c­Ö£.—¤,eñçšNöè²26e6÷@Õù’[o¤þýųOxÃî$æ†X¦Fùªç¼MN¥”¯Ÿá¸û[Þ |Í¿ó?V±eªò=†Ù¶xÄ‹Õ.ag¹ýtÌŽÈñ@·~‡ÛwÀÁI ñ¦÷já#œcÄ:±VœíݽåÒŸkõŠWªµðÀSŠ­VNkáS#·-Zt™å˜m^°wóÃ=ÕªÊK)¿ù~“#×,­ù¹Dü<„{][×Vãšë¢üUëgfnbǺ¯ ‘byV•Ü<ÜäZz&Çú ?ù5¸ÔIQ ‚&MÃEg þa -Ô²dÜ5•4¯$•vE ¤A ¬à^ì{âÁ•×—ÒüôäS@_.8EhgüÎÿêÅ Z.ñÀÔÖ©+÷"÷6åôgŸNl™ˆ…™ D˜H¬ø^꾩;H¾S^¤:yç¹±È Tvbü­ž¶Æm©LZ¬W}{¾I¸Fr²íù| Üu¼R!€„Ɉw먚w£)Ã.TÓQeý%î^€±÷RÐÇ•8’¡×Ûz8;9¾³«û€µ!ºW†u”èȼ¹oìcùBfrÖ‰5fÓ/»¿l,ÛŒõ2‰t¬‰áï¬3UÊ +qVŒk7ÜqHœƒ5þKç,¬í=1Uº¾¾ûÇ 9Ù ÇË›w9w·bîè¾À0ÕGf W<â{:¢FÄ/D¸ã¦Gf{¬¯Yšõ ŸÊÏg¸ØwlÒ#~ëÚ¦é#þŠ|÷óµëSúfD…’ïÈLŠg ñ3¢¦ÖÅE%K˱æ@ 9ç(ßÃ*LtÊ›ö˜]vd"Tb´Ä¤AEK@BPĤ@ŒÏ>þà’oLÿû’%,.‹ä€Äwþ%"¿ É" «h¦:Me@_Ô^P}mí”Ü.*"R ¢à+_|°ñ'½] \-+8ÚÕÕª >8БûúÇ^ïnª?2™»þ@ê¦á>1”¤¹«àž­ûâ ©jc#Úúª‡¾kp)â‰zz™Äá±âÈØ‚bâ•+^8zä£míç+•æ°']éuà,q£­‹‡žGux‰1–hËõþ/ÛÓo%¦ÂZ#Ô©ŽÕ¦êâÙÖ°®aSV¬}¥DYg Œ¶Ö¾Ùí³–Sw °¬ù»0¯ž+?0z>PŠšÛbÛ÷?`Ï–ø3÷Xïéˆ<è†+ î~D_ßM·}?~¦àù¾fivù&õ­k›<è§CùËç3³÷FeRÕ»¤üŒ˜w¯ .¥âBæPñ,\@.€(åÛF2qK ظþ›?øŽÆÄ©wî‹RÇҙѾÞšîZÔ÷£äØñ‡Œ‹ìüU tè`Ï—«öš8Ðtçg Æg5N¬…³ÎT»û‹«R^ü®([ƒ~mëÙŽµGFnl(51¦Z;“æ¶ø¶}Ïxoë/ýq<ß©ö zañ}¦àÞ(Oy";=+”Ÿçæý"y¾hï±kt­¹5|ÇL#~á^}À¸Å…xðHd -ñÙ#äHW®ü ÏtVR»ÕA‰Ÿ¸h,·þÓJ$÷Ü=ŸL…ÔÔÚsÃpLhë.m[ö’\´R +ö†Ý z_c.ÝyÙ'&‚ `ƒ:ßý¼ŽÕO”kï_ÜÕyTcbðÛ£PÃÙÜÈ+/ÿÆý¿yH'2zª°û»;›Zdx¬¼øòªMÙP:Þý›j-f:PA€‰¢}ø ¦Jjq5¬ûL¦Fy3EvcQYî¬Û{êþñÁŒThIkgÀŽ}?°{뇮ôÓ-éJpn¡ñ}Q ã¶gJk–V]gñëWä9¹0à^×¶µÕzÞ}W¦üeò™9€;jXϤ؃~¾Å5q˜$Õ½®2ph\oi“½¶<¢uÄé(­´S1 "C˜„Õâ PÀNKµ–ƒ ,nQ™ÏG/õ?!ÊA‹(C#)(ñ½uÅÆÿ_½ñ-#D¨opò7¢±"Þ€™uR:L8¿u€P}*÷]hz—ò|ò£_9ÐÐ!ë‚€ƒ*rôô_ýq(RB)¡rкù—ŠÖ9»å䮌¸GE~tï“]Ý‹òãƒç‡Îe›»oàY=y$ë2-å ôNWêѹcÔ{öáú‘M  )‹É†åýÕwóÑÉ]°)¸4$js!! ×o\?QŠ8†bâÌPœb3ô.ke e>üy¤`7kHœõYÈœ}E¯\g,H5,;•ª³Uù1Îâ‘/DžòÂV”Ÿ¼³eEYX匿ÝÂ8V¿y`{a$ç\u…¤µ«Jê{ `÷¶G®ô\õtDÖ‰ÿ/êH-ÄÏÜQ‹à×.Ë®]–Å,P~aiëÚ&ŸËغ¶ùJˆ¿Ø¿·ä4€ñY†{£<ß(Ý(â£Tâ™ß+U ©8_>w¨Ë½ÃfP©Ê®®e‰Nbí&^~ÕiÑ fÞ¿ Dj{\] Žƒ -Ð ¨êÞWÑ 3T3ï Z>#±s'¡#uIý»ãÁÂQs6ÕžtåòàB 4k%  $¥99(Y{²k R­Å é§¾ö— "¡H(Z±ãŸ†ÎFÎFbSΦĥœÍˆûѽO¦#Ζcw|Ÿ ÇGt¦)îI½dAýÅ»„ŒpÜѺ•ÀP#Œ9*ojZVZ+(+:žù†$11iØ”Ø$E.µiÍf(fòÿ²‚bbåɾïoÌŽ_ N0¹èhåpiàü¶ñqàŽ!: C‹3câɽ)ç'ƺï,mØÖ¦µu°ëkSN­* EÔ{ÊøÈ‚Z&Suè0VjÎÝ[ûŸ¾¾€+æcàœ4uNñèö½ÏxwÛ‡¯òtÕ#xÔ–X1ك{£<ß1Cˆ_ æ½1‚ߺ¶:'äRÊ__ÿ÷Ùwîé§SŒë§üÌÂýøþŸX[1.¶™Bd tGJGZ©t”ÒJ¾ð,i0C˜¨Úx±êâ…áìwè°Ã$N˜(IÍYKÕ5 +d¦ ëþ],`ªÁ¼@ô²®„VÙÊiã`q;a'`8°39çØ:bˬÈYrÝc¢Ó.LýÝ¿lš2Ž”€c°[ùá x|Û£—}rü²ªJu"*L e›/ßBëJjn‹ý¢¢Á}ëÛˆÓ»‹(qJ!,5:ó{€ƒ"§ *“Wټĕdœeaæ-]‚˜ÞHvÅ`õîWüh+W.ðÙ}°a÷⓯d8q‹®(Êþ»ã‹ŠA,Õœ°³¹ÕÙÑÃ{‡6oër Oo}­Ö®r¬:_{ó:}à¨ùAu'-}ü³‘°†2¢¬ó™Œ²Ï<[Ý¥MÎo-϶_ÂŽkýµ6ÂÀ¹á²GüâŽÔ?¦æ37&õ1òÿà`ß± øKü|Ÿ{ó^W©ì<ßÓO?Ûqç¾fˇŽï{¢ r¤\Bi†}m°øþeÍ–Ëa”RÚíÑ¥?ûeó+O+ÓpZ¬&ÀjzéjPS4. ‰À!\8ÅO¶g½gˆÃêi5¢ñCbAµê™j ‘W÷LÉD[Ø5l›ÏÊ=ÍVàZã`8çØ ±9!»täTäTJ؉çœcE"áʇ´H­÷ü%º fæ ÇL_GF~nëºÀÊs‚Óuë½§¼›™™}‡7Ñ6þedÙ’?@‘â·ÎµEb~ë+Õa+ˆË¼êý°b»V•}#`d¬`éºÀw~íÏdÅԳζ/íýA€¡ýa×Ö¢5Æ”º÷ Áë_Ú®Ñ{Êl^§ïØ®ßÙS}|÷i àñÏFNYXû­ïU7ytdrå2*gò—Nç»cϼ³ýòo¢¸î^Þ¹/îHÍÛcscÞë:vºàù¾vYög*«¹lñŒÇúÖµÍôuÊO!çÂÝË;÷tÄ×…øך­ßû}8G¬ÈiË. Ó*q:Ó¤ ³aæïôµˆÃxÌüùÇÜoþ$íÕö4Á*c‰ จÀ°}ÄYf%ÀÕ ÕëEåNÄ€4ù½©µ¥Q@ãÈ–jνjäÙ¢:®I@,$¢¬c"X8qÎ7¨œ°bøyÛ›GNZN…Xq ÷ÝŸ|Ý1§V>¤H¡öm§óVY¯ë¸í;û0€;Ö¾Uãµ+Ò æ©û Ì°Š V¤•ÒÕìÅ‚À ¥¨ÁÅ¿ú•¨ýÍ8¶«ÞG]ëâ8©”+1 °=÷•5+°ïq ü¸”Ú°Æ®ôRëöÜrÛ––H=ðýrâ5wl×÷ߣ_ycêeò§ý Àúå™b‘Ê%0ú2®ðŽ÷žðÎmÝØ“Ö?\ö|_Ü™šW-àçî^7iä¨y¿Š.5ò·>Ÿ¹H¥ŠóˆÇµ*ðf/v_³í£½{¾CPDŽIY “@t¶E#üþ3éðßÜ7™Lˆ8EîMc‰ÑàêŠ+Ì€†† àÚF!©¦2¸¢Ãi¨“%@]°¸JÎÙz>ãœjûYOvß6k¤=Zt>iëÏçµÀ¸:õCØÝý#Ç¡H(tä§b;hüðùo…+R*rBÕlTëu‰R!‹ W™¦Žb~¤˜ÉäÚoò©~çØN÷n?PÑ«àf°ªÅ/ý Å*ÐÄ*PìÀþ®Ÿ~µ:5Ë?Z:P‰¬¾ÚVW¬Iœ1õf2B†à`†"RÄ´úÃC½ÏvP'¶Ëš·÷IôRŸ ˜‚ûlèõ=›_wC„a…AèA†:ƒ Â0 "ÅRA„©(Üý=õÓ¯^ÐëÂpï/¢suÎØ$‰+ÕöªC“#ÝwM®ÙœSÄŠ«*ß™Y5t<^»b© ´çÐ^gŒ3‰3æƒ_¸`ÈWݼ?ùéÀg?~äÕæÈ©â™á+"æÎÝ?$‘«˜÷k½®:Ö—t^qÆäœiÅ¢´@æØ¼7êØéBÝËߪs˜M[S=ŸQ÷|ø×çܽŒcEkòc/¦ŽVÀÎr¿²¶žÍ“ƒ‡˜Ú*µûôaVük+†ÄB,Û^{¿*Œs¤º°¨É5¥‚ƒ7Bĵ ˜ªGž÷Tt ¸AEøÂÊHö7VàÆÍ\ÝT­™9ƒdªVÝ_q\ÎD-ù¤)o*Ùˆ¬ú¼ômHÆSâ¢Å±ƒ‚°¤»'?9ÐÇQβ¶*)¦ªsö'ÝÖsAK[ÿT_ô+´‰Ë&.ᵆRO['Ï´¬[SV:PJ+(­µ ”ÖÚßh¥ƒgŸ–ã{å|/ˆB®ÄÀ¢Nì {ß“l’Dœ5IWâã?êôžÙx–™º{š´Ö»¿ê0¨}gÚªÛ”š—Žõ5h]6:Z ¶=-=õß[ßþ Nµ·Ï>ê¶mQ÷Ý­*eŠ+ôÜ«åóWë]~×î°kÇG®rL.£1íMù¢ÉMSF7et.£óÅ[óú]±( àä¹ËÞšKN$íÍ¡¿ŒN\íw±@Í»ï&=ÍŽðC£•¡ÑÊ ò|Pù FþÌ»ˆÈ¥cX§¡UÛ?n%nG¬#DG¬:êpTl¢lÌã‹Û ‹ÚÊ‹ÚR¥TT3éhðïµú—ÚÊÂõ9"Käˆ58÷ê-Ê !?DÈVÍ{õŠ#²U+ä¬( ¶îW“_ OøN¹bE„ÄB,Ä Gíµ‰ð‹‡ú|ÓIWóïµIsõÅÝ)]ͼ_|ì èÇ/tÿø¹Ž ½‹ÂÀ›÷ ŸyÏüm|yã÷g4çôúUUs=Y´>©l»$qIl±ÉÔ‹œÈ‚Œ¢)«®N¾m“&Vĺ–Â÷¾´|ý¢ BÅ÷ú^qÖ8kœ1úìe~Ô¯ý]àéoÅ?xþÚŽûš‹Ó7ï:[3ò‹ç‘¿åªç3ë–_ÑÈ/P¸Oß¼7Š[›"šWæ½QåŠó”OEì)3ÉŒ³ÆÚÄšØ$×ñ*Úáríà­Ä÷ çuì´íL`e:3®3©(’(B”V©´JqÍh3¦²×9~“­g½·Ê‘º0arô:v` ²`ÏqWhªˆ_’«å3 +d<ôåsÛÆ?•9ïNÇbÅ—WŠ œŽÚTÔ.ŽDØ oÝð õ4`Tów¿ áf_=™IgÛT.ŒÝÀ/âêúþ÷³ßûn*ÐaDa~÷i÷Ý¿½ÌßçÊeX¾„Ë42nþ‚²Ib“Γ$å¸÷ùÅþH·ò=b³eóbÅÕð€V¤1¸þYkõ'ý]Ö˜];À…ݧŸu¦ŠøG?w3ôç=­yC;ßý>AÞ¾ýŠæýÆàîuö|ÙÇ5‹;S‹»æ”òóǼ×UÏjÖ-Ï^…ò K7wzž ĺºÊçដXäFÈ^--l˜×—Ä%"qW± `õ;ß^}ò³dÇ)ÔÄÎêÑÅRBZ(2ò¤îÿûhµ(³(_¸’ê^ô׸ÄJE˜;§G 4 çuÕÕW«AÀ+ªH°KQ¨Â¶¢ ”…³S•3õ‡¬ 9 ‘üþ¦![f{SqpNÄ *ÕJ.tBåÀN𒱡-›á`ÿËþÓE-ûAý-麞Öø]LSßü{4®6êÁût¹Œrq"GN˜æœ~ô‹A’$6IÈgâ¤Û¤ú1võÃgŽŸJˆ…‰‘ª³Ÿ#«™±fvĺfí{_]µþ}}¢‹BäLâß^ÑG>üà©ëÎw¾ó=oÝñ±+p3p¯«ÿ|Ù[ø%]©³Cs±è:á^×ÑS÷u˳GOM­ˆ,Pó~ÃËŒó:ŸiTÝÈWëHnH¬´RÒ¡.¨K®”'Ë¥‰rq¼T½èÙÙûFöÜ[D8´8ÝÚY8×Õjm`l^j*)“DÆ„)¥9J«(£Ó¾g:ƒÞäÂÛ\‰(ˆF¤SÎLÊÁµjrÄŽ¸Áž“ƒv¬ œÒŽû'¢³ª%IkrÕLhÈj`ANþã}ÿÇŽQX@ XŽÉ5©¨]œί²k}¨¾Öë„„|cÆúêïùÌt–US™•K×hr4³zìƒÕ¢¯=Í‘^àÞOÂ&‰51ÄX“˜ÄØ$é}¹:ŽãØÉÄÉ–õk‘¿þ•êG@#Ðð‰)bÅ´æÞãþ^gÌÆÌÃÐ…Ýã_wÆÔ/ÿœºÞ¾ü²õ,¨ÿ|¹¨ `IWjÉÜùy¨£§ žìu#¿@á¾em€ý74¿iÞÕG^EÞÂ{û^_[»a5. VÊSÏ]uP"{ç›mâþÏõËD ‘«8Ŷ(¹ÌÆÂPj‰Ä#ÎIŠ#(¿R•·(Ϥ"„ Eþh†NÃh‡ÉCD.¨ðš*jGšX3<º}I¨‚¦]}Õã6¸þù㕤X±%?yΉŸŽíw­ŠwñÉ0‚%%`q~V+õ ‡Í]+ïZ¼áµÝ_CµcÍÔéx/?537 /|&(Q*É‹¯]׸Ä$I'±sÆ™Ä%‰5ÕO¥«8y| &¥4ün†´oê-³"² é¼3‰%Ú|ô0ýݻſº+óO½‹Ч>¯¿u¹¼è²º{×w¼yç'®tÀŒ˜÷F*{¸Ïª‘ŸÏæ½Qu#ïá¾àt3pÇòïuUbçÉ……3sþQª)•nNeZÒÙ Fl÷öOýçÿ÷#0+Ë“jëïªÿ³Zô¨¥Œ¥ŒCÆ"å~ÿĹ´ŠÒ*Jëh7þÛ“IGÐ!邈‚zñy“vúø²d§* ̻ҎýE9…Ž”%e9Ù¹÷lõ+±pô;¿Qøë/:q~,œƒx¦;ˆ•dqÀNRg+â½°8G€âÎ}»²ÚÄV¾sç?ó;5Ûε/¯C©t3*¥‚“Xkœ1O|þ‚ŠÉ+éž]ß¡«Â½»#’…»×Ù¡òÙÙ4ò+zÒw/o䃀ƒ`áánº{¯ ó{ó^¯¨©›÷™B|]é\[¦©#ÛÔ™mîʵt`"wÛ”ÖýÃ…üùÉüðD~2Üá8ç8Ç™%BáLZ§"•zöGߊ ã üÑ+ÿåý¥H‚Pt(ÁÒ!›]ìnŠ$(ß¹¦1–!Ko¢¤…µãÀ±‡–´%-¬,iKl9¼ÿŽìƒ÷’Åü¿s¿òW„j… `=ßÉoŽ'>öñÞ–Î Pºóýõâûjlæ†Ì«“þˆ®Û¼ÏB!ÍeõÕ¯'ßüÞËEù jÕ2Þ¶›¤Ç•@KS°áþG†ßdU¹mÅÝL¬˜uÂç;à«àkž è­1b̆Â)Ê¿ý'g­UÄ;3-ÿN"tCËE3¢ºy_ú3ŸÕ¬[žc›$ ÊÈoYÓ`ÿñ7O ƒïÁÝk6Œü¥Êµô¤>ô§‹¢Œî\SÒٲΔt¦¤²Ãj»¡ÈPÚªtÂéÃi•Jq:ÀL’)P’§»e<¤`Å`’vœïn]JQÀµ"HrôòU²;R¢TÕ¿#tÐŽ”#eXYR–Ø íá{y„-ý˧‚ßþNú}>'BpG"$âP\)‡–³%ÿ` gœTñ]a*ÄB àŽ»~ Däûh…ëLf¢TâòìÆšô¹ÇÃÆ[>þYr‰1Ib5Ö$q9¶&é}{ÃødÒ½ù€bVa…‚²bö[™^h؉€½µg®mœå5·íÐûÞVr[ÌÙb®^Hãü5OøÞ·¿Èëw}òJtwDgÚ¼7ªnä—v¥–vÏ åWô¤œXæ½QGNæ}ø¾~EnþSþæáŽ…Â÷«höŒ|£J©¦RÔ¤{6=ƒžaφ’ΜS·ÿøp_¢Ò¢sÐÙ´ C)2˜Ù'%þm{. ëi$%ˆDè÷ÝÎŽ|e¤÷ìʱrJ{Ûn)´RŽ•e¶žò¤«”§O<òiXRúß¿Ñþ‡{:ÿèH7Ä D‰­¶œ©å6’ê~¿ÔKp˜…Uïn»ãI€‰Øÿ{ƒ½ej#bgOùIÊON}‹O}–m’X“8“À&ÎÄq9v&®×¼{¯C((«°*ß­æ‚3 C„!àý{õ  êß}2Îg­-æœç{íb¯åßï{ë›$òú]ŸºÒs÷ºÎÔüM#~!ÂÝçïõ*šúúêüGüÍkð=1]bÞ5F^X™ ŠÃL)ÕTs¥0·§ÿõ=¯¡¥çåCaQKZ¥CÒ!‚¤¨’²JJdŠ,eúTj4„ÖF‡„D¢#h¿MéúçùÿöyÑŽµeí¨š¿ –KºÁ“6Äl˜-ùËÏ?öùÈ } IDATÇû,["'¾Ææ/NöüÍàR8ˆ£Ñž”²ÒÕ_¶ã…ÃÿWµAm'luiMÕæÄÄV ºeÕ0ʈ+³ØÆï{?®äóôн)O|–ý>&›Äžò61þßÞ÷¶XsûEü®#¤Kw¶ÿ¼bŠtµ-׎-: q𨠙j»œªå4¼vËnx¾³)þ9[̾=ùCgŸÏ|÷» ,ñ83T>3X°´û¦ŒüÍ¥Á·@ÁÝkþù1ï˜ÿõ3Óå\‰‡ûÍ—Ö\MDoíþ[ÀJ”cI¬3 æ;¯<ûøG>×YŠ1ž„Y[VΉëÓPfiê‹tVSÖ+ÑZ%:óÇ^xñ—ÿ”ÿüsîÿy´lËd*äb² Ù„“¢ƒSÂyëoÒ:¶¾¹"´%å=6žøÈ“‘¿ùݯ‰/¥ÿúÈ"Å* ýKîÁ×ÒT»†‰ß?Ë,ÌBÜÈ÷-;ž8øÞ·­Jì ÛðYÏà‹ÅZ2ž$61Ö'3b\’Ä•ŠÇ=€ÕÛö( ÅÉù¾n­Ýþ°õ¶JLh®WÓ‡ IÏ÷¾š<öifò-‰™‰“%Ð{øÎuwYÀU2 ri릶 \Q÷½õ ¯í|üJÌ¥yoÔ™Á²‡û²îÔéÁë.­YÞ“pjA™÷«èÈɼ‡ûú•¹y5yu¦àŽáß§¯Ù6òûwÿÃÞw¿p 9Ð0G¤M¥5ýÝ¿Á™–ŒpaÊ*8î(ærWÚ&Ê–µM‚O”O…¢èH‚Á’?8eJ°e²¸ lL.&‰™—°5ô±ÒÉG'O<<ÞwßÈÉ»†Nn8¹áÜé5çÎ,ìg2øü'~ù ?ÿÏ`§¶¹ÂÉ׺ÖôºæUýçIH÷ý'ON ª©m©¯ŸiüÑ6ÝöIÄ_oýL]TMðgQÅ À&Õ"Hr‰Mâ8Ž=î{ÜŽêpr:ü¤cýÑUú:øF3½ú-´6mM€(BÔ°B)& RDšÈÒ¬]ÿ6j«¬®”rå´Ï߯μïÍoÌG¸{¬ùeÝ©e×cä"Ü/kÞ5eäWæüˆídš×|O§4-óÞ¨YJäïù¾¢@S (ÐT½Â~ýËžu@¹$Ìö •ïÉY õ˜$²6t.úÀøi_Tˆ%PŽþõ·2® [!“«ÀÆ,½†\WÊvF9ÃÎjg•³ÚZ-N[§(ëTÇù‘汉Ìx1*”SCô-°’“òYÊîZF-*íþwR¯œ¹$.O…LĉèŸ4¿ŸÀï ž%KT,UùnMl“Ä$‰KŒMgª5ïk6½£ˆö¿ÝNÖ<øú¶cÛï›`¢ý¯gê[µÛš‚‹øî§„Tý;3×úJè=v3FâHâÈ/±Š½߯½›iduó~]ˆ_Xº&Ü몛÷ù€øÍkšd†Ì;æ3ßol\Ÿ×ÌùÚ»ÿ9ím;š´¦ ÖÝ2 (Фš„³ÂkNX%Ê&K´³) {¬‰¬ ERwœ½søl : ýéþR@ѼÜébv1»„mLH†œagÈY-V;§œÕb•åD[QÆ)+ÚA;è¯ÿ੯~ùÿpöüdÛÐ8` ït¶;ÏŒÁQ_ÐÔe½mo\\õò‘+7~˜H­Úüðž†kk¶-|¹?:ãÏ4ÙÄ«&36Il÷Ù ÀWœ ^·Å¦í…ÿчé;î+0POM&£“ €\–sÙ©'Ai¦ê¿ ][h]»ú5Θ­-ÝR‰Þ:yöêþýþ7þ"¯ÜýÄ•ð-GnyoÔéÁ²§ü²žkù…hÞ¯KGúòžòëWæ6Ü:Êo^ÓàÀ Áó?¿ÍT"¿jÓC—ÞØ»çÛ€ªµfwM+˽÷ Y¨¡¾L'›þ%M0Ì™¥Ö1˜(ÊØd‚$ ('jÓй“KV‡ha%ôŸßëþWk†mgHbGÎ*ç4…$P+D › #q‰sÄ¢¿÷üwœR†4EÄŠÆ €ô Žf‰ =Íi"ÀÂÇóc½F;ÿK7³½|yßê͸ág €R&©˜¤rQû‡™Ò±S•ÖT¦­9;Z*À%¾°MŒ¯|°vý›Š¢ƒxNÊ›WD;4Y¿§IÝqåð¾À–Ã(åF'“T*ÐÖøF²ß|Ê~ò VD¬˜]m­Õ¯^ö¢3pD`vƼôî†ËžÞoü€—ïùô•ÎþÀ½®Óƒåe=)Ë»S§®È/D¸Oß¼7êH_Þ[ø +s‡çS"Ú§|ϤµÑÛ7£‹\ü ®»®Þ~A]sëžgŠ#]÷Ž2ó™®%J‰Kƒ«Ý9•³Mò Wž>¿dùç?þËßøÞßjb›á,ûv’ΰ& \1¡°vˆ ¡µîù£§uKW°ü!JEŠ”&–ºBªõÌÑ®%;»öüUxµ§ý¡ÑûÇ^^Ô Bôæÿ=ögLm¿œŸ­®¤J…*Cº,ú|¿N,ÒÙ‚¯Ÿéí½oõªW•w9ii.D.2§ýxí–O(†"þöS6Óbm9ËÖÒÖ¢ÛÖ02‘øÊ÷†Fd‰˜‰‰Ž~ÿºe/"V`uµM·n+ÓÍèô@ÀòîÔòî€+Q~éÆàîå]ü†š‹ŸKÊϸy‡Ÿï1ƒ7#š)¸×e­ø!šÉºYyvœ;ØÔ³có‰WSdv/ÎvÛ‚C’Yókœ[gKŽB iá0aP!H7ë2Íwl»ûàÁ÷öL¶lO—Ũp\Èr)9 ´M‘…H(€0îúØ`1ýúÑ·uS§Q‘Ñ¡ãP‘V¤ˆ”"ŤÊ-Ýá¶O¶»tªsëÚhÕÖ|¥¸úƒýüŽ^ý‰rû=vñ3™œokÝÌÕqâ,óu÷Ầ’ ‹ œ£l“´7Ÿ“¸çr‘Ê%•/,éê8­u0T,“K—‡S¥Òà¢E“ùÉS…â©ÎŽõ‡këà€²Y»x‘Τ¨«]ŸtéH½»Û9¢×¯‹Å:±Vœç`­Xל:‘‘ñˆ…µáÜÈâKÏíý¯=MÀË÷}öJ'?Í{£& ¦%«´dõDaê=l!š÷ö–ÀÈUG|\]ÃãqGk £5ŸÖ<›ÔlÀóֿϸªF>à(`•d†íjÎÅ«³¯9q,„ŠÊNŒI¸Ý7u7Äêpÿs`.ô¿ô±G>#:ÊcíØï\}zdÉã™A6$ g•¥°j/tŠã½™A£BÕÔiThØÃ]”8í¯,Ûú Îîø8€ÛÞþûÞ;nëÛ¥Å=Ïç«OE"Q0ÕDl4[¼@’D:;)j6¹ÜX”$ç‡#k홡;W.yÇTZŽN¤‚e+ÚKoj•êzñÉÓϨ?N¾(¸Sý“›Vç’Ä­\¦m¢€è‡Ï4xøþ3ŽX¡Ô8æ£ý@ò&»Ëí\}ÿkOxiÁÂÝË;÷F#¿á~3æ½Q‡o‘ŸAÍ;ÿ>ãæ½QÖÕŒ¼ša#¿îì¾Í}»òÉðO·#g*ÉÊ_/ÆqBaÂQÂa¦kµÎu¿{ì;V…†CnYst¬¼{÷³{ÚÿÖ›ºlT&¥I³-·ç' <™ŽJSKg²8“dÅÑp~l¬{]¶{Ý©Áý‰ ­¹æÙ±ûµx“Z¹ý‚ðw`ÉæÓK·¶OžÿɽOæÇʵô°ÖUïá’Ù{7)f%âDÜŒ[ø0’Âxhbʵ&mÙS‰I”Ê3Ov´öF‘=zžk–šC½ÙLFß¾¥Jç²ÍÙóçmŨÎ%çF—ÕªP¤¶6W²Å —7¥l¹L™0 Ò*Uˆ+}§šOõ·-_4ì¬gµÍááÎæxl"óÎÑíM¹‹]ÑÊSû8¹|ë•Î<›¾ŽÁ{·Vu#ßš ê·ÜÒ3ºÍÜëªùÎÖhöŒü,™wÌ·ú™Y…»W%qÞ¼×½üÍK+juÉù%[Ÿýøµp¶¼î·**SQé¨smÔµ6ê^W r• «tEg*:SÒIM­ü0C¥e|ç嘭(ç˜ÊæGĬÀÁrhThTdtĤ™4‘JÞúv`…H­Úþ —=½Ýû$.é ÐhÞgÜÈ×ÚÏ¼ÚºËÆ¹s§RÕ¶bIÒ;ð!Åtð,Aâ+8HOä“h[(ƒæ ÜÕÓ,DBØx÷;ÛØW¨ÞÞ*¿º×ž=ÔW(Ç®[mQK–Z¼þÞ&_~ã[K:kÈ‚äb¸?ôêßäÅ÷}îJçÜÕɼ7ï:5X>5XVŠê`¡HdæA÷å½y߸²iãʦ~t`Ëêfš ¸c¾ñ}Î:Öó™A|qbè[›ù‡MB­“Kо*è\v­«„ÙJ©„ÙJ˜yyïS•0ët*RjŠ‚ÜŠ%-ÍKmç'”£Àº«BSÑ•1çÈ·‡+Ã… dº×Ä*b()¼úw¡sØù8ãšfù2On9v>|O…<ƒ”g¥¸«Ö‰ß€šsä„§Ò‹Ép,6IÖt={èTZ¹èŽ•¡tŒ“ânpr´P ÎŒ ÑâÅÙžEi!Ñí­?àÞ÷Ò¯¾X}Åž, M&Çý—‘m-åžù}»{ï®v¤±Ž¬¥ #͇^ùÚÞá•?}ßç¯tÎ]~&ò»×òî´1â?Øù”fþkí²,F°Î¬õUù;ˆŸ=Í£ü=›f…Òr\äâo ‘/åG¢Œ¹–|èOTÊ“ŽØ±²¬kízû¯™Cò£>ý†òo”]ùh«>âÎïÍÆ¤D; ²Ž|0)NBЙnîW¡Ñ³f0½ô•&Ö)µjûÏ_ãtÜûE˪åØù[Ò!—fj­uv áNˆ&'7ö ÅK[÷ H&ÚÚÚÜêå•pI8>žcp”¬%Å„h`hré²V_$*LB´óc§ßúÞTVóÃo·}îŸ x꯻ŒVÆDhÍ¥•uʾ4øXkwq]ëÈY‚ª79è;ì]­!ý'²¯šÅö;·D>y_Þ“žÿAü¬ÂÝË#¾êâ ‡NÌ€ãÞ²ºÀþÞÙš€6_ø>Çp¯«’8Ï÷(ài"¾\š€ˆˆ(‘0Û^tL˜‘ ÓgEì'hx²s­9íÊ•å$;4ôâJ½ÙceR„Ãhq’mQ³8 3C1©çþ²…ƒB–Üv-¸õÀä²53þËtÈÓoøsu1+_‚B<3 šs+€ÅÊ!c†ÆÚtÈË{Ê´¨«l]v*.[>3Ž€êvRëˆò^.<£O}v¨\¢o=ÝÕxcEu´‡\z<ŸŒ¦ßz¼µ«¬Ê4<¨' e’`ë¡— â>œ¸ü9/\óàÔ`•æ§Jžï+zÒ 3Öc–t¨oÒ[ø«šnñ³ wÌ»|æVhš‰|\)TÊ“ƒŠš[Û3Mía¦ýÒƒ/‚ûÞw¿®9Ð*ÖŠ´â@‘V¬«¶ú.î`›äL¼¦åîÐIà„”çh´0!NÚ¢&ˆÞÁöÿðO=ó')ë"çÒNÒnz,žF ߺy÷”¿YÍ\Ó`÷‰¼É6†r¤$Ž×À¹±òºáüª¾¡Màjæ/èxùê,”0rêã òóÆ«ìtñg ?ªû"Ýó©³Ããñðxܹ¤´vƒñ‹œ:7yî|À¦#¯íl>>þñ{¯tÎÿ8àîuj T7ï~2ß¼Ò˜÷Fê›ôd߸ªiãªkæ Q…ÐÒ¤ŒOÞ²UòlZáVW\ÉÈû†*Ò°j“J7_ïƒ+ àÚhp&¬Ù>Õ\YÃÖŒ,ݶ²ïíe¦£Ô²ˆÜ "œUA«¡ö('âR"wíùÁo?ýoR"HŠ\Ú¹¹÷‰:8s é VõÎ=Í€‘Ÿ^Dœm­&9‰O¿}àÉæö-­""~U˜N Ÿ¤€—f Æjÿ¬%¨šy÷ï5=ôèùr™~ú£ÎÆïr÷ãçLlv1`âÄcMb’äÍÃg\%p&€¸Û7—Je'2.: j9Ðܲ¾«='ÓâuÝàà=ÅŠó;Î2©¹6ò—šwaíXOUæ”'—‹ˆÀ´¬e›8u¶Ü[P嬃P­5;U0­Ý›â9£|£|8CÌÍÁ"V® ‹ë¿ÖêWYE´8%Ìo¶_9C¨^Á¢åcq…NmóÿïmŸH’Äcl"&’$ô‹«ƒ?0|¼÷Ž¿QaXh_¢ÂPGÁ‘½ï´6sk3ãgî^}ýEOùU‹3«~æãx\bäçØ¼ã*û›.rñ3[Z³€àîUwñ5?ßÇÓkïÉyíØ_þf¬4 þ#GV67wµŽ ‚ \ÖŠ·ô½â´nß¾5ÌdƒLÖfÂlXØšÞ½/¿ã²ì "+É5‡BOW…²ópϦÕì-œ\jÞáý{íS)CVH8Õz¦<¶@yt‰Ð(K»Vžq.×zêPgßþj$ˆ§¶ ´^öaÓ!011A (5cŸ eWðF>­|yëÌê²p0Ô—ŠhË¡ñ×뙿©Ê‰Q"h_StŽœ#Ô äA(žµI‚ás-þÿ2¦êÝ÷ßãL°¶}}ÿ®û÷N íêÚðãöʸƒBçF:U8µaÙHb$aϧ°qššóÞ¨ýŵ¸fÕ’Y1òóîuÕ¼‡ûÜëÚÔn4ò"—yÕ]¯š2 ÀäÂ1ïºù®5—*þÝ;[dQ öwifãÀPЊ‹œ +ö_0uì|ý·ºTðÔ£ÿ¸½© %ëážK«9[$?ß—íXY HÎl˜¤cŨ/]YQ¿—˜Å[|Q"ä—I}…QÃæ&2ÆTa¼é3gMlŒIö>(N¯Y´äÜ÷4~ÇîÍÏ·ÇUZkU•VS/œTÚ¥#•$ÕŤ®öh¡D43÷º<âW-ÉxÄÏž‘Ÿÿòˆ¿}c+ft~Ü45]Z7òÍ—4¿¾.-P¸_Dó™MäÝï?§~ÿ¹Ô—v-¶) ”)W*ÁÙƒ‰5-馦Tœƒsþòß¿Ý|ÕâÈtÈð-f®OÀE*”¬hri•›!#%ó^qUM²–$(ê³éÎמ§ºuÔ*À’%]Ê1øž±B‘€&NÓÙݹâx3€MŸ;g1&1ƈ œÑÁ}ÛOUê0ÔA¤Â@…¡ CF:˜òï©”‹RÀÐHÅ“½«=òYüϸêXŸA#¿€Ì{]›V7•cëá>ÇFþ:ð4>iü«î&¿àtY«>“}äkü=ó¥]'¿´‹¾´+¤€‚spÀF`yº)°‰VBNÆÓÕNX¿÷ïÞ¸ÒCNÁ˜íB¥ºy¿yÄ_ wßÙ±Qç{Óõ”&G«€ Èõ{I± š$¢Ähd—”ùïðg¬ƒ5`㓵`&9xàL¾oIÓÙO4~£îÛ_ÖQØ^×AXè\¢=èÃP‡Š¦ض´"J»¡Ñªg¯›÷îùøÙ3ï:q¶è)_÷ò7£…÷ºôNøð}óêæ9£üuƒ©Žø ü5ïWÑÍùJy’ˆ.êi3ù¥]ûžø½WŸø½û{4°¸o·¶&LbºÂØ©­ûŸß¾ï';ö>{é]Õ1u³©|ÉzÊç2Ê/žÏˆûø(€O|fèÃ?ßxûùÞLñMÒÝCþ.RLŠˆÓ†cq_9ƒ±SÊ ¹¸iÃIŒML’Ü?˜?±´‡/X îÞùšöò¸ƒ|×R…*UÔ_e÷wüÿä½wt$×}çû½÷VUçnä0ir g˜ÅLeY¢([”lI–m®W²Å÷ìõ‘íó¼g­uxïy÷Ñë(K¶åµ$R¢DŠ"Í09ƒH''L rê\U7¼?n£ÐƒÁ`ºÝä÷ÔÁº««oº?õíßýÝßÏ ùÉžKþûž‘o¨õ5Ô–#åKwOyF>´øžo?yó«úOo~µ4ˆ_‰'¹†{4l,= 1‰T…ÁÝoRÙ+ÇÙó]ürÃñN6I@,@d¬(I”"J¨š¶5ÉÑ}áÞùÔ_7Žž¥€/7(šûDýýTI)©’T)¢¤È¡ïÅ|ó^:%3BÃ=d+¸ŠÏ3ï7ßšðè7>òÉ1·¿güå檀Môk:Ó*b6ÇíÑ”œ¤¨pü;ï$ŒLJ¦ô"T(1»DYgËpÎ÷?a­þîü‘4Þü£&cÌt-Ƙa™Œ¹¸;5ö=Þäí]ñeŽNÚî µ¾Ñ‰ÊˆÈO³.>Ô±.D€Þ‹Ëöà•hÞçÁ]+ÏÅGœè-Jç&­ºÎx’¯ÆÈWŠüY îž4Ùý&õ/ÍÈûâctô|`f4–‰'CŠEhî'r~SòäÏÿñtMÛLu³!…Á](¥•„* õüþ\Z7!Š£ÛïÁBp/÷”L ö ²ÈrŒ|>Ü×·ñmÛ³ž{ªÀ¾W¿ç©:7Ü6‘ÿÉYñ5bÈšÙÆLŒ‚Ò¦–%é”=¦B­Yý¦P¤v:?¥×©òÁ§7AZ—Tïo¼u3-fYµö ³Ìd] ³,æ³ Ÿe˜V>Üë«}¡°0} tÜÖ°5Ùj}ecäKlÞóu~ëË4ò•÷Å•gä#ºÿj1´ªÀ±gµ®Šøhˆ‘ 4ïË’gÞ—‚ø¤? ÊLºR ¡Ó¹e–šÚ…A%¡ßûô7„`‚ûì¬AUï[õ½û%!’PIÈÉ-wœØzWîЗÛ¹kÄ0ý…ÁW–gÞ—…x­šÙTÏ×_ çß®ÍûÎ&7í˜ônœêÕ“$â«5Òr¹ £ÓçÃTI¥¤ €hð°Ñ(8½u IDATçý?h|jãhrˆH£1Øæ°éކé3|V]vÚ0­T]‹s?ðƒæ}5èÝîþÀøGóC×µ§B‘«|…õÌ{9 ~ á®uþbJ›÷Îu¡åR¾²´ yÏ׉ބgÞ‹„øÕ¦,ÅÈGC @¼Òà~ÕRé—+ëJmöogªkʾÀlÒÞ¬¡ž‹Ÿô×KƒÅä´"„IÁaR¦FÏûϽ% ‘„HEÈ™M·zG¾¼h{)Í{¾i‘ðŒ|è*”÷Ì{À¤úŒãǸ í{%Wè±}ã”wc. ÏhÔ_¨O¦ÅÌ©o7‚1”1y.,¤RÉlzâ`sã5®‘>4ðØz£‰q(³!ÜìªéîÃ̲˜Ïd–É,ŸÎ–a–5q08}ÄÚ´srÇõ“×½kâ]wÏ}ÈdÈÓ/ÐàežF'ì‘ @c­¯±¶¤×ÚyšWþ~­Ô›gä;[®BùJ4ïW…»§½ Ý\{kWá<»é½¿¾ú£ØŽôY€Ï¢—çxú¼%”•£ÀÝ—J7ž7ÑŠçIIAQ¨a§A0ë· æµ÷3G1ê£Îé-·=7ÅÝó× `ÀH×õç7Ìåó-Ø‘CI€Ò¯BZŠWùL ¿E¯ô˜‹Ì¸¦iªDú’Ýn¾odð䜑½˜ TÕfƒ;9ã2µLf¥§™+Æk©¤8×b…eI0 ˜Õîî{ŸàIE¸ÏJÇ“–™©´å?WÕ¦ÉÔ°_d,ß´ãò@’ÕŽ §‡˜®ém†žý-ƒ½‘}oZÇ-æ2"4©ÌôZhm ¸PmT§ît­ŽX ¨‰ZS‰…Ã\•wuÕ>ãÓÎ÷Ÿrê«}ª}ºîEAT°Ðy¾‹ÏŸt­Pó¾Jé ƒçâó¯‚;„PfXìh½/®?ˆ‚" ³U) :¥ª¢VS=éÁ©'oÿÂÇŸÿ[I 0ÒØ5Ø}ãƲÆnM»øhˆ-þN°>Ÿ_fò¦:v¿¿—PêÚäºJ@)(yë±F}§«›Û§ëš§—ÄÇcÓg±î´4"753Ń €€œžTæZÊèô›¾Øµi;bŒnL%›…‘!TQ JUíµ#ñ‹ƒÑ›¦ìmb†2 ì½dy°þz ‘ËÒ¬‹÷7Õú O\Ö°h*+¸{ÒF¾«%ÔÕpn°Â8¾ –nÞó¥]ü¶®È¶®€ãç 0ïZÿîIy½ÙެP¸ëeý+3ïùâb¾‘܈†»–ð…˜“òZ¼‘Y??[ÎFÓ‰™é8g„ôvÝhŒ`¢¾ÃsYWj§§”Y3ÿîÉvÕÜ7¼<#¯Íû`ŸÁŒÀœmÙqÇ )¥TBj))…”J6mM6mK5íHŸy­.É I˜é¸¶Ïž2ýµPf4«’wø¬¿vÀŒÏH:è³fj#û›6Ñsªƒ$zD k8ࣖÂçŸòù'#Û„Á3hý&£1lý£½Ç[nw2þLÊŸŠÏ§„€tvåïêT†‡ƒ€pÐH–ÊÈGÃ&€xjÍÚp.¢©„[µT_jä+Ѽ¯ îžÆf|}!Œ<ù?[BõÁeJhÉlÇŒÊâ{1’ µ‹—ÂÍ8R—Ï—•ŸËŒÔ?¥$JÌÝ2uêUŸR~(¿RñÚ6?0µs®aÈ"½R¥à(+—§èl,>žѰ1Øp¹0-©s²[n8¦ ;£ „0 BA)a”B)(eÉAyñP˜»D¹‘hwJJ™ìd{㇟·F™Ù7•ÈFcmëy´#Á¦žÄxL‰w‚‰™››á‚]ÿ.JHûGŸðÔû¾t¥ñ¯Æ¼_®¦ÙX|±|yš÷ËÕ5‹?7˜zÂ=_Ûfcñ«1òEùØëøL,lBT%EÝ%ž‘u¤e(Bh8`^~åp"u:J£H., H åFO¥B)ŒÔ¶ú—U¾r-&WQ<%4âcacàlP î];^uMv q Fs¿PêqŸ0 BÀ¨¿‘ÖmK‰DÝ&cðõP¨3Þc6Ǹß?Q²{Ëk¦)D°§¨Br[Hî\<ÿ®X6K²>D¸(kfF/þåÍÓ±Æn æ Ń2›áXŒö=ÛZ(¸žÈjÄ7×ù‡Æ‹…øJ;€sƒ)øîÖpÁ«´V–ŽŸKhÄoïŠ[)â‹âßáeLÎþƒÊó[áåZįRºÌUП» ^Ny_|L[u(I•‚’D)ª$”Ò¿œëÕu ô63ë߃>мFz—?))ËöÏñ‘(€ÖîtþlMÛ†ƒRB (ËÙvÍwBæ@ïY{mágé?ÕϤ"u› (æÄÏ?Q[Uÿ¬$Ñètë†×e2’Ô¢„¥• Ã=ïWYÙ1v .>½QqíÑgGïoKûC ‚@lÞQGgkGPBˆQ"„ôˆïíðÆ›¯üZ¯®æºœ‘/å+ˆïž¶tD¸\V/ yÏ×öY#¿Ê—ï¹I×PjeNù"Â])Ìö‡ ÌæM.„øQÝpu~¸FÉ™†®É#Ï{pw–÷yÏ[VŠD‡­îœÎý™äšZž"”’œ=ŸÅwΰϒxA›Ùg÷!Sý¦‰5‡\öíÏÄaZÓõxF/£†‚¾¬kGh€ž<ññ‰žºäLW<ÞµûȪ^s·"<ºe \ÂA8(§”æ(‹„:0ìëê?JIñžíXå9)â+îÚÂ{oÛŠ@|‘வbÄx~UkÞBsÛÍËž,DAÝ"É4€3W©ü$t.ÊdDoùOÇœË>„z£¡@ ±Û=§[ÈÙ™U=f÷ c^«ä÷«ÊNùwð‚A|ëši,ö=>;™*¥”RH¥¤Ô?„*7תDn²uvÆUJ¡ô¬”þ¨ÈÌ8Óá¤ý‰ƒU|4SúImäP©×üª Óæv˜¨;ˆúG/lsí†Oï}¢ÉM¿uϯ„¬pØŠšñV3Þ69ž±b3J ¥!RKp@("á |püâ…áñ¦p¢±cbô|ÝÕ_ù••LódšG‚†Þ’阡J„;=×zn05wk¢–Þ¦âW\'\ZnBä²46åŒM9 Õ>½-}Þµðþ}‘ú®ålä7ÂÅPÎÈ“K¾1ÌEi3ò©šÖüGñ#ÏK3ïe+ï2pÎ'!xÀÿª¤×mÙšYuwU5§$RÕÛ¶þHMó›”¹”qÂaÂô%¤´ óP ’Ã]Âõ¿¾÷ËÛ~ܳ1;òä¾¼àn~Ã2(\QJȱ‰7e6¢ìÐ ]íú–ã¯í\ýøµ‘'ÀÅUùJäû剒z¢elä‹jÞóµ\#_R¾£\¿† ÔA!äÅ…<Ägªš.ßß=ò¼¹ó}¨|¸çß8:ü†b’R&oô¿{õO¤ŒC -/fjÓÎ=”qPiXiîF _ÊÍD™•!ÉánáÌæö‰?«Ù×ðÙh(°à¡¼œ™†]¯Ù˜;ÍÅåq¸Nnà†­Ó–²"aIZWç× M‡ÆVHù·Ü=•-åKwOK§|ã3±°AmΠ˦ÏåÈ—G¸fñ(GQÅ…ÒÏnD@gÄgc îÏ»Q±p‡·˜ùÒ¹‡P¸¥»ûÀèT„„*MϤÍiÿÑõ­ýšsÁÚs™Éî+oaŧ2éšDÒ™®ŠÔö…šü&ƸUŠQÃåÙ0õ™ VÖÜÚø=Û?Ùûzä,+NdÃå‡ÊOˆL ¯O ¯O·ªÏ Á•àŠ‹†PpdÊ‘œ]¤Û¶õM­¿ü +P"Í#!@$´’XM%³‘™ײêX €r‹Õ52³ t¬@Cµ¯¡f±pM!ýû²:q—‘úV·\¥0ÃX²+”ï‹·kí|^ ‚#½”XŽ2³ÔÊ_fsu7Ñ­S! dtÿ­£¾Ÿ@˜JX&„iè(Ê ŠÅbÕ=ÑèÙØ†‰ÖªÃÁ耦ÍR•“™ÉuéŒohú=M®°[jN¾zƒ’²v½}ñ¹mù£Z$á½¶ëÙœ‹¡ ‡Oû 7ãc[;[ YÚ»¹~6\³d#_¡p_âֲʋ/½yÏ×ö®ˆÎ >vvá¬ßµÖœòew­àl¬fqp¿-á®Õ¹õe(J¨AAÈÁÁXiæÏÐ@ ÜR‚´”4•° MÀ$Ê€2#y®%Ÿï ‘:;x 2XUwÄŠL$¦«kÚÆEVþdj´ãðë#ÇfZ“)ó¿n&aWL™ý§¶))kÚ%„BqÁ ”àƒ¯^%êRÓò# Bc§ª¨´â“×ivt,-µ¹Þ¯gÉ—ˆøJäû²êhÄàÌšR~máîi{÷l¸æ2ÊŒï+€»ÖÚ"¾¬øŽ% ¾BᎥñÀÆëÞÔ9å̤ P”{Òj–ï–’Ö¶ÎmŠ@wÓV”€bö¢|¢kǧ^P2Nd2qâ‚/0núÒÌÊŽ¥nN_06o{M)ÿ~çÀ…î¶ׯÂmŸè7w”ŽŸ’P²ªÕU\0"ŽÍJ%„ä‚K!—gÞ7oäUuß§9y²5•¸ž(³½«ï«uõ~ ¸8ºå+îX&ßt·†ô5o ¿¹# çüó³ˆ'ÀÑK_¾¯îžÖ„ò!?*¸{Z„òÊ÷%Â]kÛmG=X3¦#3„K€æÊ)ƒæ åë»Ô\ô†‚øê¸Ï=*œ4q“”¤'þàˆ‚Ýlï¾`÷ÙÉ´åÿ~õÖÓÙ¼ãWìú¥IÓgÿnëæ_ž<# d¬…kóîØ\ !9—ÚÔ+!çR%¤âB Á%ÓcŸÐ㯊|g:ñ@ß9F”A mí*j×Õûõ÷ñ+!þwOZCú„œ(5åËîžvÌyòàûêá®UbÄ—-ܵDü;îZ»Þs&Gm€™TCœ+€B!gÒ=¸c64ßóíµ×_t¹àBr!œ3©ê¨35q=[ ÌÝ}ï䛡èÞ~ÚW{üÎO%&ÔGj×ß×gYÖ™Gš¬»é"…еJwּϒ=gÞ•þ“kôç@¯ïRˆéÄúÏú(¡ŒÒæŽBÒg]C.\3xå+‘﫬¹¡-—WSJÄ—!ܵæ!¾õg µ~Fc=24è×|Þum¥!ôQéï¸8øÂ†>|Á·¢Ä0˜IBÞ|¤~Á‡øÂq#¨Ùžp]Mdéœè05ãñ kc×(ó»§S/¯ßøÑP©MŽŒ&GÚ8”nþôhÏ·œ¾€ªö„aQÅ„â\q&©g’ E¸¢TQ&)SB(ÆŠQÅ©RQ>:z¯’iŨ¡rq4ÛÒàÐÒàÏG|%Â}õÒXßÐÚÐ"Àé’ù²’ÆúŽîˆýjù^1L¯Ú¼{Ч¸æ{4dñenÞ=yXù™ª¼Rm«Ò¾§ZçÝR1 P¹$®¾eÚuˆË wQ¿#îrksçÏlʬ‚05~}kl˜õgæUåi7ޤ.œ9<³yW¤qjŒÐÄÀ‹™ñØ5÷KÊ@…\1¦¸PŒI.À¹¤Tqª(SDHÊgߣ9v!$g/Ä…5ïZë- ~ úÁÑl…½PeßÏ ¤´‘ߨ*6âËÖ¼{:z6¡ù¾ªøLUÄ0}…Æ+«TñÂ5¡Ê”;Üó¥/H¨„kR¾VfÞWuİ®{,™R®K\®Cª7¥}MRpÁ¹Ë].„{ñ…ÞCF£" m÷÷W5ðágq+lŸœË©ÉôF7n¾¦Ö²| øéÖ±)»¾eÀØ`€]ŸÎj8‡2Rä8gÞìmr8(8‘ë‹î%5ß½*•Å÷bôôبÃ5„œîOð°žÊîù*ÇÊ‚ZÖ=пcåÁ}Þïe®bÀ@Çöáö­c˜]*56XÕvw6Ð ¥Ë…ë×Üɇ;E¹¢¼íÞÞ†ÄXðìt6\Í.-ËÓ茟­>ud”AJÎu¶ûØ`•e¡¥sÀÁï7úa‹aY¦ÏgX>Ó4 Ë4LÓ4}¦éóàž‰ûâãõ­©ÀÀàhvp4Ë(a´ •^žy߸>¼øžï­|ýjQÍ»V1»†ŒTšy· •.W–Aõæòr×,¸TuÅzÿGÇZ;2Í­!à88~°ZÚž mÿø°ëpÁ]áºÂåzKõÏUëÉôÈëÞ6L3"l¦0‘ AÉÚk“½s VS²ªâ QJiÃöÌØñ0€Ä´¿¾)S]—:nÏ0B)¥”’Ã/n»Ð€ÁàYC"©j(©‰nm (•kÜ ›•2qU¼†|“qwrÆ©Yz›œ)ØÊÒÊ2ïX±/Ü=Ðȇ @²¢à>oªÀû¥Ì|Íû/|nôc??æýyàgµÇÔ˜Š»]¼À—W8®p¹t]éº#/ï¾äñÔnbŒ5$ÆÖñ¹©êuŒ‚1B.Í hd;qéu2ŽäsÃ>w²ÚïWÛvMz·ì{¼ißZöÿÇúÏu¨­V!îsˆÚѺµ‰\ÉèàLkc@ÇâËY%è¶êÅg eä+îX1ßIiÛ6ÇS|^vÍ;D Χ²Bßö³pYR¾‘™çž¬ßû\ÝÏöÖz·ìºoÈÎ:¶msΉäDpá¸C¯\—ÿ¨÷ddÃÐæu ‰QFÙ÷>ö•™s’QT· ÒtûoÏújŸ„-”-¹PBìøä°w×ñƒ5>¿Ú}ËÄå£jmͽFÎü¥«@27€K§U½_ÚÊñ%Ðéþ¤¦üÆõáwf¸f%|×\ Ö*ªViä+Ѽ/"úúu½•ŒÜÿwü¢p]é¸ÒáNÆ®+\rþ?7Ü9" ƒ1f´~éÇCÕM—ȼõ¦¶fûØ©ã’sÅ/9T €À¥ÀÜu}zãfÀÄIe×&iuÁœÏÈ·5ÚÊ‘ò%0ïùš3òí+G|%šw¬€ïkw­ùJ„ûU“8“Y‘ÔF>Àʇò7ï©„‘J\ò¿¾îcƒÒáÒÑ1wG¸®µí¬›=y{MÌ °\#¤={¡bûŽÖ†ø£ì»÷þÃc„‚PÒpó˜-"6øÊ ×­WÄ>|j¿äüÚO y쿐ܵ€zÏÏøÐ½“·ß×·_¸P¦ß&ÊÕÈ—îZsF¾=¼iù”¯P¸ïèŽ.ß¿—84³VbäË`ØËÒÒ3ô½‹V9 ¾‘™T’¥’¹—vó}#7|tp.UÆq„“sñÒq¼™´ ém?LˆÜ÷ïï~á/62F_yn—¾‹Ê  „BAò+D*.±±’‹ë~~ÔÆ Oׂê#ŸÌÍ|+xºÇWÀ—¹\]5á}`430šÐÖ(7ʯ‰N÷åŒü²_¡p×Zß«£&€r(¾¼,# 2$Ó•dÞ—¥dFhʇ,²¦”/Æe4béTîE —s×å®ËW8®p¸ps.~ìÈ&gœ©„#›÷÷ ¥¦Î·‚Gþþ&ïh¦ƒêÞ… ó>’óÍuÛ@³ûö*Îó'Züè{õÁ ŠÄøË/²ð ´ôÕLž‘__ˆï\RªÔæ=_§û’§ú’X&â+Q;º£XßËîž–bä#A QQp!Ë^ÊäùµB|,l(H½|?êϤXg§ @8Žp]麒ëœHG8.wœÑ£wyû×í|Ãâû¾w¡âÿŸ'îÿÂÁñÑèÉá󧬆ÖiÃTþª0QË`V÷K^ywÅ…, š•\(.nøÄHþ`ýfãß\  H9k`$£)¿~M|纀ދk_?ÀCüU)_¡æ]ÃýèÙø2ø^žŽ·_jÍj¦ ’‘ȑӶ’©Hp×ʦY6ÍHו9çîˆ\üÝ•îÜ“†ÚÆ»è „7Y¬JdZwÜ4MˆÐs(jš¹È ,ƒ‘w¦GÜ¡¸Ø¾™Ùƒ£OJÎ%/;g°²Rýå‘_ZB¬¦Bឯ¥ò½&j˜,'óž¯+ùJ4ï«WÂ3ò¥E|ñ”ÍÒl–î:øîºÒåÒuÇO¿WïV¿õEÊ J)1\®¢‚6úJsS½uã­öú YÊä¹ÿ‰ü³ˆ'”€‚4lÉ{.ɹâ\d"T®œßtßÈ‚£Z­¦ÎLÿ¬‘ok ´5•”òåcÞ=šÕlno~…k<óŽ%ò½Ìá®u¹‘¯D¸*Ï'‘ú…G‚¥0òE5ﲚÍPG_Ù.\G8\O« Ç›í³Q·qc†ÁȉÔóÔpjLÔ®_?1í°,ZµvݧL¢.ì÷[ŒQÁu%ù¹¯¦zfUd2Љ’ó¢ð•®¹ˆ|©_†p÷äùyˆ¯Póžw”sý™•©¢«Ö<‰Ó»¶EƒE<ņ;ǦŽ{¯ ‡ë(—Kp"xîqÇáŽ;9ö‰ªšïѺ÷‰ù}+W}ž‘_5â+îzrÕ Ä{ó«e‘¿’yÇ•ø^¡p_\åiä×*‰Ó;+C|)á v]Z2:0>ù‰“'×ë»¶&eçOÜ~þì¶ùü¦ß=ʨũåFa̾4=J@™"LF@ %9üâ&ΕÊ¶áØ° J ©ªyLr¾½sRqöã·ë¼òæÄO_Ií}‰¿´gî#s÷dž™!˜QÜÿ] Ì{¾ú†Ó}CiíÍ3ò¡yp×òŒü–ŽHyR~¸cõýµËJU3Wî_“ïâ+¥ÍMñ4s©‹_ä¼ÍS‰á®åµ«î;ǹ¹c0Ø7{7ÑaI!¦C!EßoÜÐ÷ A¨$ŒäXO@á§OíÛt˽ûœ,úÞ2)¤Å¨$drâþõŸynÇÍ£ÿßÁÑ©)áø¿7’{>²úwÃ|[ÁÝSßPZý£9x~(½Ü‡W¢y_D'Ï'4Ü·tFNöVRÒä|¯­²èU!¤hØPK«\Oq/P³¶” ‰ôZŽa&Å5ßc!céˆ/½”Âø°Ïá„wtÎÿÞù™¯ýygôù;>;ñ³W™)vÞ$ì$·Ö¤üe²éÛ ’F(EÀ¢i[Ú6Ì3ÇîhíÜCA,ƒŸ>ÿ¨)ìPmÛ”°§¥C·5Ü:ûT—”z7­rI/¸´‹ïhv4,ò•÷Í{¾NjßÙÒP&”_ܼãr¾×UYÆ+ îË•F<€54òåw-ÏÈGùš1_ó®¥[ŒþâÙ®¶#UYßý«»|þ‘ÿ Àž{>K¤¢>GqA “2ï ª}3 ”" „Rbû»÷g³äÀnp¡÷½gŒïøC–’R ¡$½æÖ32k»G¤_v3¶•ø¯ó†dúŠÈw](f p ï+Ðù¡tÇ*Œ|Eèªp÷t²7¡ù¾µ3rb­U¸ãm³~ueÇË|ÒµôòÌ»&ø‚ZC¸¸÷×÷}ì×rõ¾ûWwÛò±/îÝ^š(B@)!”€b̾·ZÕã_ÈeûÒ\FR$mK'Kœ,ÍÄ}ññ€`•RRí¾+{Ý“×ÜÜdz¶›±y:CF¿¢¤t£Øf›, IDATš?ž¦Ÿ_ó®éb¼Ør€»Öù¡´&{Ǻ`Ǻ«Dä+Ѽ/K'{Ú¼oíŒl-ˈ|¾.á{…š÷Õô”X«I×ò1ïùŠ'¹fw,l,Bù5Ñî÷ôP¦¼üƒÝÏ|ãÙ,ÉfÉ/ç÷ Ô¿>ðßý,Fª¦n'”Û'Ȭoið‚Ñ);GvPJQtÜtÐvȱ=¹ØËPýw pí­’gl'q36Ïd¿KG€á³ Ÿ•?$ŸŸûüåõ,ž<ó¾â+îK7ïùòÌûZ!~)æ:>S_훲ßpךçâK®)O¸{šIr ÷XØÈ·êkeÞ©EgtçßþÜ?ßùþûŸ¹øÝ»ž{ìƒøLá8!”RJˆÔ|omô¹¢Á©\;Y Ï&”m“þŸ½ À–ß~†§}ÏnÊæi›g³çžúm7¢©Ö?dþ £a,iñà^>æ=_ç/^ââõŸ­•Á]K#~kGdkG¥ ×,îðü»nQV‰*Ô ÛRù’­fZ±f.3òk÷¬ËT>»@šùºOîu3ê–OüTÿécBèÖ[Äғ‡ŒÖÆ@.ŒäŽ9ôÉÜÛE錸6ÐþàÝŒýÖó3JaËNîf²ç~ðPÿÓ_™ QB‡'²#?e~ËX†ßÌv_ˆûB…?!å wOÖçùJ4ï«×‰Ùùm嫡ƦlBP‰¥¸ K"òE‹N”CÞÎåØªˆ¹øžÅÐ`›š¤MŒ×]¾Ã¾ý{êŧ?dgÉîŸ{YßH¥Ì$„^ò~–éêÜÝ”(1b¸CnYÿà nÚ>ð|RmÞéÄòÙì[¿ÖÜžPßl·tfHÇ_QŸÅ|¦á³L¿Õ\7—(é8þ@…}ß-ˆÎ_LÏóò•÷Õ˜÷|8ŸÐæ}[g¤”_ºy‡çßÕì×N¥©Å"Há圑/â+îZÚÈ€ÂJåŽï‹ž=ž+Zë÷ùçÝ»îþg[?µ§ëgˆRÿüé?`ÛĶ >¶hˆtÓ &¥ô§Cϯ¶˜›b%®„¤íÁ=nÚ>°'­¤Ú¸#›Øû9oÇæŽTsG ó›Ìg2ŸE}&ó[<ÄûÃÜ.ð¿²ÌÍ{¾<#¯á^Y*Ü=÷"òÅDü²àÍw¯¼¾×UYDùb(žâ:  ÑÈWŠª"¦´¨ýqàåjýKK»¨®ŸßUÿÁÇ…íÇá¶ûãÇï\ÿ¹8þÂm¶Mºoñ… jRÂÝK.S±ÄkQΈ©›\7m»é,¤L V'ö~þòÁX;þÖðY†?·1Ÿ•ÝþMÍuþæ: äB…ôïw-mäMƒ˜Få}û/¸Ž÷&Ž{©5å®™ÿùñæWËñ±ˆ`&QD#ìÍÙ ñgÞó5•p'.€º˜U_œ÷Æ+O7½±§À¶âÝÛæ3.zË¿WÝñžu¸íTR:ŽÍ ·ÝÖ_Ú³þ ?>ûò­¶MZo| ƒÜ@¥ŒzË;jºÿÜ;¡ànÁ3Ž›Î¾öíêø@ÝFßû.o×?0ÿ%pg>Óð[Cã¹€~8Æáwb|&_ëBW.W¨#_póž/o¢µà±šåšwÔ3ïÞMºÙ ÊØÈ—îZñdÁŒ|…Â]GÞ§¹î.ñÎ >!ïUéÚ}ûÔå÷v|Mdm‘µ…ãT’ÛN†ƒ;Žpa;<ë¶~njÒŽMøt¸¥!à ±Í7%É™ÌO–6OùÇ÷µF7+šzÝÈzý[õD±þ{xÆ¿–úߚ ΘÌoéÍÐxvh<9á(_W??‚´2UœyÏWïÅ”¾w® •9å‹ w­½yù®ÂP~pÇ"ë›ÊÙÈ—¸*}Á|¥hܵÆgœ±i@Cµ¯±¦”w]uÓ{Ç.¿uþ5·½ Û¶Ãm[ÌÞâݨê9©O6½ïB…I!«ÙðUB݆¿ õ ¯4Þ3Åf^¢PÊ€Zàºõ†ßÒ ïÌg2¿™sñ—¦Àû‚v0ìX=â+îëBÎÏN«zó«]åøÒÈ‹È ñ+Û~Û¯¥³ ¯±NgE:+‚~¦·+íVbUE ÓÅ7ïù²i;ÒgQ½Ù޼úcòT¡æÝïc² ½ØtV„!ˆÌd¦¯«cKü|O¸÷Œuþ,ëÜ8wEQõ©¤RJ/Q•†cC)—™zÑ*rwI¥¤¢QåNS·ö¼¯Î5â?©£½I'eENVÕž2#ÑùûŸL÷Æ«šŽ3tjÿ:â·›ï!Ô&ÔVrŽÑ ǘiRË –Á,“Y&3MfšÌ2.|ç½zŸí·õ˜>Ñóæ†DšGBF4lDCFb¥ÿâXØD¥½CæÁ]k:áN'ÜêˆUµª£ÖT¢¼úz–À¼çklÚ›rê«}z›Za4oeæ»é½¿¾øñÊñkwOñ–…øhØ©°.®`Þó•ÊŠTF„F$hñ©ó§ ¢¯Ovt)vðs`WJ)e8Yå0S)¥¤‚R@î.%•‚$Deg˜¬ª,´;kùÈÏE4:I˜è}¹MAvl{DqÕw¶‹ˆÔ–ŽÍЙ@ͫؾ`íO3“w¨zß·¨e2MvsŽìÌ2ûÿ÷»çF»}Èô‰‹g$ÒsåŒV€ø 5ïU Wx‡L%Üꨠ¬_b¸{Òˆа"įîXb}àyáø5]ãºÆÓôùáøÕ,š-s]'³Íµþuu~\Ë®òyïùÈ€—ö°½/9wÝmQÆÌøx÷þò7 À?~î0·ÿ •ln Šq%­ž º=¾XGÆ`Ù–Ý1.èdo2V=&‘]·é'ÄgŽo¶ªÝ›Nº£Ü;è$¯;ù‡ú)ªîÊLj”2Ê j0Êe¬ï_æÇèõ’ZOƒ£Y- þ–?¹°äо ÷Í{¾Î ¦tµ„ºZBú÷w²ŽŸKØÞÙÞpìÜR»®îÈoüÙþ¥ïíÅâ×ñK‡N äÅâ§üêË'¬‰–{ª5ß ÉanõÚû’C­lUÛá7¼¦¤ c¿÷ŸòྠºZCÎ]HëÞ)£ûÚßAÝvÇ1•”©Ä…ÁêºAHúÖ+Ä|Õµ£") ‰‘1S™† w 1 jÄ0©Nû£ñ€e*ËR¦–¥,Ÿ²,ø|Šâdͧÿé]ó†¡ù‚ù©÷WP%òýªpÏWWKή)å7­8Õ_jó>OÛgcñKDü*ù~õøL¾Ö0"_VpÇÒb5ï¸H¤¹ŽQÄBF,d®>ÕÑÁúl׎&ms÷¤zéÀm7»¾z¥ý»ÛB„äàÀ2bð¿Ÿé® ½ðâß}6ÖøjjÜ­YwžQÙwvW6©Š (WBYnºÍçmû­ÞžÎŽÁ0(5¨aPƨ Ûd ¹Í˜ûi°|ꉿ¹cS‘âñ†ÍhȼꩨD¸cÑÈÌåÒ±Ô¬]¬¦Là`lÊ›rª} 5¾«†kV w,׿{*½‘¯Ž˜Ê'–çi#_‰|_åu´uÖ½ŠY'§_!̾á¦ãJ ›0©”èùò¼}6´…œH¨ÛüGİǎý¹ÛðuBy×Îo¾ðõ¯8<Œ¦îùÕ?A³›$Ò ‚3ÅM'¹›>d„ˆŽÆ€[}ÿp÷*GÞÚÐÁÄ+u_ªP¸/˼竻%¤Ã«g/”ÚÈ—ß=mïŽèEƒW2ò«‡;VÌw­’Q¾l᮵ â+î(Щnk èŠF}«îQ}×£o>ÓæVvç³7„}è‹Þ>ׇœîO¨Ýð§ÔÊXÑCÔp. Þ ÜØ™×¶ ½uÓ­{ Kyã'ÿœ{ã6Á áZ’›,¤F)IÕ°ý.“2ƒ2J™Ñ÷w¯füºÁžZ(\S‰|_1ܵº[sy“¥D|ÂÝÓ"ášµç;J‚ø2‡»§|Ê¿“᮵¾)@!dµUdÛ>ø})åOŸ­¦f–˜™kov•’R)EêÕ/ØØp:/)¢vóªÞ„ɉá~ÿýDe>ù‡ÿ¯}ë÷ Ÿ4ýj×} …sûnÜ®i‚Rš‚ÕÍôÓäÌfëœP Då*3)($Æ$¨ÜÀXöKh(r)Ê+îX5ßµJIùr†»Ö‚ˆ/ܱz¾k•ò•ÂwÌ"ž¢”B¥ñ½çY$ +®,øÅùò3O~È¡†âõç2ÌÊ„¨\ŸeOÖ¸.bD1(Åêš÷2æj %îOþùóÌp?ûg¿ 0€âàã_±,aĆ;ÿgï[w³` mÃ1’’ÜwqßcúIOÛ/€ˆÈºiä ï(hÆCI7Ô:ó±¼ t#™w2ܵJ†øòç»V>å wŠï(â«£&€©xÀÝ“a+`¦®IžŠtíXÔŧW$÷ÅþM÷…ÿ¥ÿ\ÿ¹ÿìé¢×/“¼*úñÍ÷p´‡Rk™`Ä}ãÑS*oúä7º®y¹ 8þÿÅ Ë”BÔ5o4W½n)×|ëûó¹ë_)3(c„1Ê5Ø[/Héo»QFû¿õn,GëÞ忲ø^@¸{*6å+îZñ^öòâ»Va)_‰p]Z*½R_ì/I]-!²Ì¹µ/ýóoøÛY¸ÏÓú_ù1¤TJpWH)”RÊñÇ>ÑþÀŸ@º*}Äyæáÿ"?ø¥ÿF9 Ï•²ú|Ér%¯±Åþ–¦öÌõÒ Lžý½ËŸ+tË74Ü)5£o½Q<ð®{“#?üÈÊN.Tÿp%õ?*ßQLÄWÜ=ݰ­@Öá{ûk¶jM‰ëÌP3 W“=1ckÑcY*AÌ3ï:×eIRj®/Áe ¾xopï}ÿú¡oýœ/´‚+h¹ïOÝŒH«Pp÷“ÿïo¹ò[fÀŠ„`4}lQKnlþk@I™qà'•´¶Tݲ¬—ŸJïenä‹÷|u·†´™;S#_¡æ}{WÀ±sqäñ}5F¾ÀþÝÓ*üÛî¨#_âék¥ŽÙOà‚ú¯Ji¸/(àtÍñ©„™ÊÎ,$DÖ¶›:Ñ~ìŸ>¿$[×½ÙÖöfǵg¹kn¶o832ƒAæHn3Ç>⻎)võõ„¼çËÎñ/Ù‰´Hï¸ñˆtÅ+oNüä§K £wµ„(™¥‡õ 2òÅÓÙ )Mö «6òo¸#ë«1òÅâ;VQG¾Ᾰ<è¯I#ÓE´&¹I§ûSÚ¼onoîX t*"˜‹Ì¤â>×¶žù§;üÍ;_~äŽÄ¡%~`Ï¿ÝôÆ“×]ˆU@" $¤:åú3Iá¾uã›ûÓÙL&Ø~Q C ŠInHnB°¾Ãï)¹~ãé®m§X(xúâ‡[ïû-j8ÃÓÆ™¼ëËwÓç]|üêüo¹‰”Ïlßý†¾eï WÕÝ­¡Ë'™û‡Óšò뛂åIùÒ˜wOžy_Fïm¡yp×:z6®)¿s¥ˆ/V|&_ËÕT"ß—¸ìÓã{™ZXÛÜÓÍíaBNöÎyç/ÿÓð׿ö÷Þ-wÿÂ[^zôú³ÇC­Í&3ùØL²þ¶f8”¹× IÎ¥’sÉÅô³Ÿpøì›¶ŸÞû›{„“=ôêYGF©Å‚‘ hL² ($Ë,Ó¥ÕÔòmö©úÛ§ÑMÛˆ)ÎöVKWHÎ¥+¤« é qê¡yãwcjÎü€½/@rÿ=X¬öÎÆ¶0€Ó ›ÊòŒÕ”îùÒ|'Àéå=ûÛüÏSŽïGÎ,/VS ¾k-‘ò51ÀäLYào‰ZÖšþòA|™\G·tFÈlW³/í×üõƒÿ¿Ã<òò»ûN41S´5™Ôäî¦îî•\HÎ¥à‚s%¤ä|æ…_ðÖ±1Ûý?Úb ™uÉ…©Ñ4? g8ý~&&©Ï`¦ #êN6ß0$ü7Ö³ Pœª‘®P®®xéï‚€D ¥”Pê†ü@º\r!áKÿ‘ä‹ÏúáúïùÈô‚¯qS{À©«U¦-7ʯ!ß¡§j,ñoK¸kíìŽêl“e!¾t ‰Æ§øº*ëJˆ¯™&* îË•ÆzUÄ\ÛŠiew'{[;#[»"„ ?,ã)°œ,ð±²Ôms‡KÁEVh²K.”È>{ÍóÔ -›3x%B §·-Ò*ƒ!_ƒ?h¹“‹ LÎÛ©e(+3tlËù#; "áÖÚé01¨TI*9¥”(ÆEô'ªÞzê~%å ?÷}2ýo÷| ûâü/>ÑpϽ£óÆ¿¹= §ïêñýþá´Füú¦àš#~máŽÙ©šm¡m¡åºø ÒRààˆÔlˆîÜÅ’)_Ò†såTG¾`Z¦§®~`UÄ\s#¿æ:Ñ›ØÚ¹÷~úgŸýûy÷¦V6Ëšj‚É›¿ÅÓ\ .4Ð9W\H!„à’ó±©fjˆñDŠt×çz$”ÊNbfËMßãF?Ðóú›î4c4@§ ’€Rh„¡5 P p F3°ý,tϯï›õò^ †¿þÝ÷+IÈèWæóžL¿øDÃÞ'Zïº÷Bþít9I¾ùáxB°ú>+ÓšÃÝÓé”F<®fä+Ô¼/KGÎÄ5ßwnˆ.ñEœ_½’®”ZS‰æ}5\w2Ó©.í¼kù˜wOïûýRYñÃ?ú÷íÝÑ.™MJ'­·Œ€È#ù@®ájÖYWÚ.wr¦ö«à\BºP®‚Ö¦„@vb}õÆ1 fø­¦à-Œ*JA@„³Ž¢(Uœ+j¸ïùrb[xÇöÆ­×<ôãm_øk?÷Üþc†Ï2|&õ™Ì²˜/·™~Ðäõ—¿„7öÔ[æüÔ¶®¡8q~©ý´<óÞÞ\Ž“®%–‡õë¯8éZ¡p_¢yÏב3qMvÏË/¢5à;J­y§ÁÝ“÷ð’!¾ áä³y%–òŸIšÂö+ÈÁ±”°)l‡g‘uEÖÉì{0Þú¸R QˆÉ'ãDHqÍHvÇ»ý´þ–ø!0JŒM¤ÝL‰"DQB`š„šîÁçSøà·ù]ßt'²|Òqg2Ç{Þb–ŬÙ.Û–™ë¯=½Ã°.9{Ç÷E¼\­Ï7ïÛ»¢äø’›õä«8­Í{{s°Ä”/óîéô@JS~ãúÐ"”¯,­îž<ó¾8â—×ߣ°ÊïìªoÉØËë[½¶ ø¹Bûée)ëȬ#>¦·Õpq| evªúúƒ xø×¾`lÊ´j|µþÆ:ÿè„m’;ŒúÄøˆ16":¥ãJÇ•.Ž+w‚ ¢( KÒ8"C BM“)‡Ìð%©Ó DEꪒÓ˜YO Ê J zî…*¢X×ý=fÍ”áKfGHf„'Ä´›ðµï\O%„B@(™úɧ¦&(e²¶AìccýÂe‚ÓîžX¿qÎ?îØ%ÇV·Ê|&9Ç›I–â’\†p÷49ãÖÆ´´òS0*Ô¼7TûŒMÙ+{øè¤=:i7Öøô6:¹ÀqÖÆ¿{ÒFžPJ¼Â:¡‚gz‡ÒUwФ24ï}ýAÿê×òo<:kO®Ý˦ÍlÚ*wEÖáY‡kÿn»Ãƒuà\*!•²nýµSçÂp' 3™Ë&3CEÆ|=•<©òS@;÷*3E‚¶ d˜›q§ÓÓç“îtšÏd‚Çþà®­ï §ûÿíݾó>æ3µ‹ŸØó †Ï5|nÖÎNM䮑Ûnˆï¾}jÞë" ¤õ5ú†ÖÌÈ—¡N÷§N÷_bä+î«1ïùZÜȯ1ßÔUY^¡‘‚T­)Š”6>•põ1«£f1(_†pæÊ:ÎÓ‘Ó3GNÍýÌÍXvÚ°õ'{a×gvÛ¶+lw¨¯Bi¬CHVų;IA¸- ¹ßò; CHagûÆÙ÷ÒÉ“™Ìi@¿H ¢3‡_¯qZ¶=ž:8ÛÝëû?kG€Ï¯ ?Œü€o½ÇðYCH,KXœ½miÝÛX¿zͦ¡8rf¦P'É›h-*âËÙ¼çK#³‰§§BÁ]Ë‹È_sâמïZ+^ìúö“W/³¨F¾Lôп<‚‡¿ðµï½vS,™´ig¬][ª€%8·9ϺÃÓƒ Î/Õ¥?Sÿ´’U¥³µƒý›(>y¦Kp)Ü‘ìôYE\£&"²îÙØg¿h½hŸKPIˆš&C»¬zÇ» %äiäJ†=ñ«†ŸU5çØÝ÷ïwèÞ}¶©û‚éwM¿ë÷ù«kþøìÚ£ÀáSƒ{n E6ò•w-mä-“ZV¹@lmå!>Ÿòk|jæ%J¶üd‘T‚5ŸSqWS¾:jÖˆòehÞú—<üË Ã³D§&ÈÔpÝ–ªæFCJ$g¬“Ü8øßØÆ‡›:â µ]u›š6hMµ56W)ìÌ´”J*)”VMÈמ>vÞé©é=¶Nà\ó–'eh¿íôK.”„æZÆ3K'«Ì³w}Àí÷ØuÏñÙQQsÑ·'!E 6–ÆÈW„6µ‡W:ŽÄ¢åŒÊM…5ïù:<«ñ¿–ó« fÁ§³BÏ»ê-k4º…U5AJ´ ?kK=…ð±UN‡–!ÜÜ|èI¼¾kávH×nŽàЩ™ñaKpj“ds¿³Ý:rîøÄhkýºBAB‡CõÇÌ€ÏúÍ ÿðÆèÅ,¡$9Žª¦ƒ3ç|6…”R~!Ç„cól–§3uw¥†ØL"NM'Ts†ÓÊ…à3ÕÍ#éÔºç(iz–5h°š3ËøÐû 'k8YãÄk›“SQNÌ>¶0^¯ÛREô,¼µ šIºzÞUo™w­,ó®U[e8ÕŸœ˜qjc–Þ&fÊzUMñà®52iäMº–éW›²5ò%.I?w5”kVgäóz•‹ú·Añðç6ï»6Çp¨g€5ì¬`ÿÉéý'¦•rÛÄÍ­a£î„ $ÙÔ‘7»¿ÖtàåP öSôå¦ÑOm{ßa÷ø­ÕF[Ð_•H¥’ŠHP¡˜4IýÄÉäÐÔ$5ú/ƒJ"m»¿*v—p,žõ3cc>#1]møé3ç_}ù»¾¸MÍ 8V`aˆ\¿µŠì?YD¸{* ‘¯D¸Ï+ùàͯn®#_.J@…¢T¸ö:;ø5ÓÌtd² DRAXlýÏéøÚ¶öSË`>¦)?3UŸ®Ê/hû‚ ç´•ø:êEä;Öu«Ûwˆ¬çsª?©)¿¹=¼¹,ç]‹mÞóuøLüð™øÚð½¾ÚÁØê”ϤëÚ†8&ó|l”/C¸Wù´{s9Ø“›ŸtmæÚ ÀMŸ{t÷ßÝû†úv_ìÝ6ð؇ÌÏ [­ÛpÛÇÅŸ Nt@¨Ê:Gȶo2S0S("„¢RQÁݰ"1¼‰nlýOA…HÄü¡vC¶()¤ë“®Å,fX9¸Smä-#uÛ¿yó‚¶µßoÜVM@ö/…yÏ—gäW†øJ4Ç3òe†øRÂÝS™ÆgòU6±šµo8gä—ƒørÓCÿþ þ¥+šwE òδë0×a/=YŸ¦3Óô—¾úØg~ïåîϱúóþ£ïÞÒ¹¡;‡Æž9÷ä Kßf¬4š.*%™áe÷Ÿ9Ðaeº$©™¨› K"QÿŠpb‚W*Éâ×H(©¤”®Q·c`]Šù uæ3謑Ͼç=Èö‡æóý¦Õ„âÍãó³àK£óÓç/®ÄÈW"ܯZŒ³§/ÙÓWÖF¾d*i}1­új ÀØÔ2¦AÖ¼0YMÔ0_û©›œ‹™K1æehÞúÖƒ xø3W„ûî-Uä…°¹Ë.öùÚâ©™ ˆŒÕÚÜ%~¿¤L¤'O¼,U,i:ÎøQÓ¨V D)Lj I$oxFˆzE1¸¤’‡S£Û©!|á žÜÎܰò™™mF`‚ìÚÏ?eŠD”ú((U”‚RPª5Ÿ{ÀuÜËDç/¦5Ü;Ö5îß~Zb¥e=}I ÷Ííáž%ì_T­‰yGéù¾¸{ZJ…áb¨|àîirÆÕì®™WÂw¸ÊW Ëá@p&\v÷G',Ãpô±¨êëÐzÓ“‡ö¨<cˆmzïŒt)cd’G¡`”<ý©üñøCóûxܲ³ÀÏŽLàü¬Nó\ü┯Dó¾,]ââ éYf•·Bi­àŽ5ñï«Ñšù2K<ÉIƒ»viF¾LôÐwÁÃ,–ð~¹tÅ®–›šMú_¿#ÿ® ÁTíí *U•™p#ŽÌ¸ƒoÕj=ã’ðyÃʺNçßû½Ÿj¨úœ£â.(<ÞPH845઼ÂAPÕ]`²\Ó!”©!¯aøY3„p‘Ï)¿Ü?­#ÂWÿÙ£®oü½¦ WéÜühµy0v+<•JÊÈ„Rƒ»TÈÈ7yäz¿¹¤|Í;r9¿)Sp—²Nƒrgm”Íá.µäK>aÎ[ò ÂÀ¡«ÿàtçú³™ºw”#få¸R< h™Ú¥ã?â Aƒƒ…üW 6D`J)®¬šº*·æv°Ð`ø‹\º•W áP´ 4Ö ºð‰¥ ÆR½¦ú!<¤û‰•’ªË• ÓÅî›5m•ÕKåC_ªrl®v5Üè?µq÷SŸ) ;úþ¿¯Ë£:º§ŠˆN]²‘y·*Ú22…hÞål¦tþ'¦ýÕåN5åÎÜÜúçîȩςÏ1|M¹#‘jË'X¦‘¯.w0¯³Ê]~ÕóS/ô~3¹dÆ*ßBÈM½÷› Gþ†˜‰  b&!ˆ…~ñ÷fªÿsÅ¡¥‡}ÔR]x*ø>Uƒ"0ÛêgÖëoéŽÙÅÃÊ|½CWØ1 A€{¬h*£µ….Eh~,«V_ü[¼óÛ8èpåg¿a=¤c{ª|rq"­S“e­5ò…÷tÌ»UÒ¹ooòloò¸ñ¤Ç59ª¬©t[¯CqšŸöK²×”;j2Z@YæÝª‰™ÀÄL€ªJŠj£Ô çg^P,¸ïÛYÂùë±*Ç} Nñ†ß0|Aà úƒò‰á >yGSçt€>ó=ô<¥žqéºSÓ>‡êÚ¼º|çP`¶.èUHÑü›Ô©ƒ¡¥œ‚ã5ÌD†¦{}¿ë›^òM/Ÿýñê»·oâ ã|ÿ¸õl8+8šÌ|F½°”)¸›2±.)Ÿ%åݼ#7|¯­tRvànÊ4ï™B|ÁÁ]ÊÚl¹®Ê™ïÃIHûv”ƒq>Þ´ ß’Ó·æ»/hø ÃgˆðÃð®ë¿ 0 ÏlþX]Zb‚:õ CŽ2.5f·«¤ºkÏ— ÓN•èÎ’>üòÌp5˜ÔÅ}ú„W~ÿ̲ofÙ7½ìŸ^öÿ!\ùízzo5>î³µy·JÖÈëéZ\”²©Ãs7V{ùÌÊpGAÌoJPc5òùŸË”†Æ¦üc“~ªœ«]ù=˜ž¿÷‚Ðû„ZDÓôâ”ÑQŒ:ùaE¿×0»?hø‚%·{ä§ô ¯á9V †¡,-±Ys.7¹*œšæ(¾ƒ Æ~}aÆ©¨Š¢*KýìwÎÜ|¾hð/A¨jùƒ?ôÏ,û§—}3Ëþ™å¿ÿlègÏ/x¦»šõ¯{´¶Us½;hp È(#Ÿqón•iäwdÓÈçQYç{m¥ÀºkGeC1òÕeãhÞa©™óMµyC|ÏóBAïoD…ûþå/™à_rø—Vý@ Ÿaø é⃾`h³š?ó×þ? „&¥t¬LÞëæÙF§{QUT§k¸Xsè¤Tnº.‚ú㇥Ks޲#à c[Å¿àù×ú¸×ü*Õ£$áî›^öÏ,±á`Ã.ÝR ”†-Èð½i“Ûæ”Ï*Ü¥L#¿£É“)ÊÛļ#Û|Ï1Ü¥Ò4òOÜ¥Oø>_ÐPçj¨Ë³‘_«ý;+XÖÀ²#°¼êGY{ÿD8y¯ù#ù¢cìwIèĪV5 VÀ4>ðE1׸¡aIS”…qÿÒ_ÇŽ,=Y~0² `ËÆ¢Ü4’íùE¸ïßYà\b ¹‚>=è‹l©V?û½¸›bfWÅ1Vʽ¢d×Bi—CCPÇ/«TDPQX…pŠ ÓåfESIWIWISÉ¡‘C%]#]%‡Öà[ûsûkþÙçül$xæD5㱤Ôiäëmdäs wS+F¾9#o+óŽìùwY¼aFÀy”4òñ±|š÷D¾5©{—¶l,Ð\_<ô0Ëã O'UY†{ä÷øÀõ¿iNUsªšSS]š|ÎܧWm»ýîEAç×M]®¤3ÿŠªÿ$˜”àh-µzÂ0°ç•ò¾÷èöùºöƒ£Ss‚ ‚AA «–Í 3Ø]¯\»Î\c­¤so®w7×»íæâs/éÜw4{$â¯'läíw]ðk;ö®Ã½¢X¯(Ö+ÜzE±£¼ÈQ^¤—o|fàímXtî>4:u½Øèú>îÕÇþ{Çèï‘¡ÁÐÄã ¸ûêÄüPЯ‹ VT¤”V¸¤…‡®"lçëFÿ­ùµž?Pàý³cŸ%¡ž}¿"éó’µoñQ ¸›’do©w·äÛÈç˼[eb=5#oeʼnØÇ¼›Šq}b&»¦öí =\liphktGd²é«çŸ¼ ô~)3p`øµ‡CîåEd€ (” )ÁGØY^¤:UÍ©i.UsªšK3þá[ýcýzÍÃ_)6Œ  3ÜQö;Æ~7Põ ö n½äƚϾÙÐñê\Y¹kjÚG¤D a¥éÅñƒ5Þ;3&?}XXÖ<ŸŸnïI)©û¤¡‡ î-õîÁ<y;À]êºÕÈ®Æ2ò64ïÈßmwSÑ] ú鶬£hktoÝì&$äïQÏ/½ ô~1Ö˜j² ‚-è_çõÌiÃ~^U…¢ š¨&=@ª_ÑŠª¦ ¿>ºXbˆ 3 a`¾õ?–Üù7c¿ @Ÿð.ïø¿E‡ÅLñò£-Žš‡%Úô,*ÊÊ]F§§} Quí_­=æ—‹Ëú¦¦ÅuÉnJ6a0hºø7,õ6Ÿ[]š“§qg‹çZÄÛîx‚ëg¢i­‘/T¸g¢_Ûíû [7»´7• çÂ1ì¨p&aóàÀñ‘Ç×yýÌihã[H €jžå,zé+“ÃŽEá ‚Y°`ùv&b"†²£¶óÖÂôGÛÙüõ%•ÀeåNãß2·|á`-€¿úÙgá2ì}ª¦­îlñˆ†¤Ø|¸ ùÞÚྒྷCÄÛǼ[%/;[<éœÒ¼ˆ¾û§2¸» UNmiÞ#Êâ‰ätþÏ'ßMµ7•È‹¤¼^„zÞðè}-ªy?ØYàL¶b¶×~àö¯/ÿXøƒ†?Ⱦ á _ýAìüìÌ;ÿ“³²X=ñÏܘþÀöòÃW~b‡úÜäïxçô苇jN^¹?tOøþeG붬t*͸:Z<®¦M"s&”·'Ü­’|ÇjÄÛÖ¼#³þ}C• Àã‰ÈÕmì) ôºJ'ˆ„íú-ÆQfá``x~{¼›ÐØêyÓ B﫨vOM;^ÿ¯®ÿüë³ÁÌ !`¦4¦&ëÿƒ“Š˜@ßí! î›p øùí7¾¥A‡ fóã3w¦€ÆÍ*Pp2Ö®õ΃…Ö|y{JþiX¼áŽÏIýL4ÕT8C½¦¢0ºq! p—º14'Í{G‹§³5ó5‡:+(kæ}÷·~æp wB€ ÌB0 „€`<.ý~è …–W8èä SÓ…¦¯„8g'Þ>q÷D‘K-vªGwm®«.¤‚÷®¶R…põNf sçÁ‚${k6k®ìoÞM™¨£µ4¿GWã{a™w«Æ¦|cS>5΢|–dþîv¶%ñ»ÛóVó~¨³ÀéìÀýàïüÄ噟ù¬Ê|E áž!€!àQÉ÷™±¢œïç £{û&MgMgÍϽW×} @m…³½²£³±À‹‡j³qäÙPW[@WngØQšæ=ˆ/ ¸K]œ»68çr¨.§šïc‰%úîŸ^ØTãðh,u4"Ü%Ê%Ù­¯D¼h7eɼ¯Ug[©BÑå›3±·ìyÛ  ÷åXÉÌ¡ÎJ§û³µÎÑÎ/ÿrqºxq¦xôò¡›=(þ¾ðj±æªt;*‹œ•Å—ÿ/Ø¿§ÀÞïüÔá^½³yq¶hä‘—×øÕ§£°ðýÓ£Yú2¥][Ë\¾ç§–²²‘Èßaš÷p´kψfÅ¿Kʧ¦‚릻î(#Ÿ3¸èÀÝÛâùx?þC]• ,ÂÀµüâðGÏ(iºc³†Å?ÞlüOõsÿ+ÄŠÒ÷WûÙÐ%Üè&ÂÐÇ#à Ömnäwo-£lÂY0ò…wSWg%Ù;ZJeo+­ÔϘ|OÖÈo¬rø¬ÀÍûÚÏÆØ _Ê%ßMíÞV*KkúÖ¡FÏ;^½/F5*|z%G+”–4]Yž-Zšwéþ†Ø[ž9c8xpûë—×xûÓu¬ú‹‡jfàÝÓc™8ÞLjO{€‹ëý˜²!ÙÀí4Œ|Â]šwë‡ w[ùÿnb=)#_ˆp¯­pRLv[|­mŒ|^ààÒÍÐïk÷öòˆOõ¼ëÅ‚{î5?Ü%ŸÌùã´x$C##JýXô;‹‘¯Ií³§ßF›F^6§üüh-Üa5ò­¥öw]5¾úhlYR~S+QÊZ4S[éa4cn^ì€ø|Á]êâÀŒ4ïûv”ïÛa¡|¼Ÿ~ŽÍ»Tp²Mš÷ñ©ù‡ënsæ ýÀ‘u c_>R àíSQsöwNIóþ¡šlCù½ÛËA´î=VötûÁÂíðDè(_ æ=†LónįS?“¸‘—k¿É$žHMùä• ¶Â)×*É—bôwÍ™.Ü-ıg9€ž÷½ ôÙŠ +P{ÕòÉЭÈ#8s¢†vðØ:Ë—×1S ¸›z'œÏ¼x0ÿ‰|÷örúnÄ_)%2[mMñ ÷uÍ»UWïÌÊÏÚñë×GZ|´wnªvQ¡Á=µõ¤L³Ÿ/Äçe¬uuþú´\Kï>ó{¥%Zïó±à~dW%€S—sjÞ­ª®(©¯× \YýkÌZ´¿I]Þ5üÁÚæÓÈçñ:*uûþ‚¤|Rˆ/8Å…»)¹Mgkig^)«þ=鬯ÞJ‡’£S>ùÆÚÊ\yûÀÝÔ¹kÓ2–‘ýÖUÞánªy+è?ëéû¸ÀÙ÷+H¨Ÿ]çOôå#µ¿}j$©ý¿s&döó…øý;Ê ñ×°Ín…—òjÞ“’yÈ#âãÌoŠ–ÕlªvxTPæ=}™ÍoV“wõ|è] þ¯þ€C2d·³Ú»B¿¨gß«T`½š™x±{ ½{fôÝ3ÒÈ×ä˜òûwV€èœ à.uËÌj6GE|Â=qónªÿÎl¿4òm¥IMÌ”âÏ_];èZˆp¯«tR&\ðèdN¼ Í{ÏG^(è}ö/`i6ø£»*ÉæÝTç¹½OM8ðüøç×)­!¥W*ðn¸;|.oÃú†[÷BF~³;å K)ÀÝTø]]9G|¢ý R«ž´‰ê*F2GI¸uÙD¼ áDåtÿ”,9²«òè®JGvW2á¤ànêÌ7êPÙ½öõWŽÖðV’ÉÌZ½{fÌ4òdzOùƒ¤àlbkØæX·î­oä Ô¼§©þÛ³rÂ`W[iÎ(¿g[Yýg¤‘WRÕ4Γ ÓÈ×U:³Jy[©çc/€Þ§"‡UÍ Èc»«lè(¥L²G þå#uÌxëdºp7eù¬">ÛÍ–Ó×­{ ’ò¦‘/P¸§cÞ­2›å ñÉõÛTã2‚Q8F>ãæÝª‘¬ùLJTÏ'^Ð:p—:uyòäåÉŠR½²Ô¡ä»”3ïz÷̘\ÌïøÁšã²By¥@º¿šF^½à”)¸K]¹={eÅÈ—edŸëj϶2¤ö’Ê4¨<)«p—™ôdÚÈçà°SQµ±y·Jy‡®8ô‚¸$e]#ŸÄK¸_¼9“ÄéŽh3YЉ|Æ•=#oõœò‚Ð{4f+Ÿ\œøøâ€§÷V?Ým/ʋՅ3¯­2™Ì¬Õ{gÆÞ;;àøŒù#]•”óYÁijÛ–@øeä3kÞ­ºr{öÊí]me™¥¼ w$îß×í!ls#¿¡ÒI9tÁ™2òuUN½Ì{ϧ^zÄ™ÍÄXV•ˆðLwuÖ/ž¤[Ÿ‚Ððïgº#,|$ ù¼ÏfJY7ïÎËð}Û–ûS>{p7%¬ù Ü.ÙÓÈo¨txœsJ¦iä몜Fì¶€m¼dfÝÙLõtaÀ³ûªŸÝ—7ÊKŽwò‚P!òC_¹QŸUónÕ{gM#_}ü@êgC)Ù³<)š"†UÍ'í›íŽøèÊíIù]me»Ò¦¼Õ¼#A¾×׸(fßàDú|~d5ò’¡|]¥l;¸÷œñ‚Ð{(~2³®N\›?—?ÄK -ô@8™É½L#ÿ|Jˆ?¶»Šˆ îR¦‘oßRÒnK#Ÿón•iäåò,©)îH„ïõ5.h oŸ¬fC• D¹7ïV™F>)ÄÛM=g½ ôŒ÷Ã]• œŠx~üÃóãžÛŸëü¦y`õïþaûCä6¥‘zïìø{gÇ<¿¿æùdNȱ=U |ri"k‡–k Ü y›!>Çp—º|{æ²4ò[ËÒ¡¼U ø÷dê™íÕØg±À‘IŸ¼ÆlHÀÈçx´ Qe®ðçÇ< :Kõ‚±õ'Ümú÷?ándyX5®Þ?º­Iñ¶XCq ÞîÎØÛÈçXæÚ[É"~­yG\¾×׺<M‚•6tͽ'`äó5Z[=ç½ ôîeÞ£3ëèƒsÙª\WëšwùpÏý@«UïŸ{ÿÜ€ç÷×<·?N\óôÞ*¢•Që‚Pâ³™îÚÈÈçż[uùÖŒ¤|âˆ_îˆÍ÷àn*_F~c•‹ìaÞ­zœŒ‘·‰zÎ{ôî‹™ÌtVóéd õÞ?;.3èÖÏfçt+ÜÚª‡©ü"Àûák^ Ä?½· ÀG}…÷dµÊÈ7yòuy‡»)ñq) îÈê ¸Üy›/ÍÈÛÓ¼Ç_5»³‚ԦؘÌ9kÅáßm¥÷Ï}pN&òÕëR>ýÞg¹Wj­VŒ|þo­d5ÛRŒãWÖ׎PC­ Àƒ”Ì{„R^¹;YÙœï¦L¾?žôÙî=½z÷D5ï™j~òÂÁ€ˆVÖ5͈"Ì;€?ö¬rèߟ»{û¼èùýÕ2ì’¸—z¦» À‰ …dÞÓï3cò}`x.3Ç”€ìcÞ#´k[™¼¼_ZcÒc˜wDóï„;reä î°Ð\³Ýî@’¹{t™­¸^:”Ŭæ{åÝþý{åy dbèýsã’ìÏí«–å¤ÏvWèówX°¾=WFÞ¶pp9ŒïÝ«|l¸#—вšÈo¬v îR2‘W¨*Ù®W¼dæ`G€3W3ÓüäÝ3£Ò¼¿t¨ö¥Ã üZ3nVÎXVå=…·Ê4ïyŸ1_ ÏIÊooòäŒò¶Õ¥›3Ò¼ïÞV&)îX—ï™5ïVýº´ÆªU.! ^©Üê¹ì¡wwTó~ £‚3wS¿ ç3i"~ݤ%.ßa7ÄŸÿàüxM…³¦ÂQXÁ{Æ;ß0|sogón•™ÏìI,‘ä{öàn*ãF^±°Vú6õÙÄò£‰e›ªóÍë¹â¡wW.’™µúÕéÑ_}: àåÃurµ¼LÉŒe ›±®ÕsûªÇ¦|cÓ~ÏÚ¬M[4e©½ûá9IùíÍžlP¾Pà.%|±K-vÅÿ%ÎO»Î ù…{ÄhyüõùE|¼dfÿÎ Fv ’ˆðò‘¤[¬kÞÿ`K·Y¹À+%’°%ÒªÛÊÂËŸ…9õ÷™îê§÷~®ãšC!#¿ãsŸÕìÞV¶°l,.öl+‹áåWñ½±ÖEY6ïVÙa²k^´îPð£ñÐ5¯¡Ö%ï¢r¬ž«^z;£š÷ý;Ë>—ýÕàÞþtD®sýÊ‘ºW’§¼U¯­³«.ö­’´êùýÕd â? ¯ÊBx{*k3Ýš“”ßÑäÉå ˼[uñæÌÅ(㮦VøÞXëp?Wp—JÓȨy!³ÏOcnßsÍ BoGÌd&·2×ÜH°غ戬™û"ÇŠàa ÿÜþjÞ?·jðÆ?êðôÞ*»­‚Ü.¼w}8cF¾á.Qnñ&åw¯gäWøžµX5¾R3ò ÷¸uœF—å-Tc­«±®(wGSûv”3ãܵé\~Ñ·NÈ5¯_9Z—BÇÇ׎ռʿ‹ðï1f3™SXmˆø\êúðœ¤üŽæÒÍ9ZËÔŠ€»©hã®!¾ç`X5¶Ré0\€ý–6U¹(±"}óF*ˆï¹á…‚ÞQÍû¾åÎ_Ï)ÜM™À^ŽøµæýÕcu ¼ñɈհ/Åóïy—l7f6-X«úBËcÛmÄçkÕì#ŸÒ k!š÷º6òÖD^ àn*ñ¬fS‹€Ï²^д±¸iSqªç¦„Þö¨æ½{{9€ 7òw©7ÃYÍkÇ¢ùÅÅb¿ñÉc¬n+¶,Bu#øÅÅLžØt| ˜Þ;;Ó°N^š”k9øs¬üšw«®ÍIÊïhöìh‰CùB4ïÉJR^¹?²d:D[)š‘/Póž¾î~ú1e ñ=·½Pл-cíÝs£7O޼yò1€×ŽÖ½z¬«Ã÷oþÑ埛[{/"ôX·Qð_¿ûçß8ü‡9þ^L?PÂ{ $3ke.×w¸³"£Gö»)ÓÈøB„{âæ=B¶Ëg¬Zkä î™*õþlqøÑ"€¦MÅÍ5òëjo{˜ûlcÞ­zãÓȯÊj‹ ôæ'#~ë™UmÅ–9ô°¾ø[Ït[ß+ŸØ¡J2)}zeR.¡u¨³âPGN)o7]š»>8`g‹'å H)Ã6ç»”Ióú|ÌúI_¯ã”ˆÐRŸzdÜsÇ Bo[Tó¾·½ @ß@*¿U¹Ñ'GÞüdÕ{¯Û°°à–É Ö´YFè­M¾"x¹š•Ù?e™½ødñ64ïV]\ßÈ¢yOGÀw„¼ª¦’Ýš-ÆU6Ê8‡-=ZÐRïnMžò=ƒ^z[ ,™YW&Í_;¶aìο³2:¢'ðºþÝÁ/,¸÷oèÍññ?XÊÜ¥N÷O¾:àÀΊý;Ë3²Ïµ²9Ü¥® Î][mä îé˜w ßÔ׺ Á†`”‘ßTíð(;“°.È'­ I">Þ5g϶20.ÚØ¼›2Ã÷­›K‹­Ã¤V“þ_N¬ø÷ÿrb}ÿñö܈”é éÙpwÏý;²…øB‘iä%Ü KiÂÄw©‡£Ër=ØúZ—ý)ŸU¸K >\¸óp¥ð»â IDAT@kƒ;AÊ÷ {AèmŽjÞ÷l+8v_i»éµcnÝ›_XpËâHùâºæ}­…7%ß.Ÿç&‚á` ïfȼ[uöÚ”œl¼oG¹,‚Ê” ¼[%¼Ë¡ºjaÜ“fN…Á÷ˆ•¾Í'vG|®fØÞy2òmñßs× BoSìd†¸àfçÞûóÅÅâ»Æoxõ؆×_ï^7|_Á¿þzâwß\\,®ñý"7,מ5W°Ê†Î…g-d ñw©ŽÖÒe¿±ì3t´†‘Oß¼£ øw)ûù—úÜy° )¿µÑ½µ1:åã]rvm+ã´«ò!6Ý·Lä uÕÃG«Ÿ5eµðÙW.®ÿç¯OËé {ÛËä˜ùçVWg¯Îèh)µ9å3wßcȶF>_uœ·ÃF~ëæu ÕsÏ  wsTó¾kkxe±GûKæ'ÿ¸ãÑŸŒXÑ1s5¶(¡É ß_ǺuáÖý[7»å¤TÏ/”˜po+óå[w‹xaàRäàêê"V=¢•ÐÈ, \B6#øÖ0ø\Á]ªo`FV»îÞZºkkÒ”2à.µbä[Km8îš)ó›ó=ñÆg¶2òõ5.Ê÷$¬[÷BF^þYö<ô‚ÐÛð$D®Õ›aónZïW~§;¡Gä3Ÿ}åwº_ùn„ý{¶-|;ŸšÔØÕ–× î±µbäí„ø €-{¤¦$«‹Ïå*T’ ì=´Á [‰øm[JÚ·”O¨r5¯hêj+påv!•¿z´îo%²{ÌÈý-¨º àÚ/>ðӉ߯(u̬NÎúüîÿ…¯•­\íö|£[ö<ÿµŸüƒU_c<´ÿ êѵrlÞ­ºtkÀ®¶ÒζÒþ‚ú‰'«¸ïòSÒÅV]|‚²¯O­k±iäó²Ä uóîü×ú~„·Žý(Ú6m¥\hpi¢ MWßš‘¦»é©M6µýÛÑûM£K]‘ AÕ7¶07¶0ï§å’2QR& ÕgîcÓ»G—0z¿iô~SÓS›ä®®¾5cî?ƒÇûâ¡ZP>ánêòíY­¥ñ&ñ¨yO|6“IùÎ|ùÌšwØ–ïé´¤^ÿ(÷ ™ÚǼ›êyì]ô߯ü3Û›¢,?Ï .°læµcˆHò7"T}Ð4ú iòW'Fa{šÀAm9¨-ÔÀÄò¼_]ö«ËEe+w3£‹]Ä£ÛÿaôA“æú«ö–i¾ÛJ¦yø…{²2/yD|ÆáŽ')Ÿ‰ÐƒÑe ÷†ZWn²ÂݪáùíM;š=f›=-f\œ‹þVÛéµ£u`~ã䈄¯%œñOŽýhÚÔpÄ£K°‰8¨ù0€)¸]Ú«Gëø;Q%T~zÀèýȽeÞ¼ïœÎ¿y7Õ/­k‹gg³G0n Ò¯A ¥ÐŠ@nÜÙZÚÙV Ëů eGÿÞ˜¡ýA޼ýÖ ìñ‚л!4ß/?ßìÙÙ,;rx@÷WÖ1ð†l¿Æ¿Üo¹ß$õ}Ž,¢¯ýÙ æ‹xÌCãÏ#‹ µkCeÍWGî7Õí<+÷öË?¯È ·!ÜM™¿ÛW¯X] æ=>3ý¦‘Ofü9}eüÆþ½±ÖËê£é+7F~ÝIXùUϨ„ÞÚÈš™ëCsr™›-¥\h±L¤Vû÷ÿahzí[„|EZø cYšw«Þ<9òáÖn߯yÄG!{Ê>XµÃ úw›]ú#umhÀö&Oû–0îÍá.%{g[ig[)åd\*Kp‡ ý;q¦[DfÛÈÇ-ÒÏ¢ßO\š»64çr(EU±9xVK®¿j®â„›Ïáæs££µ££µ n9FJ`óñ…ûï.¢´z.¨û"'ïÖ=?û•²þS¡ŽŽÖfÊ¿¿t¨À¯liÞ­2óë̉ϧÌ|¦+·F>³²—o¨+ð ;ëIå>‘Ï£zƽ ôVG-xßÙìYörÁÏÎÖÒþB({åhoY÷³2TùÞÿù^ÄëÒÂÃé‹xýÎn,ồÞxø¡e]ëXU ‚ûT:‡](p—¸;p:á÷‹|NrÊl`‰ø®¶Ò®¶RõßÎÊÔ¿ì™wôÝ?½ý¦ ¬Â}Õ [øŒP>RŸ,©g  ·*Öl&™¿Ë»òÎÖRyËdóÉWŽÖ!:ߥJŠ'×¾øï]/"<Uʺ¤Ÿ)Éw²ÞdB…ÅwÛ6¯2ï7ïFD“½öî’ï2Žø¬Â6Ìgr ™«‘·!Üø#½;š=†;,cJ)L[Ï™»§xÂÎÈÇübeD§uáàÍ“#rÿ¯­“qMúzùp-Q!ÁÝÔÍ{ó’ìÛ6—Dÿó¦+·g%Ù;ÛʺÚìû7²VvñïuEr¼Ò·ÌÍ‘Æp® ùÞ3éÐ[Õ¼ËÁUk‰¤)Éw"ÛõŒŒÝ£Èã_ûâÜBµ|bí$³îV½r´Ž€YP‚*8现y·zvù 66ò¹Y›©³­LãJ&Œ|¶Í;lâßówX¸Ü˜’‘·#ܧ½PbÁ=¶Ìæb{Òè2˜ %ØG!±öa~Ödzl¸xëäÈ›a#ÿJ:FÞ~%³±µî°ùö-%í¶wÍÙÂ{ý·g$Ù»Ò6ò9€;lÂ÷<êþè²4ïµ®¤(oC¸ñ²½ÙÃQÌ»Ôå[3òwnmz…'’Ì(+YÇ&ÏÌ×Z?üîä…¸p7e~ÅÔÿòáZ~õi!™÷2Í»=ŸK™æ½ËÆy¦}÷O/4m*0üh1îÖÙPã†"÷çÚ¼GFî f5¯ÓO_=3^½eQÍ»lNp#:Ü­ÚÓ^F€ú­Ãó¢WŽÔxëTüœ¤Ü³Î6ÓsˆÑC|ç„Cê¥Ãu~õi&Çi³­öÍ%H „1ù>`Òø<®š-ùN–ߕ󫗔ϱlw$iäí÷Y/(ÜäZ›‹kwçufs]îyþöV;¬FþH¢;,´`í[J@ %ì&Öí`äówWÂXOª0!gp‡äûð£EiÞó‚x[é~‰¼ áÄ'J{“‡‘\ƒ‘¾3Ò¼wo/ß—Ê¿|¸LoŸŠqÈ>‘ É['G¤yåH]\Ê¿|¸෠ʼ'¥»ó’òí[Jڛⴟ|²uåÖŒ4ïö¬=[ñï&âsFùÍŠÈæÝª”ù<ªgÞ B¯'ªy—„)uº^£yÿΜ">)JʉZŒËÌgb þåõÞ.¨Ø]:ñdó–#Ÿ'Äç×¼[e">.åsiÞ1¾jFð9@üæ Eî٠yš÷ž/½%±kfÒZéÂéó×§ìßY¾¿£"åý$¥Ä¸ªìáÚ²÷É™MÙ8ª·NÅ5ò–ͤw©»óÒ4loòlÏ-åíw)3‚ïŠ^{–c¸cmý̯³Sk¼ á$ÌÈŽQÃ鎆»2ò²ø—×”`ñI¶Ã™µŠfä_>\à NfÖ•y_˜cÄÛM—ͬf[Ù.{TGßdò=u567ï’X'"ÙmÑV|ïYòè-ŠžÌ¤á΢é@G pº?­Æ,ÑôÒáZ$\YX]~oí‹ãÓ›3|LQ$ùÎÀÛ§F î™ýõ0ùží>òv3ï2á~ÙbÕsoÞ£þ={Y꾁 *¸#läU•4MÉJ¬›ªz–½ XpÏ’Î^ aýPg޲šÊMòM«Œ|3™¿ö›Xß‘M#os¸¸|sF’Ý4òy;bÏo²f5Ÿó¸¦±®(hpÐ`„gÛÚBñ€²mK g§Nùlÿ”4*wUfpÏ/ªeNÔ¼×V¯MfƦrdÞ¥Þ:5òö©‘ÍŠ7ר2‡Y¸Þž“”ßÑäÉ*åí/Ó¼ïÎ_Vþjfü–ÅÜû,?s©ÒÔý‘%ÙD¡±®(ï”ïñ{AèuF5ï9XŸÁÌgŽìÊ â“]ähí°*¥1Œœ²^>Rw÷ñ¢¼%•3›ì¯tJªâêzÖŒ¼ýÍ»UÒÈ»Ôb—š—H¨?A¦]·l,p·ÐàÑÇ|’GÄ÷ø½z¹NfÖêÓ+SŸ^™pdWåÑÝéS>É:âu¹Œ·?‘e‘/®•C¶Õö&e îRׇç®gÚÈÜ¥vo+[\6— äÃÈ'Ñæó™Õ¬Ûû,ÿF>^2³uK çpqµS—C]צaä_8XËLïœKpû Uƒ>æcd¢%åHMuúfOy/ò9—iäw~¾³šK7Cmvo+Ë%å“îœr]Í“aÞ×ýlŒ ²¡à  WjÞ·n)p+íAŽîª@Ÿ\šHê/¬ðî™Dá`cõíµ/~6Þ–Ô×MS1f3…øÎx猽æ:É*—l—¸Dhg“G®€£·]l¨yÇêaUù sÖŸ )¥6èúD«¼,ú̶âÂHs2SZ:6òÇöT%þ®ྩæGâ¨ã½d’ u7ÖÂýáh{Z‡›M½¾z=¿?oˆ—E,×ó w©ëCsríßÍž-q(_ˆæ=ñÙL—Â]¸³´œNÖoÊeW²Ü(ö ÷gÍÈÇË0ÚÝ ÜºŸónê㾉ú&<½·ú™î굤v=Ê{ÙûK‡j‘ð$,©wÏŽ½wv ÀóûkžËåí#sy÷ˆ/D¸'«K3’ò{ÚË2Nù̬Ï-«)Dóž‘Þ87ò=Š„^ŽjÞÛÝnÛ î¦$â<Ó½ÊÈ?w †÷Ï&gÞ7o¸ºvÎêý‘;ÜxJgÕì÷Ï…¾Ùçöåñö1ïV]š»68 £ÅÓÏÈŠRkEp1;F>éúÈØ²VO"Ü‘éÞg&ßÓÙaâÐ+b%3v滩gº«@DÀ‡çÇŸÛ_ àƒsãÉîdó†«k_¼÷¸#Ç—€’aMÏí«a0^Hú $+{ÂÝ*îWW²Í{ú}fL¾›ÄOG^_Û’Õ¸3»çÜ(ã-Í]É Ñ¥ÄùAµ6¸™íw'.„Œ|ÊÅ–ýk“÷œÁ=ƒúà|Èȯ[}Þtupîêj#ÿù„;,Xß› #Ÿa¾#œÕèéšRXE‘[6e¾k±™ÈoÙXœå{t/½Á¨æ½µÁ àλÃ]êÄùñÏ×”;ªË)5ãÊgYd¦Ì»Ô‡çÇ¥yzoõÓ{²EùM²·y7eš÷ÎÖÒüI~uq`FR~o{Yš”Ï<ß4mr‚2 ÖÈg\æÜ®¤ߣ{ôòßg&ƒzv_õè”olÊ'Ÿ'õ^…ÄÚGv3R/¬eÎÜMç3ÉLHT;š=L+s‹ì/iä]µÈ¡Ú§,e¼pŸ%‘O9”Ï ß¥†- ?ZдÉmÊoÙXàîgYl3p÷³EIù$Œ|¼š™–7§dÞ›Ü×›Üד}WõáùñÏxv_u‚”o®¿Ùn†0ôhw–ÔT¶î>êÿèâ8€c{ªŽíÎ<å K-¥K>cÉgÈçù>œ„”¥öî}3}é»fžïå’ìÖ'v¦|àn*q#ßãô‚Ðë•Ì0˜R2C¥úD¦ŸøÕâ™}Õ H²Ãò䙟DzÈÖ”T…deNH§G›U²@Êý^ò««ƒ³Wgt´” 峤UqÍöä(Ÿa¾GÀ]Ê4òøu\ 1#ßãò‚ÐëËV2£X–¸Ù}-Í]5¹¯—ê“„ø9É3ÝÕ`œ8¿ªbÄ4òÏì«~6ú`ckÃ…µe‘ƒ÷¤yð‰è…ƒµ’a›‚>¹8![;éªLsÕ”…»D¹$»õI§Ÿ›µ™VÆ]“A|ó™Ù6®É¥y·*Ž‘ÏZ2`«ç¢BÆù®¡…7»×©8L\ž T($ZK®ÄÞ’ÁÅq›ÐZO’ÏVï9Å5{´îLñØð)îR¦‘ïl-µá¸k.Þë˜é»1`ïöD|&ùÞ¼ÉMkÌ{„ìfäów)«‘·N ë)ö‚лÕ¼·4¸‘j2@PÂv;Í5Ú¤ýZØ©¡c˧÷VñÑ…¨}ƒO„ëIžé®Ž˜ ÕÖx~m«÷;÷÷¥~Ü ëøÁN²·eú:ueR®šr¨£â@GÒ‹ÜÊÖ.× Í¼ÇP¿iäí‡øK"‰ùŒñ½¹Þ ÂPL¸KÙÊÈå{ÉlÓÈKÄ÷¸½ ô.f±ÏŒµìdp¾à–’þTw:”[s{ˆvÏúÓåžÚ[Å–‰¬1dÖ“XüÚd†rR6sü@-˜’m“)}Ú2òv&ø…ûºæÝªþ;³ýwBF>w‡SùZ5»ïFÈÈwo/ëŽIùÜå3²ƒ‘·Ï Û»Ÿ­4xp¹ÔØ·ÖÍõn¦^ðáµç; hMñ-%W‰0´ÐÞmôjÅd.H'.ŒË™PÏt‡ºÖä«[dŒ@)7:}uêìÕ)ûw$ŒøŒfâÂÝ”‰ø®|S>_p7eùˆÏ ß›ë݆’$N~¼}ànjøÑâ·ü¿Â?”þ]´mR;ÕVu•R ®Î²¾xg¾à¸ºU-%ýί̽>»_!££ìtÄ–ÇöT±¥>$A™Fþ7^¼±vNÓÍ»“Ú[ z~ 8éö8ÙÐÙkSöí(ïÞ^{KÙ«ëÚ`™÷¤ÔžÔšwÄç]nÌ\ˆiä3À÷4‰c#oõ”z—ýÆÿ>þgÈæ:·Ñ‚ò;ó]n+¹œà~Ö-O´&ûRG÷T2ø“$á.uâÂø‰ ãÞÕVº«­T¤9` žÛ_Öv`y×¹ëSòÉÞö¨ˆïhñPÂ=qónªÿÎì•;³ºÚJ»Úò@ù¼›w«.DOäÓå{úvù0ò64ïVEk¹Ü´ÉÍœîÙ.¢Å"Zèô½Ñ>ÿ‹–©Ÿ5ŒþÈüÔíù]A¼túwæ»"^¿2sD!cwùÇ+/EOf©ÛÙÞtªÿö4H€DW[ AÄÀð¡¸o|òtþúô…Óöävæ¬*¸›ºbùÜ"ÞVp—Šfäµ4÷›A#5ühA½i“›(]ŠÅ=áÞSæÐ;VX8eøÑâºs ×jûƒA#ð/Ðuaönžø‰Ãã¸íx@"¹¶¼È‹ÁWqU…Y‘ÿò”êð¥ªë¬&1gO^š´¾½ÝsA@¬ V䬨ᅨÝ}åñ\½3 ³µLþ%ßNí$*ÙþìÛ˜w«ú¦÷l+°kkÙå[+ˆ‘m¹®šyOSWn¯rñòÃÏ­únÌH¸wo/“¸OË¿·Ô»‘Þ@_„¬3¡äÁçD=^(+p7•É~œ‚D³ºì˜A]ôèš®é]wêºCQTáçíø%€[s» ¼Ís1‘]~Ù1Ì ƒ˜‰AÌ Cáò`=ƒ'ñ€†„ûîò»ÊNu”ÞQzÎ|»90S+™{ÿiYöþT¼5ÿÒ‘á.u1¼´[W[™,&)P¸§cÞ­2±ž#oCónU„‘OÝ¿§Y‚CñÍõ?²§y!y¨[7—-ÛÖU“û:AöÛ2”UO …D‘âŸfæ»  êÁ¤€Îøƒ"À%o9=ÎóãÏmó\Üæ¹xsnÙ¡[=—ÜšÛý%Ï A"Äw%Lyb€AìoÀÂðè‘Ê·—Ù%xÅI Ìu›ÇŒ˜æ}gËǯ\½3}mð©§ö⩽U”XÁe²â”ÚZæ^—oÍtµ•èl--”c¶*Sp—ZmäéÊí¬ð׿p7u!läóVW&Ö3häí ÷žJ/½SQ Þ3ÖSú`‚®;t]×t]Ót]whºSÕõ Ÿ ?~>TÿÉ͹=oóôEÛËjns0m{î —Ò&/Ü}(ßàÄҥ駮̹:sèúì~sGqÈD+‹ü8ŒõŒyÙéìÃóY_s##ºr{¦ÿάˡ9µÂ+ŠÌ‚,Fþ ŸHYÒȧ¸~SöÌûZ™|OßÈÛï=U^½±f3Y“w“ïkƒøhvøK%·X¬V˜öoƒp?ø`À"µHu–8§Ëãpxœ Xì6™Û´–ӼР(Bò ³ÊòC£>C[*÷û|þ€¯Oy)â¨äàjŒˆ¦³õÄÚûïègÃ' ߘÝGà¥çÚ=jÊæ\`b«m— 76˜ÔÇ!¦‡Ü»†a•IUq-B²­ÞÍɱÇRBüÓÝÕl™1›AÝx§;â‘©=¯[3c#Ÿón•™ÏìÚš:â Ô¼#Ùú÷¼/õáâ³W#ŸmõÔyôŽ$‘̬U¨Ðh“»y“ÛÒÙm…ƒ_¥»ÌXÁ£¤ Pë M8BÞ[V½0Œ‹ÐX«p}vÿþÊ÷¸*ê0¯ H «®ä3!X  ÀÁÜ CAa†aì(ÒåQE›eU íe$âí©’ˆO|ÆìÓ{«Áø¨/GÎ=6â·¿˜ÊxX„®ÎJŒv´”Ú*‘Ï1Ü¥B.~k™D¼uÒ@"*P¸ïßYŽd󙜶ÁŽ.«‘ëå›ë‹‰lgÞã*‘fËRC«ËI­Í˜ANÞ)¾Ë,`æ`%¤yWÃ.B †]üs;¯˜– ÁA%„õPY¤8Xͪª€a ÁÝÂ`Ã`ÊFCeßJð{ßÓþÎÚäýÒÍ㉼×ÄúÑ,,gšŠHiÛ_¼`>bož Ó[ðn#Ÿc™XOÊÈ4ÜÏ]›N¢~&Çê‰(niMs}1€¡‡ö‚{ÏF/€ÞÏb™w™º$Òo9´ýêSazä¯(w±Y<#Á •™8XÍrÕ h’‚ ôûÁ@À`á(q8KtG‰cbl@õµ.Ìë´¨b‰Â3Bò=Xͪ®€ ʘðü~Ÿ/àóù>ŸÏ ø´bí¶ó«X=÷5šö´¿³öÅ‹/&x*¤$߉!WDЦ§öTøøb&Íû«ç¿íˆ@@ü¿ÁÇÚŸÿD¾¾ýg_QX! AQ Ô5ÑÈ0B3“øûò»{[{IHj6“ ÷¼ùŽÖRWïäù0’rñ…Î÷DóÂ…×ôlòè}îÉ;§‚fB7Z¢H`)œÏ„²@€ˆ! KXX—A›q@c$éßUh?›X”#«€´íŠ€¼Ÿ#„u‡Ì»dù¿¡éz‘–Àú}°wû¯Ö¾Øw#²¼2®N^œˆ?º»êdÄ?µ· àŒÔV®Ë1Ò 3wüâË€ VòrÊ¡Ò!¨ÙB#ðüù|mªñú7CµF7ÞÍ@ÌŸ/ÊÛî.ßšI0«)t¸#ñ|ÆÎ=¥£»ÚÓ¼ÇU:×*ó]FÙm%—Ù%¸H`õ(«¬¨Q' Ùä\”’¦;tMWuM×uÂ/xi¼¢xáúì~˜ÃªA D(éa(¬€Lf‚Ô  \eÃ`Å¡ê.‡^¤Øê¹DıÍ{¨sõ#…Sàdx9Ó£»«Žî^/®ÉPàøZßwVíU„ö¬0Tc BhˆCqL$HfÈù§Ì`PûO¿²õg_Úñó/i EøZ©µ"øuiU+YMô6mOÜ‘ ßó>¬Wk (í ÷žz/(–yOÿFÄ<%ÅjI±úˉV‰xס„jáW= ÀÌ,<¤jM UÄ‹K@„,‹1C¦=,cMU˜LB’]„ÆWX/rèź^ìP3¾”‡iÞ#ŸÆ-ð IDATŸZWz«Ìˆ<XuzëàYà‡Á» èõåˆ0ÙÅÊ1U5‚,Iv&0…XÏuÃ÷ÌÇßxñÌ·åžÓé3“ÇDÞ>æÝÔå[3’ò»Ÿ fœkŸïö‡»)‹2M²›z½PÐû0łȤ4ôpaV?Hཱུý\$Ø%àa¬$M©“$káC¡ .¥PѤ¦³PXÈd†`@ 3Áð@–F¡}†0BñŒ0 ¡ëz‘¦éŽb}›ç"·Öëicjߎ7×¾xþú«iž“—&N®6òÇvW“¨±‰¦wÝyg_ À/œþöªO0T"O‹V·Aá‹+ ° ‘d—O¤ B G†šˆåÞÈÛî¦.‡íy⟠ó;÷ŸIMÒ½ê9t%ï «&«ÌŽ" >\èí0oŒp‘.Á.•Ø#Œ•PÂfˆ’þ]ÕtßR)3×~L±AB&=ŠŒ—€¦)5>#š’p†!„!T§ª»t½H׋õSw&fÞ³¸ÎªÕȧÌHóN"௖†~~á”%¥¤0)'64ìžh²óòÞh%Ÿ‘×Yɾ  _üñú‘’~]ZcÕ¥p3NÓÈ?1pG\¾·6ºA…aÞMµ4¬t㲑ïÙì¡÷~TóÞRï¦, /,ærv *(p²œ†žÎ uZ‘¹/ fæs+ª¦kº¦Êû9ˆ OÕDÊÙLn¨š‚P2 Ã0g«²Iv½È¡éíž>nÎíq¨:~¹6™9wí <ÒÈ—{ô²=ž‹îÛ_¼ðõå†oøÁø[ßÐß, ÿä7åÌP@Jø>)"ySÅFéßCa=Ã4ï¡û!"’wæý•³ßyéô·ŽúÍ4OÅŠ‘Ïæ*wv6ïV™4ßóde5±øÞÚèpç~!ÁÝÔàƒYíÓÒàÎ;å{¶xAè½—‹d&B7çö..–F…K°‹QÌìV æ¶™ Á².ÅŠª9T]g*t ȆL,ЉÃð`0š!8ÔoFéܵ"-¾'â˜#YÍÆt‹#»*§fýÓs~‡wU¦° ÷¯L×oýé—~öøkþF¹÷ß- }úm?3î)À——ºd…<™á  ^Möp8C ¯7+ û†w¶x¸:8÷Ò©ï"à€_ ¼,Í›#ßZš Ê Ü¥¤‘/v©n—Z`7þQÌ;bÏ_-¸okê8,ÈWZÜù,îŒw*3¾RŠUs{ 5)*®“GäÀ¯ "&&™Ì0ÉCeÈ™QDE¤-éÂPÁ¬ aHg©@ ]%€…ÀšÓd°ÐœšV¬éEªÃ¥éEê£Á nÌÆš°s°óŸÖ¾x¦ÿK™?€S—'ÞUy¸«À§W&ã½#$ ÷×§ @°ÓWD£¦)ÿ¨ÝwÏß>óÛ¥XÉÓ͈F¦.`Tl f doe³DÒ½‡-ü¢{³üÒ/|üí¿Z`CʪNXî$R>WïÌJw´– ‹³¤=ÛÊ— óùʼnh¢Á1ü{[£Àí‚2ïëéçÝÈ÷4{A莞Ì4¸AÙ‚»”GŸ^^2ØE8…RĪ‹Áàp9¢²âfdÌ`K%µ>1+@aábé & g„ EÑ‹uÝ¥ÉÊ™÷/ïÊ{ònêð®JÃÀ§á'‡º2ò¦_Ÿj"VäL ¿_"_ò7~ÙØR2;ßIÆ/+É»XIÞÃùLȳ3V‚kþ\~øÞýêÿ¹ão—‡a^ß^ªÞþÂIö4ÛÚ\½3+ÉžA#_XæÝª‹7g$Ù÷l++ô¸f}ÿ^ˆp­|ùž/€ÞÁ<$3íž rö€…`©[›~8Z^_à ]"|DJx|O0@ ¦P‰L¨(†*ù °ê#Ææ„&&"€ÁóÂ0XVθJ…怪³¢ðûý¶—žpcv_ì£Ý>,#:ÜU Ž´êòÃC]•‡:+A8ÅÈïùÙ·~\~À7&[乇†§™˜Y0kÌ,4"xÙ×`ê*ò Ílbf>B.l^CÝ2O¸ƒ0 ?P¤ß1 |-Ø(X‚ ÁÎðõ/ƒF¾á.Qnzö‹7gä+67ò1Ì;Öå{Â=n§ÕÅSÎ*>ã&3^7•#¾¬\5iGé¹…»>¡h ÕàW hp°¡„-1»B) „Ë9Uÿ’¦ph ƒü…˜™ˆBU á,aÕ¡ªºª:”k÷ª´  ÒñXž‹IÝÚÃ>¼ëçk_üôòë9'‰èô•ÉC•vVœéŸŠøl÷O¿óÊ۾5¹•Ãþ™™)tŠ˜ÁÂà€`)Ò‰åeoeTÒƒQZg8T²ºšÊµeÁPBcªªØõååzC29 „l„¿í§_ºùÍÚþâÙ¯&ýÞd.¾°Ž"à.µââÛË\°åcÃÉö´­ZÜœ%M#ßÚàÎ6â{Z½zïÄLfÒ†»ìè²vP}›&cô÷.v®ú„aÜ{ÌtƒYŸ|¤‘# 0ApèÊŽ*æGƒÉ't˜ä÷‡bw‹ÂcYwªCÕ\%º[=1°c{Ùy:é3Š,µš_¼Â1b½è1c:P«dU¸9ûéþI;+vTœ¹º ñ?¬¸  §tÛÈ0ÂÎ]f\2aaæ@@°J˜(»³OBÈ&&!VúD„l=‡ÉN¡™M§Ë?Óú‚¯Ñ`=ôÈB‚ýöŸ~„å™¶"û‹µmv¸}¯ÀÌ{  ”~?Á«B êÙêÐ{+V2“òܱ–’~ÂÝ_a¾ßšÛ àhËi1 ×39–*'X 8eW_ ‚ªÓ(_f•‰…rJô†ø~ þ9 5 ú!Š ¥ÜáPX?ÈP@²dÑg # ¹T­Øí(Öù‚ØPX\›=ÐT76³*y—ßšO)ÓÇš d¦Ç/':u £ÀŽŠ³W§$Ü¿=Ñ6:Î5› ÌÕÄc÷å((ƒ™e %;¬ øYѠʱ s|FI… ß-Å30ó9ó‰4k ükÈÎ "…@DÁ"Þ*‡iéóPZ“ Ü\ù½íe}ùF|"pÇϤqÜY]W“AÊ÷lóè½Õ¼'w¹p¶Uƒók7ëp¼¹<bzè_¶P!臭½›UQ” ¢’¢ëSúà Xfží>§LÿKÕ ÒÀ¿-©Òï¤ÈW€å͹ ©ªc~>P^ê`Æ—êÇf‰kyw™< Å=I ; æa+Eˆo„—Uù¤LŸ&åuV8½&U!éÜtTȺ”oN6KߥʃUõ4ñ±Yµf13XÑXSÈ,~]”e8Cf=*qø4J²|»ó¡K¥}·± BæPÅdˆæ!¦[?üÖdëO*ïd$…·*©D¾Í{R’Î}o{ÙÞö2€úâà5ïZá{š÷ô•ñÒšžv/€ÞLÖÌ”é“ AO´Î‹m¾ÿß§ÈÖ½$ËÔ¡¤( )š¢*Dª¢)Š¢¨áÍ'J@«s+ágêgˆ—”¿èl(zPªŠYñ¶ÂªŸxÒ¿!Ísu`g8E¸›b0C¬”Ê„SÂS|ÅÊðTÊÚ#…X!íÿgïÍ$©Êtïç=çDäžµuUïÕÕkU7½@7ˆè€ (ŠÛˆˆã2s™qùƹ:*®0¢×¹¯óÍ|ê,xÇ ÁF\Pdºé†^««zß»ö-³23âœ÷þq""³«·êÚ{æ{I’¨¬¬ÌÈ êO<ç]X9\nþnÍwô|·÷†a œœàûÏ|¡ñ9Âw@sDp'bQÓ@Õ³©f.Uσ Þ– ìœ?¾½gÆ]Îzþ8SÈ_Špx¯Œ­{ú¦KÈ&€’g¬òZ¾(=½{3ú˜Ô®ôûÆ,ä/4æjü†’­T°®úéÕ…ŸºV©[À;®jøXÌukÂÄd}Œjg(˜Ì 5¬›<íæð»c^•Ô"^˜(Ìfg‡¯˜Õ{dˆ‰â†0ät¥J¯acX¡*~ª&y²Ö=Q;^;V?Ò?<'qgC?˜‰/Ü ‚= ĺæÚµsÇˈ¸»4wÌÇ'Šñ;3v¡2’í. 3›¾Ùh ÒÕHWXÌyj©ŒñÑW¶þL,’=4g,îÕ âû8Ïþac…Sa˰ÈÖR¶Ž2³(Ûäã³möÃè be²³í3 ÅK°rɉ“JÀ¡Buz¤=¼'ê9ɹ>vß&¤©äècÇÞ{¬^–ᔟT¸ÛØÚÚg)¿¾¥z¢(?6ñŽsåÏÌX!?]½ÏÊBþlˆ¿kÝ Ü¿mÒ™ÊxEÍ7«ó’ øsBUÕœŽoÑÏrÀÈAC,† óy’Ã$‡II” K†J9ò ´©-¾GÝç<ņ<ÑoL¾$:ëòIGÄµÑ H%D,Ê%öƒÌHòÄòêŽåu§–Ï>Å„“…F› ß;'aR0)è4L†Y0 . Àn|bˆ•ÕÌcùKˆâ•›ÞâH¡¤¸C5IÐâÇ)”$¥lÝ(mÄB˜v‡yî¡9SéËÛ¬;ÍÃØô3BPì3mn’ÅÿZ^!Ò)¬x­¤ùi먆©÷zOÚ¥Y¡ß§Ò‚·(·d¯ÜX3³?5±eâ„ü˜áŽ3ûÏŒˆˆï•cÚ{[Z¾SÅ>ܵîN÷¿pn¸Oά[vØDÃèÇ1€²¼À~4ö娟xpqÁ6OÆÖÒË«A«˜*‚óEʱäañ-;JHzë XSg.=Û5Q‡«—Hm¨TtÕÕBÆ@ÌE”kšH4 Å$™“Cq_§ÐYš`ù¢Í@p¹ãõ€Sƒƒ;7Ø—W»ý[Šþ´Æ7oz«öÙ÷Xûü]}ˆ×/Ñ­|‘®&Ûš&­Ýƒ¦øÁvîÿ^^žÍ—cª¿Ô`àîf@po;¾G_ ŠŒž6Ø ØAp3Ë×Ávû›ÁDŒþ8OŒ€{eD|ß~¶ïNcLx?3"¾ù×r<|¿@þûŒòÓwT`zY$ä/TÍ4IñÓŽÕd´àÄ¢Z/ CÐBAÚ¦ ° #"-ìóÉi"#„0Dšâª‰¢ÒìÄ=·Qɧœ»üAÉW¥ÅMYqKCßšš¡Ë“ÃYm ‘NÌšD‡Pa04‘G({‚Jž OPIÔÔ9L4+v\ CäYäíeÇ$S‰ýSl˜ OzÉQÆãW}gã5ßu”p”x«lRÆ))%”")©b¹•<ÔËQ M¥Ï 'w„9“ 襆$Vß{à4ãåÜë¨ÆžÈ»¸b"÷Y$¼p±ïçöüâŘ4¸Ÿ?¶ÏH!?-pǸ…üxàŽ ê÷(¦]ÈϸW†ÝŸ7-ø£á¢¾Ë9Åûdïökª[{åQÕÞ‚Àog l°]&CÁNá‚üéú,Y¸zØð|Iñz€]I0(ú‰W1MÝ(’I¿#u ÀL›%]X X”Èèè ÈöÌ…d&ÉP€`(îíóÔÌ>VÎ]dðŠ¢³·q÷Кñ ›´°uÜâ}DÜø«·ú>ÿÀfÆ-ÃóµÇÚgßgí3ì¹3ò‰ ëÃXîÚEÑ~¿·ÀRé áp²ÞûÍÍ ¾´'á"1ììøÌR@ÞKI&+F(zÊõWæ ¡ßKIJAJúqâ¨kâ“÷óˆ÷ʰ|Ÿ!*~ºøÅ„ü8áŽÑ÷k?4d¿|Qz†x5Ó{ä>ú’÷øiç·€³ã{ ÎI}òjŠ M¸‚<d›À0ÊO bÉP,Ý+G÷ òYJ&v2$Ê3È“\+hÐ×£® Išx=£ íj€¥‚ÐÌë³­³<"b(†$“äùÙKjvú*=}ŠSÝ‹0¾iDW4Wƒ1Í@ž|ÙwÈ/aÀ‘ÂÎÀ¶Ã µm Ø“çp?ÅÓˆzF‚†Q,Ã$'ê¼|õŸm÷ <ì,µþîŽfõ©öl ‡Q*_D©HÙ^æ§¥m+“ï kTOåÑA*’Rp!àÙ$žžðc`Ôp°}ïÀšeÙ™@ùu+ªxaZÇ0mií³ˆ_ßR=~ q”qý#GäÕL%å—/LhŸ1âÝÆp1Å;×C:–%!B‘€AÑÀæpØ €•a‡YV,ŸûR‡%én"‚¨ÁU€AÉ×vð^Ѩ„ïÁdYvI5¬E•íR–„¦6Æw}ÍÂa*À¶E'ªx<$CòŠäI“ð’ è>ÇÉ Û=îÏeM\6ÄŽtŽõHL–·³ÿ™79”]ªvÿÌ=¦xùÀü DìûÌ>+àh,“upÀl ú>¼k?Üë-ÊgêkK £1» >d±¶Gîg”øÓóŽ}^ ^‘|¥"yù%ø%Z¶s¾…»$I «Ü‹5ÄD¸ÃY,%I‰WlºýçW}o’Ë(Ã"ÀšeÙéBüºU˜n¸ÛØR™WCزû|”¿xÇèý™Ê˜b¯ffÂý®+ïpÿ毡‹¯Dü@MÕ3¶÷_óúø¾ e&LÕ –X0X1;ÌŠYvÀŠIùìð“í«_ÒØêvhŸ™ë\7æ¦%˜=CsÌEÄY““Ë©aà rk±®,hX’6Ìl\6 cD¬šµ6dl$Ã,$„Ô,afíÃLìd†Û]Õ¥–¨ÐYœ±Çaǧ½ró[‚LCoázçëô"c°Që…yNL(–Š…Âºó}Ÿ°}Žˆ%ˆ£Q 6TÌÃþ«NT§qrv¿ë µ-ÍHÇ‘p!C¼?sw±ó³‹H@{<ðÇûKEø%ò<è"ù>i¾G…"´í“Ñd4®/ÍS¹GmÉÔÛbMR’Tøù‹&¸…¤Ñ‹÷±fYÖž¼¶ïjÎξGaùNÀsç@ü„Àcã»)£ü ìmy×Uw¸Ói¶{%å§FÑ_Võ,€ýWx½:€îôì0+s:ßJ=©÷ô¯põ¬_ßóY7K&âHç™°9¢õÓ™† ÅE“Ѓƒ9A‚R³ˆ2AkäI Ɔ™f6€6ÆgÖ`Íd˜5³†‚!¡öðt¯î7ÛO4+vÀE•°Nø_ïuOßöo…ƒÙoÐñíý³uÇ¥B<Kyå²Th|rÍ¥!&7n-x*2͆iÅŸœð<<׎¸ƒ|cÿ‘#`†ëàU/CÂEÜzܘ‹ŸYLÌyòJA&Òš0Š6-=¢=2šŠXS—(ÔÀ5Äor›~uÝ·'ê œ5Æ wk–ó¦ñ3îQlX8ògR~úùŽñÄh;ù"ÅZ€Ó­þÂ2Ö+øÎa0Ž]y\:¬–.+ŽËuß^L,LŒŒfcˆMoîô4(©‡ÜÒñ!]*àx±4\@:‰úY¸f-1 ÏÙñé%B@¢Q~¥BÀw»š*T™òR‘…þcîQMÜŸ]Ütõ¿OÈ8WŒîQ¬YVe¥ë¶öIgîL†»³"~¢àŽqòÝÆŠÆ@ÈO8å—5¦ì´“ÇØâ®«ïpÿ³çÌ™Yµ‡Ðɼ IDAT4 ÀóLûdîysf €=ƒç«Q|õâ6V&—ë°º2iÕ7äW¸&•3¾)•²¥!O9n2WÕ1 ÷¨“˜)Gdš`7SÈ{ƒÅ!Oj¥RJ¤í P†Î1³PI"£iˆÉ0†fö™ Ãhh°aøl4ô‚=5MùÇ~ûgKÒ;lÂÏÜev·kÝSzJ#'ùûܺq=H‘pÞ2ßmTR~Ꭰá;& ñ3ïw½øN÷ÿþœp_ÞxZ¡À$!~yæy탗Ÿù­eémÇm—3iì=!­úlîyù,ÙQòJCž?¬“É„ëºÈºa^u`ôȬx'ˆ‘.•Ð_òo´W%¤TR(%¥P&ˆŸã཰4áK;ša8^JÀˆ¸—F”^òØOŸ~/€%éÌè@n€·Àû¿¯]^… eD4Ünå_Iõ(?ó6P#"ép\2.®ì— ÊRìÆP“ÒÛû6Õäþ£*½vÀ]Ý7XäÞt~n-œMm§ ’1â/¬ °È…ˆÄ$PÌU ^0“!aWVU@y©è‡ÎMüvg‘ýò‰O$è'îQØÿ}˜4Ê_*p·aO6«aâø>1óW-ÖW4¦-è'„ò”“*Ç£klbw{ycz’>…á`BéâÔN ß+‡Äv·a]ë/ÛYôçë’& †¿ÙRVp8ÿAHˆ×¡ÐIÔW,¤ÙH6ÒH­ú%ÕH¸). E­xŒ 9@&AF‚Á`.ºƒfÅ–MÏÞj‡ðõÌŽ^‘ÙrªÐhX¤T"1x|xéÄ· †E¼ïs84•Âa§Ý:Üa ç\AIrâàk­öäªH®ÎçK(äP†·¹ºzC14Jrj)‰˜‹ ‹Å®# €àACÕÁ¹ƒˆ8žAqH0L0Õ‚Ùhߨ¦öô:žÿC÷È7½ïà& ºñ™Û¥¢'®úîøÉ$%8nkï·ˆ_»¼jÂiÁ¡?sý†zƒy¢^v"çk·²|_ј'âg&ÜïzÉîÿÝÄ{´Û퇇ì#ËÓøY§v¦ÔÀ€W»8µëÌïžkF« ;U—|]ò}Okß9.µRœ-(,VBð/³<ÙŸéó|bfí•4Ki”d-e—”Rʺ4ÂKÐÀª“ˆá@‚ ˆ3•DþÌ¥ —ùÙñç 9ÿÅ~k—U±mBWêZnÚÒºq}ëÆõÍ˜Šžl#à.Xà8b7ŽêÁ¤Ly¾<9Íþž¬O`_Ž*mˆPpâsKñ…ô“óˆ!ÿv€þ/g"L0 NTax@„9ï„MDõ|#Ê“a¼ v<àÐ>¿'¶XOM“Õq„ÅúÚåU~)v)Ɔ•5Ù7¬¬yn÷¸ Ú˜H¾cr„ü䟇ßSUK=l˜·Ýú¯çò]/½Àý¿-ÜmL†gˆè:"ò¬GuéA¿”µˆÌD0 älsðÂÌ2WîÌ&tÇ`¦¾ZùC'“™\¾?áyZi¤T,ÙHÙ)•¦^‚QÚ^'º}6Qo."‰˜Iþäùw¸¬êYÃÒ°0%/è$Cô{³ÎÿÖ,Ë2xòò©í Õ`nª½:忉i¾H±ALÂh˜\ÐLLÂþ¨=CƆˆÁ9¶—³¯é³i¥é À`ÔëÆæ—ö~m¾RLLý_á~ª-vo{éï[¼3S°œ¬A¾ßvˆ Á`ŒaíRþ <ÿ¡ØQß(X~ísã?SPTò+ª¶M„â¾äÄ{eX¬oXY³aeMôå˜c‚ùncœB~²Å{˃ï”D‚ˆì…4Àeß—ÍâûØ©½†5ÿο˜°òî‰òs+Çöƒ-Ù-‰l•7àiO›’Ö±V‚¤y \”¨Ï.@D ¿=¸êš†SPIYÊ•¼’ÏR²V¬„‘’•R'…PÊ4ØjYxsØ= :$èuLW¥: %2½{ö¯?Á†8S¼3uer޳K©x:²æƒÁ}ö@…çF(‡Ù“Â) ÀçŽg³RÅC` í¬ixi¯ëPß aO ‚Kb@åÉ@YÈRµ,ˆ{EØ| a7ŒfÃÆªø7ð|©ðØ©ñ‡)+= „üŠªµ+ªŒ‡ò—(ÜGÐü¹Ý½ö‘+WÖlâ'f}õ\1†u×É(Žýׯ^íy %–D_˜·JI")(]Cƒ}¸ûÈÍ0Aº¶mØ;¶ômÿí7îºöN÷ÿæâÄû˜Ÿ6IÑœÙ2«j€Ýti°Tôýœ§¤£”kG>¡V–§U€e¯ M`!4A€“†Ói}üh!-ûݘd·ÚË{^¾äå<]ÒJI)¤TRI©””R*¥”˜ë[ ß9.zÍq‚@%‘ÉMW_ì§X½4 `ǤMû´mßÔ³8ZAíÈä`K“)c`|ÃFÃè GÀ3Cª ã*î󚌒HÜrŒâ#såeýs®í;ù¤‚Ë1Kùüûöˆ}sžH¦N² t ­Q?}Á6 ¶K¯;^ÿ“ñ÷œúÖ–ïâ/E¾ŸGª_¹²ÆnŒò“¢ß£‹ŸÐ:óïýË žçÙm2¾•êŸ<¾[ ‚©ä bÝô¾¹+aÍ|oß>?xàÆ‡>?Z{ÿ¯Ç w„B~ú(OlØ”<]òÙ÷o˜‚iVž³¬z­Oå ²k°l$ù¥"RÕlÀÒfA– %%…aÉZ±F*6š¥pO€çK†èåãÖ³©åydÄOº[šÓÍaϨOç“ ÷(@üÙü!Â6_‘×ÀLL `ºv \ueí/œ:V¬Ÿç0K†a–ÌÆË—Œ–¬´1RJÍZ%ÀP‡•’AÂO˜ÇÌô´Øsx¨¹1  yQz”ˆzMv$ÓÄû1˜HS§Œ†1Ì ÆwõžÈ&1 b ƒh®·=ƒAÄqÝ íƒ~9G­íc[„K‘ âÔ7–ôÝq ÷µmµ?]úïA"Å= "âêÙÔßaý™‘^}÷qŠ÷ém ¶­­ß ùuË«FøKîôÙ­r¿rUͦ O:ßmŒFÈîýŸ×¹®ë8ŽëºO=òæˆ÷Ö.ð|ö}Ï#àóóV !¬Ÿ¬ƒûdíR[àªyÂæ¿) r¯»§Öuß à†×>8â­Ç`(•]§ºSÁ°_ÒÚÓ~I ’ÖX·™3LÁì°²=jökþ†~üPÜÂǽÚô “HÁX-¯Kù’ÑB)i”b£]p‰•1ÚHE ”±s¨œ×±çð€æEéæEiàBþ²%;÷NÖá¼¥oÙw«÷>àî½Ñ7XŒT@vÍV¶“M-²Ç+Ì-R1X¸‡D†W:3û® AÐ>¸Æ'€I²plCÊpÅœâR1¸œÂé‰4÷q_3‡z]TæÕTˆúK8¬_·¼ê¢ÉÅèQ7ïêµ|¿(!?E|Ǥ¥Ö|óŸnt]Çqûå‡RUFÓ?{¹lUâNSçûVtRЂÜn ˜d•möWF¿m.´/½ñS‰è±¿Yòé,ý­7à©GÞ\‰øñºýð4´\îÎ׏K{¾_òc1E Av!•DÉ´¶, ú3Í™-ÌØ3¸þ_YŸÉd²Ùl&“ùïïù s âK¹’1Zjm”„RÆøÊ(0h¿ÇÒ±W{ Y¾ŸGȯZ’a`×$À+´¤ &N,Èp°ÞJ¹‹§aFºCÑÚs8ù6x™ ™'û/î€OñM³õ²2{³˸ò³n:vË¡C×ì­|)ej94ylr)Èâ^pí<ê=a÷¢,Þ™Ä%-Þ+ã…öþuË«F#Ì/Qñ~Qa±~QB~êønã\B~œÐÿHõB)H)hÍF³r˜T þ(¢”ËUô@ dû™â=ÐMáOR @k<þÐJ¥ÒëÞúØXA9¦¸åreò;Q˜ƒ› M[×cabO€fà‰.kÎl=ó•¿öƒËfÚ¯J.óržÑ> ÁH£”Úh¥t ™â}ßßßß×××Û;Ÿª|‹õó ù)îç®;–`háÃÊ+“¿4N<ÔËáo˜­³ßXßÞ9›5{Eˆ ›ßjáP˜îjOªö!¯H`2 êeëËþ{%îkPÏqûö#ÞgÜm¼ÐÞoÙ½nEÕ¹ð}‰Â}l%ä§a²\Ûá¡Z~Ìpo|è½Niˆ‡z}ÍÚ°ÖК…¬ºÃ¤¥â’ƒÄ»ýã‰6¢˨›®rB @·ýõAýä¾%|Eö)ÅZ“RÁßϸñÛ_¿å©‚˜I*]òuɳG@ÙõU]o ;]vY•‡5œ„.s2/Ü—ÚòùË[o—Dù¡¡»×$?ÔìU¾ÅQÕqªª—¡K¥R©X*‹{ÄÑßö>ÿ£ö_<ðÔ£ÑÓ¾ñ•õßøÊÈæ9Ö›O?«š2`ì:0éâÀÖÛ¾óÖ¾e¾]×¾?Þ¯í‚iå\[ëKÌÄN’ÃûÁ#¶w'ˆI om·ö¡}ZÓ©}ÄŸ«/Ì“s¼š„mcö .æ¾/º‘=:|ÍAF0ªo Ëôwq_غiAN%P·0\#Īaš&óMR¼ÐÖoÙ½nE åÿÄxÒÛ7ïê´üU«jÎóÌé˜  ‚æÍ‹2cøñ%?ü@ÓßÿåcÏ ÐGæ]a4û¾†E¼ÖüV/ã8æÁÚ ¯{+-x ¬h*C"C•Ÿ€Û?„ݳØþ¸”l49Š?¢2ŸHU:ŒþäG/º¼è¬Ñ~h(Òò+'…òËÒÛ2Ig(vµ]_;HW¦â¸ÀÖÖˆ°?½Úûë/,?ùß„d’L‚AJÑ0šî\ØõŽú}W¯|ÝÊù7ùD¾“T .‹¥R©r7þä ú±5îç^Ô ˆ” +öܾa:ð¦«ßjŸ°çÐÐ-¿²)ÃÀ®ƒSwÏߌÅ`¬aÚÖúáÃ`»É IÑ"žmÃ{ÁBBIqbIï£~g=û>|ŸI€­G/Ê7õ˜Rí&x%xEØQ| ›þh˜Lï)Ós½'OÇâ¾~Qù•ì.¾ê¹·ŽáƒÏ4ñ^‘<øKT¼?"ñ~ÄOµ?Sñ¶+tÉíueóOþÜ0³˜Ùa˜fÎÁò깚og{~4gà5G3e¸*šŠ”Û­D‚„Û>w|j±ã^…RÌlèah­ñ©ì<­ùîSí_ûŸëLH1T4qü Î@{>Â3‘mKH°:É"4ßíÊêŽc;áú…¯KááàÁd=J u)€Œ!£Ùh2†æ½G¹fqŒŸq^u2qüæ[‚ðØ“Õ55é·ßæÿÖ½ýï|#Ø@ ‚íu.X&É/9ù‡B!9=›zè ÷–¦Ì´Lݶ½ 6¥»ÜXL…“ ÃÙ)€qO€=G"h¶ã•pha·$¤vÎò’Ô9¿ƒ5×î›U\8$˜U$¸ÌsŽN¬ö:À>ÄWìj|fÉá=«Ž4·.Â_Øà×—˜Ð}Ü^{Q}#1q}:‰ïÔ¶x_r©ÖmÔÌd¸Û¡â_hë¿Dá>!µ©U¨â7a×L›~·Ñ¼(SòŒ…û…üeÞµêÑ»‘B}ùèVAôá…ë‰,èÉhÖ¾öYÖ†‡‡#œ‡Î2°£2ƒGáÐÑ —¾¼mzGñû†«µfí³öÙ^%|¶féçZðõû_4!¤ýÐHój¢"“rh;<äæ;‰Àŵ®.l‡ê6·o~þàóîÿÇx$0Ïé›ÅÝUÞ)’f(=KIRŒ T¼¥¼Ñ ÒDº÷p0lÏ*úSß{† Øk°!%H "É$ IŽ@/$ç:éÚ®×[!sDÌ™ÎßR&>•}&vS!C)¡)EìûÔVÝ{h^fζ×ZXkÆFQŸ]¼‹ Þs 2Â÷ÈÊö3na}Lçî8 hX쳯ÍÏ_4Í“ù&/"š_Þ|&E33& îQl:·Ÿf¾ÛØshpÏ¡AÍ‹2ç¢üêGïJ×…ô‚HI„Ý AD¤5k ȬÖÈ~cgRfã²>*“»Œîðb8ض¿éóûAxðã‹Gô@M—ã˜;k,Óƒ›ÏZ3’H€¤ÑË¿{ÇDH¼OâW4¦ÈùLÉ–­ý.€Âñ#a‰½õÜ™¥ÜÑ9ÅYœÂÞøc¡—d¡ØPèà žb! )IÊ!©H{¤=’ŠÚkz@¬™gªµ'€Cõ̘s´6×0TÔHudòe¬Û£o]øÈŸ‰|ükO,4>m_x¢Ls2¶Âxä ¦ã ÿ¯þ6oï[ñ›kp±sæ‹÷ʰŽ|2.S Eá¥ø娴«wÓéZÞÆtòݢܒ½r㬈7@®é"}ùðó‚ÄG­D!ÜZi ­a û†µÏ¯;Yï¸øÍê>D:5Jžá0'"pièïÛ àûw/ ízzpn§RüÓà®Yk&Î4ŸŸ»R}êdëÊß¹êûsæ*ôD8:û¢ì¼³NôåÅ)î ˆ ƒ‹½­~þX®ëglÈhxEYÊ 7M$Y¸\ƒ'î/jZ ÙqâM€ø+‡ŸBo=|œBñÎvy|· Zûƒw¯{è=r”ÊB~©5+¥AèÏù‘‘½¯æ¶ s&[°$ÎïDZ>:J@©¿•‹gØqj˜5±![”ï¦í%2¥îųUcÓ< ø‰÷`#XemÿvÀ÷P¹³!!˜“àà @0 ‚“uÌŒ|7±Þ.o½@k¸¾:õBÞ½üS‚p,žOUY²ÓfÝ·Y÷n6½Ûâ½ÛÜžnïïb¼¨¿ÚÀØ“¤t¤¾‡ õÕuf†˜)Ó› ““ʲÝÞr½eåŽÐŸá–âB£éÙªSŽ+”²Ë­#(HûïÕìƒ='±ž°â3<žßÓ÷üž>—7WÏ|ÊO*ÜmŒò3Ÿg ù?þ€åû=÷¹[¿°k«£èSkÖ„ $DëmA¦ö¡ Œ†Öìv\8NôWC@e.³5VñúûÚ xèîea/Erã8Fû6-á•"¬WžZFîùúŸ¸çÀ7aŒ¡b¿ÕÔpRÙÄ £dà«äˆìvÃZ4l H ÒlÀ:xl×QIŽ Q÷rŽ!‡ –ñWOíÿ·¯nøÆWÖä¿/·”_Ù”Y9%”·þ÷íÙ#ýã™üSΩß{½¿/õ† ƒƒ5êAòX:\eÂÞ‚ ­xgòaGW1Qmz¦_¡ÜËþŒ=Ý¢Ð% ®+\W8® äêD5@Y¼¡²éï,Ù+7®˜ñˆŸšˆ„üôðý¬â½2Fyà ·0w¯¼üã-—ƒ‘Ìë„Á¢«ˆT<9nµÖœƒö ŒÆu»ç8oºªÃ2.–,;3vqõæOµðÃO,w~½ö¤Rü‡ûf›Š5U68Ö¿°`• |âØîL ¥k1Ѓ‰RñmÝE ¾yQšÂŸ>éÊìæËªžµOx¦ç•ö’Å=0ÑàA"|ñá/ý˶“A1[XÉvTt ä-†r‹êÕp6K’…Wˆwc „Boн˜È_ÓñûÀŸÑ˜ø"ðg\ëϹ#ŒëÌô‰'ìí®þÉ·ÔÖÖ>üï7EB~jo#™A"ïxî“%_-j_$ê^$ë’«?¹NÖÅIÆm%"IBÒþl¯”ÔRªíN䨠f(iÙmõ»§QÞšû•”/—Ç2x"qô‰Ô±™ã®+”ƒJ‹†˜lλÿjp·1ó…üˆ÷ʰB~ò#››2öŒ"yÏ¡A{&‚þÇÁ-‚è#W¢á~$²ÀŒd Š*ƒö†Y2†Ù—`àšó6]~ló5'^üì< ê…`»2ƒ’±ùêãpóŽy•¶ ÊA¶;wdúÛ..äÑã•ê3› m†ÕV‘>ú†‹6§vt ·jEX}º½ï»ñËÁ«^ß®‡vohd^—J'sCy%ñÀöSRÐÕ R{: +çÄm¯s»Já$ØËCò]ª)5L™xßIMl˜5±¦U][vׯµ3/Ø re5ðg$;’l^f¬Ê*wú¸ß³ñ¸z _¸n™¬öÝþ«ÚZûÈî ¿{’óâ-.¯zêV-Å>$`¹ÈhçŠ÷Û'læNV{Õ3BÛ$Ñ/KGMÎhÛ´.ì…1Ø…ê ÊÉ‘a²$lþL¹`*㻑kì¯çc©c|KnžÖÆ0ÇLÜPù×ìÍïÝ;zX_Šp?lÝÓgá~ysõˆÀôÆÃ=Š™èÏTÆžCƒŸþäKî;økZÏÌv¦Âð@E¹ 8ž¢D:òT!®…€6dK[}«ž_ o~é1«Ž"ýþÊ϶øÉÝÍØrÍQGñ«FÂý,&L¹|5%«ðåU«f¹î_lßi˜ÍDó=< §Õþœ'* ý”uzìèíÝWîì?­ëúck6õ7J/lú\:üÓËç¾{õKœû~±ßö[7„#蜇™KFiðÀŸ1d4 ¡ù"Z\ÕDÄ$™‚$v$9ñª²Ufá~ßF; ÷¾¬é¾ë–A*–¿ìÕß«­­Ýþì{ínïžB¯æ™Jfi]¦JHÚ/‡v6¿ß⸥íL0›‰X  !p*–¥Q Bƒ—˜UL ¨ãà¾,Þù<þÌÕaéâe½ ®?µè-nÓír‘•¤Ž?š=‘Ô)ý æ ÝÉ7¿w/Bjÿg³Š÷ʘùB~*cªù>zñnã›ÿt£,ô1óG¯0Ì$ÈXÆÛòì¨2L@ÞËŒ¿ÿYÏß>Òmßôc7Ì‹,x,+ø>ùçkkk‡»¿íù–ò«š2«&“ò?¿ê»_ùUó^;\·êˆ›_¹ï«†]/=êä¹ù“±a;t©.—zEw“ÿ,v„"Í–DYW*’ð}§¢JÖP­ ðs4XƒCwž;²]h}§»è}Ùå&\/¾y IDAT¯ôÜ#dŸGÈ_Šp¿Øˆ„üÍÕW´Lå§î˜ùþŒã8_îØhvfÏöòcHA†QB4`¨ò‹„¼môNBþf­¹ù׋”âí/;ÈŒWÞ×ÂÏ>¹Ââ~×Ë÷)Å/þM£õdØà¬XD‰4ÅÒÙMا,²eî™·Ò‚ýc'vNÞÁ9—?S¼Ì­<×ÔÖêü&ëø&“©d"Å:0OT,l'ItÓ²ìmkª6?Ú2øè¶A6 RqÛ×#—&òg*Ä;±†õÜm>¥=JŠý¥¯nì =üåõ ¾~NeòŒpX8,Kœ*ëêjëêjG|ŠHÈO*âGD2®ˆhPzƒªIòœ¬J‚0{‘°“ûPVîEâ¾gV®Žðg¬~g^ØèØ$w9uÈXÄß?Ðàm½ËÏ\Pݾw`G(äÿÓØ5£5òÓ‡øiŒ©ã{KS†.R¼ÿ×*¥>2w-Q°\É̾o<_†„b­c9¸cvp“$¨ìÅ 6ÐÚ`É/—(eþàÿÝàñ»WØ…­í×µ9޹ò©EÜgb]ÅSe²G-cCñ»“†ùž¹-=»@Ó·Þ>Á‡²"Îò£”QîÞÐRÉTUÃ;H"Ì‚…‚p`˃ºuMö×dH0?±cH‚ T‰|^:¦T#ɬ?c»Ø•UÐéæŒd7 íï(ÙÞwÝ\w×õõ•É3ÌxwX8|cá iñOBxyœ%÷t×ÁAÛƒlåâÌÊÅ“Kùªy¯½lÿW[ýþC*g¡¼P'Mj¡II!(èCj¯[Ê-e(Ì \šrg±‘É3¶/(óÑœ’øäóCû¼­wEÔHéÌ8—¿ÅûØàncëž¾­­}®h™j!?½âSÆw[“Òz‘ÎŒã8qSPФ´ã'8Èð}Í ´Ï¢³ÈÆd&F,†x‘ÁBD̰*^:L"JACë{Å—?±8¨MÅH¬KKöÔédÌ!Ê Dâá ó½ -Ÿèhµ=È&/ÊBþ"Õkvp³ÝH%Ó©djMñ>!›¢®˜Ã`i}\:| ·h©ó†+Ò¯Y—&‚”¶—›éljü°Š¿Ú¡Ð…//®jŒhBं„™ËæÇ¾üöÙï»±6\ëŽàN‘óù3›Ò¯Â2vžOµ™œlÄ7;Ãü4wv“w]©‚¥€”È ›DM–¦e—æ´äwQ.n ÒD‰NêšS"¨S}°v€·ô-e2[oûÎyv²Ò‘·Xÿ¯÷(,⬟*ÄO;Ü1½ý#/J9Ê8J!hMÌá|Kx¾–‚)üaŠ%É·‰ÙÁ²i˜ö(j`zEFcPEÞ ³öñg/3žÀן¦ŒöW·* ùGËl¦ ©áì:"7ò2`XÀþÉp$Û+6ú»îÈqE“ñk—W(Ž®çá¿JöÛz°Å nŠãgmî†æÒs†É&2BBk‚ÌO….BÆøæÕ)Œué’ö0$…l@5Õ…S‚®$ËæŒ“b ß»¯­f&&”O«Yeƒëñ[«nðûJÿÏ?×îeO˜¬®ñ'ßöØ,ßXË~¡XÒqWQ~ÈDÐxÙ®C£Bž—gËpÐþ”™lrReߌ°Å؃îQ"¼ËYÄ„ÅK÷?\sؾ{ËM[^õ®nß;`±¾vyÕ” °a¿¾¥Ú"~Kë J œ¤˜ ý>6ñÀq”ã(©` Ù­g¥Y¿žfÃ(案Ê׸\¾YyäºìºAãIAôÆ¿ogÂOßs¹ãð©7¶kªšµfÓ w7NNÜN^#F°…ý¿-ÜagÀ™¥è¾†å®ëÚa “ÍM™¨×h„|"¿Ã±Dîn;ÖtèÄ©½ Û» ûº û{‹û B0‚të–ƒ‹ €`Î(ÛôGáP|¨$”ök$IÞ3w‘nj=5"mƵpýjWdj W ±]¼eP,–NHyá áÿUÆîë«&AÈG“`Ùx>qëéxñ_æsaO:«Ö­ôzœ¶Ê "t9?´«4Ñâ*ø‡5Gì»av‡ò‰¸´k|8&3&D¼WF„õIõjf‚xÇè÷1Ãý©Gßå8·ª›Œf &‚iiˆÂ /[A„aá&À _sYŸ>ŸÉqày$ÂZsöû̓ïÚ‘ê¿Ö¬ $••» Ü›‡‰²l/«øB!Í[&â»d³¡¥ ×uÇu/&ì GsSæ‚ÙJ^¶%1¼wÑœU >± LMh?ŒeÇâK÷!î/‡ëpoiQCÒ†m?R<ÜíÏ­Uk&` \Žå¼RJé!(ï\ÁwiÕ#ýbgNÚ&•d ÓÂ7¡?sšxwx[Í5~WþcvÏSôÿåøO/x(,âW-ÎXÄOŠg@ëÆõ­jàÖÞ¦À‰!¼C5~Ó;ô£ÄÑW{sí1 ¼ÄP$@×Á²áâ‡Cˆ‡¯.XqÝ–qŽT°vyÕpAGÛÛ.…¡Õw·ã·N´Ÿ!pÇLögGù¥õrìÌ%"£™ˆIÀ ‘ÖB6`;wÒ×&°’äûˆ·£îC߯qðÚ/íaÆÃÑÌÌžã»ZÍÉ ëQ• „"ÝîL¥=R´pÇg (IR’°÷SÀ÷4ßspÐ>ÒÜ”9âG¹ZÙØ8w äé¾›å½mò2àŠ¥ƒ»Œ¡ƒ=F¾13ˆÉ”XÆYk01‘!`ÛÑüšùI€¤bÒi (XÌ@?ß9$‰4ã—»r×­JÙAZ•pÚîF(Þ9ëJ¿c蓲ò~ÊY%ñÏκÐ:"v´|_µ$3!ã¹#ñޏ¾6ª9àCu+ŠEL8ì33~€=Yø¡šr/ ³Î÷^ï’MÿG³¯ÿñ¿yìá'Æ»óëk—WY+ï’ ü$ÅÖÖ>Ë÷õ-ÕÿY½úÀ&qN£­:¹Ø ò§y³”F)Ö>2Фd¥øöB† k æàÞhHÚ^N²ÆæÉÄâ$dpAëûAU8¡—A¢ xÍßµøñ_63ÈS¢ ðÇ; H½E9aë>P䊓íƒ÷V¾SqØJucìB¾š]JD|—’¾XÌ•J¥›oýÑ„à Î#Õ#£æÌïövmM÷Ϊ^\ … |Ľ{fMm¬‰Aì“1°­€í½J03=¤`|¬Y˜xþÈ0ÖÎKY;¬˜¤þ¹1£iÎÖá e¼d•`=Þ: Xܸ*ý‹Ý9n¸,ÍD¥Œ/´OÆÆ$Y¹F9¬\Ó6o€R1ÎFVÌÒ°2ï`³àuåQZµ$8ã¡|%ÜÏ•¬CD ý›Þâù&öWÿ ÿݧ^õW_xLJ–‚…€×r}oýxÞh^öLšÛG0ƒ?Iâý̈V\'„ò3G¼cRù>6¸£‚ï–ìF#-II#%¿¡¿š k6lÛz°á¨TÕf¼8ñÀáE䮄¾ ¿æìð“-Ó6ïlH13ˆœ?Û õµULTu€¨ë°öjq§Yí&}%ÖE÷¯Èß'ß7¼öÁ 8¬g‹ó[1ö»tºK6Ô¿À‚Xáj3¢4+éÞ½±eD¼¨o?@Ú'㈅àÞ‚7++UœØz¸`Íü¤a¼p,A,ÌšÙ™¾Eq“9¡nDpgÂí0tSKDO¶¸aU–RŽ"¾³rY¹FºÜ6û2feX2+Ë÷ž²6ý“ñ8Ö~7€ùË??ÊeOçôùã‚|¿ùÚ®Ç3kÝ÷oÿnõÞs=ÇBüòïßN,@œnÐŽ#\GüìÊr2Ì-Ï¿Õÿó¯ôÄÓ‘ÂçÛ^urßµ†ÖôýŸÍ=Ï>ŸGªÏXÊOÜmLâgÜ13ùþøCoð™ªÚ°4•Àø÷d·’l¡ÿºã³X³60š ÃhÖ®‘a£P±ÀZ±DŽø~Ë÷xä£ËÛdr €fH¼ç/ ù8¦eÐ#°J ùÓ=à ¥z$Þéëé^ß'{»öU=Rg”1ʪà¨mº¥|1¿À\9X"a¹}Xƒƒ ¸÷@¢ „ÆžC 2>M}Ã^Á«ÎH’F8¨K;[æZ» i4^8™‡`!1oe•H˜ô1Žõ±P¬âÌ„'ö÷ÃÐM+ªìµÑÆÖ½¼%Ë„Ò0>i_Ÿ„dåòáÅK¬T·|_?°‰ Lyî1ã×Õ°íhÖ\ý£?b—…Bþb)~¸¿þÆ_“ÖÝÔõ¡`°ù^õþ³¿"©–ÿ㺧osá8â±õe¾¿â¥~þÛúóïÒ[^s\kÒš´¡?9û¬Ï9¿ ž°m&M1b¾ÛXßRmÿö·ìãûþWá{Ëâ €Ö1­hýä;¯ðÙêÙAf¶ìë»”b¥X ~ÅþcU¼fc˜pzºº‹pbfúòêûÛ:½âÇÿ,ã$8–Ön’c)“Hq"Í©*Ne93¿˜É¢ó„ò}Òš‚û`Cø>ùš´½×Tùeô„ŠçŸö„ë^=Yp¿¨åëJÄo€zô}ƒlBÊ$À•¸÷@j!ˆt“öàÞ‚ßWðk3B8̄݅”#×ÍO1` 1cgïàü– ™~#Á±ôŸnZZmáÎÀ“ính®ò d¼@¿w®®gV†Å,×tï²)˜ÖlÈ„öÁ/n^öê‹7:ÄŸîo|Å)mÈ×ôÓ_Ö¿òºìYsãïêÎúäÈÃ9÷z)ßø’n€ž<Ç+œñî'Ãwoñ­QúìkWT3ñÓwëWV ๋GüLƒ;&‰ïã;€ù¶WÔšþùiòù¦—tÛ¬ª§~?*FW¿üšn€~ñÌÙð×w )¾ùäÈïÞr}‡Öàñߪÿ¢Q-â1Ý”ŸF¸G±ae`׌žò3î˜ù36OüÞº&Ïc[5ئK¿´m36¯éPÀ3—ŸPНÜ46€`0Á÷ ÛtŒˆpóWÛ:<þÌÕÞ^·Èw4û¨yîþYkâ¾1DE¥ã~îÖmÉÏ^^ ;k"œÀŠÀ“¡påo•ûŠØ§Ñ™Ï€À=ÓòË:¶ÜÓÖƒƒ-M™drɼ<Õòlua† éí ~Ž>Myž•úÇcõ³æwtAq]Bu¼Î!¿AÈú¤ÊÆÄ§í‰·¯T‚@¾ÁOI7Ûîä#&gò߸°&¨?³©¥"˜Bw¢1Î,Öc­8rÌ"[Ÿ¦ÓƒŽ•¦‚øöAëØ öñjçþÁË–dFCùs=çí¯ì‘p°ñwu7^Ó àú«{~ùìȆ9ç‰^ÜÍç<+ðÛ®ë$!9K™ÒOÙðÊ?èýˆmmýñkWTM#âgBrþs»û,â7¬¬ƒŸ91ñúÝÖ…ï«xÿ·¸À_×/‹Ôz0Ùu¥­ þÙyÕ ¥X £¯ùÍBc˜e—ƃ^ñ¿Ûüé{¯Í,dЬwíò |M¾†¯ÉûÖš£7= eãj"t–õåfg!øiÄùœè[ŸíëðÚ·ü_ò¾;PŽ«:ÿ;÷ÞÙÝ×ö5uY²­®÷$K²$w¹Èr#à`Æ & †!„Ð 8`JH@B „_¡˜^6¶%¹J¶,KV¯–‹ÊÓëûÚîÎÜs~Ü™ÙyEÒk’åp¼^ÍîΖ™·ûÍ7ß9ç;¿Ýn£n,ð Òñ³æŽ<9&˜NhŠD'ÇOªú2ÓŽ· [jéõIñ¤*Oy²¥¥Àâ •jó‹Pè›\ EUM^“ôš;¹\)Ñí^˜!t|Ž©ºˆ>ï@W(¼0Áw¿;9Zްøà#ŠVþáê9Ï5~otûá”Z͉Àý­· ,¬¥ïýòdéÍ«/E°Gž>5Ê_}q+€uƒ»¼ñ’#Úh¥´2ÚÚÀþ}O°ÒuW4øÍc1BòžŒ æU»¯ðÖ3ŽòÎÎ÷Ù³f4‡Cy9•"v’wŒ{ÿêÁ@±è‹¾ï‹p`])dXˆî<Ât4ØZ)Ò¤.xfzãÆRw]óâþë_T®œÝe>}„Hõ†²ó¼ {?ò-°s×&0ôwœ“õ¦žl\ºSÒÐ&Ÿ׸LYˆHHà‹ï–}qwú¾A´¯ßô}ßAûåwlP57Ÿ7iz8IȧԱк֟:Þõ‚=6¹ÚyüÖg<ïñá”z–’Öy©ZªÊh"{<Ó%éàÜêtà[ j‹"h]˜k™ßÛ<·†»ûfìê *(--ª @îfPT¶@Ö'HÜ×¢”vv’¬ ìñç|~ÍlmdÙîÛF·vìrØsùáÇÿülÚÉÁ@LÞW­h¿|ùÉ!Àù  °òúe/¼aåa‚"RÄÓ„™­µCö_yÀó7½ Ïüùyî¤ÿº9ÁoÞ3·ˆ SÁ¤A}>$j… ¦@–”@„ºÚ´}ßúŸ–)#q"rƒ5E$\Â!#ÎÎ㜉‡ÆÜÇNÞ]Dãª/Ÿt.ÊzŸx±ù”:*Å©P ¡É5_!X¥˜”UʶÏJicAÜ×)D¶«³É#â¶îeÿpÝy'ž;ÊpZ €ÆYUIª~"òþ–› °CÇú§ê¬ZÑಠ۟Ø\;ÔZ²je›=š ù¿Ó¸_ Ò¡ªmpX„ÅÚ!\†dà±a”á˜û’yÕKæelÝûjò#ßp¿|aƒøW—\3žø>vòàÞÉ ïnÚýѦÝ÷NmÜtÓÿ ûtY(Ð7lœ àÀ•û‰pä–ç‘·^¬ýÓ¹#S!Jƒúë‰D5y0H ªøúÒk!m-[+ªá¿wKæ_–‚ <§(PP,™ÏPäqyÑs(é„çƒP~ñ},à^ŽoJÙèÚ*4·JY¥/k«ýö"”*gÀ¦PŒá5ÊS“XRõ5^{§mëðëkK§€Å"gÒP°Ývfo­kèÕʆ)€ ›«„ #̘ƒˆH4%;4–ˆÕ¹HЉWÁg9V´B„Ï^NîQ³ÃXb‹HÝÆòˆ‘ôÑMµ.»°ýÒe"ذ¥%¼by»0=öLî×ÏÝ"»s»s¡ˆ§BŽ°ë·¸qÉž_oŸ|‘±“÷dlÝÛéð}ɼì醸³¼'ãDŠüÙLÞ1Žø¾pV€]cîÿvªÂIÖ!ˆ–Ý̧Ê¿ê0gý^¾~€Â¤>"ô,¨2+PK˜µ£ê¼ƒ`fL€éݼ ­ªî‹{Þû\÷Ÿl­øê’Hh“ü8J’@Πâ‡ò›¬ÊôwWÏühËo~ùâ·½gãwÑiM@u¥«ˆl¦ØCŸt¶½'Ó[p3Wì3 H ó:’BÇÄ–T†S ¶M¢ó›§´ÕvrŸ€²º €”¹ã\Ì÷¯ËûZÒ7 %¦)1õS®'º!úÿã£Ç]Ö½7L…pä!!"¸äå[6œóã1îÖÍΞÈmñM7³–îûÕwó÷îlà ï®ü ܹhÈç>±¹öÒeQì² ÛXðÄæÜ×ÌÚ DÈî&–E“Ý!äÐ]ÝXÿA¶Âk¿~%ëKæe—ÌËhËéQäÏrpw1˜ÈŸåàŽ³MŸ™÷ý?p§ ´:¡&“©ÐŸÑEÇ9÷Ï»ö‹{}àÇ÷¥õ{‘ôÿ›_Q-¤Ï@HÕäÅ:ˆFÖC±WÐÕ²Q?$d⮹I¼ àPÞ{ ˆ ‚~rÙ’JθPøQw¹èèèpcf½«ƒtG#ÄvãQfÕµ*Ò,èM§!’ê ÜS‰6R›Ju…Ž ÔwïÀD(âÐäA°¯Ð<ÿÀDÍÒuÞD Mº9(õÉútõ„:*îH/)41Ÿþ¬$‚~´$^31ñX|< ¡ÁxäáûІZùÂ*ÌòN/0Ï4ú…Ð?‡"tíÂí Áþ©—.M®ÿä³5.^Ò`ãÖW–8pKi*M‚ºC)žìt-b-_=o˺½KãW/}f@lÝ›sD~é¼êÓñ¯–ˆ‰üŠ†Ú³ßky|ò« ³ªh<È»ëAýÔôMôÑ#»B×EŠ”RT‘¥ò**¯„ó狪ì)]_ûŽtl·Ò©?zï¿óáÇÉYgg¿\!*´HدGu¨‰eçû®;,)±5HP#én <9ï0)Ñ ™® "¼äwµ¾ˆ…µ‚œõ (JÿRmÂrL¢2 ÖkC(ìÓý{»\¥­i¬Œ×w{þÏthû;u€$(¸B_ÜzWu¼n}ÍÏÇ´›±ý@À¢ÙYñÛä^·ú83ýüáR›([vEúemgóR”BpuÑ„àKÎ*S^·eIüħž«^¹¸óu«›Ž·¦6n3®F$ɸC‘F&‘€ÀÖ‘w'Ѱ”ðý‚¹Õ@®³û4–imÙÛéÀ}Éüê­{Æá(òªw+j»{ƒxyÓγT¢)ñ÷‘V†Žˆ û½ç,Ò¤>~dgE5*²‡¨Al}ÀCWþó>yïÜäCåß\”þ·E~{7fz*úüûæõ|}‘!hÂŽUÛ_¸ù9×¼¹ïš7çÁ²LûÖ P/¿qgØ Up'é|ª™ *¯ O24©ø„C%–5‘á1ýðÆØüôú·uttþ~ýá  ü|LÒ)(-« @6 w¬ÒšUèÐËJKªÇVe`ë$¨å=¹œs¦ R+0Ó›¨x º­è ×b ñ`‹Ü²Ã™®žZ·zfíšók®›SÄáù_l[¿«7ÊéÒ7íðÞëêâ×I\ @¦e0.YVD„Ý{raÑì¬e ÎXËq€…¦cŒñ<÷OLçITÐg×\ÔoÞº,ÄRúþÎÅÃê¡Äà`÷ã ‘¥](Ѱå•Ó/½ `¨Î§ñŒ-{;‘_2¿zÉü3Z@y¶Å¦íÙW4Ô:5ïl …þõ¿#}þ8’wܫꨪFA‡¦¼B妘Ó㇮øâ>û³¹%'.i1Tö•eWî_¸m»÷üMÛÚÞºõgêbºèÞ½þ?—8/°°‚° ? ?HÂ:•›å‰(«ByUB¢IÈ5=º[îºpÔ{fŒà ³³£³³£$ÅäC)ÆúÄD±ÚÚ°Ò¡ñº@tt“ÒTæ ÷ö¦kÕ¹ªÎWµÕ’VJºU"3½ `²=ì¡eQ°ÇoÙ”?²«xÔt^p,n$&O¯™_½fAµ›çí>s¼ŸÝ«ýËCmQÁJPoõ¶P÷qgR6ÖæàîbûÜö¹y³_ȤÌK/ÍN>ÄÌlà faÁT£Cˆ÷ŒñÂÏ ìçùÆU{âçnÚV-B능—sVû å=ªŒ$©Vp˜Z-I4O¾QÍL{Wpš$ø“÷¥c€øW/ysöxá,„øqE‹Çø‚;€lUÕ†pìÆ!õ×Ö¥?Ü;ý2ºŽ.î¥Ü>+Zÿ €dëeõ›ûžx2uÁcçÿ|qÏ{Ÿ Ji©KåˆYοo^Ðþj… IDATßÙ=¤?÷²,*²a½ƒ.õa…—ñÚQ#ü×5 ò*((ë»PgÞËQ»8ª®M|+-©*N•‹—‚¬;ØþäÞ¶£ÍùåeФXéû¾RÐ:kNè“"Eí¦Ÿ§Ê45š)ÚJ_h_ÜÚÚЙÀhðàþÎ÷æðŽëê¯h¨ˆIúWÖºïÂîoûÜ/[…Ð×I}mÔÓJ=Ídežs <#'»{­CO7ÎÔEîŽR‹:&$BSŒväÝó¼˜Î{ž-X?ϯ¹6a')¥Ó–ׯy1"‚Rˆtx®ܱ„#î$ "¯vºcëžN‡òKçWåÿo€»‹³–È÷û1Äü+¥ÕäÚ$×&]mÒÕ&KÀÒÝ!=ÒÓ)=9éÍIo—ôvIo·ôuI¾[ò=’ï‘|¯z¥Ð'Å>¹ø û˜eý;gûEñ âÅÅúb}±X+l…ïÚ6uvø;¸lÇB$´+!XÀéé,ó~2×÷Õîëö”$šþÌ=AçCÜ/Ë¢¢¦”bUMPj”øîæD¼wvvv¾Ýþ?zàž'~òÉçCz^a$IÕ,“©’T…ćص{;ÝvÕ”úT¯Nõ·Aé QÖÏgòÌô&P4ŸU<Ðh¦º]4e÷Ô\kX‘Òº ×Á÷Ú—Ú>Ôáþ×,¬º¦¡rIÇÛ¯®}ך:ëÃúøÄZ?ýëfçï\…ã;6|’¼ÇñÀcº{ƒ)SX<'ëP>Bö‹WM MÖZ{Úíy1“Í[›·7ßNÊ>Úœ`Yc€X€á²©ˆ5(ŠØC¨º‡ÆÔÌÌ&Ý/cϧ_Ÿ[FEä_¥à~ò8 ‰üÀÃðµšÆYU4®äÀ–[þÓ²X)]8^æðâ¾àÉuldÅ~ù¿îgÁºwÌ×L>=\ ͼ³ýÛÝÛ¥¾¸TÿÓ2Ä‚El@6pc›(®[wï¸øW³ƒ€¶_½×åÊ$ò‚ï/2ýq¿¼5t÷á]ô©éjTü}\ºÆþø½›¿¸fnÜxàÞÇ^øìc/ýã‡ÿùÉ#:bîQ- g²’®HŠ`²vw' «gU_3³6>JõšT¯@)¡l@7÷gê  .·á,j¨ÐG­‡ õ›ª4Ï-<ÒÔ @€Õsk®™_]Ö:{¡‹úrÔ×®þ䲉zÅ$—þäMûõQ‡ìI¬õ^:9¸»pE/Ûö‡ë,ž“M";GZ¼k‹P“Ñ¡8£§=£ØØì­¯? 6üyã-GÃSÂAÅ‘RGÀHV¾»©&ã]0¯¼Çþg2¶ìéÜù3ýÞg6†$ïÉHù3÷±NC—ëÒWoŒ%7'‰moøfÃî$ÀQÛò‡›¬yv•¶¿éªË^¼}Ÿ6b » ®úÚ䯉0ƒEH!&ïñÛ9™È2ˆá ?Fºœú½}©®&ìi’° F ¥ˆKü‡[nùÏQ„ÖüO×ç-£äï~Ùm!¾øø1O‘gÈh¼çÆ: ß9þú=®QkM4ëg@•Rª;êNûU}neU¦L«‚°zGñè¢ôT÷„b %õÏ”7/+ο¼VÄLz~à_Úï $"ãâhÔýÙåS øôÚ£Ü}ÿaŸåÞkgŠ398Íèæ\!¯½´õ¡'‡ï6¬÷w}Õîh¦&B4ɘ樱 z¿`uuo{óñ=[2G›Ò¤`D7EKõCw(b!¨8á£ôªÉx©rƒbÒÑ÷Ó·ì錅š-'-­y•’÷S‚{›v¶¯h¨]ÙP àéW´´æ„åØ'²ã8Á̬["ñ¯$o„qÍמ`Ý]çIô» EØó»Œ‘x;'þ÷âÒ =B8#»ùÎÝhÊw% ÛU· ³XÆŠµçoXõÂsWX´v}=¥Z òšÂ…€Ã\YG#eðãBÞ](#;Hî¹aZ|óóëš¡D{Кþc]»QxÛª}<º³wÏ‘‚Ì™’zhwn͂젋V½\,¥Yd¦n÷1°8=•DX©R*aQï”-¦iKYÓ’Þ)ŸM5-ÇÎMOy±BÐö[A œX ž@"$‚÷_>]„îY÷"€÷?ðBÀò÷×ÌÚ²`dƒ>âyw‘@¶íÏM›ޝ% ÷µb0©f%@&Óù€|ßòÖ¤lb·_-í³½ÎðÎùèQXI$5ᳬX.Έ©Œ1e8ùÊvÜ8ˆ°l~õ³'€øW)¸46ílwø¾²¡ö„ø“µÛ$3®Ãq\¯Ø}Û·æ~ï÷)â~5ïýð4­þúózôOÎ7¦„ì[®ÞèÙgþtaTEã”—­Eˆˆ(ŠÈ{T9“H“b\úع^òâ–UÏ_ðȹýˆ{x,HB¼Ä+¸C‹äÚ°u$ä}Á€6 RâªÚIIt~C=¢óú¯­mw]ð€$Y0-åypO§(!%¤E¹&#O´'^ZLJâÞ% é©; GÝqN3¤sÅÙg§ð ᮦIÏ*íÙ—zGf”OØÝ'‘âî=n¯uÕI¬w}äòó øðúƒþò¡w.ÍÎ>¸X»¡þê‹ÛbWwfvgz䮉ÁÍÙP-šëª×ºµßi-Ø À¦`,-îz6ÅZœ*G±=.š ëÞãŒi©â™™½2cʼ#GW&½Ú_ñŽJÇܗͯ^6¿À‰PþUÃ'ïq8X_ÙP»²±ÀÓ;Î4Ê_²¨îÔí”´šÓ î.ö½ù¿ù ËÑ_ŸA@)êés%BD€1¡nùä/Äœ}Þý \cPHØtBB‚Àwv¬0zˆ¼œIÃæK*<1ˆpåS3ZöÒ3—¾¸ì‰”üTR:r ?Ü[vëë©c\Oµ#þ. ÄÌ=SÓ¯~ÃAgÑ–>ê• å¥ìÉ }²ØYu,]&©Œ¤Ê¤¬zƒ‰ÓÁŽÎ;ša¯íˆJ qëÓô]ù—TŸ—1vtHBIŽìOí££1ðÉU³!xߺ}ÿñ¥ Œh–“K“Æ’úp" £l9tµw“Ùm(Õ…_VîpIuÚ´…Û/B È[?Å&Ížš0…Å‘‘B#Ù0Ù!¡øæWAd2ž—ñ’à~Ñ"ê©ç^yüÙ=ˆ"ò¯Rò> pãéíßW6ÖžyˆV»|I«™=c !Ôß#ÿÀR%A|­Bˆ¿ì+ xê½³×-?Ng,~d ˆ\/&Ð ª¹ |w6쮇;Ķũðâν¯Û:ãW‡Ÿ¾èðò Ó(’Šâ]t*~÷‘>9­…w¾ñ¿†¿ùãåׇ2ìØº÷Lí…t_úM€÷®©+0õ—Úo˜WD£?€$ôKÛÓM*jÓŠ X˜šêöáÁ©GÑMér¸CŸ\^9ñ‘¶–G‹­WTL ¶J@3võ¼0¿Àñ†‰õ[[“ï ‚Š¿ñ7°´$ÈÖÔ¸å-Žyo"Ö?Uwåʶ+W¶=òtÝCÏ7^7w3Ö]–¡•Pç6šP£uGiŸ¾oó6HÛ† }ŸHPV§üœ‚óVî« +VÞE˜uÆxež)+ý~W.îœ<¡ÐÜ–ºtY‘¸Uˆd8sEÆ=žDä ÁÝ…ƒõ•g”È_²¨Ã÷s„}ù‰—Æ=\²T©¡(¾ÿâ/$ÂǾ\a‰7fñúY™²óõBA‘…°EŒ¢Ý7ïG@ó~=?l¸,…£^€Àý~)‘kåä¡”6FAÞÃ÷LœýfßÂëîaŸCOËba€T»’·%¤ªI©Û¨–æû÷Mxã ÇÃ3ÀRz5lØ•*gXŠRežÉ˜T¹ç>ÀòE“ê 'b^W]ÔF„è»eàÇ&Œt«G%"¿ æŒæŸñôŽ3Gä¸oØÞ6»«ÆÙÙÙggw O¬uÜõ›ÿßW.þXÛOOž;$²»{šË|‚–o˜îÌÂ/q?òÞO™ §;‰A)É¢:*a¼£]Ž9þÎ Ëxíži?™}ô‰eǯ|nrH©Bˆ$šˆ¿»ó=·ÜÇ1f–íz±/lšUZôµD%sŸ[ÛÁ__3!ßÙÿ 'ýVëÿôpáø…¢p'ÖlŸïy¶˜&ôÒ’žÉ-3»¢7$@8 tÂâºg­"öìCGöÍœyxîLf£S˜yà@øYOpXðø„mÛø'uuu‹/þ·áïŸQƒ;€G7Õ^¾¼ýòåí?S à]óo\¼óâ]š ĺCsÎàGeé i€„‰V¾íœC± &Céj²½DФ">ˆ«½±ìe<¯ÌxežÉ˜e ;‰àÀý— Wœd\uQÛ÷ß°ªÀýž^”wDþŠeõzúÎ5Ç;yOF’ÈðÔé'ò#¶3t°Þ8;Û8;ß&ï¾?ð4gŸð[5ö¸ãí¦R©{:xž2£Ò¼?•òî|÷X6:ÒÔkò̯|úqüô’—oé·yÀÇ>àã×Lçà„ ŒDά¤E)yia5Q L>Äí BÙs¤¥ÀÕul‘„¡ DJùÕð…Ùµ@)!R¢„lëGI AcÓöm—ú…4‹i+|Ð=ñFý:OÐõñ‡-«ÿëí™±ƒ{¸ ý)üÏ9÷õ«_²†¹¹À "0rGyÕ­Q! °T©M7¬oó6•õ ©(YVÚ¸12¡gdY¥g2Z­ŒznoÀÒ…7áw.:ÀE¶¶yK h8Éç­eÆÂAyÌÙŸÝÝáî¹pAÍæ³˜ÈŸ&òžŒ§v´_4®D~¸cìó›vÈ9|?}мSiþÞï2´>w»×,Ë""*ÈÖ¥%2«%eÆïs™Å܉@ª$¾óÈ”ˆåæÒ—ÂrpqµÎ=˜ˆqgëÄÿ¨nýv}ë[;êKø.âÉ}òáÇiH« €ƒ½K̯|zaå† çüø¢Co|tíKþîê6ˆW§ÄS¡´­¸ )94w²Cv“_,º H±Î?ÌyÀ öÃQˆG XïZ߾ÒXZ3§ ëçW?¸;÷Àî®Tq¯‰¸rëS8õ ²¸yësV¨ñþ¡Ãÿk¿¶ãæð>ê¸xI‡Y¦É ·½æhWv"‰—5d˜ ‡(/p‰ €H÷j.§pR8‘ªÐÔKŠ…À¬“f‚®…Q¸á¿`a¥8•"åiåiªL}÷¾ú;þ • ÌEk |ÞÔœ-X.°-Z¿“Å2´ÑDtソüj÷¼!·âgMºåú¦[®oúñ“‡\aŒ1Ü]¸›.¨¹pA €³åϸ»ˆYüŵ6Ž·\sj}æ”ô|ÇÜ.?¾qí?M¥R֪ˮû‘Rð}õ¥{ÚDðÄ»g…äúƒ{\{Ü_.ˆi»K¯’‚ë·VY«DJ%!É‹›X2¤¡’DcY¬•·çê}Ÿþ£¢í«ežQžGN«ùDîH*•ºãís3OGÍLRrÚÓ½@cÕã±öÂ*R $jØ*ˆ]oŽ“““fâƒsgœ3ݽZýŽž {úâs&XS¬†=øb;´fZ-31T(Î\53”PÚ›{hOWX5’DI¾»vEññeq˦ ;ŸPm¾0öÝ2Fò~鲎K–– éXsº§W˜\WXÖÛ··"•õL•ñªŒWe ~ï˜ÙZ±–X”s)bC•kãyZ[„7µŒ”&R‰¹¯D½>`E„´‚ÑT–ºï¾úÛßÔäl üNtA.¼H^H£=­ÖF+­•Ö¯]|ðujk •8ÿ31¬_øÛ­Õ ‘h½hÑ赚Áä½çÓ'ë‘öƒûi"òW¬ÿ£:|õï›Eðä;Ï÷AdÓèFjPy•sÿ’È^ä»%Æw€ÄÓÊ2†\ú2€‹6ÎTDe±p=’ïçzÆÎæLXkrÏ5š´!c苺€¨  ¿6Uiið=†a÷‚ŽÆ±óÊ·pùÕð]ªWÄú©/øÛu|öªY®pÔÑs'Â$–…öΜG(·7“ ÈRì””‡÷v°fn5DÜßà†ÙÕAžÜÑ‚4Ž.N˜òbÑrÖîé$!Úw´0ojjMC%AŠÝn}erGå‰òD{®QÊ“M•×0{,¦GÞ9º}2p¿|y{8ôJ°aË@Tº°¡sb]QMíéKŠÜg¹¹mKQÎj£”Öª8b|?Kˆü#ïƒ#Æ÷§¶ì݇wœ’¿Ç\x8‘$ò‹N‘ÿt_§ï“÷ ï:Ïñès§íFœ\…È÷GÖ©NWD.³úëŇKËŸ<Ç ÁÎ÷&ˆLâå¢Tkø^ó÷hÁT<’û3ùžT*•J¥†o©šº‚¥Œ±£ër¨¿zè€oåÓWÌH1)ލºèë›´è´è”ìž ¸ç|yò®ã Ø4Ó½ Øõóȃ{;ÁtƒÆÄ¡8CQr5Ú›X½ zõÂ* ¦¦híÎô£ðáÅúÊúÊ•õ•#ò+º×Žï.f¬ZÙvÅŠð'÷ä³5ƒÁÀæÕÇ[SM­i ð½ïN0Už©2ºÒèJ£Ê´Ó­–¥WÀB"JÂÁ:­Ö=]·å=ˆ¶l@°ò(O{ã‚Î èˆ{g y&(íicŒ2޳c´6Ú£Ž–C:ÿÆ‹¿é’Ã>¹R¢Æ›ÂÜq–ùWÜ‘€õ‹Ç@ä“q2|_4' `û™ÎŽÄ0³Q¬Á±öo õÏÁSï>ÏZ±6w”ÀÂ¾ÓøÎBBEaY¤SÞcñ=(F ‘Ö©în)ñ® Þ]»…¿¢j÷R«nü!€nýé ·þóÔ?–Ò?gŠ›3ûÏ &ÿùÚ½>Ë?];Ǥ٤D§DG"LåwL¹pÇ”%t ç½tˆ-±uZâ:Ȩü”ôàî.buýüjAž˜‰%2R#bü¾Ä‡°º±òêÆÊhò y1¸‡X*•ÓÄ—•=ôÈ;Ëñr|c¤»cäýê‹ÛâúñÇŸ©}bó x·ÝÞ4wIqÁ…ņƾÛ_ßôÜ“Þ÷~8Ñ 5î$lÙ,Üǽà–Ó[éê»-¯)¯‰T ””㤪Ö3"ÖmW`»Y|h]Bóø¢úßÔFGZ‰äsûª¦;V5ÅŸü»¿˜ª•¼åæ##ÝŸã›wwlŽEù…¯(ÊæDg6žÚÞîP~ø"òŽ“äWGî.v šJ<Š×¼ö¾_ýàæC6˜¡´µ¡á—VQ¹pîåUâšÉw#!ˈCy¥(!¾ún.v_7•W&Þ˜©Bo.>Í Ûv, ÙÖcÕ?\û‹7­ýÅ›®yí}ýöF†Í8ãx½>Ä*´kûÑÿçí_ýÓ~^êêêÞ>½EiQ ¤cAF¶Ô]®( ´f#bI"Í!Ä'š ” Ê ê f&áðPêŠßÙªb/§*"Ï€Uån‰RBB¤„¤D ,SÜ÷ªn"(C.—`FB!F î«/muj €GOÜâÿºž!"ÊwP8"d æv/˜ÛãwB<“ÊN±NqÐÃpxÆô$¶ éê¾µ ©`¨ ±]¿¥Ñ~Jƒ,\^%\ÔA7}– a–ƒ´"rÕ/ñ·”€*]GBxOR½ùƒÕ­ßz¸Þm…Ö#Ø“§Œá“÷dlÞÝá(ü… k6ïz´šµ6q—˜Á±q{ûÅ‹jÄo<©VspÇðëßGÛÇ›È퇓­ð­wV¸Äf„/%p¸d¤›wƒ#Î_àJýB¾ˆ¿#BéëI¦X#úWÃ0@¢yêºoþê7çó…àljˆüiýx`áïâ…0± 纃¯¾û‹nõÍÓ)Ñi6iÖ)Ù\»úÙÚ«ÜCW̯QÌæüC»Ø$q¾ˆ›t‰ @ûŽ5躆 "'Ë„âŒÛ{añ»%T±›B"OqM%¾¶¾D©*Õ?×*B ¡†¬Om/"—ë2Ù¿8; ÀšË[¯½,4ÀY·±nÈþ kWn_½bǵ+wöµòá¥Ï]: ]Åž\Ñæƒ ]…\s¡»+0•:•Õ¦\A„-£(„>‘¾öž7æ=Êk´”ä5úº?„•"¶ÝÂ=ìwYˆS¦U7a7*aLR‰¯Ý£8GË -3ÏPPĉêNkaÇ©·ttàîbóî‡ì.<ÓDþìwÇC«š¿…¼'cûøù[î¸K€÷¼¥ (°viÕ˜©”Tšd‰^ Ö!±ø®µŸRøîô&´fÏD+õݸrc 8I£èŸI‘ˆkguäý'ß¾ÀëßrÿI6a0‘?ýŒ^ÜH±н¿÷±â4ùöOþç7¸lõ¤ËŸó·Åœ½¶å¼ß{ßwÿý;K3ÿس¥Pœ‰ÛSIR•¡ë 4¡az檅å$â÷†ÈÎL"älOBþÒ({$]Røo>Ú±çXñË·– yüùª ÄB ñ5[²Ñ캺rFÔÎ4Lò~ý-yË<üdýà.½ÑÙÂôµÇÒü²ûlÃÙašqÎ,ÛEúNÖœrÊp?Ù›+J·Ží°£<o¹å-¿Zÿ«­Å]îœ}rûy¾ß£2šˆ]G’ë*[ÜXÚÒDÖ¨qL®l(K•Ù9AÞA"lp4!K ‰F¡[Ò•!`ÌŸšò-RžüÃúf?€o嫦Fr Ù Vi(—Ùq˜à~ê÷yðñ!ÀÅ37ˆ¯ @ŠBS'ó¹.ëP¤*zT)•2"mH(啤áŸû¸Å¾­hPЈÈ; ym –(hÛ+*SæsŠTŠ÷]Oœ)TxêJ¨Ò£ªÎ! ÌA4ú ""ý«­| yO†cñËÖ,_Xàt£üÙFÞ“±:`÷d IDATq{»Ã÷KÕ&!þ”àŽ!ñ}¼È{2B"?';ºýwøÉw¾à­ïzòk_¸€î>¶K4‘&Ü{Î"D ÓÛ‰¨`¦Ÿð¢5‘Šõ™ÐóÝ9È3‹84‹Œ!>á I®Œ>%w7ïf N»O5ü;ç¹e;y¡¯qD»«$pƒ'H"Ët’Èß—›gÜ4kÍS›`a!Ÿ/PA)(C_ùÖe?éÈ»X “‚o>Öþ‡WÔ”:„»²‡cE•¦,.5m h°Š]HgÅ·àÝkjÑüä[Ý'ÿĺ£¾•ÀÊǯ""¢ ÙÿæÿþÔ}ou?/­èãGwøËí;,ä“Ó‹ïZ#*›)ü[yº³³Ö§^8eFb >ÄÂ0}+ |äðNÄÈþÉÉ ž¿ãƲ7Nâás¨·qVùs ì½à¼òîÎóË·ä`ïÃ{yðš{¥K9,u8‚Fg+ù†KІžP׃±´mm‚;‰%2L ÷ð/ÿ¦5m(`ùÚúöw\UCÊî±>ƒ’Ûxh>£µˆ[$÷I Êý›ï€oA OuWBóñë'³%¨¿½ÿ% ¼oý¾€¥ªêýqŒ¨fæDmœ×5î¥"½…b JôÖIeôâèkÛª)Dl ð=\¡äq-P,òª°ºåÛŠ±½ìœ+àBážTÕgY!0^¿ºT¨[D¡‰Ç#@„Žò“ñ2óЮEƒ·‘ÇÌßO«ÙÀ3;;¾¯h¨Ù4®ö“÷d8"Ã¥“tvû§\¿¾ŸVòžŒáù×Ýq€Ÿ'È{2.ô üŶ|ððŽON](qC“ó+t'ÑýÅ÷ÿ®o»ãx­“h,‹€Ü¯2æº=¨¬ÆßìÚ$νSZ–ý·ÿ÷è6°ç>ŠÂ·<Ôê3ç—o0«|«cÐz–žò½áL¾D%¼Ë $­‹ˆˆT:U(èhu جèz VfÄFÂn4#åKë›Ë2ôŽÕµ$øÊCí_y¸ý®•µIå]¤K“#þ®Y„Á)h0”û$…¼„GR tôµÁ™“’Ï\7ÓFüùƒÜö¶õ£Ûó'!{|nZ²G©W”™Ox;×F»˜A!¬÷w† †ÑB‚2¾T6ožVBY?­Y=[ugm~1ÁêO{¤ûŽªêïA@ÊC_8‹U áx?&¢êƒ™ûú}ˆÜöÊ ÛÙ5z¶wœds_ÑP³¢¡À¸ ü« Ü]\²¸.FöK×ذmùÕ3î.b"†õÚ;îà'w»Þô_‹~xgX>@èn“¿›¾<¼ãGv~rÊÂXy†ù%Åw`µ›àšha®àC/m7/‘ÑàÞéNÐäñwCyd-ìô™Yå[ÝÍ9[hïïâGš¢)»X%W£f.·&¹æÑ°ƒ£\h¬Ìp@¤â“ùüºã)。ÞÕÏ–$‘ݽñÂÙ‡ˆù»±¢uè ¸âobÀ$ù*« 3åõÒÓVÁKXOŸ¿vö–ß殾`nÀsû†û•²<•NúePCe†”L(ôQ¬îq’°K|Sȃ-”Ê»³§r}—/ÿN@•¿¼<º¼­3_®Ë/K±…ó8£mº¬ú>†%! [aóBâûQr7y ¼ÄüÃ}‚¹?qhåà ¼îò"´ux§{ÐǸĦ+N‘u…Ãt‡ï'AùÓXÿ>œˆ'åÄÀæËA±hvïÿ1*kÑÝV²øÄÔ…>xlçít³•e3%ñÝ]œþî >šì$"òÁÃÛ?tx»ûŸkhøÂ↰a´ûôÌcÐú»¯g¹"ž_ùô€Õö.9л$IÞçVlž[ñÌ ×@D ±8L®FØî7ÇþlD¤ÔÓ&bæ—Íp°îP^$”eî}øð߯=ÈŸ_3!îö- y$‘9îJ(̯²˜3µeDÄʾK¾ß‚-õu8OÛŒûÕŸ>pð£û'k%oºéX|Ï/{ÙóR^*•J¥ç„—J©©†&iL !g0—ߺ†¨œPŽ‘ct‰t3º™»-÷XX¶ ,–‹' ôX:àL ÓzßTYœž¶Ùæ1Uáåøº. ‚bŸ_(о|±lm(òýbÑ/ƒb¡X,ýb±X,Š~±àG·Š…B1 ¿X, ¶÷ÚK[×Deþc÷3l¹ig‡Cöå µ®Hatñ*%ïH@ù†mmñ²{h@„þbg˜¼Žßc"ÿš;îð¿'&ïÉϼì'äˆK²•éÃÇv±ˆ¼A‘JyZs:ÍÞUÌú¾ø¾ø+¢»›v)" ÒDŸšÞèÎëêÐÝ&Vä¹[¿9êͦ_[ØF°Ç°«ûÓK§ô‹oðús+6ÇiO‚8«ÈyÏLÈæ*Ê'S·¡…>-šE±hf%Blëãâh²MÖB±X(6´îpž *(’_P(ßxôÅ´¡”¡]?©¼>t#ȧ~ÖÊ>þjÕ$k‰åP›œñ¤IósW0©À¼#[m‚¢ Š*(’Š\Ì”âϬ?àî'+%åõnð(zšKŽcgüh˜ûyàîâö×ðÝ_Lpû•M¡CЧ9Ò7WÇ×6!¥—£wçGþõÌ€3.M)ÒÖKår¯<­V$J¦ôv’Àã Úû8Y€-‚TJ¥I—ëŽãR!I{Ns“¾ÞÀX–—–ãûÈö§wW_ÜF$îŒöÁ'†.f¼‚¿ÜiTv1ÿÀ}ÈG¬`p€;€mûsâÏÉnÛŸ»éŽ»øÕ‰Á}@<ûú!¬HÞpÅ”ƒãÊ›~à‰ßÜú/ºë]¶’ŸÈ½à¸Ž;MÿÄÔ…ŠÈýN…ÐÝN=wëŽxÛ¢~“WÜ1P‘¡¾‚0§íA:ÍÌçûŽÉx¦ÌxiÏd³öø__1¥ä)¦¥Ôe#@S×G&U~bÏÔ ç½¼Y±h±á”mW1¦XÞVTLšÂ+EÝW>¸x<&rœ<âë\ÛÊ3þË ÖÌÌlY8¿Û" abKˆT”TUõô‰ÞÚ­‹¤Si:Zs5e%a‹eÀVGT”BEyW¡R€‚2ª­­ O†g>´ö@J«{®=ÄG?`&ßÁKܶ ŠÂ Âä**ûN…-Ê@˜Ä Võ@) ·“¤b¢t7”8°xnÀ¶“óQ“w$$øo=Tÿ–«›!Ž'P®5(¤îàRÝ•D¶!uŽÑFéŒ6eJ¥T®-ðí¥‹°HgÞ!,FQ…÷ŽLðví©À«T"â[Àˆ§Ešƒ Ȫ¨´¯ÒeZ•)9ªN…€ *0®ñZ~ô“é'ÚºU+Úwöº#›ex¢8tlÚÙîð}ECí+åûx┩T %Ê›Ås«lÛ×y&>ã0bÛþÜþè]?ý=`èŸèx W`m)Ïâx~ì¯;(Fw’ôtËeeU6YÛ<V»q`…™m‘Ùú&€XxbE¬x>®nܶnÇJV?X©;H§ÙÑl “«"Τ«_=œRP*lКÞyómŸþæ¿ýc½÷\ú›uûRšî½v&ÅPd „²»oRŠýÄ÷ˆ¿héý`]æsÛ&\ÞØô„+¤+Ö•è—ð=ÇÉÐ?VÿS¸“¼¹YÌÍž¾Çîè_þíu¼ùò£¡õhÌÖcéÅ¡yHê· ˆtFé´¡”Ò)­3ÔÙ\TJeÃÑxÚøa ¯J“rý_Q®'•R"Rîµb–ÅjU¦T™Úµi€-{:—ÌÏ)%Dؼ³zÈíºìÂöø¢qÛ#³Ü]lJy:15’÷á€{I”7g²»¸þŽ»ºúðÀw¾¶xÎxâø‰âDv`ã£VÀVež,H¥3!¸3k‡ïÌÌbó,6`Žj]bKW.ØöÈîÅ=¶ºBw2 A9A¦T‡zf†¹%,vބʨÿÅ_ýÙG?rO|öÚYDœDv§ÏäÛCÎî;Õ7gŒÄ•ïHà;úSx¥¡#((8þN¾‹›h§~¤®ÔÊä˜ûs³cÄñÅ·6íö×½ýµGà{Oð†•/»Q{’€õ~h‘zw?yJ•+“Ò*­TJ«´ ,õv X¹±¿Æc- *0DR °xpSE+(Å€vÍÆ6oý€uÀ*Ð*P –h?œ½q€´HŽ7]u¼¥-ýô¶Ê_²´#FöG7ç(ç³-JD¾±öDðýj÷Ñ…CùW¸~fpÄ53ÛNPZs@ÿ,‰ëk·¦Ó¨©T&ΤÒe™LY:IE/!˜ Çú=»{‚bwpÅìíéµÙ—¤&W}íübЯ馪¼Õ1÷°”H“Ö*ßÞwï_~®­­ÍçЄ2&ïp…ð «™{VO Xî^ûKÒv&öL/3îô[·Vj±­ã¨IRÊ÷}ßWí>Y&Ë¢’Õ±¶ÆcÈ'¿Ý -‡ÅÐW$¥dö=¥c·Œ˜©ÈÆ6Öc­5Æíƒµ®œæ;Ç×»°wsaíê[¾ýB~ö/E£ÜØž7=÷Ôr#eýòå™{áyÖìÍ; ß wÔùî°ž²~1S~ºpߘ?ë°.È^9 àS¹k®Ï.ˆ-‰­àxYR}Îõ¿z)’åO=àJl¬1I.ìZåÙRàsŽº£‘ ³°&ÇŽéíwÒ~„¦d¢0¬i¶”ï3]ë ŒH!""ŠY·XÊõ S ²î ¹äsÿËüé¿þ#€;ÄZÿ]ß4ÏXŽïë)ëgOù>Ó×Qˆû‡<Ç÷ú‰Ff4®[Ø9ÜÝ‚q§7Ør@‡ÃQ8‡ÅÈÔ¬ç'}d›3Y ±:ÖFkŒÊIYPVÉZ1±R£<¥”òw[ј˜1l­±Ö÷®P?6™ß=ùXú`>ý\ÀÍ[kšØ})òýÀö1õ KñM;I>óÐNpwî’ ‹ñSÿ4¸>wIÔÓ©×JÓË©Þb—ë…†Äðî‚£NF2`ƒµf%ªCD±€,Á6œ$6:öØ‘7˜U5ã[J»@&•ÜI…$!ÔLD*+×¹â!"&hbdò†âRxs‚{®ÕðWÿõ_ ãÃæ]kßµ&žl‡Z³Ôwwà.Ùµ©0KÄkÉòÜáþ ¯vdß` ýßûùro¿×b]‹ãZ¬„õž“ &† ¢“N}g@@e„Ь¶&ŽÝÏÉ«xìO„v!Óf"‰k˜MÍZ=&ã,Fm€½[Ú›‚ø¥÷Të¶w:Ü/ ÊÏ^ ïߟ=ÖàgKf&äø¢5òS_V]—}U°÷wuÿ5ÿMçß]Í!’cçâ8îtLg †pM.™)Ö.l$Ë„Š‹lúe2qƒjP6@Ý7™DRö(À5[©Å¶µ5[àPÚšG±pìfj$»Mæ{4°Þ¬t¾Õ[ï‚òÃÂÏ«‡šúºŽ×8¸û’ƒãùfÄ5“(åû²ÎèNïhíù¯êݨ×F"²ç'î]ùZ ˜É2[£­+¬4ÚøJµ{õå¿ùé^.‡:4µ²•žRÊsghq2  o¬µ:±ðÆyðó kÏPILýl(¿Dá~?Íëvž_^¬k­Í2ïXðú÷÷ëáIá>¹R¬KçVÓ­™afÂõF#`ÕO°D–È,Iõ‘ìG2^€e[×"Ò””²0#C”“"¿$Cê'Ø@Äé.ƒä{¹sØV¤.û0Ä‚­`׌ŒÉÚ$6f+˜ÇËl¼Œãe¯HŒ=ˆZiMN/ÀÌ¡7´³íÛ[7çe¦Rèï[#ßt?ÓñÒÙöçÚïö M  «:®Å¦¦GûÄ ¶dc@½¿@2altÇ !”Š´ŽM›8¦R2¼Év’"‘¬ŸÜÊ-ãC]Iƒ ź[ýû£ ­úË—/¿£ãÀŽEW#ßD¸cÁùþÐÀ»6hR{~ñáq¡üÒÒêVwðhör:§É‰,‘!˜ëdî)Á=Y¡È¼»‘HqlâÈ:Äû*¹Ü›ˆÕ Bê· éeïêØêئ1‹EŦ5k:LÕ‡Ûº$, wÎîd…Õl:Ywé|&&É;Œwã(ŒÂ0¢RÞiÅÂÖ–›[^ž¨ƒñ¬4‰yw:mØ™÷ÝO´íÞ4·”?¸{ˆˆKãZ¬«–IŒNíAH1†]‡Çd@0$³HøþW_ï OŸ´Ñ:޵ÖZÇ&ÖT¬ï{ꢲ“BÈÑKÒÇð³SDxÿÁ³¯ 9ÊïÛÒ>Ê/Qó>‰Ró¾ßD-$ߟ>ÖÃÀ/lÞw=Qá²—ñ Nù šVÁûõê¶ëÕmîxCî’Ò€¸îÜݲ«uċē„NY7M"Š,`á+J†j[€YII mû”† V;óÎ:ò+ýb#bÁFØÄ¶Ãº®š››l;F÷°¦»Z tÛ¤]Ç“~ãqT¾k/Ÿ¹IõÑ`éÜw×ÈÆ™wÁ_ø½R‘ôHf¤e£ÖzñbÉîÖe)ÙÇ»ø±Œ“5ð†¢Æi!~‰Âý¡9ûË—_¾4ˆÅ„øæšw, ßëaàÅ™&3÷k1d5“,ŒÂ¤%ç«¥CWK^/'ûWw´¾¸.ûj^î)ëüëÅó׊vojÛ37”ÇwwbÈúìmÑ$ÅOClÙ¦Ó–lÂwáÒ € I^ d MkÝpŠc1D pîÔ@öqþÀ 'ºˆF‡Ï¼6t¦näçâuX$šú"ªCüS;:žZè~M‡;ïOfž(¸0ögiV³ë‰‚»ù’ÓÛ•n€Çrç/Qñ[ñzÎd-{¹p†`„Hí<»h^È!ÊõDž‹(²`˜¨(ó€ø ‹XJU¥Cæ·Ã¿«q«ù"Lj3Xúm6`Àf’²N¦(¨ÇÆ” H0ßÑcl{Eaƒ‰¿o`Plý‰W¦©Ý› 4ý H‡xsƒø1¿ÑQ5Ö՘꩸; N×M3™ô4ÐX릺ü{¶_½œŠd ½@‚ŽëÞÄÎÎÓ ÀË“{ 1.~ü¦ºqï=¨ù©d5KÔ¼OKññM×Âðýð±ŽOžÌLî©Rû<ψŸy§·+;ÓršÇóç¾3ò8ç ç5ç ç,|†êvž Sºú**$j®‘!Õ¼5Qј¯h?º×úç}Á×´‡XP¨X{+¬Pã}V IDATñ,$HeиÌ7Êtp½$×Û֢τq'cáâ(Šã¸>).LÃi=:ß}}îRººpiäétÕaŠúÝÜQ[ÂCÛÂ}ÛÂ}˜iUÌùkÅs׊öljr"ŸÉÍä®XJ7çb ØÓsf&†À×þzEò|õZNf„ÌHHkt‚øx4«ÄD¼ÂÅî4Ä‹ñÔ?>ÞE„Sß9šÕl} â—(ÜgPyêÒà©Ëu#¿qÍ\˜w,ßëaॹ»Ó…ׇÝÍçÍÈ7î©ÜœUóg¯UJœ·œ76o8o8olÞrÀ$™ØEó„Æt¾ÔÛ×f½ŒŽÉ>TöÖ|DŒb-ùÐ}Ù¿° ’ÔV³öÿ­×?ºò.ª0̦7Žã(£( ã8Œâ0ŠÂÈý7 cC²—Uï­þÛÂ}éŠÂõêöt¥aêú¢w4±9|_Ãe³Úk~îZóyW©’*®éÔ¼×Çr ×0’Ù6ødp«k(œ½ÒF„•Ý‘ üí7Öxá(oÒZšzE6±è‡ë[0®–æþ‡×˜Â§Íj¶¶OBù¥¥Ù”·Ÿª/ºœ_ÄÏÜ1ÿ|?t¬À‰æÅî“(}‡˜Ä×]oÓ•ÎÑ~s \2w¸ÅpÞpÎpNsÎÚœåœæÀ’Lcz8#ŸuVÇÒ hSžeU¼×Ý«Åú ¶©ª*Ú3±§e¼Z˜Õ%ùc&ª²‡ÑLiSàdVHŒ€uFu«ÅQ†± fb­I@eUÐæ_ïüÜÛ…Ï=–;ïVS³ƒ_Ê þnºº0]é—+UK¯'¼oˆ/nœå«zîÚh"ßÊ7üÞxJ8ó^Of¤ "mÏ–ÙZX›´“'áo¸6ùNÄTÿ¤ôÍï=êe¤ k]¯¥‰c­µ‰cm4õÃ.GJv)'ðï~ôb7yºïþ/¥ö|â—¨yŸ¥N]åÏ–Ž^òª l(fb;'ýÿâ÷l.8ÄŸ»:ßcc8Ó°¬š,¬¦ÓoGkÞÓäßyiØï6Ñ?xq €£›_ѵ¸Þ\‚ÓA¸âc­g×@ÜIÊûäÚ¥}ï§Ë&y g^rXr[{óz-ÏŸšÛxàä¥Ñ8žˆNÎ çî˜O¾8ÖƒIá>§çâçÇs¤kå'ì+<Ï.'gý“kû<Õõ3¶`6H¨å”•°ˆõ73âèPðFÞº—¸ .K• ò!: ¡Zaóœm!é$ˆ‡Îž“Œ¦®ÔÅ»qwg§×Ü¿t)¥ ÀõëõØ ðóoŽ>Çñ´¡ü‰síŽìðôþÁO®õç©qw÷™ƒ×‰@7ÀëGÿ¨ÿÑÇïø«¿[9µ§žvGvwà.Ù¿­ýô•%¶ÖÚt%.~Gç¡N\ZqÍ|ôÞ{¬ÀÙó}÷¦æ¾ë„šñ8ˆù7ï›ò¯t†ek‡®¥dgZ³ò|ÏSÊó•ò8KÌ(³aÒW­µ`¶n1Žc"Î] t€å!#î¡ôð‡YÄ"&‹ ™òYfbxŒ€oö[‚!6-bˆAÓÆ «¥ÛÂ}ÁkÁK÷?àß<ú–_³ŒÎwý¨qìÉùº=®¬ž®ŸE wwN‚½ eZ(Ó‚¹š‰¿$E= ·‡»NÛtí­[ø© þƒGúÑO{7VçN+Ï÷ýÀ÷}ÜÊtz²Æ~÷òãßá™ýƒ~qzâ‚§÷± üìÔTýã?þÔmÿù;«¦xýqpoÔþúŠë+‹¯¢fÌû89¾ƒfî¾çǼcò™=Çzx±Â X7ãmríÜT`šW¸?‘?ÓÕ:L™Ö¨Eå(*EQ)ŽÊ‘Œ§¤çI¥<¥ç€Pa˰`Ú#¼}B=©Ä“’öbÏ»—×¾ ÄŠ+|½ÌUFü÷DTc/d¯Ê~Y)û@(l9°UŸ+þš‚¿v¹ bÚ8¸»Ç&îœ( ÿ›Ž_‚A61òlˆMz601Œ&£“Oì*ÂmúÃ}rÌØ°`C0É׎œ=räÜ‘-áÁûï}ö:{u8hº÷aqMc>ã®üݳOH)„ »«ªÎ¶ƒ;îKxÆ)5ïï?8ðCOiÔï~æ–,§»O®Ô¼/¶¬fþáàÄ¥gÞíì<¸sÚqͼÁsÍ÷ÝÇz8·@53SÔ…kÃãF»=Ts¶™ibmÌŸe{Ù°E¥(‰¢R—#XH¥”çyRyžâ"[&TŒÕ¿axû XR1¼˜È _t+Cê#ú¡¦¬C¼ïi¢ª@EPUpÅ[Ó_µ¦¥}ý mj9>BvM¸àÉNùÞx"60&†[_M¶û²Ö• "z7SÓUdÏpƒ¼ Á€-ìÉdG¶Õ†6Õ„gV\?¥æ}rÄOX]îâwÚÝŽâoœyôÛwÍgŸ ÂÏ_ž ¿x¹35ï&àsŸ¸óÛŸ¾ àËßZýåo­žä6jóîôÊ«Cμ/‰D~”úÐt?Ÿp‡ãûîͅݳÞß1¡Z3³°æ½Q)âJùy[ vÚ˜?  £µ•¢h$ŒF¢¨Å•HáIéyJ)%=[Ù2!ŒÕ> $1i&b"bcB66~RXA,<ï¿scáÜÉÅ# ®yÆòP>ÃBÔ„¨JQT‘T¢,×=šé.Œ¤ÅìªfìŸ÷àÉ–\! EüÊÍ01™F×Í»qˆ‡µ£mpnS%þÝ€-Ê›CéRµ †(¼šÓAjä÷<ØÈ§où©}²áè¦ÛÐdÿêÄê¿9ýÈý·}ÿÁšrðòÓ“/œèðÁ#ýãZÊøÍÞuÿño§JvLî©Òëì_ˆ?°£4ßæ½Q'.¸P~ZˆŸOú÷¦#~ç±.,Ödæ~M%«™g¸?ž?Ç 7Ê{£‘0 £r•#ZÏKf¶¹ä«0%¤Ê8¸‘Ë5(1y‚20ýqËõÚrÖV%„aà fOÄ„ÈØØ{)D‚ª²ñ”o-É+xd$Ã{jOnˆ~{ô XÀÂjJü»hœ©·6NàΖ¬µÄ&ýxDqÉe2 Ù+[Ãxo¨‚¤Ýý£•LþBn~6ãLnä‰ÐÕîã}×üÚ‹«]MúW~¾ü«Ç˜ƒOžÌL¨Ÿ¼”ý~ºÏuøäzýC÷|í{+¿òí©fî3PZZ³[ûRÞ5mO[?. NÔÿPÊϳy‡ãûù«Ã篣©F~DZ.>¬ffžËfªÆ¬fZ‰ü\èñÜy0½YÞ³Ãÿ~TŽ£r•"X(OIOyÉ\NÅ-µäc’ÈZcØZk-eD Ü%“!ÉD– „$Øm–1H¸þXàQ#OœGÖ²°,X*†¨ ªJª$FÞ=Îù³a…à V f\r²>ú½ä9$|oÈg4Ø`ùcuóÃÄî£çÖDêm¯Á"x%€¥Ú¶Èî¼ ˜±j X=¬¢*쪌+¯œ;{p"ÿƒ_t(›6¾ñÙÝI/ÿ‹ç;ÿò…nÞõü/“[}ô™¤±Ìßü`zu˜ŽyoÔéÅdä\£YÍ®"~þáŽFÿ~¾n:æ(«§=› Î-óÞ¨ùù4ïåÎ3àúGFå(*…q9’B*åyžªO]ö¨[^^È, ÆÙX‹ˆ\ ¹Æ’( Eù!½ wJ+ŒÍIÉCV8‰CóîzG²€me"ÖÖj¶¬¤„¨ ª Q¢šüòtýò_D„„îîËlê¿`]ôÏÀ& gêK=v7úþõUb‹ô­?.‚³m‹Âm¡Ÿtß ºoQQ•£*¢yû=ÈÈß¾½ñõ·" àóŸ¼ýÛ¿vë¡ßêCGú‰ðã—¦ ÷T?z±û‡/vøî ˾ùüÃw¨ŽÓÌàîtúÊÐéÔÈoŸWÊ/óžêÄÅ©ùyÓõ‘)ßÏÏt£ö¶c=®<ؼ/f¸7ª±zr~á~€kÿxåoLhÉ åûžï+Ï÷<_y¾ò}ÏóL™Z dMdldQ´DB¥„bE ɱ°5` ºÚm8ÚÐ~T1Š*F·J™§œ@Úì=©atù7Ëa Áä‘ &2d5„ÀõÒg´^ Ø‚ (”GÒ'åáÚÊL~wÿKÖ°wÅCv÷ ¦®õHßwÜÁ½·ˆ„›V>2-¤×E5_ÈÄ‚ ïù6Yѩנ¬«’—vÕÒWoN‹&ÕX@9®XþóŸ¼-%+ c`,YKÖâëßS‡þá£}~ôËÉÜýœÊYïÙ¶ïßÖî~uN_žêÉE÷F¥po´ê bÞ1áþ¦óW‡âwo.Ìñ[õ0ðêâ®™™¢R¬ïÞT˜·†Krä¦8mþZT³R)éyÊeîJ)Ï%3ÒtÂÖJ¨\ÖF†µEÉÂ’B°€"Eb#,)KÄÔÐ{êíâòÇ»G XDZµ±W’¢U%Ø$¸)¡ `:à Z@h6 %„T4tê‹ØlÉÕ†°E³ñ bŸ÷òëöº¥QWYßÜcîL°Ö-ë ¦õ±õú€躒u· ÇÃ!A20&Sv ³9RŠõ}[Úyì"ÀW¿» Àï~fŒ…ÿÌGîC–a KK±‡×„J\üöÄÅÏå­Æ¹ø îxÐþÕ4ŽŸeS½ µ{sgñá`žå ûá]*53×w·>w‰AïTv¸ÿFåÈóOyu¦;Ê{žR¶›RB°±±±Õ,F÷¹ÖS ɯ¯êöÞ¥xÔñp”Ëy5¶f$¶yU´JJnM{TQõ ¶ìÊеµ¥“_âú¦z6`&gá­EÃx1ŒÏÞ<;’O˜dçjûê:ßEå-˜!6D$‘R/{ÙËÀ‹Àõ™² ÙI$FËË»jZ¯'÷¾„±-Ìæšòΰ?³· @¹:þ×c\Ëg>r·ñ¿.WY(5˼§:}yÈñ}ÿ¶ŽÓWæÊ\/róž*Åúá] øFþý«iŽ)"~˱¯MR3³¹€¥w§ÝcSø¹+øqìç`¬ïýªçûž(ßó”¯üÀó}åy¼B:æéZ™ˆE%°ÛØrÄB*)¤Jæ+6ÒBÙk}«–û×)¢{á£É4&»[ßËCµ’§Cmu ”TR)ORAÕg{X$;ïY ª?ÿ}gÕ™1²³ w6˶~!3زT.¥¡¨³f4è¢gu2–»mUÎŒF4C2&ÉL¸áeZ(([%;L"%;H€Š;C­—’|b÷ ÷Ò5–ÃÏ)åÇ0MçÞ-fÍ&v¨öoK¶b5òn>ê©…+ˆœ>~tÃ#ñKÀ¿?dÓ´šêm:Öc'…;eÍÌuþÚ°#ûîM…ÝsPZóhö23Æ 9Jܺ³ïžç{ÊS«ÜO–š©jÙXËÚºþ‚…’YX({­9[¡¤i¸[Gß-T6/}!d5ŽbG:Šy0¢W1IpsŸ‰L3ŒÇ-%p?“M‹ÖÙ"iPl)®!*#¬°‘“ÏPKÒšÃjDm:*Ä`°…yÓׯûù‹>ë–OÃ*t†Üþ&w€úA²ã %xæjÒ€ìJpÆ ßôÓƒ4:ën C«µ•bý@ôouxWçÐH<<£ž̳¦Ô_¬±;6f×ÌEÚ3×W¤þÚðîz"ßD#¿£õE‚­Ø¶'òg‚d¯Œö]­LïÊS¼VÁ‚È2îUÙ‡°Y[Ö ""ÅV1“bVoGaæ^¸ b@€ÞÚê,Ý”b˜},âPÇ‘a¶Ì’­UC Dé5½Œœ)V‘Œþ†5Äcœ{’Ò0Ãhè""áºHÚ$yG’¹'æÝ0#Wñ‹÷¸56Ìp êÝÝy¥rŸ/Æ\b ¤ðþèÃ^@×rÞkÁÉfý˜î—CyêÙϼ6ä.Ù·¥}Ñù95ïNñ¶uØÖ´-HKѼ;9ç~xW§Cü|ùéõ›$®yâX€×'©™Yúp¿ÿKúê´´.ûj›ê¯˜6"7!;á»;ΠLIó!»æXx·¦ØÖŒS؈mdaI %…’YAmd•…g¯Þ]½"xG„|§¶ÒÒx–=Îá:€Î¼Ö¡Ž*"®D‚¤RR¹v6RIOÙŽ4¨áÞK¿ãóÊMÆÀhÜ8GÌÜ÷¤¢fC {YEµ¤³X®s\8C,À"ú§’®g/äИÆ8â gH`d@ábÖ]2²©b&ìjÍ~9šx2TS4î÷éA_]@ÍÜåøŽY·ˆYŠp¿Ÿæ©…Ÿ7ÄO¯?ð¸¬&…õÆc= ¼ñ«÷É•ù=› ³,ô¼QÝ:áå;Z‰d£€Ä¾ †ºM°DL‚I[@3kËÚJRI1+†â¸6²"xÀpC}ƒ¤k·Ëìz1ZãQ(ý"omÄFF•ˆY¹f¶VFõ{v pïÅßÐv>Æxذ+i_½ VÓÍËu#_·ó¤ÁHÂ0²m01uõTÔiø›9g᣿“ÙGÞ3&ü©ßèÈ[º!<á%'™×¤¡p°á Þ>0ˆÀ„‹Á™‹Ñl~,³Ò’0òó —‘ßÞ± ÀPZõù7ò3é/vî¾ýí3³õÐö i"¿gSaÏ$ò—FŽ^(¾/ˆ yÓÝ.Wç×­P¾u“,‹˜«`’qͬ™˜D‚È<†ÇqT,…îuq¹p¦ž¿ƒ Øf¨|åÖ#*+TFȬԑ›£é(Š£(ŽÃþ‹_è½øEm—ÜP¾þŠëÃîœ V>åƒ ±¥4ˆç´3‰Ñò¹ 3Œ€üG«Is1Fíßïâ¶Ï†^~ ‚VýAÕ0,SãÉŒý¯e2–l2ožpEû|>óÊ…¹/ŽœÄ¼;5&òsý`¦¨y6ï©R¬;ÊOWKѼO¢ëóÈ˧>ôÏfp³;ýáþpUwfUwfùoüQlßúÕ2ïSïs§?\Ù•°²+s§?lú#¹®ÙÔÒ«Á ªq‰@9ÛNL KÆXŽÙÆ,!…'d@€|D\*…™{Ѻ²n:iœ Ù¥ê*U$ª­99’••³wö®_vÏÕÁ0³®Å¨÷;,ÿ¹{$…W°+x,†oSkwc¿_bƒl™V* 3Q«a†¼#¬¦Ö_ƒZLŒƒOàÆ-òÖÅjµ®^P]SmGã`§®]ö:¿*ªçýz³¸eÞ1ÿm8UÏzzu­ÐA¯"wWþÓ¦ÿÆé¡pOu»¯æþLVugn÷Õzý¹ÓBÁÝéVoíVomÍò¬;ÝêêK±áþP{~ónõæÝê#+²îtóî\íÞUà³W‡ÍÓ¿  pækºÎÞÍZjpŸ®Î]>W7òsÑÝA \däHXAV%ÁÄšY[Xy ¡8Ö¥RÜ‹Öa2!€óøHZ‰ˆz¯¶ Àæ–—eFÈŒPY©!|Š¢(Ž¢°ú_Èåÿ¯BáO\êâ>ª¹Olï]t)ã{rÃ]kб ÙbMboœyäÊ×{ÿCî±l[…Ó7Ð÷õœû†¶~Rm¼s3/ÿ—&DÛç*îrsŸ‹O/¹q7ÎÄùsYÅíµã»7ýõŸ¥Ò·…5ò‹¡tm–F~IhêÙË<ùÙÎç+V4€Ž±mQ—ºfÖØòܵa—ÒÌlßï$ú^ßNŸ*¼êf^uÍ~‰¾³€€Èã¾²-™Þðª“8éëî4D’P§îWK6·¼|çݨ3_T™¼5’æ_¹¯ ñou±µL<º…”™éÎ,ÛÈH«2;ŸD󙵆ï„‘of\ÚÞ÷¦È®²´aí¿(Kåqðï*Òàì,Ï!Ž¡Côþùî/TØú$¿1' 0î\ ™<Z.fJ;kÛÂ}s½§iêæ=UÔ<¹µ 1©é»™f,‡øÛ;ìè?$‘_Šæ}ZjLä 8ÞìD~Vþ}ý±ïü§?}PÇ¥¥ýÝ›  VÅœ»6ð¹"ĤQF9O âÞb[ã7¿VÚ­„ë~"t€ël@Òn(þ¯ïÂâÀ’ë÷[?‘kùž¥ ¶€©¯¬²0„©ž5ñ]&we¦‚‡Ó7A„®,l ÊC5ÇCÃ… ª21>]5!ü('UÒ— êŒýaŒ¾· ä(ÐÝÇ·¸ì.)Ó¾<Ÿ™?3%#¶.˜‘_¡fSãnudW§C|³ŒüÌýû„53£F~ÑÔ LKÍjŸp~:û~§. aë?²—ÌÊoòú ¦@Y{‡€¾hm_´¦>ôˆE¥nØ“²™¤v_êš0’}éös•ÎÖµìn›óþÍ­öÐUÜ(*/ /?O~KzK¡ïècIDAT#Kc“¤¥€›kÑò´ÎŽ3Oé_o ÂXh‹Þ =Éð»7À0jJ½d,LD&"Ë0 ˀ϶ ‡dܨ{ötÛ}gí\€má¾Ï–~>>Ú¬ÀÞÍíÎ^-”S¬Ïƒ‘_„æ½Q§`äe ?-o¶‘ŸÞþ¦Tëðîƒkfž®w\:»tŠç¢7Îtø¤éáÏË3ßîÔ,¸7Êñh®ø»ÈáÞ¨”ï§..E¸7·¤=åû,üLüûÚc=<)Ü÷ni/Wk§·„Œü\L«ÏTÄŒÑF¿zAx·¸‚ÜF(B6w7Ï·E™Ò•Up½Þ½¾Íÿ‘Îz1ÌÄz£¼÷„ú÷ʧí~vgë·µ> Ý(2×ʺS)9¨Ù]^¶TËp˜å{ßÍõ~?wﻹ‘³• î”8ŒÑ Já+ßÅ·‚ŸœÄ×~!Pk囎-tDC_ HD ži ¹žd‰ôX”|t$åÃËP£ OAžD ¯¬øÝw²  ¯Úꥰzn¤:¸ã´g©ÔÈÿ䔿w,½Òš¦ïW5ò»geäg’¿Û‡_œsß»¥Ý!~‘ù9-Òolà3Ë»°,]D³>{ĹÂò°’§Š&B‹×/„ƒ ÆdÆmq"~«Ö^P÷ ëÉÆý¼QÞÃùÏŠÞ¢Ýÿ¨rT×p5[½õHͯ‰ì Ï&TÙ°&k¸îâ™Öî6°®áç’Ïl¬Añb°úK#ß~ÆÀZÜëGK–Q,ÁS¸òÓ‡õ¾å“Iò ÌŒJŒÖÈ–¯+鞈hSìßò¥åCzP¤‡¾å5÷>ÆÄh¯'E”$81å¶„â™åò3¨™™¢âÓY¦MôÚK®»Cü‡.0\Žúá,°âìîtˆ?~~&oÓÎgVëpkRóޱ4O-ü¢Eü¼íÀrwD³XvÞÒr@Õ´ÐÝî™¶g¯oPç… )HHQέ#ÈÊ{`ÔâUI·w&0ˆéM´(¨^.á™\åÎä&I=~Äã¾ Þ;ÊpDV×'eX“d5`Jÿ‹€­!É0†„‡Ì¶Ð Ê%œ|´Ôš‡8¼­-ZCZä3È´ îü»œ”(|¾ÊŒâW3Âc/C^¦Kèu!)Àgׯ¦¾û‰]ѧûd²§={ó¢•>ž_÷’{.3î?wpo”ã;ss]—ÜžkÞO-ú>ï˜ó>N©…Ÿâ§Ç÷UÇzÜžÜÇ}éA_]XÍçÛ=› ÎbÏ ñ›ó/gdi(^Þ¹î€ëo½æ’õvꯙeRJÛ&d‘²ê.˜jÑÊdD‡u›™èMžß<–»Î“z"üˆ§ZÞPßl`CbGóʧˆGÀ-˜:F°#´††Ž:‚ŽqïpiÛfdX k¨ £µ¹,òä²Èxã&@ùr6¬€$ ^^–¼ †÷U­•$ ™‹Îòƒ$ Yz>^Ü6¦üb3ï÷+]q%å—4ßÖSÖ/fÊÏ['™™Q~zùûä}f&ÏaÒË["?ÏíÎ]WK:-¹pæzu;…ïQø®»°/ZóF¸Ë%Ärjñ bÊz÷Ü6UÞDapG} ì†ÜÅõ¹K¯?PÚ¨Y˜<0A‚e}H½?%´-§Ö.´tÀÄ%CPA²Ô| ²gZr Bâ­"az!‹–,Z3x{~~H¹gFÌÐ:f­¹ãB6JÒDÁÙ s.hìMF k—ÜѤDþWîO-ÁD¾éJ±>­D~þ}űwgdÞï¿ÚT®9ZÀÞ8Ó™§~«ù3Тܲ`~û½Ò`¹ÈŒüü÷s‡ã;S+­™ª_~¬‡'…û¾-í45d/Z#?Ï:;}#ߪúKº#×½ Œ3ï´Œƒ;€îú¢µ êòßwÆò›©ëíÊÎw*;2Å?ly3 º©&©Ô¬âhÙèÞ„e!‰E24Û¥&É0X°ôù‘Zά`4¢weg ºZЖƒ­-hmÁésâ•WÉÔÈFà iÚVŒ¶JÖ1´†ÖÜzÃ[9ø9„»’^ÀÔPUÜÍÛ¬í¦+Åú´Œü…ûäJ¡¿¨Jkîh4òS¨‘ŸjýÌäí§ksIiÍbhl™nôÝ»¹0E#ÏÁ*ÀïÌ<þý»Mõ(êiOv~"ü(Ãï¸6`™\#µY¹ &†‰ØÄI­ ˆ!b0>ŒU!ùu–)Ù`% òEÅP-lÛ`-4PA¡….>}Fª yüÏy)!“$’œ´Á$0 c&EƒwÝñûWGµaöJ€ú&Ýâö¤CáŒá¾€æÝÉ!Þ•Ö,‰ökóîäœûÁñ‹ÍÈϳŽ7nv¥Éù)ù÷îc= ôN:XuZX#¿àî4u#?¢»V­ßƒ)G:ñ*uxwfóðv„ûv†û²èÍó-ÁBÚ–Í …p_…˜‰™À, È@€Î VõT@Ð#dÊäõ¨½…Û[QhA¾*;ZÑ–G[Bàaèù¡ï䇾‘wÎÝe2¤¨cmÒÞ8A¼f­Ykhe÷|/‹ø@ÒŸydW @áµ`éÂ=U#å'¿æ5ï…{ª“‹ÆÈ/”yoÔTjäÎ÷®c= ôMšÌ`¦ g_çåçG‹î©Ô£­Q3hé3¯¨Ã»  Mõ4uó¾-Ü·%<#`}˜Y3€yý~v…ˆpc Øµù¢¤=»!¡Ià‚©`k¸÷gYå³”üöÿ‘ÿÅÿ–;õf;VÚB ÚZ¸£ m9´æa"H‡¿˜w5)ÐcQÂwEI¤®ê›Œ$H–[f­±¢?#|2¬î­(\ÎÐôc(§Åw§Ñ¬fûÿF–(ܧ«“—Çyùù×b€»Óñ μÙÝyt"Ê?œï“×Ì4å/áy§$òSÑÌw'øjð‹‹Á™¨ÊQ•™øúi\? £acܾ‚»W©÷u¸ÌÝbH‚`K ~5+÷þ,Óò/T˜4Î]WoR¡­hÍ£5©Ðý¥já³µü'"µ-‚€]©Í ]§ j’nj ¢$ø§d¹õfWøV>tpŸæ¢‰Ø\(¥ùÒt׸8yq05ï‡vÎùÄêE®_žpd?º»óèîÎú÷¶c=Š0ïó`sR¨5ñ^fã‚?÷©>)XHHÁÿñ¯Ø™ëð½šùW÷L½ÙòÂúJgä[/%çß_ êWA4jáÿn L hrsûJžktv­C^ÍÀ2YÀr÷—*þŸlu VUrB>ß @X¹‘¥„T£)Íí×ê^zùâ–(}´í¯eïž^8³gn<÷Ô2#e=ù »Å¦fÁ}œR¾Ï…‘_üæ½QÎÈOÌ÷ÉáŽùúÛ\ÄÏî¿õ‰~)X þOßzȶϽÀ÷Ú4¾3ï‹ñòç²Â3肈Ýdãº6ë‰Á¯Ñ0¨bC0`C¹VÀ’ê“°p|wç•þ¤…±Ÿ¡R äã¼ã;O½j3\-|šÕ¼wPI _Ü6>ΔE.E¸3ïKñsw§9BüÒ‚{ª‰÷75w7ÓŒ•5û¶´3Ïz´ÂŒ>wÿÚ‡¤`_ûÎ”Ê ¿ÿÓŽ>;øÑgð³fZøÆ— Gù+Á‡øòžjû5’¤¬ëc‚[À¢¼6vÆVô)‚,ˆ˜‰ì-Ï_ ²€Ç° Ûp.‚>¼Í!3ð¶O’AD„»obÍ6¸N¤ˆd²;vdO-}x­—3#Ûk~ø“‰ò[¿x5:±z{–åçHë‡vv:ÐÏu"¿È5AþÞr¬‡‘%3[ÛAóÊ—ô¾Ü™iï–€³¯Mϼ§aú_oü²ä?ùØ_ü¬ÃZúð3Mx‰ÆÑ<=HA?ÿ¢€×†lü,ü,•KFåõQe}\Ý ÝaÂ6£ŠJÜòìu…÷<¾åñ-Ké±TPÊC*”’ö2nÐ’ðë;›’ñL,$߹Ʒ^K/§õû1²/¡yËÅ õr@b†ŒÞ·µàÌ«óWRõõ?Ý÷õ?Õðq4Oy"?§æ=UŠõ¦$òKÔ¼?½§k|>“?Ö ?±©ÿÐÓCžqVžÄªÏÅâĵ½´€"A°«®wSA–jE…ºâG{C•CæÍLµÄ:„µ€%¶ÔµõXfÔ¼ݬ—"öò3TÞ[¾æ¥= ÜRª«|_¿Râ U.f a™I ¸5Ä jfæ îúg{¦&MbÕS¾/B#??poÔì㚥 wÜïßR3³ppGÖgcä§%Ëô‰ç&ø]d7<šÄ矽ó›GnÜ…ç_l·ŒçŽÎü…š<‡9ÓPe4ŸFþ‰ð#¬àûD²¾”@„Õ¥€Î8ã×<ïší¯y­h»±–Ù‚D½¨FªdPª³ðR±lhB }’>„áAxð²2=±, ÁïžOà¾7›EÂ}ŽŽ½:o¯Ã´ä¬ú8¸±+ŽØWhÖÝùØûÓl#¿ä4Æ¿gõ¨>À¼/ž¨iùÙ,«~üƒþnl1Ì矹-•’R‘¤(Šâ(Šãè[§»ÿæ8< à…—fR˜8Åœ}žüáG<ô‰6€Å­Wˆ™]ìÎîÄw‡*C-7‚Z q•­%Nü;–?N kª Ë}ï >O••G^á¦8Ì›Î÷rqÈqÈFµð"”÷Tìk;u~»µ?ßô’[Èîy‹ÁSôãsjÞÇQû)Û<|¦lŽŽ–WÈæñW¯]%w©xù)æì)ß_^ݸæß¼7*á;áÄtœø’6ï/žëõï™c=ü`¸/*MÝÈÏr[-4N̰–™Y¯‚‚„ ŸØwíþ›¿ðRÁ2ž=4½{—’÷omÃÔ=ÏFþõà‡W‚3mo~Wmí*jìŒ9 Aá¾PåÑÕXk¸ ×ý»ÇÒãÔÂ÷ßHª]ø.|H¤BimÍ…ûÊká%;¸·]ξyräÇ?TîÉ5§pÿëÿpÀlÞýOØÎ§l4«ßoVgW±›…|ÑnüŸô®u6g8©¦¾ˆšb}1$ò w4ùÃSh«ÛxÍ¥ w4ÖÏL¾2µxÌ»“CüÞÍíñ³-­y€x¢…™ÁÉ9 ‘"þÃ;¯ÄqôÓ×öŒ¹ò‡‘¤d{6¬\ýÖ§Öðw~<¥í¦g^rpŸŸêšã{ŽãÜlr½Þ‰‰ Tla ³»F`nÌ5I56|w¿€®¥o}qŠiXÄ>׆…[ Ä¡›^ßÙ’Ë[Bf.ë«™ôá¹ßÒ}[ ó¿jê¤ÿò×4óGÕò¢Žbcqö›Ïš,! ´€t³®Ü³3˜üc»áÜzY 8ÄÏ ”¿_ñÿPZãäxWç÷TÔw¤ùŒ¬@´è“™û5IV3Kóî `~ô‹1¦ø3OÝR)%¥RxDÊ»p“Ëj^|cãMž>PðâËm“Ü—#;€Ï|¤B¹ªÉ Ê’ÿöûSíø8ÏqM²ãéb€z2SÙJŸVµj%6± mˆ-¹ƒµÛÒõU¾ý*×úŒAü€ü z—E¼K>?±åö;A\c&|/n³ˆúy<}%¬0 á Ä_J-ü„ˆŸ%ý£M‚HÉ?ÊmUR‚ù>÷ñìêËÑð*Îhkr7[XËB²™EU‘Éa˶0ÖÂX²?—wOÓ0‘ü­ß?5îNgŒé…]t]pó>N©…ŸñKñ=`Üáøîë/A¸§ºŸò³„;Pó®§|WJšÕ‚ß‹G†qGQtòú¡Æ[Ù_püôˆOÉþÜ‘!€ - Äåªv¡ÿ¯}¸_J–‚…À×¾3Õ‰zóFyÇ÷¶+[°eXÔöDÌXÑ—©pTa¶dm}/«%¶ôȎļ¿w £p@ìäghdC×à_ôAŸ U+ƒ8ä8DVPÿê¨æ[ÜW$³#Üw?ß“Wc"ÊÏîc"‚m}ñ‚È0¿Ï[®­5–ŸF—6lÖ²`! âswu"W*v”ï°$˜þwºàÛöžüüÿëîjö|A(¿ØàžjÊ/E¸c"¾Oo¾ö¢UŠõ&fЖaÇæ3ŸÜý¦µ3srΆӈFB‘÷U%Ù ‚)ÙÁýÙƒÃÏf¦¶õÖÛk¿þ½ÖtE÷[?êJÍ»RÖSSŠ{Ëä§õ”g¦èØ«®ª½º'²Àºr† ³Mv– 9Z<óè“.|ç[Wê±{½±»ðþÿöÎ`%Ž Ã_u÷Îh!âc@E/"ž$Áò {1ž7/0¾‰Ï xOăA=äâÉ * "Ä•)µÎq„UQHѧjè©ÓÇ?Åß]øÙ+ò­|ßÇáP#ê—¨<áÔâäg¼Ûq+‡¸)•^¢ht©T“J5ù’N-Éôr˜Á¼gJÀíëßaäƒa­«–…”öÊ“(j ³˜Ú!ò>ãÙFã Çš‹‘cýÿÄj ø¾}Gþ1ÇM¸!,Tš]Ä»Œy³Á ñw؆¾bQL¡†uUU2UTÄyrÄ;C|s~6Ù®µUÞ·¤ks&MEAOê'õÎ5¯m”p;¸[Üëm7¿õ.Æ9Ò „Æ)ÞÉà€8‘úi jùì¦k±íÞ’ð¾$Æ÷—âã½ÖðV„Ò÷èòuãàùÅ«³8;gòwßúèN·Jº9ß ñ9ß{ü%­T“ñ•Oº²‘5U… äk˜ Þ•¼/y¿yùk+;z«eÕ¶ãØl3æ)UEx€‹ü4m߯e.”烜;NV½s#‹Ÿ¹ Ým'<ßßÌ»µG*„ ñÿðý)Š÷Žp®W„½ƒ02xIEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/esrb_4-11.txt0000640000175000017500000001143110304376230024705 0ustar pabspabs4. OVERALL GAME CONTEXT Transcend is an abstract shooting game with geometric, morphing graphics. There are three levels. In each level, the player guides a ship-like glyph around a grid. Minor anti-glyphs (enemy objects) attack the player's glyph by shooting at it. When the player's glyph is hit by an enemy projecile, it is knocked toward the center of the grid. Players can increase the power of their projectiles by gathering elements from around the grid and bringing them to the center. The elements form a visual collage, and each element also represents a section of music. Thus, while powering up their projectiles, players are also arranging a piece of music that plays in the background. Anti-glyphs also attack the player's collage by shooting at it, and these attacks push elements away from the center. The player's goal in each level is to destroy the major anti-glyph, a large enemy object that circles around the outskirts of the grid. When the player approaches the major anti-glyph, the anti-glyph attacks the player's glyph with powerful projectiles. The player must shoot the major anti-glyph many times in succession to destroy it. After the major anti-glyph has been destroyed, a portal opens that allows the player to move on to the next level. 5. VIOLENCE --Violence Depicted: --Includes depictions involving injury, damage, death, or destruction of the following: Humans: Never Animals: Never Creatures/Robots: Frequently Vehicles: Never Objects: Never --Describe specific instances of the above forms of violence, and the context in which they occur: The player shoots projectiles at abstract enemy entities. These entities are controled by the computer and behave autonomously, so they are best described as "robots" and not as "objects." However, they do not represent robots specifically, but instead represent an abstract enemy force. When enemy entities are destroyed, they morph into geometrical explosion displays and then fade away. --To what extent can the player control the depictions of violence? The player hits the SPACE bar to fire each projectile and aims projectiles using the ARROW keys. Thus, the player completely controls each act of violence. However, the depection of the destruction (in other words, the explosion graphics that are displayed in each case) are controled by the computer. --Describe how violent acts are rewarded in the game, i.e., is the player rewarded for successfully completing, avoiding, or preventing acts of violence, aggression, injury, or death? Include specific instances of such rewards, and the context in which they occur: The player is rewarded indirectly for destroying minor anti-glyphs because the player gets a break from enemy attacks until the next wave of anti-glyphs appears. In other words, it is difficult to build an element collage while anti-glyphs are attacking, so the player will benefit from destroying them. The player is rewarded directly for destroying the major anti-glyph by gaining access to the next level. --Violent Sound Effects: --Includes all sounds associated with violence, injury, pain, or death. Violent sound effects heard or depicted: Frequently --Describe specific instances of these sound effects, and the context in which they occur: Each projectile fired is accompanied by a musical note. Each enemy explosion is also accompanied by a musical sound. These sound effects are abstract and do not directly represent a violent act. --Non-adversaries: --Includes characters who are non-threatening or non-aggressive to the player, such as bystanders, pedestrians, and teammates. Player can injure, damage, kill, or destroy non-adversaries: Never --Blood Effects: --Includes all blood-like fluids, regardless of color or how they are depicted, such as puffs, splatters, pooling, or bloodstains. Blood effects depicted: Never --Gore: --Includes the mutilation of any organism (e.g., humans, animals, aliens, or other creatures) by dismemberment, decapitation, burning, or other means, and the depiction of burnt body parts, non-identifiable body parts or "gibs", guts, or severed limbs. Gore depicted: Never --Post-Mortem Damage: --Includes any damage that can affect the body of a dead organism (including humans, animals, aliens, or other creatures) by shooting, burning, or other means, utilizing blood effects, gore effects, rag doll physics, etc. Post-mortem damage depicted: Never --Other: --Please indicate whether the following themes, information, or actions are present. Crude or vulgar acts: Never Acts of discrimination or racism: Never Sexual violence, rape, or attempted rape: Never Suicide: Never Torture: Nevertranscend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen5.png0000640000175000017500000041451210304371203024623 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ ="Ùª IDATxœìwtÕùþß[ff›z±Še˲­âÞmÀ`Zb[¤BhIHB!!=HB œæ*ÛPBÇÜp“%[VµºVÚ¾sÛï‘dÙ’…ÚJ»ü¾ÏÙ³g4;mås>züÜ÷¾)¥`µkW5,_>¡Ÿ=Q«;*`åʼa\ᤵ±råä‘y¦Á¨¤¤ Š‹óG÷¦¥ÖFqqá€N¸óxxcïO¶o? «VMëÞstÃ÷b®»>MˆÓ!þ¸„_#7GíµqÔÎé¡&PE³@øôÖžW«Zu+¦fNrÀŸ¶ñëõ¥mÛY«WÏì¹ëÖ÷`ÍšY#x¯ÑÔÖ­û`Íš¹Cø4´yó{°nÝ‚‘¸Ô»Öƺu ‡µhÓ¦=°~ýâA…#ó0U ÁºÈnQ~[¸CÙ-ÊŽ wH"ˆáÎO÷þÄ"»EyKÓþ}_ŵýüŸfÈÎB6² lbHlHªKª³I<9…¯˜¤Ì°q l\Ó}®‰&¶·ÜñcÐnY5äïxŽÎwè"»Eù“¶nÝýpA>Ü¡‹ìå®Ñæ»…r ë±÷aÊ‚ûÊ•“Ç î£/ îÅÅ…ƒ€;$H"€á{7Ÿµÿé³èÜ­9ÿ¼'Œâ[Ùa†ì,lk«¹Ub]M]P]RMjºÖÍ«V±àa®ôîâLìà ™¬ôÞŠ,¸¯^=³7Ü?²ðm¡¼[Ý?F?Ü-çn¹ø!Ë‚ûºu GîCÖ˜ù÷…ûÐ,|7Ü#òLƒÑ¨Yøn¸úÌD ‰ $`xà‹\é$ûÇ}÷ÁÙmüFâšÙ ÚYÈ.Ñéâ‰.¨.4…m,l0aˆ Ïþí=ŒØZò÷Žo>Äx#ÜÙ˶?'çÓ¶m‡ºáÞÏa±náÏA|·m~¸[&â»á>’Ï4` Á£QÎß`×®êòò¸õÖXõ8âÇ<“éS ⇒ÉôÔ®!ˆ!Hà½ÿF€Î’â}¹f¹”ž{IÚ/ø¯ón8;ˆ€W>õÆfúuKÒ'ý yê`Èð‡£ºôRL„µ}np\÷gë”÷®ûÕBãn¿ÔfoOzêq€û·Á¿>§Ô xè|ä~2™>õáâ-Å Ù{jhAüØÂ½[ƒ âGÕ¿ïÚUmÙö[o9uj‚µ‹ ‹N¸C$]üpá Þzü4¤p»ÄnÞrÚ\q53Üt°ËVÀ¾_B/éÓ7™W÷÷ýõ'ní´ðØàXÔ0C6²±°!@ïiá¶›Ø&â& ßä‡ðøúD0)0 Bàó!“‰u¥¥ ›p‚6o~7JàdW›6íÈÁ£Ç÷nš[™LÏ þC©(Ü#ø!îÝúîjøÁjØöP$‘—-Éæ³§óüéÒ©ão²°™NvggÀ¥¾ßM<úó3ˆ_¹Ö_ùÔõ&¸j7åÊ.­‹òV>c3C6 ºìÉwdpdp†I©–ÈuòýÈ#$“ù?E›•ÒŒÉhêHi”øÞ¶Ç\à~>õoá£'pïW `ÄÒ¹¡îßXßZw­¬‘à7×Oâ+gK‡.œºthÒ¡ ‡N·½ÎÂvvpÓá/̲NÍ>ò[k#à–×Üü« qå›56†Ey3d³,|/ÿncÈ&L$T5vål‰sˆ?§TfíÚ¹1ø…ÐÞ÷Vôd2½5@ÄG–ïÝûùlû‡ ñ;vœŒ¸[>⇸ßP Ÿ)†Ï¯3ÐÖöÕ;=·>ÔñÕŸ°°Ó ;¥]]Ú á0¤]CØui7„MçvÃñÆ«Ìt²°“…/ýbþ=€·ø&qîü+ª¾þ-^Ó]ãí÷—$?»“ƒƒ# ÙÌ:#š»®f/JaÈHÊú“ $*L”IDZ>èq¥”ïŸSZ¶V®„ÿ/÷~¦/}(-|T¦ö¯D|¤ê#[Þnñ}ùò‰‘x˜HkÇŽŠ²2w~~bl‘½§,¾[¬ð)ƒÉdV®”ö*ðéÓÝ=­¤PJ€RqRI®¤TŠ+)@ %¥’<)ó÷*ˆT\¿}<ÈsÓ2T* *„ZÇ/)”JŠ™ëo€×î¿K€~Ùwtòc_ǘ¡{o#äH É“ùìϨƒIS˜ö4‡ý¤mõZÿ;û¡E:]Ó YÀïžÙöÅ]É)Ï,í Þ í„/Óíº­Ãþf5À'·@pÃõôíÿµæNÝsç#¸Gs¹ä@¦/Y|·X‹êIó˜Mµàn¾·"âßctîÒ°…Æú†®Áºø¡îž)Ë:x–¼ã^vÏC÷þÒßï½<ÉBNv±°“…\,ì2C.3ädagCÅ7…aæ’5•L]O½) »ÐíB7j^¶ü> 9÷>³Úg}ªþþèäçÆ.óÁG9Ëw$;N|ìk'.ù\˺kã\Ç 1á¥ôúŠh QøÝ3àÊxSJM†¨ &‚0A2``g`·Ûíw2ç¾fÍ,µuëÁ¿ò05ÀLfíÚ¹jË–}£õ\#¬n²Gçhjÿêß°ßµ«Æ*ÉÜcÑÂw'ï+WæÅVòÞ[´ðìp÷}òfAmœ‚’­˜c*°&ˆ&D“˜$}âJB*aÙù©{7€”Z?ñQCjõΦ„åq+@ )(qÁçÖnß~ÄqpÓä©Yúîê€~딣È×\îΙõOBÄJx½ @Š Úíð½? áPÁœvêõû§.Óí톽Ãqó+6yŠì¯€æÏÝÝRãÉÖÅ''ÂS%CøÊ”Å÷5kfGîƒÒ`[ÊX|_»v^Ÿ)bÚ¼ùÝcÇNeÅÙ{ê|.žŽà=víª±6†æÜ—/Ÿ¸kWÕ®]U±‚øžp€•+'[ã«1Šøââ‚’’ã%%ÇûAüð§/¹6þ¹çÕ·ýÂê&©®¨^WÆ7¾%1˜H„gº~×⟜Ý) )eωHªïùTÏóû“·RXµjú6e·OYš¿§µõá¿9¾r‘BµYJI)ƒ]梴 ~dÖ¤ À¶m‡³kÕ [[ ÿÿlPŠ¡–2ÿ'Kë×/Ù´i÷¦M»ÏAüˆùw îË—ç û:±áâûlQëúuñC¯p¤Žýt«¢šõÊ ÿÒsЧyöa- !Ob†yI‚+Ž©7Ø÷¸•ÅäŽK`Û¶#Ú]S‹²ÿXf$hñ7Ï'È›šû7Ï)‰=¡d'Âí&¯iÆnÓ–'‘ái¬÷¾ïš©SZ:Ò–övÃÞ‘ÜTz¸"ùô{m¹S$-³K”û£ ©¿ù’îL‡G²poEƒ…ÜcÑÂ÷ ÜceXõ|êíâG&)¸CÙ£¼œæ|ýg,²wÒÄ¢ÎÄÜ è®5Ón_1ýÖÎøÊeÉ ‡s¥š‹›S‹“*%1.dÌånIx«å³íG^í7jNк cçß±Ngs–@úfø}àþËیم0âÒã…4BA*@“ I¤/_˸¦Ç;¥Ðdˆ †GyW~“ƒÍ$6¦!0Óžf$¹  ³Z“EJÙÇ0ˆf¤Eö âÏMýÀŠø˜Ópù¾kWÍÂ=úµcGEÿÍÅ>|ˆúô¥áèÞ5p߸'’ʃ®˜ˆ娢it\:§§&‰‰s>þZø‚¯ÙÓã¯Êý ÛOW¼yÿœÂ<8jÌοzƒò‡0g6! 6ã3â$èá0  ¤qSã\— KIN;Šƒkoj|öû MªøWÐ;µì)_º#_àÎ;@¸gÄÚÄŸOc…ø‘êáCˆÅÑÔþµ~ýµiÓÛÝ{†•ÏôÜGî;wVÀŠѕҜ¸÷{dl5Ý)Íô ë­khRQ DU ;¯{¼ó£ÛV b¡*Œëø†™Pà—*ÖÛƒËç+ÉËäÜuží…š?ƒ)ÆO,°oÛvV¯žþúuü0ó¾SÄ"!"Zض…L^Óâ»<;¾¼T³ù½ÛÌë~ýxóþÛã¶PñÓøzòý×1·éÿå#©ã¢€þ¬ àÛFì÷p~rP3â{ô5ýD1±ŸÒ¼ ë×_Ãá{¤m{´!~°‹ó}üxKAAÊÈÛö^|—@mÿ£4 )Š•´^s’2!óyP”&B@A@BP_© ‚€„€ò¤¤TBŒ+,nßú,HHü¿àÔQ?¼_^ «WOß±êW›s¾Y†CD´ú‰7d3ÍúÔB›£:þÔ1Ã>þ_{ïÓìÞ~}ù«÷´·‚‰šÍ{åGmŽÖÉ/ÿưä_CýmË)0L[ _~v„!½4š|ÄÒKÑÌ÷T¸oÞü¬[·h”ži¤Õø¡ä3£“ÉXd·(?æþÊ«1«‘.yz ìW—&‰‹Ä‰Žÿ´9ÿÛ`zêlüì7›®ÿZãš/qÓÅ™S6aáp †Ì`È †X(Ä‚! ñpÛ)gGc˜qgíÁ×F½õO7€J µzõtضíÈÊ-sØOüM ¡ ¡ © ¥3ÓÆÂvÆuéš"¹ès‹¶m;Ì•ž;9+'?+à—Dܤj4!¥ Y@3…@4b¸å#ü;é¥ÑIi"·®^Ô¦4œ¾d‘Ý¢|LkÐþ=r™LŸÚ¹³V¬È…{õ©g2}“¾;y/..ؾ½V­9 ÿä:h®,Ö¾¸Ca¤b"YQÕþ'„RWŠ#%P<>ó/¬¡‚J” (uþؘ·”Úš• e\ò¾·’(4óöº*P€›:šãgç¯^=ýðožh<ú΄K˜Šøƒva6$æäK8}Ð9þÏüû_z©<»¥$??mÒµ_¨¼ãáê= ¶ë§¥æ4ÛôæôgÿŒ„PM¡¥ `Jxdô GD]ü(AF›‹lðÓ.~Ó¦·«\ýûÿWC©0<¸ClVÄ÷„;¬ZU¸}{éöí¥EüÍëc 0Bàá§ÏúôOkdøÚŒ´ûžØÁЦž¾ë'R B6Ö+!¥ÎÔ‰åA P¼¥òKIÙ‚ TA A¥…@eVSiuâ©Õ[•ŽñüK̽»µCdϺµ¦B¦Å§5<ñÔûK/ÑíÍêÄ´¬9ƒ#ÉÀt%IHóU‡=¥­ÇS'Ð))9Ê -É‘`Ê©$“vƒøüÐ" 0RiIÍ7~†¹IÃ/'b¶~ýÔ¿áhc`Ö_hÃÇǺ@„cßøI%¥”B ©OÈÊ´XŸœû„$• J)ŠNßÜØœ}NŸ¤TýŒâ NoÜ e­¹¶¬<ÌÆŠ½ûëSÆ\´ðØžß$B 8i¶W3;Ü™K…¿Á¨ 9\UošFŽ ä‹V%$üýÀ-Jƒé×MpÚ[Ò§nu>[ ˆÂåãÉ;'aÒGá³[ûüÞ‡wU‚Šs¸Ásq‚#Î3,8â Ž¸‰;ßîzïÍw"̦š&M˜ñ°cÇ0ÿq»5:pïÖ–-{`íÚù£s»ÞNK™X´ðÝõ3ë×_0 ¾r&Ó[£ø‘ Üwì8+WN‘«EBçØös48ÄÿøÀ0„ϰ¨|N ·È.T€%©hðÅG:C:ÇÍžÀåX@8&Q‰ˆöñ¥JˆäÜäÔ¼NÏ:¸ *]„¤ JYYÝœùJ*IÓÇϽ&¯öÙ­ Ðøu×”™…KR·m;œŸý‡žwàñV æ55&,iiôÛjÞÏ• KnmÛóBÊ”½ñ¹3N¾Ê;¶úìvojqFæÂÿh!ô9v–¢ —I!|4šÐ`žþÁÓJHÅ¥RrÑEöñÝ}ü"F$#ŠÉR¿™ƒ± „c"ásL¶~¼ùÍÆŽ‚Ýß~xD,ü˜LL[¾¿_Xl!¾'Üa þ}ÌániÔ?ÌLæ<׌j¾÷wKƒCü£Ÿìdz÷ëðóJ¶óü"¥ˆTD‘Š(E„"J)©T¤%µùû—ttZÐL‘ˆrD"pü„ã® —A ! !IjTPâÄñõbº¥”R ç @S3(˜òU(¼0Žì½­ù¨ —ÅOû2Æ×÷Ífpæ¸Ú”¼Œv}º+õÝœùó `nÿa½Ž#“O˜;«ìmöö/šâÑœ;ßÑ}§m’Ñ€Oóù5ˆÓËD«o÷©¯Û”Ï!½ËS—\Oº¡Œ{`š‚»1Ýëœ`ÑÓ›ŸEó3û;wj·¿Ž] `Sg,3LÄr&Ó[£ø‘ Ü£ñ½áýÏ_¸CÙ-ÊGHÝ™L$ê -²[” ¡íû@•€ A<@<Àîç`~ÉLé¤ÊA¥ƒ*'•NM:4éФ]—vë]6]ÚtièÒÐ¥®¥™/ª\…rUÂl•<[Þðò§æ¯ÏNË_•V˜œÎ¥êõLO8Yš|²"íðn½ôàéúñ M9vݱïGê’õžšvóõ_»9òjÝÌY¥"{v×LX̛С:’Y4‘Ø}•»ß¨>'‹!sÕk»òÅ©EljßÄLW>Ip*Fi¼Î/ÃÍ"s’’*@ƒŠÅŸB\SÀ%â ‰„BÖ6“Àp‰„ê|q…˜DL— tŒ¹ÂLb®pçÁqë`¥}ë œpîÃQäŠ %‹ìå#­Z cD´iÓÛ}ÂÈ~ðƒÞ'ìÚUS^¥üüÄòòöòòöüüÄ‘½²µ@D¸Â=??¹¬¬­¬¬-??9rw JJŽ—•µÂ`à^PzüxËñã-©|ôԵд lv½ ¥—¯˜©t U:Uš¦4*©&)U”*BÑ%¡k’èS‰u‰µ“ÇãÚÚ€©DÚª;Ò /v‚çxz™žNÚñ§ôEµ OŸPÈ-šÃ‰íáöd󤫰ÀSÑän’ÀIJQNBîom…¿êSIö¸æº£¡°FÂÈ^Ô^ÇÚjÛíM-õÎf¹`ÂÒK%ûù4âAMä2™óÜn샚îçÓàRš£Ÿ—Þ €ØÕs¤"J)igà.ˆTD M *$•¢ó%=ñ®S•@%h+oODμ€°Ÿ?(% .¹„â½2DeX“a"ÃT‰TFãü‰ËNŠi)ö`Ý%ŸšŒÓsË7>¶gã¡øE™Ü0IЧwL¼4'Uï N÷N°Íc̼büâÙ¸á]¬Â-û^ö·Ñ¢åZÛ4¿Góû¡Ù^w‰ÝÑá8PKêN’ô›w½¸;f!¸°¥W°~VÂNzÜy)"0áÆOÿG’çÁßúvîƒJi¢Á¶Ÿ£H§4‘¶í›7ï€uëGèúƒÕùl{·Îå{Te2çÓ"~ô'¦Ž9߇ŸÉ ñ?X-}o²›æHI•$JQ)ºàn1]v2Ý‚û‰×lt¡¨BÚò;“:™Þ w~Ϥ"ᥠ„0„ÐiÒ á–"L¸ž"üºÑŒ5ëA‘UäøÆ÷ø é'+= ¸.-ïPÈŸJ9Êžš~ üQÀxbRzë®Ç\‰è#<øÊÏþŽË¿àíæ÷§Ô¨Ÿ² Cz¡µÝ¦˜oé"»³Ãu¬倥7Þýü×^é9.JÈY¤>—Úgpî êÜwåøö§=O7™ â£î–"„øQ[W/zÿp‡sò™˜€;”—·ÀðSš1é:0†)Í2™>5Р滫¹¿–ß–§4MQª4MRMjTQMiTÍzIª)¬Ÿ|Io­5¡ŠhW}7eê2;h4 ´ë½¶IüägJaö±yRšR)–œæ9*- ì. †¯¦%ì1›ß:âibÑž:ÉÑTá«=ÙDãð¼ —j÷Ú”oÊ¥‹ .Ì?VÎ) Î^ä¬zmŸÏís$Ö'¦{².:áo÷±`¨•Ç;[5îëÈŸË'9t[½ëÄQDÒ÷:Ÿ¬¹©ðAp‡nÿå™LoíÜy V¬˜4´ÓG9“é­’’P\}ƒ>mn Ë^Á§‡í“I¼²;ÝÓ®šÞøK@"#>”€É—ƒäuÿ è!o¸ª5™V:moÄÝtΜ¦¾JÚϦy¯RwǦhöxtpÕ=+¶½§6o~Ö­[0ÔÓG/“éS›6í€õëGñƒ…; ßüæÈÔ©ñÿø˜eIÃÔ =p·TRRÅÅS#výÂÝR߈ÿÚ*ÓßÚqÇÏâîû¶"U@$ô ¾œƒ>#ágz-ùä5‚SÁ‰à´Ç\ŒN9§‚Ó™>wÖűÒûúqüó­UGüS–MP DJ"P¨îÍG«Nz#ÈŽó&¦ûÕü½ÿ>FD`fñ´ÓÇÝ@%ŽK.{/Uím­ö°°Å©ãŠNŽ”8]&¥¿Ùn IDAT%:4¿¥–døm6·&ê½4]щ“LSR©9Ÿ ~ó.-;“z}ìÖ¯äþá+ TÕÍ”ýá œ †c`8†`™¥’s+jÎÆ=ÎÏÓœ9ðð KÝ»ù‹p·4dÄ9Ü-â•ÉôÔ™|æÃmá£$“é­È!>‚s—z¨¾y•pwÜóKÁ©0iâcwZpŸ¬—@%¢‰9ÇlI¦°ç)ÀÁë®êÁw*x—ÇçTp¹fyÁ´NÜ3:ýí¯xÎ:nÏwT0ip¥_±ñÓ ‘’ðÂËâ,˜=Á™f*w¾äÓDúìº÷뤷9>«6sZŽ¿)£ú¸·­Ê›œí\q¯íÕ?l ”7ëzfX» ±’……+§0œ‘êÅ•/³SY “’•šyƒ²ã±¿ka/_ýIÝæ7l¾ìÇ¿JUÝðHùïÞÀˆàAÈë¿À»ß 0œ(ÖùF3j*5g^x陿b‚¦[7ä—¿uëÒÒ†ÂÂq1Gön ñÑ3witø>d¸Ã9ã«VÄG-Ü-Eñ£wKg!þ «L»çÞGx'©Iò¿n3Ëj-²K WLS•W€DÚT©°*ÁêU@Û—÷ôïœÓ.#¯Yñ½àš`4ÿõ/†¥ÍÀ„jZK»«ÆöqSjJ³ƒøšYW^Yâ€3Ý^¹ýe §}ýäëUÒ×.Ú2™>5‚ˆ…L¦·Î þ¦â°ÏëþÞã‚SΈïgOddë›$hi—Ï„¡ô)ÚEîT T)"+E;Ñ(¢:ÛÑhRц‰k»óz îu‡Ë:¾ß|²áäìÔ‹ZLe7¥ÍTö·Ý”v“Ûü)W$§Åqe›º¤ÑH¬Ð¦].Â2$gë[ª_Ø=ya[áš‹KfWî©Í5…FÈp>^V¹¨¥$æØ–]:õÔÞL ¿ J¹ c%¿?­µÆW4ï>‘¸á Ãá3ì>ÝîËúûoR¥—Þ‰qg¿0ï¡JBy'â)뢼èf}×~ Ôëžzç@3æuÉ"ëRDÐ[ÞDΰ½?¸[á ¬Y3gË–ý°ví‡ÜÂGI&Ó[Büpl{·:ûÏXdï.¤‰9Yd·(ßS1÷Ô˜ÀºÌ{ÃÇ. wøÛ¿ÿ¤ŽÐüÉcÖ„Õ”U³ÒVÏ€x DŠv$ãtåÒ¤KSN]:5éÔ¥CS]Úui7¤]—6=¥þùÔ¦çS[þ›êþ¯ÿíWêÞÍY7-!\GA°¤ÌªC 9˃?bf¼Ãth\w—ow¸êßÑ]û^0žÛþqEéø‚ø)ù• …’B)ˆ"‚¨ø”r`2wäð´ŽzÏœ¹ãÓ ìÇêk§ä·%Íœ¹àÁŸ[ß+XU¥x­á@³vÕ2¤Ú@´RÒ>鹟€Ç.¸1@\SÀ!aFb 1…¸D ¬ˆIÄ™ Xgc`¸ÒZ›°Íù WH(¬N5ä+o¢¸AÀºÈnQ>e‘Ý¢üùµp‡N²«M›và5GîpÎüÕ“‹òL¦·†oáKJÊÔ(“½§ÕDK¯<*@?jŸjˆ‡6þþПE’ç}i_Á¼´Ýgxü¨Ñ õ>`M\  x§õ¶ìyüŒ À¬’ 1Ýï1IÛ±=M^€8WÀq@BWÚ·_ÃIC;ľ…‡ó»ø˜€ûjáýô‹Q¿s穲2÷Ô©‰1Döž‚‹·ÈÅÅùy¦HN̵ϸҦ‡o»£ïIOßÉ“Ìc®™c…쪫Ổ]Íß…ÖÙù]hBÒŠW¨@Z֌ԙ«\€<øËЩ¦fûdaû¸`Hcܨɏ-ëœ-ØØÜTeËZ}׌å¹áW Žü§¼cÏ[ (Ò½mÇ›ÝM™¶x1ç†Î\UÇþ¶½£mwú¬É K1K>ù;o—#ÅíÉ*j•¾t!c©^7c¦Ïp¶&çÆ?}hëK‡¶¿X´! …Dë»Çâ“UT¿qzQáøº×pÅû†ªŸ2'@;|´ÝGÛü´5@Z‚ZGX"„P½œ…ÿÁëO·ï=fž<Ö•ž0pÎÊCLaS!.+dv™wSiþF &/ìÌâ9€Õð+ûcÀÂ;|(\|·bîÐEö¡ñ‘€;œoý¦X´ð݃«+VLŠéaÕ[øn¸Gü™ú×ʕг˜ÏâûªUÓÎsßæ­æ·dIIÔ¹ý#;;Ò¡Uþ‹ ¤I¤¥$ÍütrgóDÌ ßTŠ´.»œ•–2f\©œ¦°sn0n´Ð¬¹•ŠÒ™ß½Pg¿rì©Ý!S;ùR=š*¾Àí¯Ÿœ‘Ÿ¹x¦gB¡ûþò|ý1?JHÛðèÚ`“»õn'ÓÖš$4ÐP0ÜЬAB˜ªUaÝÛ&ƒ¢­z2ü@Újlr‚Æ<.•y…]Ëï‰ÐRL¡¼Ë¿sêH‚Ìë™Ú[É»ý·/’´¹#ÒbßÅoÞüÞ±c§‹Š2cˆì=5?²™LOÑ>÷._ž³kWÍ®]5±‚øžp€•+óvì¨Ø±£"_\<µ¤¤¼¤¤¼ÄGC&sFT¦ ¬½V)"šDJM *%‘‚J¡In!žV=–TOœ8ãKi]mÅ0[~ÅKÛ[ÃN`i‰”qÀkbÄP­§ò–=»¶û”²?½Rýbµ@†À¢º3ÇJ4tš.ÿÄ ‹¸¿–…ä¼›V~¾RÙ⥠ÅÙïµ"‚YâÅÀêkh{£ðOÌ£6{7?‰µ Õ‚X:CõíÚ¬òÄo´´·bʱ³ŽÁY QRuHa¦0Ç“g &‘RH)$¢ ‰œ‡&ÍEB€R@)¥+Û£/‘Œ¹çk<"+¯Æ¦bõ¿àë×/Ù´i÷¦M»ˆøÈÁú_5V\|ŸÅï1má¡_o]p?βðw¯6½õ_¸—wÏSeTpÊBŒ6oz[(ƒƒ±â™Oô¾[ž£.]v…FJÅ~Î`Îx.ìþSÊ”v¢¶D›ÊYÀ¹±ì§Å'ÿðDÍö£ëÎI³¥BÓܵµMmú¤…ÉÈëÎZ˜ÍB¨ aW5û«öãìÍ6-ÜÑÁsæ¥Óp^] â3´@R‚žVC êzÐÐBÜ—y‚ÞŠt‰5ÙüZ+&kVUŒ`mCø fqãÆŒÎÜ6«œÆÐêh’O™×eØ9ÂÒJíO>O3gÃ_ú€û3™>£¾{puݺ1=¬:@ ¡L¦§>p}íjX¾|B„n?|Åú̦~Ô'â£%“€ºo~i±R¨í‹÷wwèn6 ¸ÖúWÒ³ç§O»}yWùìJÖp°|奜멵< çf3nœ¦tˆ5’ ™szë[G·eU iŽ s„F…¦I ªU¨r×yÓ‹22tJqÎâ<•ÿçÍP}+¦J1—¬agÌ™HÀLO;N·dúBnŠÍPåÇ..‘&°&‘&‘.±&ÿ×N¨À”ʱÆ1á¼5`KˆÃÀ\ÆiBƼnŒ¹–ì@Sæâ.¦wÍcâ®ÏiÙ³úl<¸[Š9Ä÷„{מ3âGîÓ|?'“éS&ÄÇ„m?GÛ·]´ñ«IŽû+?îì7ÐÙ~€rNÛÿúGFƬÔÂooèûüVjß«ùì2ÁôÔÔþîA ™S²‚ǸÐr¨áÜð6¦šf˜ [Ýé8®lŠ&MÈàœ`Iˆ Hb¬ºpÇÏ\í˜0!>MÃÀ³æçÖmBÀUe­»¥^ŒÄƒ{[ Æg®X=íòL8þvkõQo ÕÞ1Þ‰u‰44‰u‰4‰5‰tÙøßöΔÆziœÁ›|†3Ž`ætÔÄñ”¹Ý9;&ã΂™¸Í;µœYðøyá>üL&†ßgñ{LóúE|D3™žú€ú‹ìå£J;t‘½»>v‹p·dW~űv!ìBØwn÷ü©Äóä.)µ¬é ç…;@ æ¥ —6P:c¢Ú‹ãSÜÍ §OO©|O4Ö´76êM-v؉ˎlª¢ÃI4+9XãHãjåÉ¿Ÿ|lå/?•5-£n}öü©z½Ëˆ/²¥Ívffè6Ÿîô7ì«hy~§uë‚ R&ÎLh<À‚ɱàX ¢8–KŽÇ’e/·c.“ˆKÄfqi¤ëÜÛŠ8„¼)0apÕUꮀpB¹¶ìÒr# wè$»Ú²eßð/9mÞüÞùf6Ydï.¤‰9­_¿@uûônÜáý»¥hsñƒmûÓ.¾¤¤¬¬¬5??%æÈðåUÍÍåŸ}0-5Yt5óÿáYŽŒÔÂÄÉ|®ŸS–.‹ÇÇäϯjàT˜Ø±‡›ÛYX†éb|n¸ÍͰ…yRà •´Djú¤©JhšÐ¨ šÐ¨´¶5MPšþÃeŠVÛG.íÊþ Ä59’Ls¶t˜š—§ƒ®<ßQ^êÊ›œžçœüé HÉÜöª:Zž‘—iÇ”*0ab\Ò$ÒdýNog‘Œ&,ßmçE×°Ça`ŽÙ“pçTÕNóžZVð»³ø>üL¦·,¸¯];o¤.8²êÉôuL¬»ø34L¦§TÿU.~˜+óýŸFS!w»Ž Éuµ\{ø7O†}Z)ºxÓ·ú‡û¶mGRÊöh'‚û Hˆ«ÕZ] Ç·4-Zf[r‰¶ú?-.8¹xÜÆü‚Ñ9ÑÑ91Ñ9¶¶uF þîÌ4éM§m¾–ÀÅG?ÿíŸøš« ƒcèÀÈǤ]2F€%-œ7ÑÅöÀ©§þQùä3éÙÔt8[›¡­±+®ûZ4O VÂrñDñœÕâVω˜Ä\·ì¼4ÆÛ„¯ eî+5÷V©{êÉ* £wè"{tZønÛÞg±XwñÝ}¸Ãý»¥1wñÌdúT,Zøž™L «žÑM«‚Þæ;\«« 9œ:]I“r~u{?'mÛvÿè–´ºòà¥ËMÝ™üÔo:?{h%ƒ#›@†ë[OtîüáÊðÑÃîñë:GS{XxkOëþj³¢µàø>-gúé/ÿ ÿ©à  CÕâï(¨kHS铈æ®ß[“–óîû|ÕÆ×¿‡‘.…§Ããw%{+ÒÒyJš ¿HIÄšDš@ºDº@TÖoöYc­]íSÖ=ú*OµkzA¦>¿h\Ë €_÷÷ÈAF¡‹lÛ÷˜vñÖrØEEãG“ì–ú®B îƒñçîâKJÊbñÁ6¿RD‡zàÇéÈæQ´òs×Îê9é©—,¸@Æ{¯€«¯cìwpf—ûöæW$,à¡`85‹]P]P*¨.ˆô0îWñ¡Ê‡îÍ_”T>y…™˜¼öŨîu$ÊÌWÿ ö$U=íZl "]!ê­C€Á7iªnŸëÛ¤Rî¦Hj¯*,tSæYã«XH—˜J¤‹Ú§Ã„2«h‡tN}Ú©Œe*-3HXÑ$¤2ÀŽ£?1uÌù>̵`óæw`ݺE#öL‘TïÀ}ô?h¾Ã("~˜™LŸŠf¾¤2&/'frŽÜWŽ{ù¯ç|Ô'âw¬ ±ü${G¹5ƒØàáYå‡/6Ó³8ÕÑÕÕÑC¬¡ŠL‚òo<\~õOä·‚‰æúµT÷*Þ Q/ØêþœÝ%`€²¡ÆW!MÒ†dóè[á™Ú”)Ú°!‘!°.M4ó&º«‹ŠÚ0zQ7å%ÒDíS S®54t.ŸÝµ²všã¸M +’ˆŸo°¾ÅXu+Ä`&+ˆïåÿÇ÷3ŠÜ-íØqV®œ<²—¦^á>šk«M,=„ÔÔõùiOÄwî;ˆ«Š"•ª#[”NŸ’…2Xã:uòÂpv– šÅ÷›ÕUÚ&†Ê®¸Þk|}cŵ?Ì÷öãÒÅÕÖH5¯OSmµñ€}g% ÂöŒÀ!°5x¹Û8¹áÎŽC­ØذšÙfë ™ÞQ9¹ …`†±ÐfÎ}'€ 'Í@D`̉Åw›+}ü ûÌBøQç€ê(g2½5úˆñÀ=úßÇGñCá;ìÚUË—OéçéT¤‹ £ ñƒråÑÎ÷^í$ÏQ7ß ÷ï¬  ! `Ö¼I>EhhR Hå—à—àSÈ'Y!„ªõªÆ‹Ì Y‚j‚ê¡6ÓÜÊ!ðG.z®¶îs` ¥#ß{‡ý®l´ö20H4oö;Ügâb_X?ÔHR-ÿ›Bݧ.^îó§a]Zp·Œ<ƒ÷ˆ'§­"{|%IÀHÚ‘”¢YsÊZ.îlìN8ÆbÂÄ÷Aà~>&⇟ÉôuÍèåû@Š £n~SŸŠâ#gÛÏQô ~‘K´#þƒ´}û‘ãÇ›óóÓú#û×WwbBØW š¢ø2?¤NJHðKðKä—Ê/Ãã1˜¡ª²KÌÜ,+¬äÎiFé„ìÝŒe€„VçÕJGÕÃ\ ¹ü"LÚ³æ•8¤Ï.;ùN¶öñ³KìÊï~ì_{øÝÄîp»389|è »ÃGtŽ454I“ˆJ4}~S›Ûû†{êâË0 ~{CàçŸÆŠÅ0ðÊ·`Â'Í}à»ÑwK[¶ì€µkçGèú£Pmˆ”+ ¾Ãˆ"~ô'¦Ž-ß»iRC,EÄ[á ôÈg:-ü†b :¯ÒTóW~À°ÎFïÀ°¸ŒBÙ1 .  Jä—àWMsæ;ï{+‹|UYüT\ß}·ûî¯.ý*è4d¶;°&ÈǦgn3Âí.& ñ†°7L:ÂÈÍÜ®t`äxí—«±6‡ ç#­«Ë˜&‘.›ZÛ}¯´Nžfáüï_ì¡«1p,°§Ì± îÙ%™LoEñ‘ÈdÎs£hAü"—ÑAüpù¾sg¬X1\¾U×±BüÖY=Ïub ñ=ánmlÛvdñnIo(S Nž šª»ý €!àYpg¨ñ @$„a!°B¥ ´Œ¼„€lš³P?´GT„ãf$Ã1Ð’.IÿãƒÖ½Þúê?@C #yèåðI7O8éŽÛzðxC¸Ã¹Ìs2ýøûË4W’cÎD|Ö…wDe“Û5™™ãâïM",<ùþ›:¿çC+áÎ( ÜϧH ~ÔàÞu»1FüpºŒ⇻þªEv‹òCÓΕ;wVµ”±ÈnQ~Ô4Rp-u«öYÿÎç]â §u2m|_{ðAüЯs˜¦“u¿˜ƒ1'gŽæw.æØ&5›ÐlB7¤nºM†0 iÓ¹´hʲ)o‹Ã9_3ýÖõßùòßá„0L]x ää l(FˆRfS )ŽGŠTHbËÕ‰4ÍCDZäX ¬•ÄJ )°™éN$ES½[_\§îì,óï ÷5kæD'Ü¡‹ì凯~:A~X5Ì–2Ù-ÊGHÃõï–†ìâ-²ÀŠ¹ÃŒ!kÔ\|÷ÂÙ#÷è·ð½m{Ou§4k>-‰.ˆ&ˆÞR«!S‰ÄD¢ß|-X v÷ü'eñ›Ò怀@! !A‰‚‚¢>ù*Þ\íRzû)‡7.å»6þiïŸT:€ !¥#Ð4èx­LOªÉ¼*ˆ[ÚhÈov`_#â_š‡Þ- 5'7.R iÒ> hV±&.³ý `Õx× XM­íH“™9N÷Ÿ+2MøÕí}ûù4R~ »Œ•…©2˜ˆºø‘á; ñ]¶=wD`8¾Gî]WŽ^Ä÷wK}Îojúü=î%Ñ*Â’‰±ÄXÖW*„'ÞצèD|X@H@P@;AAÞ–>ZNëÍ'”Ínú¯ Jï‚»JÐQøø+fPËZ.e³ÛÄyë‰/H<¡@AZ Œ"¢ZC e¥‡8ŒV2ƒº(Ÿsï¯1 Õýö6¬KDe£»k23ÇÙñhy‹7¯å ŸX€»¥á#~Ìmû¦M{`ýúÅ£xÇ«qŒ ¾Ã =p·iÄG:“‰NÄ÷ÓàmÛvV¯žq¾*ïxXMZûJ™”Œ':§”ë5þRÚHü©RsêœúðF[|¼íØ4 ® ¬aUËÅ+†’¦ï®ý'3ý$cžÇâ’â¨×ïåAÍS¬ˆóžœªf%!]B5šÔNëÎßžÚìj¨•“Z¾rµ³©Í4™™ãjjk‡'*¼pñó¿Á_c¤5dÄGO³°QC|$ÚüFñÃÍ߇ {îèßý|Š\_RRþÿyà>ƒ-²[”ïS¹?ûZÞO¾4ùÏ-{ó¡‹ÿ}òy»"Îĉ5It¾£š½^ÕZk÷½r8î]jâEmëM½”q[@¼Œ Ç„cÊ 9óBR*‚%hJéÊļ¡Y1"—¬é“¥ÀX z´Q;ÜH„‰…‰”ÀJ àH /É“:Bt†òŠg¦;±§N4 %²î-Ìtm½±¿ÈѦ¡ñÑ÷QS„z¸¯_¡Rðï|?’|ÈXk”î}*ˆ\&sŽ,çn¹ø1×öíG÷ÁKMÜ÷icZ^Ù¢9o/žû¶&”¢Þ=¿Ïd‚ò¿þµþª;JO6,êhy…+² IDAT±µ{w f„0‹ò˜0¬ä¤•Ìp…ëÞ0$Ã’áæ9ë§\P•œƒ]Š%GÀµpƒÖÒテÇRhϼ(áS&•Ö(+R²ç +V"á;SYÛ7Ü™ï ñ\cÔd9wËÅGHÝ™L$¦žnØp!ÀÈ#~„ýûŠT7ÄÏQ·mB¸[YÄwÛöÑqîÅŪ¤¤tîÕ¸÷©´ðÝÂí^ìñoÁ4¤A`· 2rmWO™\~á¤ç3SY"; a¡7²…-FCÙÁªS N0'„%NÝ‹”DR!Œ¤¢ %Ö'\,=,R³ü;Yx9YxA.ï¤óæ`%,óÎ Aš@%°ÍõmM mX‰üÉÉXŠæz7–Âu_!ïx3ð‰Ïþ÷óÀ½OEñ›6½=&Ý}‡¯‘Ïg,v÷F|f2瑲æ”Sc’ÉÀ"~ûö£pþ"ÈÔ#þÉGà©G@Åê‚ngöéW.ó¥åÙ/œ :E´Õ™X›š.Ê8¸4Ͻ4ßsQ¡W× :LC!„Ê0áH)¬$Rrò•~öT¾çœHÅÞWÍ#šce-¢-ЂyhÁ<Ëž‹ÍïBŸٺMa%šëÝX ,;k%3ÓH‰¦z7áxpºð¼m®»f¨¿ËÑÖ@,|”AFñ£¶®^$,üHޝöTOšGs&Ó§vì8+WNÚ飖ɜÿJ¡‹õ£&‹ìÐï Tc­?¬W¸â½pEµðž6Wþ?öÞ<>®ªþÿ½Ï¹w&ûÒ4ÍÒ%]Ó¦¥-û¾hÛ”n€‚~WPø¨_YEøˆ ¨(¢ˆ.¡m ”E„®Ð…tß›¶IÚìëÌ=çýûãÌL¦É$Í23™éç÷zÌ£ÜÜÜ9÷2mž}õuÞç}Úþž®™xÃuºm· Ý&†Y/¶m?ê©äV9ª)ýøÊfPsì@MäŒ1£Ïp`#©`Eަ6µùO²Ò}ù¨'l«!Á[N;Û,qÖÉK™”pi²u囵Rz­/^:òï?·u㮳/2•‘ÁÂÖÕµÂÖ#žÚóÓµ¥VžuE7ÝÖbM%%ëÌ›ßñ¸‡q®5ú¶}éÒ,\xAXF‹8ßãîF}Fü€ÃÀÊ•ÛGïa„»Q;âŸ~  @ÀS¾ÆsÍ5ìHíHõäýZÉ6÷TíH×7i%uËnÃä¢M{þç{-•Tﺹãe3;6ƒ ÂËòØFâ°ähò*´©Æ÷N쬼±iPòСe®ªMvUj‚œF.µ»­…­áR ÿÜÞäêºîBWBýˆþZ5×Q“NpYƒ™03mm^bv»,€‰½]–%: ‰øX¶í.¾T&FÄGŠïV¯Þ¿kWí¸qñEö€ú€øØ©“‰&âý™LxÈn´bÅÖQ«_šzF>Ôñœ…ŸÑŽ4d׎dG¨'ïS‡-gØxqóWÙÚ‘ÚSÆmbøÂ<´i´é†G~âÝÝ’ä¦Óš•M»v'xêŽ ÊwF|¶™´öñÝ£ö—ªn=§vPæ°Ü-öñÍîªädk2l%l=¤ö%ƒlbþäèg¤ô¤~nŒ%ë\vCÞë‘Ê f[C¦A ЀÆk_øâ§¾³‚/¶=XýD|Ô2™®.Ä@}d¼ÈÝP¾'ЏÃOvCùÈiåʲHÀÀܹ“³\Ž/yÆùœ­¾p ‚ Ä‚ À‚àI#FX·ÞßI‚5•m ¶ — ·z׃֨”Æ6d}¼DJGX‘ו  vO-A)‚"h©=c ¥‚ݰ¯f°lž@«s½/ä6-Ω}‰˜ LÌÊÒ´Dʼ™Êfv»„‘..ºÚ~ý ýÆ!ëÃx¾/–â¥R¼\ŠWK ÙýgâWñwô/ˆp¸‡QñïÁ™LüL«†P-|,d2!QöL¦³6|ó'©®­9ÃrÝófhoÀ¼›aÊ·ÔçŽlÎT|¸)÷š xÁ^"/Ú¶jÅõn)kjÆáCI,hè¸6;c)”‘Ho›Ðð¯ã ƒ'5ÎÐ;^ͳ$ÔÖPN!Є/ÜÏJC©cO—$¥Ã{ÍdÛÝ8ùü•žãWeÿ£î)éæùã¥KAH•”¬ß¶íÈ„ ùñEö`-[ö€ ÎíÍ[b¥N&,>ü|︟ވY¸#’|›mebÉli¶4,ÍRÃÒl1, ɰ*—~l§×¹SÒ¬ór•c+ÇÒŽ­¼–vlíXuG¥líµ”c%JÓŽ>-7qðf8/Á‹Ö-å­b¼ÜºÆ[uôà‰\Ϩ‚ü±žÔQ‡[µÒÉ®j|ã›UîA³¾8é§B«I·]òî4à{QQ~ȹÖxQ¯;p7ê?âÃÌ÷(k¾£[ÄÇT&R‘@|áþåBâ/ã¹޶´¶”y)K±­”ÿKm+mÕ70mit Ò˜’¦<.ÇëRK9®¦"13+)3­î¨£½Ry­Q×\6rѹàwàÕðjxô‰·· }ZÖg®Àwf{÷mÙZ1ÚºiHÁ„–ŠO=pÔ ãØ&Øh{)÷Ë5=Ày?¹=ð¤ïüjµêDIÙ a[†; ¢Åâf›[¤Ó’¶¦²U>Xø Ð Ê¡N/¡RΞíåM7Ï/Ä›ðÀ¼y3ãhZ5¤zÈ÷˜Ídú‰ø0öÛoBrü´D|ìÃÝ(ŒˆO&óß×ûø.«å©Ö­_½J9–Ò¶r,¥,íØŽc)ÇVÊ:t¨E++{g%ó1¯vË)y'v+DZsÇÌ-ÊÑ^©•d¯<üIöPÚ´Â)óÆC¿@üñ}Šô3_y €g^’TÔ0ôÆfáŽá{OŠ O'ÄÇr&RaA|Ø÷ŸÝhàŽ†wØ©Vv“ÔÌ’!5dÝUÅÊð]ÜÛ‡5+Ç5xo%Ó‘ºcÌ#‡ ?"oRž?Ž&š/ßp"mê8Ö ZO_o_õÏ2¤LξâB›_ù$ñŸwÂzr¸ùósv~H专¿§'§Nžwõ¸›nð=¤ó*ÏÆµ{Žÿ®Ü–Þ‚«›mn²t‹¥Z,§Ez[“Tž„g=(„o·U’Z%„#¤"¡ýçš§w{š›…vR))ûü`¾ 啾/=ÖŠî¢üè(Þ+#»R7ˆµL¦³úƒø0ð½çàŽkÄøwp°rå6ÅÅEý!|u2¿½$ ï_€3h µ;[í°4ËÊI •c+e—jq”íìòâÀÑ)óR2Î-®˜ L·Ž.>kó²2h ÖSç·ÀÑðêÊõÀËÎÝò·‰)ŸŽý÷Óê“Ðh}ಷžÉ„¤‹¬­K\T\xS2¼žköT.¯Köxòfž°t“­›ýpo“ž¶ÄúríIl®}øÊ/I©H8BhèýÄR‘0º²ªZ•—ŸÜ~™tœ},ÍÄ1{mí ù¬\ uÊ•M§âcîF}F|ÿ÷çÛÞ { öá ‹JKwïÜY=n\f‘= >#>"u2/Ý ’zÓßòŒgöùw­%Ã2”×fÒUÛš­Ì-ÿt´}¬zð…÷ÞüÄœcol¬Þÿ‰&× iùŠxs0f¶A›_Ýœ˜¾^5îŠñptÅ  ®qPÞ±—EÚyêªü-•cÕEBz¼Ëg]ÈîKÏvñYðzÖ}¸[8^áxk_kÜ6lÒ1K5ÛªÙrZ¥·ÕjkuÉ£n«Ù[›Þ¼{¤hríƒr)4IGø™îg½#D€ï)íÜ7ERY·@©gaåÀÀ½s&ÓY§âc9“ ©¾!¾ïõï«Wð7|ÙçAâQD4ÐвÊ÷\‘*‚L³°ý á\Ä)¶N¶9Ù¥“]:Ù­“Ý:É¥“Ü*Ñ­T¢[%ºYˆ¦:á¶š®®»¿ñÎKßúÖºµ-?8ðìJøJã ÑþçyêuS[jŠ˜¬]ÿØ Ëf’L’k‡‘v€OoY-’Gžù÷•gìY)‹êk1ì¼³Ñì¬{whvD“#7%“£ÆžÕ’â²E]›¨õPG4U ç8µ±gHFÙ¤GÎûêÁÿºÕ·û·Ó¾8+‚v8…œÁépp¬¼ ¬Ep`ŽåíRzLÃ>²óòåë¢òP‘UÜÁ@ßúbõÑ¿š÷iO¾ø³ðUN|&-pïy¤áûüùgEð™"©eË>Ú¶ípQÑи"»OK—þÀÂ…öü-}á{Ÿw[ !ž wsðñ„;€ŸŒSm5Þ˦j¶XûbwƒuíǺֶÖÖ¾ÕzbõjÙÀå)‹4ì©¿-ܰñ fÌ  ±ù¾×«‘¦ØNËZ¹† ËÕÂf²YXš,&‹…-s[¡¼µ;ëjwaWáÈkˆ¸ðšB8(/¼žƒßýÖî¬ DVvÊÈŒ™_ºÀæ—6ï~o˨K]îF÷¶×\V³j8ÁÞ!Ui7v²k!¶ßäªcÎK?ýI*)TEU4A¼T$Tâ¯`éð½·pÇéÃ÷a½Zô;ê-âå<ÐóÑW¯>°kWúw……™»vÕîÚU[X˜ÙŸq¢ À´jaá ÀÉ;«ŒŸ5`ÕíØqÀøñÙ]]°reÙŽUˆäÂT¬ÿ·v& cÛÖ¶K[.-mm¹´ekéÖÒ¥¤KK×ÁRGI×Ä¡åN§>¥–8úúéßW^Õx¤¢qhAl+窉vvîà3 jŒ¬iCÇÌY³æè&¨F¶3A$u³[µ´4Ùv¢¬²á`ÕàI£'ίƒÔsá.@Òè×צ7Ù£Ž« #Fž•S”ཧÞÜöf²g»}¼LJOúðIùß[²ýßÚ´#€˜M¾/¹›ÿ[85ÙÕÔÔÚÔØš’äJzº,‰6ÜKJÖoß~½„;€¢¢¡Û¶Ù¶íHQÑÐÈÿ ³8”³@±¥ÙÖlM{zÊšµ‡:çœ1€I@”om;°©éü›ò@rãÆ¤éÓ=æ“î+˜é0SêŸ\íØ˜š_édUe5I=jñ+ûL¼<p7}^˜w.Þ̬"¨~&^*gBª‡.~éÒ÷”ïýÏdB»|ïœÉtÖ釸Èf2=4ÏS»ß󵑬-­|™ŒR6+Ki[+[kûð u#³&Þ1wÿª©¬¦|Ô\CvŶfK³Ýö¥t†8ïÜ1 H@òH™‡™Êv¦fK˜y¶càn¾[öìF%˵û,—ÓRßIYÓ¯X¿±¼uÏÑ‚‘ƒ‡_4™¤l\·Jpk嶃I)Î ±øè£»ÿ&‚Âòg5²=¥Q¸k?èÛK'Ü·_Ýà—ûöû‘ý¨;©™LHÅâãº22¤NÉwszR?T'N¸ÃOvCù˜ROà?Ùåðñ¥å4‘ëÙYžãØ+t‚['$˜_•;A»TB¢v%ýKÍ‘kΜx÷$Z "‚Hd2¯߯ö« OŸø`ýA¸l¸,óÊŸ–²uWº&Y]î-ÿ´¶ˆem{ö? ÖI߸ŽÐï™yÙ×·&4Ù^’Ñràâë&¥Êýukߪ_ó:€Ê=Çiò‡,ø~è§÷½˜"À+‚"vÈWã¦1¥¯xÆW?c lØ$=õfrÁÌ¿óë’’#ýi+\p‡Ÿì1^N³lÙG]­l2dÒÄ— Ùï Àù… /<…{&ê±åâ{Õöýô°ðÑÉdj»m&³8~Ã=JÙfí’ãøê¿¥È7-gü½×›‹[®Ç £Ó¾¤a)mùŠâÙR,5ÛGŽ5ëÏ_8wîTsñÞ­uG¦Œ—Ð P ­ca}c®¹æÓ?¾Q} þPæàìqMjí'™hh»’HóÄâ›÷µÈ½i=æÍ™2k4‘ØúúþÐÿ >oNië?L_û~ÖGïÀ1ÞØyÀ!6UðæŒ9öšcØÃ§DyÛ…HÀ±má¶½û:™ÓÉÅ/]úïÎÄïòZ„2™ŠÄ—–îîÛž|qx¿¢·b«õø1öJ¥´JP*Q饕JhùËJÍÖÅ%wœtµ­…[ —n-\Z¸5¹ÌMn7"+>\±b³ÖÐZ³^ÿ"o|@úw¯44¹`þeެÛqtí6eŸÖpkC†ãÝ´ÍòpAVÑÄáW_9jáU¤‹öUdN›pCñäÏ„[ÿ÷PàYª—¼Uóòu]9qzRâ– ÉŸ¬aÔòÐe‚_,Ãʟ̨öõMæ5øýƒÀŸ}sªóæ pIÉÆÈ}àf¿ìyófF¢Íol"¾mßã]Á™LðùùL2™ÎØ”¦‡{7Š» &¼ò™èø÷–›Ïg¦Š›˸ڞýÛࢬQ~»ÃÅ­WSÕÕ×i¶5Kg´¥ai-5l¥åÑc-ÌÕ–K&å¦C0¼G¨±JßúWû©j3Ô‘u;þþ삽âм‘ù93 OÏOM®<èýtìUã qâŠ[›½9)i5™£†ùÒÃ[–ÿ'cÈ0ÁjäôA¤¼B9dº?jµ÷Õ­žw)®;1¸lÆŸØøz•댕%î‚ÉøcÇ‚Ã÷yó¦‡÷£6d0oÞŒðŽÜA±Ôô$“ õ®øNiL¹dÈéÖŽþ}@àŽµðÛÞg¸#Þ\|0Üá'{ … nŸË^Qñå'¶]ýîç/)Lë w|m61×Ìgü;lÞ 6?3rûÓ£v?5æÀ¯!¬¬æÖÚ£ªö€Ò‰'ÙÛ–"R3Ô°åµmÿ[²YÙ²²3«!cS˜©æpãŽ#®÷Ôò5Õ‡ÿS¥“‡(+ùÓ«ï«Û½5åþ9Ä|`m+ jwcã˜3›&œÝ8傆i¼Ìš|B4ž¨)˜Ú>•ªü ÔIӰƹ§¯|Í=*Üá'{x]|ÔàÙyùòµ‘¾Q÷êÜç)M÷:‰ïÑ Ü»Ò¬Y£˜¹´to4oÚ·L¦ õ¥ADô˜VíO;ɾ©¹²†©T¢r•JÄoþÀleM)xü{!®VØ—¼»¹tΛ‹óþõÂÐÿyØš?A°tµIWó¿“’ô6äLð䤶¥%·$ex¼Z ɩާ¶¥™¿Rrâ2÷½gùá9ÿõ‡¯9w,±Òšš>üá–¦ï¦t ™‚V§íÎo*XCW<·uÖÃN²=sßãcÿË«G ÛWíN¿ú Ûn¶ìfËjN}÷÷RzQ×X5êrx}É {ýå1^©ŒþŽ4Èüßå c&á™.KÝËx&3# põ0pïJñˆø@à~ÿý_`æ%KBÔÚúò™²íeø>{vèÒ…°Þ¨¿™Lg­ZµÀœ9…á0ì:åʦˆ¦4Í .a‡¿ñåØòÉ'¼pOþÇ£]]ì½²€AÜ&Ø"–±EŽL€¥aãÐ_$8MéÄôrRŠÍEG8Z²|sÌ!=m.W›äu»]š]€LõMÜ3øüéTl;ÖiC8uÈ9©òÃÆGþ!„·áë_ÒÍcþþ0 ”]ôÙ¬)c2†Ï""N|ïerôÍl¸õ¹PËš´ jN ¤Êþà•„±ðÛS¬c WJ€{?Çé­ŒŸ?ÿì(߷϶=ÔPqÔtÜ ß-º(ø2bæØ;¢Å÷HÀÝ(fß!“éâšH"þ–⦇¿ó'娮ÇöRBÑ›¿ìòâa9ºú¸3mÛ¢EgÃbؼïÚï9D^Cäl<:žˆ•+RÌmyœ™—9ÒRR:–%¤ÇãrÝ·oNÞ¶øÖªZÃwöÛí¶˜'×5¶”ïHΰ <}èn{ü¹MßzBHoÍ‚9¶»Þån,øÇo Q~Ñ¥d#çÊ;>X…Ä[<æÇ_ýcÐòTƒuݾîI*)ÔðšMð›S¯Sí'⣙ɄTôF¸X¶ì?,8¿ÿCET]­oêŒx*-=p,= H#>¬™LÅ â{wÿ•‘B|Ó¬+˜éà틽?­ 5ëÏwsq͘ ’«Êt‚<‘?C‘8zËíD^Üáy?>6’ˆ‰´jq’³Àq¶»g*!•”Ž”Ú².ØÿÆEmÏQƒC…Iîì©7>eFÞý›¿<•ò™·ÛÒ¬š¼RsR‚5îÄ&‚>» ¬îñ­Rx꯽Êånp¹lwð÷^bí­8û¼´Ê=i…Wº¿øXðs~|ó³¡zhÿV5²u#<ÙÓ&}Fü€ÃÝ(šˆDLì#¾ûÅ«/¿ð…;b î 3wî¬Ù¹³&ìÝÇ̈$ÜìÜy±Ô},ÉtÓS, ñã³wì¨Ú±£ª'÷B7{uCó`ûßï¦Hôü“Ý_žøa gÔxÓRmwc‹wpÒ¦ ›7»6mmžzCl<:R5³òB{ű”Ükðq9”¥€eÏð|œ[¿?¯z7A±Ïï*k½øgLJjï&÷Ôk¨ùh­ë8O?<éHc¥ ‰iÔ&äÖ%än­2fûzíxy|ޔޭR¶¦—o¶u³v)oMºC-ï¿‘tÑÕçÌ»vú±åL¯10kÓS `RëAÀ½è0ST”·}ûÑíÛåõü]L¦¨(¿ç¶m+éîcfaa/‚œ8qø¶m‡¶m;ƒÝÇL© ºíL0iRi@6iR1kVúEYƼ‡w®5r™Lçn\ü€«ÿ;ó…GÛe[˜eúP;ã…ßœââE³UåÇ­÷\s­¸‡”qî­¥ o&ÓYƼ#;êªÂ½{Ew!]o^㥲çŠį\¹­opD¹¤ç½µp(ÿýÅi/ýáÔWk€Ù· É­³­>#».#‡'ÁÝ´÷­™_©Í&š[EK†4×%=Ê:¤`·´É¯P°­ÄdEv«#µíV.—3}–r\‡þçj;w¨ãµØ¿vwÅÎrí(¥XkLâš”ã7Ä¥IT¤Uœ²Õ™ª\¶å4@écÎUI)Ùï~-õÊÙƒïz@íCß®èëæa§ýõÖ IßÖ¼½/$M)Äc‘í YR²!¦ànQÄ÷³N&NÈdzwΘ & ûkGNá â#¸w¥ â{¸w=Bø‚økçx>Xçª:Öãëg;kþÕ0u”#]^év¤Û+]öð«F<ùý¿ýùèñj/¥îÛ¡k*Û · €¬·Ç¸Ik—)EZ«VïÅú¹Tˆ=g9Éåëˆô‰Üïn9šÖT‹ìIc²F§ù~Ì£âÕ)’zäДÔf—hÍÈûxfviˡі»1iû¶ŠÖªéOMºýÊÚ‡¾-àö 8I÷¾°éºß›À]JMB «üSÊcð?ý‚û)ƒø Ü»R$‚øh.L ¾Û6!.øŽ~#¾´t€£w£A|¸2™°!~öl(íïŠgëÞn9c¨×r{¥9WfÿÉWC¹øGŽ×9DÈäRe¤=ùœ¦O¶ƒDCu«Ð$$À pâ‹›Zᨠ½Ïº<º|èÅ$¼É‡×‚ôG™©†ºÆd• áJ IDATΓɚ¡ Û ÌÇ>Ú¥T€4F™uùo7í?_hÏEiÏCˆ&«0å“hN=”Ϥ;?  ñg·8‚„û^Ú¼è·YÖ #Þ€‡ûkÞ»ñæ1hÛ;+Œˆt&ÓÅMñ}ËdZ²äýØÍgŒú“Òøá>Î}ÆÀ½¸x"À+W~ÚßJK{ wÉoîÎxãÓìÒ¸?ÿL¹Ö¬Íâв˜„(:[kTgçÕdç8 ².¸€ÛX9ä8Ô°Çi¬°šŽ»Þµ¾ÞF–£'4 ?×ÑnY¿‰“\Њµkh†ÒPŽ:úŸÝ hai’îôŠ·×];cÈʱޭú/´è´ÊOÈæ¶æ áxÍ#¥Üû\Ò½/ð>¸hxËÍR¦ #ßÜáÇwÀ§pÀŒ°¸Ç;:ˆï'Ü,ZtQ¬ûw£>¸x÷Ù³ÇDê™z¬¨Yøþg2¡Æü@qñ¤p Ø7=÷ÛÃDt¼Erd#“ÎFÙGž6/µxjÅÈ„$ ‚\™5ºí¨&ÀK—~Ñ»ô?pטּá;üÔî ñÛ›p„…P&R‚x­X3W4aÐîm¼«¬ÍËM”åIHe)ˆ<,$•Ð(k¾£[ÄÇ8ÜÂŽøˆÂñÎ÷¸ƒ;úÍ÷•+·x ZʬXñ)€¹sÉøE×- þ²¡^ÔŽ¼x2˜÷¿càuÜ(– ¦Ž`ö_gþÃn4jmàá¤öË &k`ˆöÝYXƒ Ó7ŒÈ÷s ಩RèŠ×̤[f¤¾òòü0~ ñ¾²©…D|lf2!Fćw*5¤â’ïJK÷˜“qDö€úŒx?ÜQ\ç`ûIb @û˜n¢"ñ€z”²=Vа„öz€,°ΟôSÀ~ÿÎ$àë Ó# F|fª€>öÖðÐSc÷æþþÁ°|=)‚<¶½ƒÂ‚ø(Àñ5¿zÚ¨os­îÅÅîѵů¼<_ZVS# !A€…[ —ÒB !‘ï%„R)„%„R a )IHa[¶Í[ÈjÙ‰–• ¥€$„´Ü B‚ÒR xÒ‹¤-¤B)ɲ…%…%…-Èä’ì²… !è®Ã/T¬Ù¿çžg÷ÞõÌþþ¶?C+ÜçÍ›pIÉúþÜ+pï¿Â^ÙâÏ¿g2ñ2­R½rñ¸Gö™z¬+¶˜;wrx†ûá0Íâg /­;\ 1Ÿ5 À¡×7u¼’Ð1…'3Aƒ„ÏãkfD$|¶“]õÜ¢ RZ-“Ï€Ϥ‡p8Äšc~ÌâýŸô“2lö‚ôí ?–sâYoRejҬ˄rH;¤¡¼¤¡œ¼§èágÐ[Wnø>oÞÌ^SZ¶lͶm‡‹Š†Æ)Ùûlá#¸w•–îGlìÌ×uÜO{ÄÇH&ÓYáDü½Ã\õ*H 6VU Ÿ; À¡ÒÍ/$ß*$ÿ— àŸ\ÀÌ ÓDà$U Ji±M­ k÷¤¿&8øo jG?›=Óƒþz×èÓSݵ7µÜ+-…ý$„rÍ(ªùéäü ß…v„ò åíÍ|þ×]}}ˆ\N ¾ëOë‚U5ÛPûþ«±ø¸®Œ ©Sò=fán.Äßûõ€D Bõ%C”—›wêpà²oÞÕXnnç;üÎ[€¤p=+»Å•jÞË>¾SÇ4ߎÛëg:ü=à»hr~:À ŽIvÕßÒðCÞU-Üi®“ƒvÜÖB¨ÆÇÊ„vŸ'´W(¯ÐŽT^RžÄ¥/™KJ6šaû§Ç#⫝̸,8'ަUCªWˆ>ÜÈgbñÝ×ÉÄ9â·˜3'¾c-“ ©° þî[בH¤–ˆHz÷HÙ‘ŠNT?ùÈgÞ 倠À…¬}9 ˜ÓE}£ÌbÀ7 ëxÁLÄ`Sçhj%}bOКëbLÌË€y£ñŠÑ Vó×kn£=Ze5UM¼#«6ÐoýÉ'’•+ã,R©©¼+o¾ÏŒØçÉÒøB|0ÜýgNÄG9“ –åûõA§,‚4A|iéžxDüœ9V­Ú¾jÕöˆ ¸‡E?¼u“€InùÌPIoÑ$‹†æï8VÞ±6'WK’í‚r`!Ì7MÁ °f­—ð‡; !Á, _a»)!?Ç5|Vž:Üjü 4ö2™H»D3§ úÉ:ÛÛ¢š3§±`ÄLÌ‚„† °¤Äû§’PB4 ¡ô×µ6·ž÷ÐwssÓ{Ñ<¹“æÍ›YR²¾¤d}ì#¾ÿ½$ãQwϯƦ…ï¹7?m\|Œg2Õ ÿý[?k"‘gn.€„G‰²¢ÝÁ`?™èƒ€…W[ø@A|²÷D“Iòµ›íö_kÐ3 ÁZA3@$kí/»$c2ÓÌØÌþÖ‚^ÕôùBHñóoŽ?~í✾ˆF(!ÍÏ¿ûN -„ª¨ª¶ô俦cUº’ž‹?åʦx¶ð]/’É«=¯4d7”ùÛ¾÷ÙÜ¡Â!wp‡Ÿì†ò½ÕE³ôó®ÕdiHv v mYÊt€q¤vhLNž©ˆ4"!HJßKø_$‰¤ –%„%TïhY‚$‘HÊÏ2E)„”BÚBZBJ!¥å¿L "IB’´HH!m’„EBŽ2„m7[n-],mvÀJx¾T(\‹~|a)Î<ŠX+‚ìv„Ùt›bGÀG×=\oÿÌ“›¸Ç¸–-[Ó½s7dÔJÆ ¾>= ‡;:×Gƈ‹´zïåž³ÏjX´jÕö;ŽŽ#²«.~ÅŠ-._3?IÔÞqõN.vøåj¸½%Á¡Ú ß{(ðKÐ×ð•3úRlÏý‹Q-A¤bÅÐ $ ØtñýPÐ )ÿ lrü‚ÌA0›0s›ÄtÃM¤Ed½¸…ó—/ vòÛv!´9®¬ª6þ½ào^ ß—/_`þüزð÷®¯<}\üÀf2ÁŠÅõM}ƒ;üd7”SuZ€7ê­‹7pŸ;wŠÛv3Ä#êFÉ €¼!Ó}}Æÿ›ýÐf=ªIjH&kØ |Ÿ')¶=`äÍi³J´ûz!¤ !¥$iû\9IaY° "ß2)!Ì%¶|«™ük©Fdf1H±ÐLÚ«]ŸfÝ0æßŒ/‘‹j’ŠI±øÅÛ×ûÝ:Á1û³ VEGËØ9ƒ3 ^•á„;üd7”lûÿ©Ì=vàŽë›V¯Þ`Ö¬‘Ñô{CmÄ¡‹7á;€9s&Äõ´jÏ-|îx|§z—gîHfK+©ÙzDÿÊb-ÁüUÏ£ZYÇ^<þÊà>Z_d×ᯆ÷AA'YûDÍ`M$:þ÷5¶ñ-…òEó _¥%ƒ¡ÙÙNk˜àuÀœ¼hˆ"Ò@ë«ULZ›zJ0î8w™ß¹ké+žQ$Teeµ9ŸŸŸ’òç°4"ÉLì¸ø>̦ž¾¬ìàĉ#bìF¡×¯âûlÛC 7ˆ†»98½oÈw?ŸßvboÛ…ZKÖ–Ö–Ö¶ÖÖSÎ}ÌÒQ×ý㉱÷OÄîŸl|òØÅµv^Ðxþâ ®²!|Õ‘æœa¿Y¶€A’˜}íÃ|IŽo'VS.ék0 F^Ö Ö¬Ú(•9o&(Pݲj0³/ÜÁCå×»æÆ ‚ÈÀlª6™ %Hç秤ÿí¼ÁØ}ùòuæÏ?+r·è^=ÏdB½7Ž7|GÔF¸#~ø²ø=®ùŽ`oÞÅ·‚¿Ûößg2Së­“´¶XÛJÄ»”²´¶Ÿoù†m¹„¾ó¼ˆï};Ey<¦h½Þ4|ˆö’üä[„jvi"üá;ûs÷öEMæ´9’‘ fíQCæ$+IÛO,¯óG$-ï˜÷ž"­\ò%)Úݺš|ª¢²F•—Ÿ"…Ê,9 /GvNu`ùÞ¸ûGˆ?Äg2±0­PLð½ÿ™L¨1cññ¾²©…D|È“­·Ï`U×þ@+[)ÛQ.¥l¥\޲•²³>þÀ’â…±Kà;çç¸ûÁO[«êÖ&Cyi+fªC6Ða¡+~ÑÌRk f‘ªt×!ꮘ9+%„ü«5ÈØöŠ×å­±¤GJ¯%=–å)øÏspÄ‘³¾¼”)`á+«jH¨üüs2«ô¼ñš™B|¸*Üã ñ÷ØA|wýÅ¢€øðÚöNƒÇ(â;g2u:!¾³m¨å[g1Så¼{|X×¶r\J¹”²½/,Ë›˜UðØíæÊ_ýç˜$±_ÿúÏþ§f>ˆÚšÙ!ƒ¬r áÊÍ)?ât\ïJ¾ìE˜ÿú»·ÿÙ×¦ÈÆÔœ–˜<új·Áº]/%l§õøqi;Òvj>=KZÞ‚ /°ÇU>ùæd'©ª*kȸ›ƒì·öÀâ(DFña\¾´lÙ‡,8¯ÿCEZ!Q|G„Q¸ûosˆï Üâñ¾wwÜ=¯¹êHå¢û”²•¶•r9Ž­´K9¶~þåœ ™#žøïÀµm÷|^“ýçË,Á’Y‚w­8l¢w¯îinÈ’ê_Á‰ƒ’>|Tl›6äóê'UYú~‚~R’Ç^%¥ja¢9M¿¶#l%lÕzü¸9¶S·gưM¯êÖ¤Ãco Zʤ„Ðf6µÝ¹KûÞNx1ªÕîQC|ÿ3™PcÆ:⻯“‰ÄŸº?p„‰L&Ô]b‹ï«Víø”dh öá ‹V¬Ø²cGåøñCBfñðƒk›«*ŽÞðS¥l՞ϸðÜó9ã3†ýúGp×lE¶†¥ÉÒd+HMö’ÏýÄù½ïµk©nimjLð!Þ_ÑbZ0Ÿð&ino?Ð.þ/¸èjÍ–åÕ¸`Ýýl“Aéƒ\ÂÖ†òmÇ«Év¤­Þ:ªZRýªÉÙ¾²ªšÈ¸û,üºmð|´—2E‡ï‘€»äØE|OŠ cñÃ÷èÀݯXA|¶m:½ùÞ|Óù *ÿüÏý|w9Ê%žýcNaZþoMã-Ô^9Í•~ö(ûá%ÞþâGåhirŽ|¢;–Mþ’pÜ“èûⳆÊw²`¸+o2kå´i¨K>¼[{=î4·ãRÂVéƒ\d+a«¶5Æ¿»7œð4eÌúZðR¦ÊÊöÀÝ5Ã7o€?Ì:ÕH#>Ò-ebñ=÷Ò¥ïX¸ð¢ˆ?SêÑþaD|2™P7`ĶjêÓž|ñ‡øàL¦›rš¦.d‡n|\ù§Uí?þ.{lJð^wµWžЮ¬;!ˆý×›‚Gxé£r nnR8¼¥s2ÿŸm¢ƒåŽoõiû%œÑ|Tº¥ä{q†1`éæ3Þ}âxÒhiyÆT¿',Ë«Zeº•6ȶ"[ [·¨¶Ëë< Ù¿~Ù¥/pî33|û&øÓ€õ!ˆâ#gÛO¾KÌñ½·®|`ßÓý›Â‚ø»ÿÖ†øþÀÝ(¾ß9pï ñ .f¦_zJ9.¥]îß?‘=&yÈ9é¢ïÏ®ûäàîÜï¾3$± :û÷>Ö/]s¸©QI°,³;?ÒrAûÈa€æªOƒÇ•š0þ©‰hÊÛJᨦjKz7ÙÒ›VSn%¹”°TRî0²5Ùì©®µÚšZëóö“á»®¬ ÜÛ'ZGîÛÏp“™°#>:p÷ß+Vß·…©ñÁwôñÑÌdºx€@|Ÿ·Òî xA|•‘¡=}}nñºƒ_þҶ㸒~÷óÁ£?÷xç1뮜ÐÞ‚ÛX Hb&sòœÇnZüö~ &@û¶'lDZÿ°GUÜCsQsUÄs„"‚ -¯]4³LZÞ†ƒû¥ô¦Ü ¥×âféõJéÍðÖix¥tYZŽ¤Æš†3÷µ}ËïÜ«IèüüdÙ¥¤„P£®€?ÄD±0">úm~cñýé:0€ˆFÿ™ÒÒ½wøÖ²DµÁd¸à?Ù åcV]ùôЭi°#•NPNbÚSåX!ဘ ZØJØ^i+i;þ‚߯ÒVëîyaü;ïïx»‘•&`ì„–Â -¦;¤bt=ºÀeºOš†‘cÆ6O:O [’G÷&?òØôé7ÜtÆÂ/»]cí„BïÄëõ„‘jÊØ¦#Ú²óÚ¦Ž³nZDmÊiò¢MC¹w7Ün:Ì=ìk)ÃŽ0gØKìÐèŠ5±÷0j@z¸²Êˆ™Lß&K ٠壬Þí¯Ý _ZºÏب…»ÑªU»Ì™3.ò7êo&ÓY±lá»+‚l¿æ$ßpõåÌbÏ×^ÈøÕ½ƒFºÓ?ÝÕ®˜¢o² "Ä’Lcö¿ ^÷ž`â.X–Ì;÷¦h;t`÷ð¡…£Í)öïMpç×›­{r1YŠl%meªu„­éØÄÆ×_;Sf=øÄák®"‹‡–¾ `Õ¬§„p‚fSµÉß…Pã›ß€ßÅÜ—/_ `þü³ûööhf2]<À¸ø06 ß;ÿnÈn(ß•–î0{öèX€;üd7”œ"wİ…ï ÜÑÉų#ÙJ%fRy»»3˜…϶ŸäÜ/i+a{…KÍNùHHÚ¾Æ[¶ÆÑŽÒJÔNQ#M» q˜Ó–fûFûÐ4))÷µ¿XLPDZ&h"%̪Uå¤\=@Íý·Õæ Zû•G””l4Íßs²3 +¡•¯g$;‹Y Ù å{«‡;ü›©DSáí9 .¾wþÝhõê}fÍÕýe~¸Ÿâ²è+¢.>Œ™LHÅš‹_±b+ÀÝ“=X+W~  ¸xRÝÅW3 w^6€ÄW_èî=ߙݸmÏá™_ ˜tÁ,ÚÍ;µOÀ bIoÔoV?M!X0k@±XÿogðX_‡²Û¾œiÖ>²˜l-l%,å;°YJØšL0ñÎÏÖÜ›€sä`Õkão™0!7YY‡nÆÂOoÀob˼Ô;ûê-]ú!€… £aá#Q½} ›QZº/fá9­Zµ#ÒpGŒ¹xäÒS¸·káöŠÄ!éByNwO•ó°õ DðÂöŠö >ÔËr>—ý¾$$¶nÒ›714Ö¾ÝR¶%9»p˜éÿÿJ7æfíOi- ‰´ EП‘?¾ù-R€Ì“þàÓ>³þ׳ø&êónò÷Iö›± wôÞÅÇÜá#;/]úŸˆÞeéÒ"´4iዘ±dIô,|_ønœ»qñeÈŽ†{$Rše2! ˆ_±bkß¶]-.žàXyCÚ¡O¤ò¸—½Ü³÷1±–¾æ¤pÆœ<™õÊœü\î?ÍþÞãÍëÞkN2$mhš´HZôí/¦˜q?ºo1±ÐEñ†òð§4¬ ßKJ>))ùxûÜ{ŠŠò¦¬¸¯¤äãÀ–{ì+š’úx2váÞ+r_½ÑÂ…çˆâ—.ýÀ£ˆ¬;]´è" zˆï£ï ñÛ³p7 /â¶= p2ô¾í©]\<‰êÜUy…ÖkË{øü¼«Ö•°½ÂR~²N:ÂVŸö¦Së’ÒÓì´¡f×o|>9hdhb'€x‚í:wa6”Srã¯Íõóæ™ü³?š9mÅݹIÏCiEÓ¯‚žˆ¸÷ÄÂÇBà}¸/\xAD› Dñ}Éß âcß¶wVX‚ø(d2!5PA|ßl{g­\Y ¸xbO.n¹l*?÷yHá¯|§@ñ;„`"–„@þîå®®dËÚ>æz®Qc|õ:³„uݽ‹ÉÖdiËrÈ·<•…­L+á+¡Q[þ–`îKw?Ò¾›?+à$®Ù—~›jFÁrx,ønÔMƒ¶½ƒŒ7^>|cúàÆ1»Ñ’%ïX´èâˆÞ%<ù{<½ÿŠNàÞ•Š‹‹6”ŒšÂwøÉÞÃçO=žÀÒ_íªÞ‘V ÆÔT•Î=ã’+žýéõm/Á$é«‹«[yÝ)‚¤@ 6¼CPp¦²¢¶²¢6½yÃÄÂì4ê/olœûhëð´qõO̽2žàŽ®]|ìÃáNi–.ý Êpšúå߬^½oçÎÚqã2â”ì}¶ðÑ Ü»Q¯\pÔÏL&¤zõð­—áÄu‹Xv4éAEñL¢jÉ!%íAS?Sôƒâ?¾À gå‘iò¾îû5å1Æ¿ܺ´”¯ÏŒKWž¨¶"K[oW§½tìO¿Üù©JJ>>oåí¹€¥+aQ°‹»L&,.>(p6Ü£`áÃãß;í‹7ê[ÿÿîaQ¯,¼éúÛ¡^ÚÁž] Ë+lUóÊœUtIÑŠ8NÐ0¼î»5å1‚”0u2¤‰”€i ¨Š£5‚UnNJ^nò›‡’röÜýÇÎÏ4oÞ™ÿº¤äãp|©¸ƒ{X¸€s7d7”úÎ÷Õ«÷™üýŽ;ÎdnhâN½EüªU;1ж= ^"²/ d2á…»QqñD€zR D`2«œ,%} ¼Ý6„ËÝÔ.ÞMRŸ³ø™ w/4o¬+b&¬ÔÚo¿Hì/ya%Ø+Økê`^ó­cÇjœÜ!É‚•¯tR9B9Ý?a<Ê8÷‡*°`Á9ñ÷þ¤41’ÉDñâÜpnèíÛ•3frÕ„3§=âW­ÚSp7Šâû\Ù+õ°ÜÓ_BÀz{òî/§qjÿ²+uÌ%3þú—“Þȱ€ˆ„”‚H`ò½´„6Fþxe]Ee ›“ b_Å$©æ}oQ‹lçÍ;@¼[øÎ{“Ç…ú†øÌd¢,ñ2^Ð+ÄÊfN¹„5®Ä±Ír» ÙÌ™S•çé…"øe2!ÕÄ»ò&ƒYž\üHiŸ)«{vgê¨KÆ<üµàw5Þ÷U•öÓg „X÷õ—€`!@ÄD0•8渪²ÁÄøùy)B²”Zâ¥övãßãñË—¯5ùû=÷Ìcæ@D_ê-â6“鬈Zxàe¼Ìàëq}OÞÐUs‚ø·ð…‚xùm{a ÂÝ(¼ˆh&ÓG½TJàÔ§V<»/y·¼Om%©“G\<òÑowx“YWjŽ×Ýüb g÷·šQþ5«º²¢Ž ór“ór“Ú:ùki„VBw—ÏÄâ•3&¢1ïˆü IDATÉÌiøXÈd:+rˆ÷åï ænÝ«Q÷gNWÄà>Ï4ŠB&ÓYÅÅE™-Å»ù¦XU`…ª°½-OnIzáØeoŒøå!ÞâgÖÞô"Hò– ªº²¢®²¢ŽHåå&·w&0] üƒnEÊ[þ‡ºy¼8B| l¦Ïí$ãN1¸w¥!ÞÇ÷Wñ*ƒaQW×fS»ÏdfÏp`Ÿ¦¸SÄîq÷þ[øèî]©¸xV­êñ¬ ,}þ] Ûi}t“;§èê¦xfí ‹ÍªTS*ão5£éÊŠZs2/'9Ð?ðÕÅûq¯ˆ´P^Ñ}ŸË8‘¯Žd Ï`½ƒâ"p;â—,y§½~f –‰ø³©ÿGË{Wê⣸w¥9s&]"þ¤.¶ãyècWÎ…÷ó ’÷î…‚O¶ÏÊ[|MÄ% +Õt^NR^N2ùЯ¥“þß»œòž’ï±oỂ»Qœ#þ¡< X Ü£ %KÞY²ät¨\‚% ^ˆ…Á'ý{zôt6Õ´zw ÿøã"¶÷®Ô7ÄÇbàÞI2s:˜…­øþ5άìó³þzË'#‡?xk|ÍcÂOö@c™ªÊúÊŠ:"—›L¤ƒÖ¬vè©qÐNOü{Ì"~ùòu=Ù¥ïtB|Œg2µhÑÅ/Yòn1d°hÑ¥ëß;ñ¸÷êñŽøxWo¿bŧPÛ¬9s&X|’^+%°ëÚ"ë™u~Æ‹¿í~(ÉŽ+à ⱕ¿säW#÷ýjÄÎßTUE½!x^NRPÛH%ü6ß ù;ùJhz”ÏÄ â ÙÖ]¶c\q‘ÉtÖ¢E—è3â Ü-ºtÑ¢K²?Á|Ìð•Õ¿4_Æï†Ú}Pp&GÉ{gõpé¿!;€¹s'Eü™z£®ûð%9¼Sˆ£G{5ÚšKF§¥$Ù–H:Úà²\v»%^üE1-,å;°ïòakaiÓtÌù}:€Œ~sÊ;¸Ð¸ú°¹v\t¡éJK—~PVvhâÄáñEö`¾Ö÷ø-ï˜Cv£ëW—cù±cMÍø:úwÄ¡‹ï¸w_1ã*.ž°Ù,©+Å,Üá'{gO©g‰3z;Ú}©óÊ)~ñì[¾[xý^VYg¦ÿî ¹o¿˜ó¿²êoþü½=¥éêh®|G(OOî;~ùòõè½më”æ4Po]|H¸#¤7™Ì³³îP‚’þâý™L¿Èn´lÙG,8·ÿCET!Qï|G·ˆ?%ÜÈßW¯Þßîð“ÝP¾?ŠÙ ¾‡]æÌ)8tEG\)îàŽþÍ‚¬ZµcÕªíS§^;iÒðQ£'%%Mœ8±¦¦¦©¡¾®¦º»w¾TŠ—Kñj©õÞa+ûL9øL9h:ëÎÿØï®=‹àJa„;üd7”YuÅñ®*âãH]ñêdº1³!;º˜M-F1€•XÙŸ§ŒA ß[c8€[5õ_+W~ºcGUaav‘=X½úÍúÖwŸh¬¯ßµ³üDÕþ# Þz뮿þë4}ó›ß~ì±rr²Þz륾<ÇœÙ` ´×û4EÍÂ/_¾àpÁ= ˜uñ=)‚<Í\|Ol{@"`Û»*•Y‰• žƒ9ýy¾˜²ðñÕu ŒŠÏ°€ïwŠzÞ£ÿÐáúææjÛ¶†Oÿö·ôûß?WT4êšk&oܸjÚ´Ñ}„;€U¥}€;¢RNSR²aùò ŸsVlºøV¸/\xÀK—þ;*Yõ î0ùÌ)‹ {Ø}¬{Íž=àÒÒ=ý§Ÿês×®*öb\+W~jò÷üàRø³øÓ[••'2Éøâ]w}á‡?üŠeI‰‰ÖæÍÞ}÷Ý‹.˜>E|IÉs0~¯‹GãT½ê:°pá…âñƹ?ðÀóèA&,Ñ“ ÷R”2xfõý³g0€ˆ÷ï¶ÚÇ®q‡øÍÍäjqñ$€¢¼%w¸4gÎ8"”–vgáW­Úù׿¾›‘™<¾0G‘““ãv»/¸`á7¾qË–-eƒeNŸ~ÆsÏ­¼ðÂ…yy‘Ý·>š2pŸ7oFDáSþ4ˆ\úªÞý¼ûk¾¯Æê^?ÒÉ2|7¬šÿ´ïÃVÚ†Š >dedÔö㎠ßgÏñ›øÚke^o³ÇÓ\ݰ¯¥¥eú”±‰‰‰‰‰‰O=õÒ„ {÷îš>}Ú­·Þrýõ·îÛW±mÛ¡¤Ô´‘£Æt¬’Œ°ÂÄà®»×€ñýé:`ü»ñòq¤Àäê¢E—¼úê;®»îÒ¾·|ð9|Àëx½WÏ×AÑç{áî0ÖßÍÊ&³M’Ù2)UZºÀìÙcO>¹‹Ù÷ûû£ûžlmkÛ²iOC]%hÒ¤‚qã†ýð‡·}ùËß9t¨ñw¿»»­­ˆ>sÍí# FE™ïJJ>0oÞ´p µà¨ÁÝhßÿ–2q‡ø`¸›ƒ^!¾w|ÿ,> à ¼Ñ›' ¡h"ÞŸÉ„‡ìþ1c—ï2™.®9}_ZºÛl¬lê¿õÝ'ÞþçúŒ§¡®®¬ìÍÉ+¯üB}}ý£~?~|Á‚WÍþ>ÌZÖ(êÿcï½ãã¸ÎsÿçÙ @‚½Q¤¨BQElè$H°I´ÇvœkÇòõµc«ÆŠ5:qœkÇvÛIndD%@”e‘(’"ÕI‘I±÷ewΜßgf0Xì.¶ÌîÎ0¿ç³i±˜=3–ß}ö9ïyOìˆß´éqgÉ’­¹¦H”Ä[•É8ñ‹ßãÈws1@5ª#zVW%ñ–Ûö΃ÛñáÀ]?ò€… 'Äýšâ ƒïâÀ&6]•‘ñ ýêWOõêÕ+##ãOëv <ø^ú^ÿãïÄï"C(jÄ ¸'‹ì††øx´ùµ9ßCÃ](LÄèÙ­ªQÍÁ QÅs##“‰Üa³ršHá€ÈÁñz!Ad?À÷¾÷½£Æ 1êOëwœ:uýñ¥ñ¸²p$È.(¾lwhdç%%ïÅõ,qêánçrI#“ ÝYL]P>„¢ñï P`+¶Fñ\³âaá㸇<Ý!èË%EeeEÜ…ìö$õµ ŒîõcÎÜoݺճgO«¯1\…ïâõÂyn¸|_¶lZ<ß°aÀãWiCiÛ÷n]|4þÀVlåàùÈî醬­ˆ//ÿ"ÁpOºŒbö(à›} Gf¸‹ÿQeed뢷Vü£1¡šD¸#lo¬Š²Üã§ vmذ q®p·•‹_¿~{{zt«(ù µìƒøîzKè¸Ê(fîBÅÅãrÄ6&z£± ;EšÒ8KFɼMvƒ2K8wkSAvË—O·pØ€Z¾|À7lØï…V8{@u›ÒDÏwµ¨åàyÈ‹eжVˆ^‰ ܃©¸xçÚteÂdáJ%GìTeÀÝïñùóGdôÈsœB[x[íóPÖ"^·íÓw›(ÌÀ=˜B#>ÊüÝP.rÔ£>–AdÑJ8J–m彩•£˜2Ë–¡Ú9×ñ ç¡úVVž0þÈÄ]–¥ ÄÛî†, â ¸[sMœw'€åËg&ø¼QÛö®Z·Î`åÊl¿ÇcòïêQÏÁsã8¦µ*È>pO˜âwè¿Àð4&L••_Š;¡›KÏŸ?’ˆªªN&䢬—Ÿ‹ß´é#ø€{ì2îI°íË—Ïäë×'4¥±îÐÉ.(oV¬þ](Ù<ð=REäâmh9ãmáãÝ=&êOQq’€ûüù£Ã9¸ªê€¢¢áñ½¦¸É°ðå-éa0EmḇàûЉpññ˜JE o ß‘pÄÛîBqB|œl{WÙñÁ]¨ªê4€¢¢añº¦8kÓ¦:?a g‘ÝPˆO¢m÷Sbøn­m÷SW¾ÇšÏòÀC@ü Ëeó"HAvkçZwD”Y«ÊÊãQÀ@QÑ0"lÙr:>×w‘£E:×j¸C'{\Sš¸ÂRËü;€\d¨§…wJàn¡‹O|Gߤ4p6¤WÂðHánhË–3æÍjÙ5Å_¥¥Ÿq®|ñâI6˜LŠÂqñ6Édº*~.>N™LW™]¼eþ:Ùsc¶ðÁ,dr‹ “¢¤´kOâ6,¦Ý"£„;4²Ó–-g-»¬8ËØ÷|ñâIˆ¶uƒd[¸#n.>apGgo¥ÊC6€:K]¼Sl»Y1öàMd&P‰ßÝ€{ìCmÙrÀ¼yƒc*Þ*+;È9÷Û÷Üù~€eËàÛV™L0YèâãÉ“໕þ]¨ò­ âwèd”TI‡;»%zeå ½€}”%Λ7˜ˆª«Ï[2ZœTVvH¼<üàç[xAvAy³w •,¸²Þ¿  ÀÖØ\|EÅ‘ÆÆ«ãÆõqÙÍŠÂÅÛg ½è&9#?Ë qÇòÕIÕÕÌ;ÐÚa-Qyy£ÜC¼6´¾) Ì.ÞΙL@­_¿ÀŠÑïÓ›ÈL& Ö­óXïß-9·ž ro¸C'»±Â(ªªÒÖÆcééܹª®¾hùÈ1J|$]¸pBè7~Av£¹˜så8¸C'» |¤ŠS³°Hµrev¼ü;€Bd¨‰Ê“« ÜiçjÈp¦…·C&PqrñúZS^TǦ55—öß)"UEÅÎyø¯gG»ø’’]žºûîá"»YQ¸ø¤g2fÅÑ¿×ÀCÀÜȃx3Üaã¥óa* o[¸#> ¼ŒFq…;t² Ê']ÇDU’sÍJtrî'ðH]|ŒÍÂ,W|ó™jx˜ â²;,¡´J¡ojók;¸ ‰iO«/à^T4¢¨h„%†Vaa€jj.'à\!dj¤Ù Û¡)MIÉ.‘¿ÿÍ߬â¼#¢¹e‡LÆOqÌg ¾o #¨éve“£OÀ ÆV{YÕ ±ªêÀCv³jj®(,ÌJðy…*+Oj,—³Rš’’÷DÇo£DÒÑ•3ݦ4¶ÊdÌJß‹  *$ßý2™€ºÍoçL& bD¼èÿ…äµÛºõ*€‚‚¾‰<©x?ãœÇþÖèÄ\¿êh¾#$âm wÄ;Ÿª‚‡€ùÁSšpàçñf9oÒá ñ;=‰~g°¨ôÓAM°æ‚ìÎMi‚ñv Üý”ÿ.´Ù*º¸øH:ÝÅ—•}~øð¥ñãû9ˆìféy$O9 p;tîݺõ:€‚‚ÌœkË–3œsk›YÚÙÂmÅBtž¹\¼m»¡ÄÕ¿ ²/èìâíÓ6árjIAQÑ€ ?Þ­ôž½É‡;€‚‚L"ª­½׳TWŸ ,ïTl[ Ü¡‘oØðnB.*ŽrÜ‘Hÿ ÙÊáAØ™L@9ÔÂõ3 Þí”iÕ€ s' #°[CöÚÚ›òó{Çcðêê "p_÷›M›>°dɃq?R•”ìxømßß—/,ž/­_¿ãàÁ'ް9Ù…º~µ";¸Ã™A¼îÐÉn¤ðÎ’ {h /šôÂ~p«ÄZÙ¹sŵµ™ » |ÒUR²1ï¼ê@9ãówBý»ÐŒËS¼ÓoOŒã8ÈÅ;º22˜Bl–dÿìµµMòó3,³¦æç|îÜŽBIwñ‚ì–-›ésháÉÕ+fÙ°Ô= ¤ÊÊ/ãÚ`ÄOGžÙýFV¿ôE17˜t„‹/+û„Ä—”ˆ93n Ü…ìÄûe2Aޱu¯ù÷8¥4GõÍšÔɲ/µ ˆ¿ :å“®{HqѯÙrî• ²S}}[DÏ2‚û¤Ã=aÒáns7d[ Üa{ß©~ÆZolÐ,ök¦eÈPr[¸øn÷Ï=`áBÿ­Ú쬪ªÓb÷•yó†:hËÓ®ª¯o››ÎÁuu·ž—×+Îâmá7nÜÃ9–-›§ñíæâ#mûn[¯ü]·ícCÃ@ <,wþ\k˜{0 ² Ê;BFåŒÜÙå'AvAùЪ«k67k#Å/ˆß¸qÏÆ{€8Â6sñ±ïÌguâûüù£^Yë–ÊÜÃ<Þ¢¹Ö¤¥4Ñf2þrâõ%¬Ã̳©DäÜÍ»½øúú6ÖçåõLÔEE %Kø¦Mû-sãÆ½âÎÒ¥q„»Ðòå|Æwâ}¢Z¿~Gtp·mJãïßçÏ jÄ›÷pá`=<V83ˆ%“é*G Þ€»ßãsçHìjí8åä¤ÔÐà øÝ†¯¸“——žÀ‹ŠLK–<À*Ä ¸/]:%p·ƒÂ ܃ɞˆÏDø0÷€Z+†ø3Ç©ªê¤Þ?2pó™¹sBëÁâ<åä¸jhPüÐÏÍíE™C¥ÃýÑDžtùò’bá-©p·!â-ËßÃ܃i<¬rH_VvÈ’L¦«.¼‡s”–Ú΋ý=Ð]g±¹s$:±8N99."òx˜øÒãa÷99)I½®p»…߸qoRà.”Ä[¸¯X1àë×o‹}(K˜ï‘Zø(2™€Z džøXø²²CâNœlû¢E÷öB¼€{Â6MMª´ù G_ç专z=‘)Ä›÷$À](‘ˆ:pwŠBõ|¬¦X2™€pÛÆå’î N°|d?•–~`Ñ¢{ã}¢nUYy ˆ6!ª©¹„„/Ù·J?~£Fñìì„6XµP‚ï‚õa*‰¶ÝOîôñSŒ{­[· ÀÊ•s¬6 uÓ?24â-‡»ÐÈð–-Ÿ0¸ %ñ••_ 3;þ¨HŸ[Ss@aa?˯*ÞÚ¶ ÇcÔ(Ì™“ìK‰A!~ãÆ÷n¸ Åññ¶í6A|ôö$öÀ=˜Þ‚‡€'m6×ZVv(ÁpOºŒõÌQÀ:Ùå¢mÛ°m|ýëàœ{<^š¦6n|ãÆ÷açn(~)Mb2AvAù$ª¾ ⭠܃éMxXmÁ\«5ëþM{Bá.œ»pñ VE…øè6::¸ öHôε¿ÙͶgg‹Þ©ˆ'ˆd°té#‰¸¦HÄÇ/“é*; ¾{ÿî‡øè*Ü£Ðð fÄP^~8–A ÛžçžÄp}(§,zpŸ3ÇÙ™ŒŸB#^·íØîBÖ"Þ¹m~£VXùŒø8îÁdѺÖèo“L&‘ˆ7½‡šW_}lÝzÕ’Ñâ$î~rº…‡†x¾iÓ>¿Ç ¸'㢒 ¤ÔÉ$݇»?_e屯ÆkãÆe&†ì†¾Šl¯[0×z:ëÃT²2™€JÌDkEÅñ–jù_Yl‚QPÐÇÚac—_&P‚ï‚õ•àû’%ظQ°ž;î6ì°|ùÌèžžÈL& ’2׺n]="_Müí×á!àk̵Fæâ“›ÉtU,|Eňτ94²“Ø Ã> î¸-\¼!îNrî‚ì‚ò‘*épG2\¼€;ÂñïFò>þ˜Ä$ï]%øþß±¹øð-¼2™€*-ýÀ¢E÷Y;lyy£xï^° Ž›•ë[Ù¥az°L&˜œîâ7mÚ÷ùçg'L²téÃɾ–h…‹·ÏÚ¥DZx÷•+sÑ­7Ã:Ù>aJXo“À=˜Ùå­’Q?W¸((È$¢ÚÚq=K˜Šî·…€¦º+Ro¸C#;_·.Öœ9´Ö­«7ÃQ¬_M–…ÿ:²üW<ƒx[î!d¡‹//? P"7E|ÏÏ¿#agôS˜™L@9ÔÂoÞüç*À—,yHä3··…·C&P‚ï+WƥŖ‘ÉpG0¾ûÙv?% ñŽlÿÄÛÙ¶ûÉ*¾G1çl‰jkoÈÏïàó"6¸ ‰¦4ê[°yóÇç\5vwº½o[¸ Å ñ~¶ÝP¾‡†»Ðí„x§Øv³bD|YÙ!"”x¸ ÕÖ6ÈÏÏHäI­Êd„øÍ›?øâÅ“Ì:½22âm•É“µˆhÛ ù¿FL&t[±dñÿ ß°®‡°áŽØ‚xcC’dÁ=)²0pd7ºKÚS¥¥Ÿ‰—‡Ü¡“ÝX¹ê8 âwkî貿v÷ #ÍJŠ‹ÿ&²üÑ øðå»îÊrÙÍŠÂÅÇ©g}ª«kFB62=“é*›[ø²²ƒ"pñÚ¸\¼Í3™®ŠÝ¯[× :¯#»ö­¬<)Ü“¥?ÂCÀ·,²ðŽX:L‘ºø²²ƒ°Ü¡‘êênÅõ,ñ€;ìmáÅ[ø¢E÷†~ãwº‹7ä8¸C'{Ôå4ëÖ5èã„‚;„'p¦dñ‚ïˆÊÅ%’ÅÅã4­P¥¥ŸX´èþЇ••åq NLÄe…-Á÷8íXï"Hºø²²Cÿ-ÜÑ.~Æž¸ûî"»Yѹx÷•+sÂ9X 3p¦dñ€‡€¿ˆÜÅ›áìF ï8 ² Ê“±”ÝàÀ¢µ þ2ÚüƵÂÝV.¾¼¼Q¼¶#ü|fMƒÕ$ʹÁ#uñëÖ5Dwˆ|&ÆL&Yˆÿ=<|;Äe3·ÓÔbhÄ‹– ÞcK¸#//ˆêë[-3N™L@Ùñ¢·"ÿ$*z¾Ûò9E6ìùûOúιÑÜÆ2e2áÂá÷ëVI jþÙþ-¼”¦Û•MÎMi$¨IúöOaJð=77-ö¡¿05é)MEÅQ@eù±}væ GFåŒQ"éèÊ™pRšHm»!góá!Þ/“ ¨Û ñú¤+ÙîBõõmrs{Ä2H²º$ ñ••_rÎû¿;§ >`ñ»£ùŽøŽÎ¶²ì™¬”æßà!àOiÂ;œÄ›ed5N{ìJLàLÙÙÀ=–È“VVw,1UŽj‚­ldwnJ#Øm ÜPŒp‡…þ](Y.þ;Èð»..>Ò%øNwñ¥¥Ÿ:ta„ÝVÔØPõõírsS#}bàÀýo þ6Œ—wøG†”à{v¶ã8ᨲòÀcÙ:±«ìlá»f2]u›¹ø¨3³,þD¹`ÁX€WT±vØnõ;xøngŸ¬þ*ÿ¿¢Snn*54x#zV°}õ¹Ñü¤»âŠŸ#7#wD'M¢ªªNWUB´›ž‡ÐÒ¥|ãÆ=Ö»Â;œïâ EQ'LÖ'† Ü m' ¤©¼üptpwnJSZú‰ˆež~:ÝULÚV99)ÂG|ˆLF•ÜhZº¢ªäV% ø.œ{\Sš-[Έ;óçŒÇøK—N`+Ä™L8ßxAó_ü£ùËe£¥1ê·ððWÈ3p&'"޸߯ÿ—¬mŸ0åä¤ÔÐà }XèÀ½í¥>ŒRð/-À7‚[øoZàþ›[*¥´¿dÁÞqEü–-g +*ñ…–.Â9JJløwæs®,;âÄ÷dYøßÀsõr˛Ӿc…»³o”ÍtÎÜã²n(Qêæâ»­pï°ä¡-¼þ]&¹™qCü–-çÌ›7ÔÚajÙ²)@’oT¸G w‡Zx#“ùÙϾÉ9Ö®õŸkNñòïIA|yyã“ïýw¿~éOÅÜÆˆ72™®³© د5~ÊÉqhhP~7Xàn–JnUDêos´‹½[,"´oóNÇ[!k_]}±ºú€yó†X2 ýfàLŽC¼_̪U9€5ˆc>“HÄ——7Š}抋Çÿ±#ÞæòìÂÕ IDATËdºjÑ¢{‰¨¬ì@/Ê2å丈¨+"Ã,‚dR “R´/‚YxÓ㪔¢Ç[!ÎaIaZMÍ%qgÞ¼Á ¶–-›ð’’݉<©PD{09ñÆTªU™ŒY·Cþnì jì3gI6±pá€ÝÒm¥ ™Œ¿.¼ Ñ6ÒqÊΖ2–þGTáÞÉ×r´u~EXŒU>¾|O€…×mû]æMDÿÛ‚”æn˜6݉ô¶ïΫpZ‘¶”ñ÷ã]-|çG,÷ïˆ ñ[·^­©¹ °°¿µW‘‰ø¨w‡ªÛ"HKoñú¦€|¬·P]m»Ÿ~€lÿóN öÙÃ(‰qèžQÈãáÇcôh aêÅ_L0àGvztÀ!î_×¥_LÐßïY1KÌ"ˆ…0µuëu@åœfY{1QHÀ]€>~Š1p¡õë·X±b¶µÃƨð¦®][`Õªnú¼S"ò™x¸ønáì?¸]\|tp‡výdó¹âŠ´làzv³a”È[XBc–1WÜÜÜÜÜÜÜíñµµ7ôµÜ‘ ¿aÃ;ˆ9p&AvAy›(¢À]]P> %(·ñ3™€rt‘ YÆV|Ñí©½pá"”—; ñêñp_ÿ:`ŠhºUà°å$G ÐÐМôÿتRŠJD4ò† ïp·|pC¶B| ScA¼óæW ¸‡sð/á!à‡N¶ð¥¥ŸF±ÏjWOÈXüesÓªÙÙ=vñA'K[L·.²j+ïÒ þnªª²Oø€Ïª«k®­mŸGì×`¹–-›ÀZÄ ²X¾|†…Ô¿nǪ®á+q|ÝÂ›Š Ã‚»Ð?ÁCÀœ‰ø¨3™ rÆç½Q—$à©‚N–¶p´­@K€9§N%•ÑJ½©óVƒ—.]bŒÍŸ?ÀGõfŒ]ºtÉïYuuÚN~~ï/ ~²ñºmŸ‘¸ÛAÆ&MÑÁ=j ŸˆùU³¢žk 'p!Á÷_8j®ÕÛî§(Þ ¬†F¸ c8õ‘_þf€ÑUÑõ[Jzº«¥Eü7Òç†)õ$S›ƒsçÎp·àš"Q²æZ£Ûgµ«¢˜kMt>‹?p¦_ÀCÀ_;dÑSiégñ€;4²SyùÖk‰AÀ`-vÃIiºN“^7®yذ¶¬,uWKK[VVó°a×ÇùoxcD£^ö_ÓäȈ¹Öƒû1ÆÎœ9S_ß¾ëÝvwXaáM{l{RR«àލ\|ò÷Ho•å´hÑSÜS£©€åp*.GDÉmðÙUFC1Q6LÝ"Þ˜&=}~êÔË“&‰Ç{\¹bcÜ¿aÛa/Xp'@ñÞL1Lj‰nï991Õ®ìúé¿ÿýñ]=xæ_f]ÞÿãïÿÓOG|Ð{Î466*Š2f̘‡gfŽfŒ=~$<«c/EŠø$f2]• oØöxÀ=" ŸèùU³ßîû.à^\ý–ð!ô4²¬±Ó\«)“IÜŽ©Ç,X C±¨í¤=Fw}}»X½$¶õˆH¿ӧ¿vµ¶ºZ[]--âŽ:UQrJ|9hw7 sÎOª¤¥)ii,-­¥¨õãT%-MIOÏ~ê©n/†8Ž\ýÀÝwxU1sr\ Þ+W¾¸g”k܈ñ¤‚9¡ ÄbÕ† »,_>½»Ã’ŸÉTœæZfÛÜkMfý{0W¸XÏX0×jõJ Ü,X0†••Çâ4~¡,¹Ømˆî•7¶ŸyƒùƒßãÊ´T•ÜéU—Å—ÝÂÝï˜Ï-•Üx S{ýþPóÆ•7†„«Úí@ãgŒ±ná ''¥oß;µ:rÐì8E‚ì‚òÁd[¸#>.>‘™ÌªU¹_»¶.ôaI^ß䇸òò/â w!‹æZ'ˆ±;£)pO(Ü…æÏPeå— >o}}€ÜÜÔÜÜ”ö6xÛQ[[[[[ã°æöaÓç3Ñ®×ï¦Ë|dÆØim¼O?V»÷ÞG£ Mç >°ïx…±G>u\D ñ|Æw~×þ»µˆk&P«Våx­_5îñ…;€Wáð¬A|ôˆ/-ý,a{HÅwÑSÛ ÿYVîâKUECCíñãÇc?—1A:ªªªÓ7 ƒm¾™dE£`ÎŽíW˜rß“º¬ÅXnnpÿ²)Œs(þsÀÐòåèŠx[î!Ä9·$ ¶¼NÆ*%ŸïÂÂÿÓ?íP\<.pzžKâKKµm7’ wÌŸ? @eåñxÀôϧ®®µ¾¾mÆ÷¹eæULŽ-)ñÿ†$à;ëôÆ#ž©æ]å`Œ L›eTô« šü@êЬûNžjÿðÐ~'ZxtA|r+Ü#ÕÊ•s¬[·-êâQ¾ºµðÉ绡»Z Wà!àù„#^À=Y™LWÍŸ?  ÊÊÖÍÒ,¼± ß wÛ·×4jÔ¨ØÏªdwÀø?ý)ÒI‘Ììùì=Ea“'{0uµð å»_àLÉBüÏàˆ7dÿL& ˆºçl~~þ¨Q£d™ƒcÇŽºªªŽ—TmíM}+¢^æ§øÂ×××··á±i¹bõ&Wµjäˆ.ÕÜ<@ÏÝÇÄH™÷¦½­¦™Kh6'(//÷5)yÓçq@˜÷/«î'Eº •«P9´q,È.ÌJ—ÿíß6G6PbURòžwÿþ޶ðŽx{f2]µjUÀ×®ÕrÎÄñ]ßÙ#¬À½¸xÀ==& ×µþÃ?l‡2™®š7o(@[¶œ çàY³ò8Žªªzè;ˆÈÏÏñ¬úúzÓ§ç˜ý{¤ MΓß2îoÙô¶J)…K¿`ÎìØñçøö·^ŸïM{Z3MP°ôkŒR¶lz[<žû­ou>-c cÂwŸ;§ž:¥‚cÔ(IU‘9!²AûÁUÓ/cú¤$£ÿ»è2ÖU·âm¸ÓªUùâÄwî‰9]Ôú[xø™E>Š´Á&š7o€ÐˆÏÏϰ}{íŒÇòfÌÈÇÿ°åĉwòó{ƒ{›Þ1XûˆÀõ¶‹ú*ж@›STáêÕ”õÔP¯uÚšãïÿþ%Ó¦¼ÀÕ0ü{óEM{:د ¾põjã~II‰¯E™Ÿ½H.^ÒÞ’†—¢›BÐ yLpW9úf¹9g'NØ1¥1¶p w!§#Þãw!ñqç{EÅ‘èöämoá_„‡€¿‹ñeeEøþãÏæœã4oÞ€¶l9ú0aÞ9‡Ë5ÀÁ©õõõf u¬_ßÍÉélÞ98ˆ‡÷ñiþÒ¥âŽÔÒà¤M®² 8}D$ID„ƒŸã7¿ù]¸gºÚÞ´§•S: ó»NÕŠ3®[·Ž1¶¨pç¸v•è?@4X2Щü:â¨z˺o¯ZÁ9ûÍoÞŠxÄxª¤d7¬ØŸÏþþÅÿ öÜ»U|ùnlņ«Hâ ¢B¼1­*"‘Ì8ñƒ‰¨ºú|°òóóSRðÖÚÊ÷ÞkDz¥ó¦OÓ–šÏIË^Ìð¾qÝ”Èèe$ÑUÁË™(z¬@ÕÞò׎ϼû/>?a$‚ÒÂßܦýà½Mp///P\\ìw–7ß|SQ”eE+ZZø&Î }³H\¶Ê¡FÕR̬ª¼ã¿\âÿꯞPUöë_ÿ)šßˆÕ*)Ù)Üo ïÔàÂÂÇ‘ï†mîBID< âÜ.œ(àþ?DÇk5ð<ÒK°é¡É9999È4«Á­›ëäää”sp3ÔBཹ¹ùæÍ›×¯_¿råÊ¥K—Ο?öìÙS§N8q€Üó¦È›þØô¥Œ!å*îí©£¦‡à;qÎ;S9Ü_ýuÆØ“O>ÙÖ®½õîM"‚W¹¾L)*ÿn,qê°ðâN3ꩯp®þêWÿñ¸–JÀ²eS#z¢C¿nÝ6¾ÿìg_¸Ñ8N«VåÇ‹ïÑe2•,ÄG4×jÀÝïq§[ø¹sT]}!àw·n½6r䌡CR|ÊnSØBׯrx»Ž¾cp´¬Æÿ÷jäïà8ßM @ÇŽ;räÈ;Ÿ>öé ûÏ”~âÃϾÜÿÉÑ÷Ï\;‚ïœÀ{OÕÌ{0¸RÚUNHíA={’Ê¡*:Ü·­çèHf´ûU;€©j2µ–”ì°lÙÔHá.ä8ÄÓª"¢ÉŒso=ߣÜí¦à!àåî,¼¸³íÎGü@€ª«/ú=¾uëU}´^í/\¿Æsr4#¿óƒí;?ÜÂŽýÛL¿ÓÔŸ„k ¾a݃¹`UUdfffeeõïßРAC† >|¸ØãôôIì|gÝMv­'  Y>­rp•«Œo-ñ„ûîÊwª?÷l=W·îÿzûí·CÈ®_\2©z £ªdT¯«j4|׬ºžÒ¨ú/PÕ³«üàÏ9g¿ü¥ÿN& ÐÆ{6nÔàË8B¼€ûÊ•sÜoYÌ÷÷`J–…„D¼_àLÎGüâkj®ÔÔhp0}Z>Þ}¯¶Ã«à7®qÙÙ¿=­l†Ð|Ó4“Ê;ðÎ4ÅVUU BÐö–1ƒÝI$I#Çß{ï b­î³G=’•>pP¯¡ù.÷L•3RÅ9{öì àúõ믿þz×ñ=uèÐ'‰èíuovòì¦!êg.¾èÿÖhú¡ü#xÕô©2 ÿDزåL°Ab—ÚÞyòÆZ3Ëe˦Xql2kÀÝïqG[x+ùnIàLID<¯Büÿ¨ÀÝXôTSsEžÖ»€âââ¯~õ«Dô§?ušÒôx´7•>}H’È b#|×–¡#XºøÊ¥€ßéø(`Ì@TÒRIÖ.æ?øç<̾aŠ]» õÔ›2@¦æoîK—NYºÔ¸cùò™6lØiÉhñP0¸ 9ñÖð=1™Œ­‚øÐ™LW9ÝÂöè·¿= °0«°°¯ù»Ó¦äã½Ýuº÷$®—Ä4ßÔ°7ó¡Y3šˆÔ Aûµr}‹®q§‚t)îjá?þªŠÍe¯Ñò%Ï‹‰ðÉ÷/2‚)Šxr5•1¯Z½g+€Â™ |Íœyý -àž-egKâN˜wsìÎÑMýŒ”*I©ÿ¡™=»ù¿L·»¦µÄX`vì,”sP΃@à©wÇ7¾¯ÃýÑÏå'Û"Þ˜M É8ñÝ·0íVqÊdª¸ø®òòÆòòFÁúÄèYx^CökÈ~„ÉtÕ¢E÷ˆv4Ž[ÑÚ­Œ¼˜ë9‹*AâP9ð¶!%LL€÷Ls¸,è`Û |°Œà^üpQn_\\\^^îá*O8éíôÎ:äÆh5‹ynû@¾âqH+W¬^¿áÍ7ÞxcõêÕÈ—44 Î(ïðÝF³á`øM üð‰Ý ½ºHÕéMŒƒ•:N  ¨hxUÕɪª“EE#Ä#¯¿þº,Ë’$ɲlÜñû’š%‰dY’%Izhâ£ÐÆÔN*guºžßw,‡»Ðòå37lعaÃNÁz;Èo6õöS¬þ=®™Œ}$Èþ²£†» ]¼Ö=¤ª«/ÕÔ\ðÝïxMÍe¿8Ç”GòÀ±{wW;u _úÁ¯£‚ÆLy}®5œþý=ÚøåU/|ÉÂçH“’ IðùçŸØv¨Üß¼÷òy·˜=!GñqÖÚμðù ´cõêÕ7nÐoû&Lp‡n¥3ûCGåŒß­«®þþš”"I)þ¡qf|ÐKhT}¹@ÏžôÖ[oÑ“O>)Ž/*Á9ïv›-~ jWo‚Ýàœ“ñ!ãÃCû?nüèÓ£Ÿj¶ê(iðý…^x@uõEá# û‹Gß û™;wF‹Î§<šGIâDdÈ2pY‘d¤¤vª‹lm弯‹10&"lRU¼ÿ~œœÇ9Foé” Ÿ:u ÀðáÃ÷ìÒ.IØRó2€Å Ÿ—e¸d.ËeÈ. NÖ¼²ÀÄû&6l˜1ÔéÓ§Ì52A&’%ôèA.\ÆâС·32ð•¯{öTÀß ˜ŸyWõKê")…¤T’R,¥èHÛ;áIOÀFžóç:r¤ÀËW+ÍÜ{+^0âG` ªBœ™â#•cÒø‡î»ëgÝ?iú=&L¸ë®»ÆŽûÑGWöí»˜žž¹zuÞ€²²²úôéÓ»wïÕJ1*éA|˜{09ËÅGÃ÷DîÁT\<@b_VöyYÙç.œhIƒI›¤4={Aexê¯^p¹Ü‚#fœUWŸ×á>Àï‰Â¹û¥4œãá‡óÀ±goðÆ+çÓÅÍø¦·³.A<ˆ›YÉ ÞÐîŽ9’1våÊ1Á¿­õ¯˜?÷9"€¸°ó’¬ùúæýmœôáICü¹sçÎ;@º0ðƒÇ?ûâÔá£gœ¸xòôåsg.\<ÅÛʼ­¬½y½^I’ÒÒ:í«M±š+g̈ÈÃB ÁàUc|øºuë:®ˆˆ†#®WÑ0,Pþ~£¢‰ÜDn¢ºQÞä÷]ó<-3þ =V Ò…_’¤¾}ÓÚÛxó EñAñéˆWÀb Lg¦mCNH…´áÆÙ´i?€%K^ºôaÿ ‹›y7”Ä[º|Éý¤ŠŠcÇÂ<º¢â(l³05^·íw ¸ ýþÑÄß éˆWU­Ã£ªâ›þçPU¾oßçÎ îB~ˆ>*ÇCæqàý÷ë:5Ìâ˜túrÀÍÒs}~Up³ÑøÃªƒr uà*SÁÔo{•ˆæÏ{®î‚ì\'ÚSÓ4ëžžšžžš.NÈËÌÌÌÌÌœþØÌG&Ž’9bPßqÃŒîÓsh¯Ô=]Y½{P|¸~õ†·•µ·²^xÁ²+V¬ ¢ 60Ý¿3ÕTÿÞ‰”B”J”¢Ýºþæ™)Û1þ;t˜øÈ€ïo½õcX8ï I•½müÚ•vÕ ¦@Î]…Ê 2½%Ž^ÔÔst§A6mÚoÀ=à_V¤XÁþîV)ÄŠ°x(–L¦«V®Ì°nÝwR”,p8j+¸ Åñ¦Læî®ßýkxøÅmÄ÷€o}ó9"üá?^á“'}wò¤ï‘0ò¡Ÿë‡xm×—¡ê”éÓWÌñùSž¾‹ €Aƒjø+oüã—û ûö ½*W¹ª’iù»dÈ2ܧÚIê8Åw ŸúnáSß-üþw Ÿ2Šg”,~£¥wŒ^ŠW¯pŸ—§¸¹K⊂‹.úÚYŠ+¸ûÔ nÝø‘ÍUð¬sñ ëB+ͼë7¿ïjù LÎ]Ÿݰa­X±Â|üÚµkU•æ<®x¡øêv{ÛùÅ ­ªBL!ìLox ;Ì^c;tÓ¦Ä%K ögM ßW¬˜ `ýúDXxká.dÄkýß, Ý›”ñ-q¤­Äûe2õ#xø§Øo ?p0R>þøš$Ѥûû|åIm­ÐË/¿þ8bBõÁÉyàØ»¯NÕ‹ó€NL7nœÀõÇEèahxO¡Þ‡È„/sµ3ÏöWÌ+|V’8Éd¸dHRζ§¥SjÎo'¥Í™3GUÕ;GÏf =†¶0…+ŠªøT1ËÊ~æÔ _;ë‘Å0//ŽçË—/'¢’’ýú‰ˆÆÝEƤ¨ðï"ñ¹AnPŠvÇOÆs;-q2Å|°(Ý)È]¥øÀÚ!ß;½‡ÏËÏœiRõ2¤¸sÜ1½;o¨#à¾dÉC!à~æÌUUì µbÅL€¯_¿#~§X¿>¦ÙÔв-â×®­ wÿ&ݶµ!Ü…¬E|ÀL&~² â'Oþ*û>øÝ½÷öÑù«^x|¥Hä_þ<øL°ÙÂkzí Œ®îèÂt}¢•¾ËŠjÄHÀXÎjŠ7+*pE‚K‚LÞïÞS§ÞÍ Ì‚ügIâ’™ Ë5†úÜô¦¥“Ø#Dƒ£÷¶V~þ,?{Š cª2\º¨6¹›”ž72zqÅÇœüòø™S>/؈êƒê#¦€ù xÅÅwÐÖ¸/L73¹øI~3·ö´’["·Ì¿3ó")}œ±wgÖ¯_/IÒì™+™J;T«ã‰ IDAT˜#Ÿ—û¼ÜˆDFÄ9útyñêp0ô+¡«yçaoª…V¬˜ Nˆ_¿~›¸s»._ (cóÕŽþÀÁ,¼÷^^2"“éªÂCÀ/ÄWU I$IÔa›9¸ŠåKŸP²éå—_~å“?½°°Àkj.Óª“ÈÇûûê ÿîÇtÝ\‹‡:@¶ti.€?©”?pe›A -[—%È’,Krf.˸ç>7K-ÛµaÈ„x‚׋¶6(£{ˆB{ÆSqîgŒ«LÐ郮ºxXñ2Ÿ—9Fe`ЍH!æ#æÅáC|éÒ¥’$mÞ¼&ò2Sì¸xÆ ¸Äà&¸üùÎÍ3«Ú,lÚ´‰ˆ–ê{T‰¬fú´åJ;¯ ;˜Ä¥öï×Kñ©ÇŽ^S™6¡u/²:¯¬Ø´éÃ0áŽ.|W›Ai!·¯ÜW¬ˆo3H»Yx÷U«òý÷÷ðC¼)pwÜc·ð¡÷`ú?ððÏöC|ÓÞ°LWUÕI÷¢¢a²¬5wìÀ2½¯Kyå+íÇþ÷ñþÿqDuü‘£—:æT*ÇàÁ ¥¿ËŸéDæ,Þ_8ðiÇ»Â%‚Ë…cÇ*%I1üÁ±chìÚ“[>oWÛxhˆçÊÈT…AQ 00Æ.^cè3|`¿,pU}»æõÃ׫³zŽÞOm¾¢¥Ø T­œœ˜/^LD¥¥¥ÆÝ¿#pñ ¹4ÛÞÔNMíDnjù¸Í|€ÊÁÔç®rÜsoó.I’ÏGJ;˜^*£*Ĩ>p•aèL¦¨—9Ç€Iþ—±yó‡âN8pGçâõ$ëW8ù+Þ€»…c“Mod2‚ìBòñv܃)ć¸Óà!àÿÚl®5ãÑ4M{[ývŽ6ËXé^T4À3Ï<#IøÓ›¯>{é¢ç.x@eõË5µ¯Ø³ ïunè-à@“@ü“rÁ±Ÿy#ÖÎL7M·ú®uÆ>} <ÐéH"œ>U—âvßwß}}ûj…„Ww)-‡½æÚ;”Þ˜†C[ø#Ün¿>óC¿þ’ʰn묭ýÏ{|ÖìϘ®\bÆ,%Ó2( }ò°hÑ""*++“$I’¤&k…ðœöïä&r¹èÒMºt“Ä—æ4¸«þå•f¾oܸÀ£/U¼`^0…˜BÌUc`Šöbôè,EQ÷œôÏÑÜ—,™&ÜccÇŽe×À®ê(â/kŸH¸ %ñæLÆüx€ü½±ñì¸Stˆ=pÿßð fÄÇ#¥Ñ(¿§µiOëÍÝ­7ww°^À½¨h„€»É2I7#4º¸è9qXmý+‚¼ïìÀŽmÎ(ò™ŽÊk¿ ¢Î7S.oVß¾8ô¹ÆôÖV¡p.róðáù})))Ú–FÞ m—$N¤ÕÌçFäCÔÖÆÛ†¤2 CÊ¿_¼z•+ }û’ÊP²ùq®ås¿sðYpuúÌ/T…]8¯rñ ˜Õ ¦Ð‡rtq֪ɿûÏ´}áp¿pZ}蕸€ÎS¬Â¿›ûÏ(++#¢E‹ؼy³$I÷L\ÂÚ¡ú øˆ‰|†AUÀõRp }9«˜WTlÞüÑæÍX¼xrˆWˆŸ„yW.ƒ+ƒÔ·û§X%K¿~ýöÄÃ](‰ˆ÷ËdÌŠïþÚ‰W¤ˆ"“ ¨ICøŠ ⧤eLI3V5ínÝýgöüçóç ø”á£H[ *qmd’",˜÷ü¼‚çÔ5¼büÀžÔ×ÂÂs:Ô¬-ˆŸ”Û»7êëëµãº0]‡²ÿ<ˆövHÆÇø (*€×^{ @öœ9LU™ªú®*$‰±ÐzQâ¼ú®¸Ó2(Õ烡©‰s†l¹2jûư©ìKfmé¬Õ㇞2:@>6ó°Êø… ªáâU±nÈæ#ÅÅÅ’$µ´´ˆö,Ì(`Wá_OèÒ€îúô%tú’¿WÕNMÊzXür´·ÒÒR"ºkÜ"Õ ÅGŠ:È®èE÷ \ÅðŽnÚ[ª ;"„;€ö«Ì{•Áè æ(­_¿]ÜI<Ü…\Ñ ™ŒY|7b™þða„,—¼=TVvÈ*¸ø><üÚ~A¼Pï©iSÓ8pðª0 3³4ín½Ù9½yú駬Y³¦ÓZ™KKCEkÆÂüçx¶¿ºãW‰ 0[[ƒê-p§dpŽÆÆ[Z¯+“0Ýäå•›Ì|äãO`éÒŽ¦ð†\Ã1í¾éJ‹²÷è.H ‰ƒÀ ·ÎK7ÏÉóµ„ô{Ríf®(¸ñø@r²äÚx@ñüçÔÞ#¸ÑÛW¿åLÙÎûù._`ªÒ9¨ñb÷n˜?>ݺu ÀôzzÞes’IÜNŠJ '…Éóc…”Éþ ¾———K’´hÑ"æ…â#Õ«µàâªt²žŽQÓ:\´è>Îùk¯m°xñ‹?Ñ‹¤ý,˜Â˜Â ‚TÈ¡·ÅE±Xx÷+f' îV­Ê°vm‚,|°LÆ,ï~{·ñvVqñx€—— qLY™ö] ‹ Ÿ‚‡€±YoÖöËçÜ}w߉ww|ðé1«-ÁA.d±hH«Zá’Äî.…ZV³óÝWIê°äAñõºÕÜãÈ‘[*Gnn.544˜ÞuÅ¢'ö÷'MÒø¾fÍ"zæ™g¨·´yM’ˆKÓ{Ƴ²yŠXQÐÚʆÔTIaøòï›Û®p –ñ¾»®«*î~+L·>ÎÀµu¡zÉŠW¶wïÞ’$mÙ²ÂÂë.¾“d@Fã ®p>f pÜo·mý*4»  ²²’HkéZ\\üá~0/‰ªÜ€»¾Bµk¯Júñ‡É¶ÓøäÐG*Sï{?8äD”¿Ptˆ7à—kŠD«Vå|íÚúîM!2³$8¡Â=RO ñî N°¼Âý{ð fÄÇÉ‹.S¿1´÷Ô4ˆö[†ïlÚÛÖ´·õ;ÙOøùÏ>x¹\Z¯]Yܑᒸ,qY沄¼gs³Ÿðî{¯îÞûª@¿@mJªÜÚšvð€Ÿx#Ø9•X³ºmÛ>¢”ìì鯓|ûö(ƒ(@û½¹Jª •´¶bmÀkg$¥ßZ_ßæõrŸ.1†ŠªW”¤nÈÈÀeâ2F6Þ¸¾k ºXøÜÜ¿în•ò\ÑRÕ‡íÛ`îܹ’$µ¶¶}cÌù»÷‚"Âw¤€H&ƉôïúüêŒ ™ôþ^1¡ªÁ)Zag¸{6ÆÏòÿûnÞüñæÍxæ™B€oÞüÑÙïŸ ç…Ñr­'*c*cà[¦$E!~ýúíö»ÐªU¹â‡øn3³¤`E޶ðŽxîq:ï÷à!à7 ž—–~jÉ%UTÑÿÊcÄ#S;Jš þn+€‚¦ýmƒ†’Ë —à»qGc=—d.»›ó¬xÖž½¯Ê$ÉhûEí^¬[‡ r444`º(w1ùzIJ•$ÿíŽ~²ÜK–{yÏ‹Ì*gœ3rÉšS†D$iõ9Í­)õõ­ƒ¦x}dRªª~@~îsù¹Ï›Ú222 ."™ßü`ÌÍ;SÈÍMãœ7¥´p¦wwÑçZ…zõê%IÒÖ­[Sµµ£’IÜÄwµ;œC†÷’beÀÝ\9sëÖ-"Êê;ŸyÁ|èHfÄ›Š{rü‰Ù,^< Àô‡<ô΀½9º}mÜúRß/WÕ*߉CÚíóâ«0o Üí÷x+œLƬŽþ]u›!¾¬ìP¼á.dÑ\ë}bG|y¹ÑÌyŒùñŒ©i½§¤u6jb¶ƒ·Í´¹Ž·§œlw»à–áriˆw¹µ›ìâ²Ìs²Ÿ=ëY{÷½&Ë$‰RSH–xÇîA˜$ƒï)’”â÷#üó?o”å^ßÿÚ“F¡¤êb*©$s’µ¤ôûÒ—vÝÅ"ShËÖWäåÔÖè?Š$I’TPÐaá ‘ ’ðѦr>é>‰dܯÄ8>:¨š-<Óó™œ<¨©©iiiéÕ«WQQ‘X¡ªdW *à*îÏ ð'Þ¼ù‹OZ¼xÒå_^¹´æÜ´Æ…n6W!Òvp­x&PÏJ;ʸÛîq²ðaf2fI¡3§#Þ)p/Ü|~gƒ ¾¼ü hÍœÿ•{OI˘"¼|§D¿ÝúkR{@’á>ÝÞã\{ú…v·)n¸]˜xÝw?MžL?BS¦búcxöÙg¼¿ïµÆ#¯½cÆ`ñêÙëRuõÅœœ¢´ûöhçéÌtƒÚD æîÊwYN—xºÈÙI$äMËW¡6|TCnH.N2 /ã\³*É4z´[9-5¯ÕÔ¾ `ÎìgEã\UÅ”iÈ|ü¸$’;Bæ9Iê­ccn ®¢±Rœ7/¯'ÀÛ{63¦A–1¨>ToA~~~zzz[[[}}½ÖHÒ\?#2…ë/d½ ÅT°&à®èÆ¿µµU’¤¹sçn÷@3ïJÙ*ÄäÎý„ ¸_ýkWþõ*\ —4à…þ¢rÆXÜ䧇q³;‰{¼«ªlʽSåaŸ‘h­X1 à†C÷“Ý2™®²ñág2fu_éhÄ ÿÿø´À=îpú<ˆñ±Xx± 9€ââîû}f<šðßÖýê·µ¿þ]í¯ü¾›’Š=¨G¥õ¤>7½ý[½ƒ¼Þ€ãˆùO@ÿȯï R]}1;{š$¥ïØýI¦KZ¹úÞÎÊ®”éÓG›ÇTnª2Ò]r:$ï bO;•˜ &¹‰\ —\ð©¸Ô¢Ê2áâÀÉSûÆed˜5óYQs¢šR”ÌÅä"È Q*qH*$µåìè– £Œ³sÎ9çÙK³Xó¥¥¥I’¤0®˜2¥Y)´‰NI›]9ŒÙ¿ù €ºº:Y–{öìYW æÕ§võXæÑy~Û¥¥Ÿ”–~`Ž2òú›7àÒ–Põû‘Öæ~ñâÉœcÓ&Ä_;hêð£§íªªª\u ‡}$ðÝñö‡»Uˆ(p÷S˜õïÎhfB‰ÿÔùxø×d Þ(ÿ/.úHkÖ¬Y³fÍï¶ýÚ¨ÎþNè©û`ÄåbIiG®Òz¨½¥ÑÛú…?èŸyæ™gžyf×{»««×ø}köìI²ÜcמF3Ó!uô*e·,»UoÇ+ÝRI‚Ë•ær§`F<ãL%•\Z…sÉ ÙEC†º@øôÓOÜn÷ˆaS³'<«wžS1Ã4-Ù{n/’‰$ýF¿Ü,©m_xÛŽyäçg¼¶ö&€‚eàL³Õ¥›´¹Ðììlò)Œù»Hxï}ŸÊù”G\FpÄ8gœ›ÿÁ)*Ç‚bÔ××Qzzº$IFŽ(Ýyl¦/ðW+-ýtü¬ W³²]£!Üb²¾ÓiIÒ’%“nôpå3ú¾Zú_“*g*W`3ù!Þ†³©ñV¤»ŸÂâ»èùnlËç•—áû_ÿõ Î;"š„))A¼ø1‹‹ï ±ÿø«¯¾úÚk¯½öÚkkÖtPø'?ùÉÓÏýä;…Ou„ã]ÿï—›mG½mǼmÇ;þÑG§ÝñóŸÿ+LÀŒiwÉrÝûNvêU ¼¼—Ëår¹HIªmœ$¼þöÁwÚ7V?$’íY’ð›LN¥@—ÜHÍ” rùòHã‘FWJÊà¡@ùèT†Ù]ÞjÉÈ*IªHáo¶Rs+¿.Ÿ8Hh?éŸß൵7Ì[ÑQ4i,DÊÊÚÁT2ø.ܺ¶)¡ãf{aà?ßµgƵô:"sðð;H&rd"õýºÿÞf]ü—>Ñ—pýϨ„R9ã=m½¢É‰³©1Zø(w?iŶá(‰jG!cZUD4ˆåL@ý;²|±nÕ(ø.XB¡ÆW^yÅÜçVüW¬iòSóm]HúQ}¨V’¨&ꊥÕʼñ‹·üèGO@ç{Avû>8àá‡u|¨"øìÀM÷ÞÛ›dâ>.f:ßZ{ÀãKÇrUÄ%Zj¼¹z#€…Ë/˜“,1'ŽŸöyUŸWíÕk´¶w¦€1 Œq3üNnÕ€ ’TH Y½ÜäºtÓÝsÄ(¦’Oåw“ !e këÖë 2lz*¡Wßii˜5k€·ÞPY;ûÊ7ÜX;°óÝ63ëa>×ÎwÛÌÉÑ\ÿ&àFÿþÛpÎoÞ˜-«qÈGA§½=4µ|Úv´ñ2©=¼/÷q®€+>Îܱ0#àÀ¦Md¶š8qˆ±Ѝd•õõkÛöÕ’„Â%~hýú힘8q¤ƒÈn–à»`}ØO©ð¨É.äêþ] ÜYQq¤¢âˆý/àn=‰ú6<ÿŽìGv숭²²ƒ´Ü[[4´ýú7b"Òbñ¯õYq‡ƒŸ:®ï±aÚyƒ¥ÊÇÛ% =Ò:1ü»$FÔ¿ì(|„÷¼Ép¹:ˆ3wî€êê‹_4ßËíNÐád ºårË&ô* ‡ŠÔÔbÿV’ÁU"•CE©>—SÀ’éò•kÞv%%Í=dè@¦Ð•ËTH"òæ<ªŠCÛ0aŽÿï­gá=·ê?q’8ˆû23¼C]ŸUî'NŸ}¡**Mv³ìG3<{onÝz­  Ï’'±þu\¿<‹úïܹsçÌ™3U.©\ݵk×ÔG¦‘„í;[ÌžÙ¹¯®¾1•êåR PT ¸fÍšµ±„sE%‚Ä1wU€¿oÛïსr‰hôØ,(€ pNœ8ÇEAÛ<ž~SF¥ù6<ü>žA|YÙAqÇìÜÓÒñÏ¿zé—ÿ÷oÅ—|ëÏŸûÆŸ=+à.ZS©ŒT•EMtõ›oxªwXjk+™Ì;`ïHiȽ¹£ûw¿:ÏE½~ó«mâæÎ@5¹õÀ}Ý©îŸ7™ªh(%Õ•’â"ñ9@«“!HHMMIIM%cª~§`Ö•«ë6¿í“|mj[^™é½ú¥g ¼Ù‚Í\NìÖªõ%p@%ô&Ûsàç$© •ˆ éצ0~â¢{ì(šp—¶rjÿ§lï‡JŸ’ôK/½tøÄK·šZ%I‹hV*×{Wô‰ËÎ+¹ôˆÆø7×oàN"š5kÖŽ;|휸Ì,è÷öÓ¾ö¾ƒ‡/pâ\Âú7µL…¡½ ‚ÂýämêlܸA²K:yâ²Vœ*Z;Èœ$ Þ#òE!#pñůe^ÎVNc…Ô¸¯Z•·jU bØu[õ+/?Ú¹' ñˆâKKX¸pbð帜sΡþû_ ;í-§ª¤2fŸ1øø|ð*ð)hœz=S_p$x2Ý¿yפ×>þåŸÍt»{ýþ_÷©­ªÚÎI¯~¹çžŒ”÷‘£m"Å& .·ìrË#HB]ýõÔîâù Á@8}c«ûâ­~nõ»x+kä ÃÑ“Ž6–ûbóî=»÷îÝ»ó½O>ýðàÿõ¿Äž<¿†óV¯WûÄ!K’ôñ'ƒtŽûñÝü8Ðа«gOyæÌ™NŸœÁ¼êÒDZèÉN6ï%æ½ @§ÏÞgàYµyY sz{Á|ùž©åaÔèþrŠtæü5ÈK$ä‚äîÔóÝ>ò ܧMˆ@å4·Ö®­3ànÕ˜äï†ìÄëE#¼ÛX&)Aü‘ à›–ñzZ´èžO1¶É6þÔDðÕO¼ µí8´ïr¶¨U̼‰æ‘ýZ¼‰¼ž¼s™’wá»Áµ/ $ÓŸÖá««ï& ãÇO´Œ× „'|FŽt8{V0dH§ÀpÇŽfpÌ|¬§ØL ·š®bý¦/8o»xåŸrsìØQ­Xö¤Ø’´µ>/¼^(^(^0FLâÅT±Ð?m8$ýG“®Qµw݃ƒá$Ö?©ç¯ÞÁÁûõ¹¬µvçœqvñêD½z¼tåBNn.“œÖCÙ?üð£#G2³²²²çôêúïjÛ¶[æÌùÿØ{ïð(®,ïÿ{nU' I$rFä$r’1Á6`Æ`{ÆŒŸ ï¾³žà{Æ6#ìqÜ0ûÎλ;³»ã1ã€I&ƒ°Él²Èˆ,’P@R§ªºç÷Ç­ªnEˆàý½çéGO«»ººººúS§¾÷ÜïIܹsG$‚1c†øp¤ivìºsøðáj1³T²Ýx,yùŠkš@×N©º`AÃBj2³…Ä~þ*o'6Û {Ÿ8×®ëJKKV¶BƒîÁç[–˜:uj-‡Ð½ªu2e{ëNîÄ·j|µRÔ‚ï8M¦ÞàŽÛã;<Ä+¸+sà›F=ÚFÞRÔ â]¾/[¦šÙ~5uŒ7ÞxƒÙæ»zD59zrÚ¯P…òpÚé :€Ð±ˆY%8Iº Û§W¸þv¡ Ù¶ Ÿ%ÂSOµ'ÍkUu5—/[t¤¦V¾šÜ½; s@€™!±|ŵH$‰D¤ŒFè¹gúêÉâÃ?$'&=6ÃŒ"‚ét 6£0 2]›_E… Ã2 y¢ðû£J»š¤OZ? ƒ%[— ›H–O<>’‘²ÖÑ@‚æ}´• BPî}RåaðàÁîýõ×gϦ6nÜx̘äª;ÆRcÆ$íÞ½+ƨQƒÞ]»n0lØ0V˜!í1ä]ߘgN]dtIÕ:¶Âù‹ ‰–M˜-$ô¨lä âØF‡ìšóWƒ¦þê\\\¦{(µi¢¦C×áI%ÕBÄmxߣÚ:™²=a úû¿íõ‘Õ"¾ÞÓv7n“ïxKpWq¿ÿ'døÁ#þèÑ+ݺ5¿i9M-q&~ü¾«PÜ|²ÝÀ±*ºnÝ @èxTAœ $]ÞÍÖEåäÒhùÊË$ðøÔ4¥N¬^}ÀĉÍóœ’B)N¿=72ôìéÙ±#XR„¢–4#‘ÈSßiËZbì|ðñÇ»A}Úˆ‚-”—†ˆŒF¤•fTiD¥aHð̨4 iÖéŸ0ó”áϤ€L D’„Y]½Î=šŠ K¿ HàÇ·g~SMOó»”¶»qû|Ç€ø¸‰<·w÷ ñï# Àì;@|½ð]ÅÉã¶¿hÉo€X^¯XùØ#¿$€‰ ¶ðÒ³—àp^ÔNÞ5¦šäH9±¨,~ÍÚ"?Þž€øpüņS§,Ì­­[‹;vôŽÛPõ:RÊùþ{ïý3‘T3Âáð3Oÿ,•FXiD8µÌˆôè -§cŸiBZHïºqNÀ"HA2 ü‚$ˆ]I'B¯ ñ´rõ’­çŸÿ›60ƒáOH8 é4zt#6hÔè€>}ªôBvíúÀ AÜGö|³—ˆúôî³qcDÒNŸ*ìÐ62xP[¥±GKHtm ¶àïXÙÅáÀ—¶ïÐt[Ñœä]ó°mê¦Á°¢5hê0þ|3f̸Åc§þ£p—~Œ¤ßzÄ»|¿ÛpÇ·šïww÷ ñF€çnñîàêã÷rŒGzßùöɵ›ê©>+WýÆ-“wÄ<2á%„@Ÿ¾±,;rΨœ¼“mŸ¼«êF´e[€# "ß«K—øêU¹±apëÖbüøÆ.Ù!AÞ Éþo»Ü²J-ëó Ç£Ÿžž>}úôS'ÙˆÀtúÛ™ÊÌ݈iñ–‰¶í.¤B|@øIB"¥õÁ#&úöò¬Ìù€!ðƒÙî`é† ×˜¹aà –…P¨‘BJ©5ªñôî]áÛÙ»w¯eqyy{u2°gÏÞ¼¼F73‘÷ôé¢mÃ:´Òß!Á‡r-0zt"Xðµ©ØèØ“—³ëâmogÖ\ãOåç³éñÑ‚%óóóó[µjõì³Ïâ~ÇÂ…›® Ù¥_Û­ÉßQk“ÕììlsæÌ¹ ›Y?ñé§_åææõèÑáî‘]ÅÕÏÜÇrIW“¹m¸Ã!»Û\ûx¸Ã!»¢üFFtïÕ›ŒÿÕ„‡9~ì/ã—Y•óöªÕo}±êí·ÞzË}Ðׯ›ÖêTŽ÷@|¹$FŒhàÙ³'‡ìŠòÕFBíÙSÒ¼9Z·&;ë+‘”–! àÜùûßÛs~þó¿mÑ=@” IDATâ?ùÉO^xá…ôôt‹/Þà³n=H÷AxXxYx™ÔÍÃäaÒ˜4>}ª™æBׄ®"Bº Hc ™{dçª/ç¡"Ü ÈÊjJ)Éã.¯oÜ—–†á8¹Çœýû÷ !‚Á¶Dv¥Ê† Åyy áÔŽÖÅvmC ÑÞ©R'öåZ¦DÏn‚U…ûîÕ`V 5@¨y¹PÎ'ç@&¤Vzœ%`1K|òé‰H$lAÃ(ÿë™cऺÞ±"œÅ‹»£¯S§N=r˜CAgèÕtyÒ–É={\%H‚$¶tᥔkÖ~RPØŽ™üãñp‡Óg{ÕªKÍš]ÑuêÓ§ÏÖ­WKKF„¦±¦Y>_)€îÝ»8xð €^½zmÙRPZmÐÀ+¥`ׯ·o_د_f^ûÕI"zhLG÷2E ´îþÚdFf-x›jñ»bÛçGPc9{\殌û=Ð=ø|Õ|M³MÓ˜9!¡ €'ž„û nÊÍ]à׿þu-‹•îVù;'eÆ&bU›ÂggÏe–Dô`æïñšÌÝVu£øŽ{ˆø;×dªÆýBü€AƒÒÕãžõz#D0M @0˜lYBJAĆ i±ÿ~gÏzx=Ú„ íúTµ·%¶n‹‚yØ`/$ô¤˜ÚóÕâXÉ£Ët¡ÙSvuO î»w/渨võ¾ ÞíÍôä“£”\®vkMˆ·ówfØvÖñ«ÚàÉ'ÇÌ;Àßÿýk¿û݃¨¿ßãÊHõÃwÜ}Ä×£&Sm<€ˆ¯ ÜUTEüåË—¥”–eµn]ÙÒÛ(,xªgõ±~­=*Fy„¢";©ºÊ”z«\ª‘U"@³EäZÂåûêÕWÔ/}„f•–9~Ü ­`Ð ‡F§°SU²¾Ûe™b£lß9yå¸"ÁŸHDƒÓ²˜AŒÝE>+ i˜ÒeAóâ•ãçãšÊ~k¦n ÈÊJ¬j4­ró¢¢ Ú€•»c;v@(èÛ×þ"ÜÓÀÁƒ-‹Ïó˜2%ƒlïh€±isÀÈa>–ÐbpÏYTA‰©1ª–={ÑgŸ}õÚk—,Ù à‰'ײLýF<ÜÕìììÚSx;g  ßá þÈ‘Mоp¿éSwõÆw8Ý‚êâ9~‹«½[i{|Ü/¾ˆ,ß«Â÷:FºQ ñùùù×®]kܸ±jªÙ±c…þMÑ« Oõù}õ±&ÇQäHØæK$‚'¦U¿¼ 1œ²ñºŒâ¯^}åäɲNTÕâKJP^.ËÊd0hõíëcËÖ^ß÷Ø FŸŒ¾äµ«€äÇöWà;Ç 3‘ S'©.{Ô&©ƒ¦•_Í“-¥–Ì‹±þkHëŸØß†e=ôP5N/GŽpãFÀàÁíâŸ:uê€J;ÿ›o.ðûK¤äÓ§µøCŽ% ÆÆ!0F @BóÇN)_,ˆ‰ìJ–±mv<È„+V0³”Ò%û´i5|=ã^"ÞJ­Ð ü¦)ü]aw/$ ªÅw ¼-ë´…9«cN5E…6å=^Ô4­]†¹RåLõÛ_†5›/)Á}âÄ«W_0qbå+‹ü|;Í¡ƒß<FN=ìj…u§ß–Ì +†{ûÐDXªß3¶]ØHB¢ <²ÿàßõÍbÉ`þÒj‰-$‘F`ä—šDÔ*Åë¶ D Ê*-Õ¯^Õ{¬mí{òÔ©S–%¥´\«ŒÛ½Záp÷1£§™ßÂ+’݃‰`õêÕñ:̤IÕµzªs,^¼À´iCîd%5E-p‡Ã÷þÕþ뿪A|œ>CUó÷¹s_гçt)enî"<|ÿôÓµ×Ù·Ý„¯–xpùî´­± Ñ]Šûˆøëem¶þ ·"˘ÅÐãšö,]z ´ôê°aí+i*:äR¾WËLHÃw³–›ë×:XD…Bš7À ˲ΞûGw|UÓt=Q'åñU…ñÕ´4`–àËmùêº|âÄ4µÚU«.xä‘´ª[²o_$4ƒåÆØÑ)¹‡s9Šn»³ä¸-û޹+AI=bÙ–™ìLã²’òËó5y4dBˆáF²s6“h9N‘è®ürƒÒÕ4ßBO ‘)ýQ!(ÉH:µÍз_ÓJ•|n¬Xq¬{wݲÊeU¾†”Rj܃AfÉ’%3û|’™ƒÁ tB-“ ^™Q1}úô›|»qq÷ø^­&—.áÌðüós¼ÿþë^}õUw»BpF÷+éﯿþ:3f?÷*»v>thÁƒPy[==¾ |Ç#^‘÷î*î â—-;xnøRSoõ3f!öºÕå#T‰V­ÊPéÁøøêËâKùG´ YÀÇòw+.m—’TþîNÍeYë¶’£þ-½’/ Ã’ Ò…Þ$LݵShåðAÀî«e2›5` (Í‚Õ--Í®IZ¹ò4€Ç«ÆÆ­8|øT4JÌ^Ó™™±OºaC1€1cîܹSJ )è³ßo«ê¡PHÆE p/PßÝ€Ó+¼Ò#3gΌ߻ø›ÂÀÅ‹ø¯ÿÊðƒÙs@hݯ¿^ñ±ü€¸*I÷ïÿU•yðþŸ³®½š¾Ž‘““`„ ·ô*×äý–àî¼¶žWøŽ;@ü}‡;œvS¦t¿é’õË–8ôøÌ¸¾× §©òÑ£G=ÅÍŠâ7çì°LKZRJkdÿ1•¿c{LÅVdß²µ¶ùMöÆ\7ãgâJhˆâJ·Ïî“{\Oi(Ãó‘G*ËCùùùû÷3€ªO©°ŠxõW{MS6jÐÒ0Ía™éövZ1}&ö/³Êëí„Ýr„eÔ()µOB—^–K¦óZ”!tA JkÙv`ÅÒÁ-[ B!«sçD")·nRi ?ÿ<À¤Iâ¬Z¶wïÓ–%† i`ãÆB£GWhmY›7»ì–RŽ[Wp,\¸°v¾«Åâ)¿xñvÓ¦ ­ã[Ôúîµi2ñqá<þûOÙž{nŽš ýÁ•øŽ_>)ÓØç€ïÏzU:³4|0/À¯}§ù{NNΙÓgÚwh?~ü-´-¼¸;k¨OÄß-¾ã¶¿4™ªqÏïx¸Û6¿Ÿ" ÀÓuF¼q,àuŠsssœ8a¡“žÊÏaǾÍÒ²$[’ÍG¦MðÍ.gZ“ÄÚ oºþ3\¾¿üòË•VUaþj æ3J…?´Å<|´¨y3z —à'NœÐ4M¡išº³uë uڴʈ·ŠùÄ©,Ñ¡uç-Û/›†aš‘ÑC;J$Ûù»3;|ÓåÙneIR"è×â‹>Hh@òôÆî©;¼»14pèÈD¯FBÞÀ€Ôg?y2”—W"wê”Ô®]“>ÿü€I“:ÂFB 5;v\¶,QTd¥¤xFŽL­øÔW·Q|3fL¥—oÙMƒ& iÐ4Öœ¡oçûލø¯º#4h>úøãÄ»Ô âëwgÏâÏÎ0k–Íw"ž7ï 8ˆwç7©Pù»JÞgÍzÕ­ìðá‡Ùºw]­5M#''Dz®£î|¿ M¦†õÔâï"ßq‹ˆpà®â ¾ÜUÜâË`Ä»¶Vu™×ZvJ‚ß¼w­d+iO^qôø§ñyºË÷±Y/«Ž©ƒ*VXD.˜Õ;¿+óH•¼+ I Ñ X°ìªÅüÌÓͽMqðàÁJX×4­uëÖùùù¶mãH™™ÍUÙŒ,3N=ÅíÓ;H[Ì_n8a™QK†Çê¯øÎR²ÞH— pl.;÷¯^UÙ+ÀHmã´œU–9e—Ò >PíãÆM Ÿ„((ð·k×`"¾ví•wW®õžs=Z[™@NÎEà¤$¿›¼ïÞ½›™¨«ql]ØÆz<µ]^ÇQ›ÐÛÇúhÇ-W¥7  xê©§Ô»ß9ßë¢ÉÄÇéÓcÞ_²Ìœ9‡,>øÀF|éîb5®HèWÉû¬Y¯H –ä^h~üq6€ŒŒQžz*ë66>Äúõ9çÏŸép+É{}ÁÝY[ý þàûƒ ÉTwñ îÕvçX€,OÕñÆ%H¾æØ»w/€~ýú©§ê‚øÒ<€qê>ßüæÖ… ÆÅ,ƈ@ÄDèÛ¿ÿÈê:7ŧðôç¿d1wïuÑ×ÐfºÂz·nzi©úN]oµté%¿_ïÕ,¶zôœ>wZ2Ú¥·c ¶©ŸôÊÏ÷›VXZaË M:Ñý©W<`HFþNln7¥r½Ï„cpO*9K°2­üK@þß‘¨‘6å‰é§Ïœ‚ˆ¸iÓh¯^½vïÎ/W+N?~½K—ƵÕ;w^0xpÚÆ…EE7¤ä¶m …†a¸¶ò æU¦s ÙUòtu š°“z• ²—W6ñêÎòõKÄË/^¼ À´iÃjÙrÙÙÙ•F2o)mw#ï˜ñáGÙ¾÷Ý9Nã0nÛŽÇ_xäÅøå»ê=Ï}ÿ»á“Já?™Ÿ `ÆŒ9]»bÁ‚õ¸uÄ—•aÆDäñÔ)yWd÷—ûÿÁˆ[z¯Z×Y?|¿»ýWÙåkŠîw/–-;T ÜáX1Ö%ØöUã>5ujo€—.Ý_Ókßxãß~ôÆ¿~ò›Ï7ÛæïDÔ¯÷¬þ}ŸëßgöÀþÏ+è:tuuO îá¼høLÔÎ ”ô) Ô©1wžee7‚Ÿ¯ºÒ´ƒÖ5ãœÇimÔ«W¯îÝ»wëÖ­¤%%± kݺµ”òÔ©‹€ìÜBW£ kÖÉÍ-·á.טñè£}&Olš–iY_}e.\¸pÑ¢EjNSj{4í€f‘ƒ¯–±§ F†CYŒ™0-ûfY@”a€M¾ä{è›’ ;µbÐòåËÛµk÷ÐC]š4 ÈÍÍML,ÖukïÞóµ9u1<Ôu+!áúÁƒ6´×–—׸¸¨óÀ™Ò²_þÔ,Lÿ.¾3SžLÀL²Ó¾± 60À&Ø„4À¤AÎ 2 õ×RÞ;Qû6eÔÂÔ–.]ên•"»¢|í¡JU,\¸Yݹ%¸°ÔÄ4°G¿ÕßsçXé3¿ýâ=3µ×©âŠýï%P?ŠÛŠÒX¿>‡£GO¨cÞ«à(÷×#Üá]QþNâîæï*jÉâ4M¦jÔ{ _­&S5! Àô›¥ðÑ j"öÙåIA|#!ŠïS§Ú½&T/V"rFØlùeÒ˜_)Ñ“(@W¯^Ñ„¦é𦋔äd•Oet·‘:µ­ÞoÖ|µ½Æ,>Þ¬QÁýÉ'GÖ´p-qìÀ˜ÿi6€gf8ù»°NµjE¯Ï}À ¾ÂoW¾`ö^Qíà¥)I]«-X˜ `úô9=z¸µ¾¤7æ0zÌ;v¬n’¿/X°–S‡Áײ²oóÇgñ÷‚ï¨ñ>ÜUÔ#âkOÛ+E]=É`®CÛ½ 1theÍtéÒýû÷/Ó´Jjð³3^‘Á«ÎÅ‚ &Ò ¼âÒ%º®7HJ$Bç®ÕUÁiå!\ÄWè¿ZRZBš Vä\òx½OMkAZŦrGŽ22<'OZ:u²ëlN:ÃŒ7ZHjpU2·Ikà«uWŒH$jDL3òìßV3K`á0ðÔSxûí·ãmŽsGcæG'þRñ=5•ƒi ®K.Î$ÃJo0ÁR°ùÉ®= þéOâ®ðèÑ£jz3¤ôddؑ˗UµE.Ó«E¼².ˆF f©<„ã6̼qCHhÄ0ŸšÏ¥'Öxµ½ã hª-9RŒ«°TÿXÍyJ=Kö¿ìžáË«D"=òÈ#îš-Ú `úôáÕ¾ïüËoç2K=zŒ½=¸8zÄ>C+@Ϙ1G{Û4 ®Eþeå{~üØ‹öI/7óY&, –„´l¾/\” `Úsz:‡F_T›7ç=z€;kãû‚k$–— Àתþá®âwõ7* 5+Vÿ¶ÀÙåï$n î¦c=‹q“CS•ˆTgT¼ñÆûö}À²ìKÖçg¿òܬWf}ïWß{ö{DQØ)­Ð Ùš,X×иqJJÃDfC×%€à‘ˆÒa×…Û½B¾võZAAÁõë…E…EʾÜ2Ø#ü>= ¥‰„Ø•nêîÞ.+³22<‚A+´p²¦´,¶BaëÊÕ|¯ßÓ"5Ýô˜žL|¼Ùä'[—q¸”Ëÿûu¿ýíòJYÓ¡køèC èÿÒ /ôòÐ!/V¹àçÈæù‡·|r`óGÛw~²eÛ‡Ì(,ä‚f@šFóä²´ä2˜›YQ–Æ3O¿ %â{›tëÖMJË4­hÔWî˜2¥RÛ™j„šh¦iiš¨ww玠îlÝݾË`–J£¸òW¬bÈ£È|Æ ’ ÕØ–²Ë‡íœlÙ7©î˜`‹¤©n&iÞ’Sb'ÎÇ×Õý…•‘Ñ×~€åA¼0éE î‚AÇóöÈŠ’tìµIìwTIEvEùšâúu0cÓ¦#GNàj¾® aÃ=˜ððÙà»wܱPsøÿOp¯{LÃz–Ü ñÛöm´,«oëê@]׈HJüÕó¯¸Ç-!&VÍNñ„Æn7ÝÝþ€(?|£¢ŽfܶJSGÁ½¦ø Y¾SƒJ=Ï–E›ö¬c`x߇’:T³Ìoü€” @Ó´çf¾\©=S¨$ØA¼íP¨ òH `÷Ô¡c˜ìvÓ&€péòe¡S‹-Tm!ÉWûÏO7øëY£‚…"¥d„»Ò9 <:1U'nØp„¬1ɶˆ"‘ŸŸ¿qcqbB“ÇÆ7sY0 ‰ÄD¯Ýb[ý€%ÿŸ>UmWÛ·ÿ…«K¨áÓ†)Ân9Z& Cè B#h0,* Ãbúfÿ ¯/bIfP PÂ,-¶”%€dùdïö"c Aÿù_o‚0yÒ$z÷îýõ×ùòó‹U[ËœçJ“¢÷í;ÀëõxRÁ]‚•'KìþÚÔQ¯n³ÉÕ6b­6ò¶Ùò‹ÒaìG“¡ŠO‘À¶ýˆ0ªÿÝñ®X´h €éÓ+$®à®Œ×ÿê¯^S·á °€°ti6€éÓæØÙ†]GÂňÚZŸ"Q›àÁtŸ©$‹Ô#êå“&ÍcÀÀØúkRi®^[·­!ˆáãáŒÐìÞ]=ß,X›JÌÊ ŠôïÜo[¥¹/|oRïÂ÷&nñ·—¶WŠZ=Ïëv­•Œ‘ýf ¹¢ëLY)Ü1¨mÛòöøXÓl›ˆf>û’d ƒT/l+wý\Dh¸Ÿ'„hß¡½ªWXgå¯%À„Ë—/APzË42£€<^ñŸ¬–fO¤N ;C†¤¢û’å<ñxK§#¬^sD'4¡°àz4ݸ9¬kúÔG[B‚4Ü(‰Brƒ$¯sõÀ`xšêù òóßaæöí_Òü˜ª<Æ@—H:  ƒ4"ÐHèP¬7,*à &f@LàK_o={ƶc §Rƒ4õËg¶úôé-ÒÓÚµi“˜{øÒ‘P(ÐM -*!^{½Ezõ²ã |—N£> HìÙkj‚4B÷ŽÄÃ[Ì&ºøjxÃÊqawß]Ê»—q0®þ4ÏŽC[ÞwDµˆ«“ @ñýùç_cÆÿw6€ÙÏÍiÓ¶Ž{÷Ë–gxâ‰9šËw ‰—¾ûF$b·)kîSåO–EÒ‚”öË{t32+–TBüåKvuìömk@>||¬F¶:¾+M&9Ü`̘þÊ}È_·“k}Åüùk̘q “iqÏô™+N(ñý§?°+Ñ|»âV…øz;€ï`=K«Ui–eYÒR’eqÅJÔIHiˆÆMÚ S¦vèÝ{rº…Íû£OÞþhþ»n¥Éè:¼ž³!àõ8$…S–§N21@Z‹40̰d i1Ãê†Yjq©’GvT¡‹ä矇‚OLJï¨Çáp4Š@2,V…-?Ú"ºø”rNjàaÉ7Š"NA;X"zÙœ9¿øÅ/œ>ýöô'á÷¡Ýå²NEe]CåDÈÈ@· të†nܵºuC—®èÒ]º G „>}Я/õïGý`à@JÏ0­KhZçÈ#OtŠ<ÑÐ…Gà IDAT)BÁRŠVÏž=™ùÀ_~õùÎ]—®_7 Ãèׯå€i—.9s®   æo²‚²kY²D¯^½¤”û]Á8Vå··`ôë­õÎ`œÈã3çÁ ‰à¡Hù¾på·ª.Ze"}Òú£yßj*pÕûxš{ yBÓ˜aV)­ww«Õ8û¹9Þÿsv^^]¶È¥Y“'ϰdI¶;+M²S/ìÞ„Í-vÚΨÅÜydŽ'廯ÒäçW˜·\%*?¶`ÁÚ”HRJ¸Á˜1ýsÁ¹§¡È®(_÷¸wõ‘ˆ«Ÿù ®Vºdñ˖媣äÎá['²<^1‹_±`%3ø˜+V6ªu§Æ—KªÖzêq¶hÖ£?'éÐ.‡H‡ÐHèhÊ;G‚:´o*)|þ¥|Ò(½eše!¬±O÷ZþcÁ»)žüoˆÐÄÉkÍòƒùû<÷lÕaÉ-õ'‚%g<5½]iøGàõx¤‰…KóÀ˜ùt‘’¢03&ûÔ0¾4»¡öÛo¿Ý¯¸_z›a$X#ºöéDšrÂ!h œ¹µ®}‚Ó;þ™l¹šÕè$AbÌœ½ûöJ)W¯^­Þ®W¯Yš†^½ Á‘H±d]UÍ;Uµ €‚ÂçóY–(/·x½)©AƒÓnÜèB@f3=¡›ÏgÀê,¨šUY€ÅÇOJ ±‡šÌl2›€Álpƒá ·xX¡èH…šQu‹ž7\ü„¡#†ª 5-‹m>|ø\FF›ªu2ªñéìç^S\ž7/À³ÏÎa‰.u°÷Þ½Ë>ˆðùÊlO|gަÛõ‘ÉQA¸®‘„pˆ%ãFªÏžÁ`’%±|y6€‰æ(â­®ªsÁ‚õ–ÕdÔ¨ÞjÆòŽk€JÉ;7nLkÖÄò÷ Ö6Œ&Õ/~=÷8yWq)¼~óEî,îRS§û79#.[–«îÔ#Ü<ŽõË‘µYSâ/Y²Œe1̸~M*ðÙqåÕ+S_ï³téþ¥K÷OÚǵ{çw˜÷Å» Âsƒ~$$3±”D¶LϤWLª×Íeo=µé©Þß5J¥+Šÿô"zþñ¿‰wQñǶøá¬al±’ÝÉA<3"‘0€Ò’RIÍ’¬rŒ„Âá2Mhq#ºœ’ì+* †¥øñ# UÃðÒK/½õÖ[{Jö cÐÿ÷µq)þ?©'üÍ7{œ™9pàÀêÊÌ4ѨQ¢ÇSFD^¯ŸˆŠ‹Ã¦i>|º{÷öñk…àñXû| ѨYZIH˜&ðzgtÛŽD=W¥PóDꤣ û4äÐ µëxà%$ùaŸÛ™K×—³ÁÉãk4Æ©œ³oéÉXŽìkçé‡ò÷íÚº«Wzß„N^ËÙ%U;Â1›sÂï~wÎGeüqöÓOÍ9r7S4¥Òí8¶rÉÒlØždì„?@ÁÇ<'âfrÄšÂWC‡dmÛ~pëփÇ÷Ú¹s ÆÅã’ ¨÷u¢É#Gõ_É};€3ÆÏŸ¿fþü5uGüÝÕgj‚»ÊÜ¿µ*MË—çVû¬‚ûã÷¨_¸×ì³,iÙ¨­õÈ@^'yŸ×ú‹_üâå_þܾ¦eüy×ïþ´íßÜHPpW«µ{œ‚„.ÞE;>ýbß—Ï<ý½h„5Mÿ·yïüûüw´iý »âM¢iÜoÛ0Ë ³ŒYÊÝ)§ùÞ“]ÑâO—NJJ),¥i† 3é^‡Û",¶ëØ$ƒ9g¨·PÞgÛv¼eû´hÐ4lX{«»UÆß„QLÆ ÃŒš¦¸¾¸éé†×kÏ»mÞ¼y³fÍ”«»aˆýûϺ+۷ÊíÝë-mÔ¨¼¬,xõêõÄDob¢wÔ¨ÆcÆ44h‘LJ:2(U÷4ѪnQ¬"Õ© lÞSœàT.J–—/*©f%µFR'4èˆÄHhü½=Óú h¹—X}zÓÖƒ©høÚkßeŽI4nÄyÐÙ_Ô33æøtA¶d:ˆ5N£âê#%Û󨥄{9ó-ì[B%ÚÅWŽ!&Û&ZVWGzö¬: AbçÎ5 %©vŽv uZ°`ÝWÓÈxPà®âVUš»ÅwWp¯)sÿ‰xîwë}±ž€Ž¿dÉ)壣·U[·\¬† ®½w­ê㥻B¥»Â3ûühfŸÍìÿ#õ ;-2Œ(Z¶ì Ò3ºûºûºù‡‰(Iíßþô¶záìÉ/ -ÇíLÊþçOMóÆßÍgWé¹…zëCÁiD¤SÚÆßìÙoYAË ÚIšcаa@J.,ºµ4ñŸúá±/ؼõÍ­ÛßR39¹ÜÚ´áVv´{ªdÞWñ—+÷î*n ñw…ïU÷jãâ—-˽ÛpW1ë øYp gœ¤ÆNã$®¬þµM_jZ5…/Ýi;¯²²Õ•˜Ùïxû¿¹vê¦!“ûv,Jñ$zóNòñc|ä0ïßg 6{èÙ¹GéßþûmC‡ü"3ó¥}” ¯,ÑÂ17{ï½?ZVɾ?ÍÎÄ¥ãåè°>ŠF¢QÓ,7Íreg ˲ÊC…–,ƒ-Ø‚»$7 °ä‚‚ ;âT8ÏÎæ2Ѹ±ö„¦Í[ßÒ4xj|£ú™AñqâŽÀ±ýøýÑ)¿?:åPÙÄå’..ú:6N9<ÕûÎêÕ;Â]»6ÀL‘HS·«ŠîÝ»3³ß_bYb×®ü;.›¦Øºu¡e©Ë tïÞ½gÏžZµ2<)e±#CïmYÖÎÝ;„‡4/i~ÒÔH¢=­Ì­ªXÛαÁ{ÿØ:`.ø¿…¿½~ÓÏ^Sœe…ÉÅZ‹²®}Ò(?.Ýn «Qù‰¨²tɘ>Ý®•\º,›_ïÆ®]ռˈNWÅŠ62êßÄDÇšT€˜b7T¹luí ã#ï”ó¬:âtO‡é ǯ¢aC:zôl€ýÃGô>¢7Ql4õA€»Šº#¾þùî¦íÿ³4÷Ú"Np¿»pW1 ë xÿJw˲ž|òIîˆ+c¨)H§ëÿQ`êÔ>éÅÉ»>8 ¨_%§¤‘zí_öü€g úß,aµó­}¥Mšª¨Ü0,7ƒåf(„Í5_¾»vÝÛ† zɈ’E4‚hF;OcáÌÿþ³!eÑ?ÌüZ?[ñÓmlÖ{uWóXE£Åù¹¥N?&9¨g+IåÿúþÇñù»JÛ›4N`‹ ®–ÅÔy'4Çû¥¢ü†Mo*Åíçè\8ŠóGqöÎÆéÃ8•«|ê šGè^º€®•$‡R%äî)m5ëÚµY«V­[µl6¯öç£RøAƒÒ ¹¸Ø2¤I)£Ñ¨»LïÞ½u]H‰‚‚¢Ý»w»/ÜëcÉ•Jô“¢ykÞTÍÛ\÷µôøÚxüí½þÎÞ@†/¡—?&^ň?§‰aáê›Õ\·Ý4-Ú à?˜Ö «?ïê) 0ù~ѲG»Ò´ª*r6"0‰X…É´'æL:ÀŠÙê*sÇöjÞK©1–Œ¥ÞjŠS·ný¬‚22lgàøÉ«RVÈßOžŒìÚ½†SFÚ»÷Û—ëìM`Ë–©ê_fÄ#þ[õ\?ó­îéq±|yî±c׺tizoÈï_é`vóáËlF`°Ô,s ÌHë[ý«®ÿg! "=ÃÏ„£G.Q·nÍÕÑ]”Ï ||ðwôô¤½),().µ$IÒBiiÔ²`I2Íè–m¿˜ˆ†d¾d™°,²,„"v.fZÐu<=o¿ý6€Ïú™]íççâühÈ©¢ñ4Õß{ï}iá{>–’mÞûÕžc‡5Mÿ‡™?„3§É®”¸vµŒf©‰Ì¤Æ½`éL{Éùòͦ¾.Ý;|’´¢}'WÉØI÷/ÀÀæ-[™pñš¡ƒQ£¢C‚ÄùÀôǧv -aÆrÉJ¬4Þ¨*KJZBž;w®KðÅ_Tê `Á‚<"jÛö𔲇Þ@Ò@ÿ—_n& ðòȧf¢¾âê×8"9Â-ޭѳ>>-Ú¢N˜Ó§Û¥2yyyÁc‘¶MÚ9£ìÈ=|Žƒ¿«Q 7f~ïKÚž_ª,]1×­o±Õ‰‘q.“ë×¹–8X·n.€Ç{U×ѧÅN•öv ‹À7ß°iRNÎ\#G¾&ëíÛÅ9pHà›oÖ2d<„àN ¾}; C‡N¥DýEE×BÚeŽœU‚¿æ©Lj<Ö f¾Õ–~·u)§©ÏüýöJe¾Õ*Šj+ îjÌ›7ÌÍ›7@š=î½~qOõ/^2 ÛnFF =vEýN „RL`0Xeâ–‰HØŒ„Mi¡°(‰²Ïïóù½[¶ýÀð¡/ø’iÂ4I«©?¥!š7Áý¥—^r¥˜èYÑÝíLÓË ­RWQéõòpYzúß1—úÒt7s·%{榩‰°øòåR…j•«”^•JAe,Ê1qÈ/5]?vþsÍ„­sgá”BÚ7hÊ\̾mßµíBþZÃ8Чo?–²aá€OðÏB…ŽÃظ!H Ñc”ØŸ#1ó™3)RŠQ£šH)"»ûRÕï‚©©MƒA=5ÙäÅíiJãÆ”’6Û´ä,›Ÿâ‹Oïô€iöJÓæ¯7¿¸qp iZR÷åL§TÝÛh÷_NTj®äÌŒeA«;&Oš gÍ\gÎ6lˆ½J”?ö¡×¬\ùzŸ>„Šš{¼8ÃÎïnÀª`/,Áåå§¾þz €ÁƒÆPi»&H#åææ54BC<ܰ~÷ÚÍG7¬©9D£0 äåÉË;S—=\/Q•¦~ø~ÓÑÔÚãÛˆøåËs•øþâ‹c˜cͽ ˲°êç¬B–Ó5 påY®f~€Ò¡hTþ>&'=¹˜\œ484(Ðeox—ïdý܈ ……e"1£óÚµ`4‰‰þ/V¿¹:çM½zü•eÀ2`d™° ˜ü4n M¡3>˜!Ù8§ž:C¬Òb…·¹>o@¥ Ò )) ¡Ôzñâ»Ê:Í×Jwªàmœh %û’d¹žŸ S¸×wFè4=o«¿©º®¿°JÓÁaKydÚ5ç¾ÒЩ ÊË÷5kÖ´Y³fÌÜ%o^ÓÂoÎj?×ÌÈŒÔÀòʨc‰rjEqšÊ©S McÆ40jT“hÔb¦x¾;šz 1#XÞ¸§¯I¹ü˜oóæÍÆOåõÉ@à ¤CÝÖ,ÁWK°î³ú>’ª„;O5îÚ¶m«iZIz11ÝÛ8vìBé×áÒ¯Ã@…RzHÄ!žðØcs|ùÕ\·âkÝZ|ù%;Íwkf*„kVZ±HŸB§ìÔþË/íä]íÞ²2û¥/íT§z÷é¨éâèÖS¬@Û6ͺti~îÒÙs—ή۽vÝ.û¦'WSΉÄnC‡Ž3f<˜[·nÛ¾}Û{“¼«¸)âëïuM­=¾]ˆw‡UÕ(«gî%âgÏžMDWþü}Ö&gÙ3úˆ`ª GºQº3Ä4gúŸ ¤!äÁAϵ°té>{9õûð³Û£¼ ¦†Œ²ò↾Xmlõè6;1¡‰ÝÄe"£Çv\ƒ&ðÖ[o1³*´g 㜠oKOµC¬ËTµn¼ö@Øq¼±Goܹsgo¾ùæ¹cE²Ç,e£”ÌR2JíêùÆ(**WP‰^0<x¼ÐtxtÖ<Ðtôí7I÷ê'/¯Õ=¸|¹BÂNHG»G«å©©©D¾ýû÷Í+ìu ýSÍŒ 3ê!‚% A½b² /yݺ2Ëâ¶mí)¬W.ãòåÓ´˜5ÿøÿÄ’á6¸f¨m›ÆMJ¸ðDï(YÀÆׯ_ÏÌ~? /„¤è7­À–ϱísì\YÿÇUµ>3n´jÕJÓ´òÖÅŒXɬrÀ üŒˆ>øà ×€^ÙüR\"?qâkÖ®Kî…/cõêØàj%w0À9bÉ«n.OЭ҅²Ò¸­rþ*efРñîÆ(÷ÿÆij¦Ð&­m›´¶ö z²6nܸqãÆ Ê—i߯gfŽ1büÈ‘ãG?zôø rT˧Ç›p/ᮢvÄß)ßÿ8šªà>eJ÷ûj„i"ÖaSñ1MùôN{É;B¥;mÅ#¡¯ß×Bó7×ü͵_”ª¦Ní `éÒ}ï¼óŽæÁ” ?7¢±&GQQCF£&Kþü‹ß?ö—]g¦iš&¤I]Z¡kKH ‚hk.Q!$»Ã¿)µóz_KýZnldX3±ik’tO²Þ0ÅÓ¸– àbþy_K/ÅÊÚÔ®àÆM ¨¨¨\ýò•Ë‚îæîaÝ ]GŸ>t¯–wu"VÁ5…K&o”å––å>lÛN4lØÐ0 púõyE˜QaFžhòS§µ++.QL°g€±~C9€'§·BS£¦ÿñ‡ì¬¬,]……Ý6§‰XGoG; €ºtnF òuÒ._´³fÕY;¸Vx¡(/tݾΠÐðuö¬Á¾[›²^},Z´¥v¸«HKKÓu=Ò¾L퀻vmЉKWè1bË5ZŒªãÇ¿`ý†¹öWHP\„ÒöèQ¥1ÁJ£ª16 ª˜™ƒÒ2{DîáaÃ}ðàññgš«Ç®\9zEÓ¨[FM£}ûôÐà± ûР±Cz=4lì„Ì̇‹‹¸¨ˆiSänžº£Èà±I÷šìu‰;â{ýÎMýV¤ð.Ü+=~ïSøïÿûD4oÞ¼±Ñõlk–Ŋ.rKw†T"“4$<$4$@‚‡ ƒ<±‡B|4jJ‹(¢™QŠ„Ù4ž–²ïÀ‡ßìûÀ„q¿dÀ4,Ó°¤‰mбÜN ö™M`ËÖ·àL8`\0Á¬·ÔÝä=Æzå)/mµ4 ‰ †`˜Q$øÇvîØÉ§ûsVæ8ñìe\tºÂ®ª9, 5u/<^Øâ½Ü£ÓO;S° «¸˜ ž²îÝ»Ÿ>}Ú²¬ãÇO Ò1sÀ¯¬ˆ!̨0 µ•fBKlßÇx™+ÀšH¨~&†•R^¼Òv¡$Ëb]×+•6 "óªd W¯t"x“öi@ÑõÑYYö‡`0¨ë_ ò‚< Ýf½ zÒppr×ãÈÝx;ÇRœà~ó&sªknR¦c@Ô¥k+"œ:›ôë=ñ]ˆTê*E‚…`%àŒ÷€M›æ’Su®vI0dçïj,Z9—U°äšJKMu ‡ò{ö¬!BfæxÀ9ǜܕ[\RPTt­CÇÔ@€Ú¶K%ÐÎ]¹‘Âa„BM=ªÔR•ñÄÃ]Ý6oÎQ–ñ£GO˜t_á>cÆx€çÏÏ©úÔmú¬XqR¥õ›¶«~f+V«¥ÙüýŠåË«\SÚþøã=Tü=«¥™5kÖ¼yóæ/úKÓgì#ž3‘]r*T*48PéU¶õ ‚J7”'ITOÚ÷àÁ•–dÓ É‡ Òˆ4!€%Ë~£–yl⯤DÞ©Sšwo¿^Ó¬0LƒTËÕS"©‘ã7[1y/ú¸Dk¥ˆ$\ç&l‹‚ËûáÖO‡dÈÒ9E”’ iYI‰ctí/D¢uZë³ÏÏç£WvÍ;صk„ÔÔ„ÂÂò¢’`£Æ ̬{ %ˆÁŽC8t¯èæ~2ç¡s+›6i߬ôèa_óç/—w:kÌkGsá1­a‡ä³M_´‡Y„PL’¬æÛ0„-["bäHŸÄ̇s§µ@z‹W-k½”²_ßÄcǤ!û¹eìjäƒ À’˜ô꙾6ÿ˜6ÏçË1iÊCk×®õûýeeeK—.:uêæM @ÈßȽ¹s'·8 ®@Û «º¤íññûßÿ^ '!âkR™Gm“€?¬}ïoƽ¸PÞ§–R×s,˜AÌxøá×¾újîæÍs‡Í͈‰`(®4++âäþAü="jÔ( @§NÈË‹IFêNr2Ú¶-½ý‹\2tìïBÉñ-š5wµÍÊ3§˜âÞËîôD„Ñ£'$;ý„ïc̘1aþüœùósf̨p¦¹¾¯XqRݹšÌäÉ]@Ä»ž‘µk2÷ñêç4*¼~£/kw‹¬¾çÖ»­²} ?Šjv¢n÷JUH(Ûl0,P….4xàìfmp|„4 ^¿X¿ù72ºLWN~ý3qúÜ>€Œ(¤Il€UÇgFï,¬·ó#MÀs,Y|ƒ<ê‡Ä,Ëgé”E2ü½rŸ}µm¿Êç‰%–é!êÜoöÖmo}òÉGƒ2Ÿí؉NŸËó4ÓN_9)YvLïB3š4M,,*/¾QÞ¨Q¢·Ü4St'ò3wf?›¬G.œÈÏ?9¥íO:9u¹}´ÀÉ“Gûš%X€ðK£œM3&±ÆÛþU ü=êƒ`[·D4b˜WñNJÙ-c»”Ã4]•Ã3Ï<=üãù[þå·[_ø‡áŽ8Ã}Sx÷ì:.3;kÉüXÊRcFÎ*Lxd,€%K–Xµj•”rìCíßbh ;é¬cÞ¢NuÎ<{œßeŸnÓTüT‚{vv63 Qýe½Û¶—bø„}´9ϲã!£Æ?hp5Â@I_l ’jUÂ4vìkk×ÎݺuîС¯ÅŸ*ˆðé§±w©}ž#½j¿¯o1î‡ø¿ß¿p¬(ü—ÛT¬>r,^¼ØŸ¤úÎ;ïÑóÏ?ÿõŸ{î¹… ~¼þƒ¡²>¯:0»Ñ€–§¶¸*|…ø®QÒ\l€Pz(VRëÚŽ4-Û¶xûùjýî~Õ² Nž9­òU«VѸqãîsÁ]÷ì<;·p'E¸ïe³ÐÔ¦d¨Nœ`ñѰ®˜Ì^½¾RªT•ÒÒŠŠò ûõùåÖí¿^¿áµ§‡ÿR:8uFj³”ª¸›M‚sóŽ1£Uã6U3#ùù¥%ñÒ´´ˆ âXÎ!R3š4jÏLñë6$ïõÒŠÿ¶pñüÌÌÌ`0í…ž9x€í˜e›–Aåêé–¹þÔ ,Ƚ=«5ƒ5f:v­ŽþÚ´*‘xð¨Ôud=",}šFRJf–’ãq0Ãt( XÌ‚ˆ½^˜œ`ÛnŒ×qÅ3åä_ IDATÛ±xi<µR€­[±uë\f4lØPW:uºreÝèÑ£³°`VÕõ.—÷•ê2¢ @OÀÝã8|älUÔ,‡<[æ'¦F/ycö˜#GN'ÕU™ -½JÂBº©ax<]Ô êçãµjÖdÈXŒC!2‚p™€¢ó ¤ÀŠ´”; ûô™5wî\Àºl;ÖÿËO_ýϯ½úçŸ}×c9„ CÓRÊ=2q"/QbòÉç›rAÔ©csõÉ”â_â%£V›š·Žž;rølÛ¶M\'¨çÖß½+ÑLŒªÕ*Þ˜Ç$”ÿ¨ùÕuëÎ|“Ì]!»<ÿmÅÚµ'¾ÜL>Õ9ÒË÷è±råÊeË–©å3føäý½÷Þ{ø…Bˆêu<ÁƒÀªg9#;«üÊ)½"î$#Ô4üåÝ×G¶mõŒtäð§Z9ûï`to3sP¯_¥¥ž‡Á_8°Ž•˜nÚy(¨6¶B`cÖ¯™yø@w`”ðzÅô$™$3£̸2£·• fø JKK-Û’žáDµª´md¶l[¿r ¶pþÌùׯ=vþ°€¤œK‡Nfïg ÇAófš5í ’2~DèÛôRʼ¼sG09䘖· Ý)ÕL†ây§¬A,Xè`Ü8G²¥&Щ­€„wB‡n¸ý~-«¥+8ÙÀŒéÏWO¿øù¾}{÷Ùª&“AB¨7áÅŽ—šñ’R'n 2 ÀèQ3Ô9C­³zõê3çVkAˆ kÖ,,Œ„ÍÆ²èßÈ3Ÿ8qQ×EçÎͺumQtÅgQr¾Ì2{öìÙ³g+çºuï­]÷vfõºµ«T©á•†’tHeÔÕ€0ÛF¼AP$„dÄM "•@ô”=m¢lÌš5+Ù¶“îêÀN"¥LZµ\¢Uõ“ÐRî‡øÌÌ=ÙÙÁ¡Cû[÷œýY'AèØ©™ÛÛÚ=’ýs—°¬¢K‰Ö­;’<¥Jp™±{÷g»w gÏaê"£ZfùŸÕcŠ¹Ïœù–ºûHøž$¸s²Ì· ñI‚û_=p=į[·nõêÕjyêÔ©Ï>û¬Z~á…yï½÷,Xð §O›6 À¢E‹ÚßÊ"BnÓ.§Š%.¬“ÒéN\“ŽXJçèñï,˜OmüÔ2cÒm÷á»í–5¹]U(Û»/Þ b¥Üia@¯¡»‡2õ%w˜’°òÒЖí¿fæ]^V§ W®½C„Œªöû%€O׿– ñR"^;Ø0£åàÎÃ!ùü‰ Y›×ïÛ½oÓž[ölê7 Ó€ê6 Mƒ&  ÖÒ…À»…K6GôªCZ|w옗ͨI Û´lËV[Ø ¤pa7n}ÓnÅRÓIpøxŽÔµo) ¡§¹ÛºúhtCHÉ;wvGJy`—«ä޺΂´yBàði©J0‘FÄNÙïÉ‘±h‘´liÙäà8ë÷oΑRV«VmÔ¨Q£GVkÈYÕ¢iAhuc-ÀšÁšÁZY”W&Åóç¯9sÅ0Dûv¹Y€€èD/!–‡Ï·ÎÎÎ>qâÄÔ©SGŽ|V}Ýÿýßsþòö×zëUÿ+øó!^:ˆ× 2ƒ@’ùÊ•Ûþ«“’ã<Ç"ѳ¦ßN…®‡íÕW—üàK/MÓµ”7ßùˆ%n^óu{ëÝM†þÙÏÊž!ÌÛN=zD:ªd!UñœÊº”pÏ0#ÝCt÷îϦ÷è1 3ªWÐññŠ©SŸüb|÷iû7¯¹[ïÓö/î*âׯ_ÿñÇ«åñãÇOš4éþuf̘¡Þÿý½Î´iÓˆhû®%-¯fp¦Å@ß\~pCù•=5A§{1zû“ø;#~‘}âEñfΜɕN;6«ùÅFMD‹/&¢1cÆØ6ÔŸ¤Ãàó½.ˆlØôkF x9©ÂÅwrRD€Q@Aöýg€«+(Ó´NÞ}÷]µk׆ç_`¯>HJR?E5ÚÍ®d¦š)µkFj1Ë#×öX¶ÉÞøõ*.þÍKÞ4ïÝ+ÖišÐ4Zº&5'fÞÍ—q+=-ƒ$»ƒMZNœÒ¶ €£· $B'h¹+Ϝ㠆VMÀŒô„UHÓ-PR’ODÁ`jiñM@Ie¶.t¡ë¤·IqtAGÏ13k €ƒø·…ý¼yóJŠïJËÖE8Câü9ÈËË“R®Y³ÀرcÇ`ÅŠ¬ÐCÐCHzƒ5ƒ…Î.Äë¸rõV Œ€Ö²e=áש=Îpèľ#¹À°óaç£IÆ7ûÁfÿ{³Õ†½ûîœ÷Þ›£§x{ž¤ãZcÕ!QZ+ÈAddT»zõvR²—| ÷P•b­˜×¸_Jœ?ÿgM+?™D×#ºñ©zâæ•ëzXpy“ØÖ­Ç„ ꑦޫC‡&Hvâž°€ ,¥D۶ͤÄêÕ‹wïù Œî݇uïî2÷šÔâáÛ %¾˜?ÿÕÝ/À÷ÿ?›ú%Â)SûFôiSIF¶®>o]‡yæ˜y0/#~ñKذzó§+78Eìó“F>ÝmŒyÍlÞbë6[÷Ø*`»˜(;Ϙ1cúôé.\øÁTø¾ŠÖ4jJÍ.fáBëÌ®JS>4‚N¤£(N—óîªç¾ýÑë ÙgΜ `ܸŽƒâbÓ˸*mßÇ‚£Š›õ”w…ÓÞ]eFÖ3µ„áÌk;St Ø•Ù{„½âVÇ(aê…^8ž×Cý €>ù5<žåx}Kœ†A–’ú†tMïaYÛ‰]°pñï,xuÃá?ì¹ðÞ­ü+½úN` 2ŽH0ÕŽ[¶eA‚uÛJI¢5°~änC°` hh\ì ™÷O–BÓ¡ë )¤’SÔ˜‘#»ÜuÎ\;% X’FÐ Ð º`væÍ›7oÞ<gî¼QµŽ—Z$aEc#ŸþÕìٳnjÃÌþ¥ÞøñãÕž½Ëwí]¦¡¡!ÐÐPtþÖ{ 5jR£i³š.'÷+Û½mïñûOì@ŒöÍ:´iÒ¶e£V$™jD𽩳_xÞEù… ç,ü`ŽÏâ_«q`Û°Uª‘àI˜²à.\¸WϳâW­çžÃË/_ˆÔ7ÞX”|¨þè;ƒ =òç÷÷øú’šhE^SICzy|‡¥_½˜B»öMÛwhâ¾{YˆW¹VÅÕu™t]ÂŒ¸yÌׯÝéÖm{ê¢×çÿq ßÿ®$õïÃðýq÷o’ÂiÁ½\h)Ð*AO4ØRÚŽd¯ìH{ŽíÞ}dÇŽC[U3]žÜm„mÁ¶`›°MØqX&¬¬¬(âQ˜%ˆ3€çž{N½Å¢E‹îß)S¦ÑÒ¥K¡¡áÙÍòÚ,7dC…*É=}ãüÅÛ€ãHÛv,þ×ó¿H^-55ìØˆEM•‘Ba'{ßÑÞ§Ð>ùìUf1àšWµè~’§‡™‚zuöÛ|û›)„F—¯,àç–û H«µ¥W$¥Xdÿï÷ä£G÷麮k:]Ó{vHéÇ_z饦Mû›¦ ’”Ò7úGf4^ZRR\”ïÎ-Û´lÓºvå¼¢wî¯Z3z×ì+X€ Cˆ‚’P@ȺÕPVT:$CÓ•‚ᆦAè¬i®L1sæLÁbãÑO[Öƒ.p::!RÓ€Í?s§L=?㕞…$"¡;\TpOZ¶Nú±£Êc3VJ©L“&L˜0qâDµ¼uûÒõ›ëÊë—/(. †DÝšÎBc¡³¦±Ð°íðÆG²|ߥU·ŽOtVtÞ¿AµªC”búôÙÏ>ë¢ü¢Ås/™ãB¼ã¶u¼NAbѵwý.`ðà5ñ€çÓ%À¼n›7m]OÑõ²Jº Ã0©,¯F$È/ìzoÉ1Ã~ïù2öÏ% ÷^¿^ЪyÍvíš‹æxÄÕu î{÷~¶wïgFŽšV£f»]»©ã³Nò¿£Ç'¸Oúd²9òúÌ7œM}x|3ÿ7 î†^C'4-Ö£[·ŸÀ¡ÜýNìƒ×¦»wûA}:q/omrl²mrQ>;+3;3 «…·¹0ŸŸ}öÙgžyÀâÅ‹—,)ßWpòäÉDt${¹êĸ‰ÖÝ—]O£œS7,© ë³#oÐ4í/¾\®›ÇÐñŽÃ–Åb±˜bĈŽé*ïÆ$VV—üÊŠ ’È»ºÅcìnвFJHÉN“€/ ««e¥¼h:]º|I”Š^}]d6ä_|øÉ¯½L­WgèàÆ5ø}™Ù¶,ڴْ™»¦öš3gαc르›5kZ»víg&ÿT³5;fÙqÛ6-;n9–åX¶m™›ŽºÞ! ‚Í3êj§ïÜÑ„&RWOÃÛLê»i%4 šJ®²Çß%k:ô¤V Eb'®Ó Ar³Zl&ðþ}{–¯Ü§ApŽH'¡·iר‡õ‡@¼ºËËcÜšwé¸ßÿ§ ðý[ɦ><¾nˆÿJ÷Ř1mòŠN-üèC½2@èÒ®g·v}z´ïÏ>õHtT#F²-²-8&V vf f V)nß`x UK—.õ-7*”JÓºÕ>•àF§÷7äû4;‡Úu¨Þªmm€…Ð^óKh æ ;yMǺccÕªÃ~ŠUyf¤Uæ5…;ê§Cª]ƒ$„¡Z-ºº93î]E0ÕÍŽ*³ »"ä]¾é(=ª\ôèåýJý™›É,Þ+>üý°m‹5ª§×¨Þ¦RZ3€„¡PȲ¸ŸñÄ6R´ÔÊÁÊájÕRjBê¶eIÛßÌk°&X,*)5–Ûjœp¼lY ¡Á‘Ðuh:˜!„R6l8LÓ\ŒzÐÕ­.´º<’7AÓfîÅÖĉ³'ŒwQ~åÊ9+VÎQÈ®ˆ¼Òâo¥š ¬ [·ò™ÜL eÆ7ÃÀõít=’¬§+)&A7Dä@À iñ‰œ‹§Î5hPk⸎INÛB¼‘áÄ¥ÄÞ½öíû @çÎÃ:w&=-Q2„öe¦£|á î‚;îÇ÷o1›ú­ÄW¥É<(rss?Þ¹s]W®„zí©‹”ú.‹e¯QŒ‚'â•éÐ"ÛT7XqØŠÈÇ`Eqí_»ÂS§N2eŠz—äŸúĉ‰hÅŠ\ãD€Û]&ûË׺m-Ò@=7ê—ÓGÿ2fóåëìæ]“BzÝ+qæãÝkða«8Yì|¸õuf3ä’ÉãïLí|<ð¼Ì`ޤ³šƒì‹3Aòþî4mÚäA»T½òˆ§~`͇¯²[DìåZUØl`I,™%Kæ1Íþ¡NƵk×}jÈ;jç܃²A f3V$XïüTë3fúZêõ&© š`¬_u:‡ŒOÞ$ÅÜ}þ¾îùRºÍtÔãº!¸÷¿ˆ]9;vçìÐX jײm›–]Ô5{–bH‰¼Ë¹Ò²ªU­ÍŽ,.Š á€\ºÄ'Ndær§vêÜûNáÿë«?Z’V%H§U–®ÎZNAP"B#&>ýÔø/Ù2¥Å^“8ÆØ1³ÇŒvQ~õê9kÖÌU“”íJenöß8 _XC¯âÙ&wZ žï„ÿü‡ƒ²”—°O™Ò8 ®úðŠÛ9ÒÓgB¡`($;îää^$Bƒ†µ4]gߣ¢"ˆ7ª&Žõ}û6¨ã°S§aÉ ˜1mZ[K—þµ3Ú¿ö('¸WeðýñÑdšŒgÏž=uÊí•Ö®]»ï|g$’ì4© Ö•š$ùJ8ñÖNÖ[äXd[dÇáÄa{ºüÅs `òäÉÊr³råÊU«Üîà&LÐ4íâ¥5,q$‹€{=nYÕ«(p;¶=i¤»å°í°i#n¹}ƒ­{‰Q¥£¦BÚ’„¸BD‘HĶ m<]ÖæÃÅŽßpÆ´ÈoH´…Bj%÷v;ÒY'è;@†‰„ ôMš6îÞë;¶sW7Éæ]êûu†‰SÅo¼‰D$9Gn¿v-Ÿ êWßsí#]ç±#ŸÑØ#VܶMëÆíkWn]"Ég¯–¶Ý¼FÃJ7ã–éUå€+Ÿ)k Óë —îÞë·ì¼äˆ»>„P~DÝ@$ ±XL2Ÿ9û©¦A÷LŠÇ.âqüdÔÏmiïÉÛ¥AëÞ¤g¿î]3+Á`f›Ù†Ã¾Œ“w—HÛŽ„Pµr Û²¤(J ’ &Mš$¥¼_£4¨W¯}õê•IÐÂ¥ïû ùñãÇ3fäÈ‘>l)Zµö{ÄA2FŽœ=r„‹ò~8wݺ¹¶m>Ì“&õÛw5÷öíBò¾®¸‹äG¼¶HD†2ŒØd¿A0†PÇ­ útÃ@È3¶“ "˜veM×;u¬VÖ¨ƒÄ›ú~úÕŒ…ì;ów¾:5j “'ÆcñÞð¦'îðñý±Ü£FµxݺܯäÕÖ®ÍÁ×£É\¾|ùÂ… jù‰'žðÛWUUš"½9ªªfIömÅJ 1=F'Û£ógrøÌ)àgØ|g…Šn= *È߯¦†kû}´- ÓA³&Âtpü«í¤iÔ¶é !LÓQ—Éaݶ…†Õ_0¬ÏËÊÿn;ŠhS ¼õé›7/©ÈŽôêÔ`ˆäå]V9ͤÊó âí·ß>tômA3ò_˜yÅêW•±Fu­Jª}ç'ZLÙW´ûÆìÌÌÊŸæ,dêG=cŤ´`ÅQ)RµFfMÛ´lÓ>{å´cYŽm5hdƒ '¿Ø G@jB !ÅMí‹Àþ¹¿=Bƒ¦Ã´ +󌷮F‡Ð i0˜•q€.ôŸy¹gçÞVlsj€Sƒ`‹a±ïKù`Ñuë´P׃ø¸*¼º–W±¿|ùÎåËw˜8±÷sÏ=ç'ä§L™R¡õöKGÛvhßðU‰áOÍ~òIwáGÏýè#…ò0iR¿=—ß¹[„$¶ž ÔžhC"éÓž‚K–ŸS„C©1#GU…›²òä=l„ÂÆÒåYËWnÕtѲUCÝÐ4]+o×)ñD èU´·ªPÈžLÛ%£IÒ5äcñ>¸áš¥àþ 5ê ;ÄûàþlSR\¿~ýêÕ«j¹I“&Í›—*ûpG|FKd¶Fõ6Þá%ÝQÔÒƒxG)òœxè­(NcãÇW†è5kÖ¬]»vìØ±Bˆµk×öî&°”ÆŽmï‚;Üâd;°%HC›–ÂrøÐ1ÇM‰z1ñYضkŽ,-y2Vj–ØlëžA>y‘ˆ+§«ÙÕkƒßúô÷o­piÑ%'ÌŽ› …A„«WòX: ÙÐÞ ï¾ûî;ï¼£–g/ð³j./dÀ«&`š¶¦ß1}ÖèžåûþÀ’§Ž{Ö‰3¢,m°é åºéõe4²m˱­Ü{ Bðr<1ÒÙfH ŽÐXh¬AX˜Û¶ÀþO¡†wk4Bõë×—RF Kéë6Šãçœ5@øÉèŸÇãˆÆÀ³¶™-fl³ÏUôã —¤ÃÒ’Äî\Ã[7xÊ”)RJßL¥Àĉ½ý6}út•ÿ:¢c§ÄÕ†Ê! 2Kܸ䓹0iRÿ]çÝË/R{~ï#¹+.ïñzš2¹q \¹úJr R(¤‡B‰–*Á ¾.€É“‚pòd‰®kmÛT‚âBü¶ÝǶ8¼i“;ä{ذaíÛ{àî“wF³ûz'>¿xñg2vÕñá‡gñ—àžã{˯é-êÕ«×°aà ÿô(EO5Û¡VÔé˜ôNb4¥´àØnêÕ‰“c’££qäkžS/²nÝ:U‘´nÝ:›ÙصYÓµu7&ÞF‘[²í0 QÇöºåð¾CNiB‚H«²‰ˆB¡m²mòªU‡Ôãv¡CóhéG¯1óÞ/W®LÅ¥Ì@$LB vc À®S­?¶Ù?-;þæÒ£o²€ÃŒp\½’'¥Ó°QCõCÔÊâû‚ ,Xà7ixñÅUç†#Ù4k2ÀÒ¯"qÅMÛ²®+ä„ =™ÁÌ…²@&MÀ"ÇäxÔ–Ø;° áØhU»eÛúí~2úç W _!m_!,&‡Hjµ*7Øpwýg'>‘"È"¨xº^b1—¼ëÖ6×qœH¤ªýu º] ñšLÓMFDÛ¥°íB<,†…Ôœbf,]6ÀäI³Ý/ŠAŒŒôTvda~TI4ä ÿ.û¥Ë>mO÷o ºvóDEéᣃg è¢ü§ŸÎSmÜ'Mê¿ãtvAa ¼>—@’øx\Þ%Ú® “t1×§Oj(¤ïßð»ßíÝ»÷Xƒ¥“'»Gµnh†¡#èäv"ìùüÒ®}g·ï>)‚àÈ Ò_µó37Þ”%fKøCšÜ¡øûß²ÿ-^!;¾6p¯pŒr¹xôÖõ:£~4ìæ)6ާËÛ–GçãðäÚ¿W½þU¿þÑGE£ÑhÔ**9ª4cg€]p FUMùl [*ºw5É»?7“S3м4ȶd[Õƒv‰T'‰ó—T«F>õ& ÕSYŽ2ðÃaÿüÃ!?f&7Ý À‘\½zUJÙ°aÿš¡]GwaáÂ…É%»Ï?ÿü /¼ –•»†¥Ãœ˜ÈàäÉ+Ž-6 @ã&xfêlygeœeœÉTÂñRÛÍî:€dÇBFu[ÁâËÐHÀbØ AŠ[µcê­Y °ä+6.ò˜»áâ;«ÃÀëÁ¬`]Ióºá¡RëJ‡a3,f‹ÙbXX¶|€ ãg«£É-D"P5=•mYTPê²x%Eùä“X,ÀÐÆ9&¤…šÓè‰,»D‚°{¯ B¯îW€­ÛK è Ú¶m€³§û5m¾@ß¾}·n:M€DMˆ-;~ `ú¤Qvû.¯ßüM úÅ/~ h_ôâ)7'&Vœ|Ó0ðÒK/íÛ}Õ4íÚµë;ž]aéŠùš5kªÀÑŽc%L^ I WI\·Ï›;„zµW­Z«cÇFDXðA™Ñ€'ŽcÏÞ•:—vkÙ7]«cÇ)VjÇ †[3Âq"• ð^–Pº¡˜lCÜë_Å..´-Ç6íÏvþ@ßÖÏrq¦ˆ¤i´ ŒTlËy IDATô ôçÒ±ãнÛ499kà&MÆp ÒdX`°¹ES»´”‹3ƒ¶wMVéH1;óíçÖE׎5 @ï:æün÷ì«:ÉÜ+( „DFõÓŽ#"˜"ÂiÚ,DR…ó·7z Þh$)QXè^†N™ŠY³þ̬Z9S׺4ôF`êÄFJ3„y³U•Ó¥KÀÒ¥[ºuëQ½:Õ¬é~Y7n8jÔÐ\¼hÚ¶´-i[Žã8¶ewhŸ¡Ê£E¨‚ÄΪU‰ »pä]-ù“c ªà3*|Ÿ2eÈ×°ÿÊÄ—Ðd’ão¯ý-Æ#²øo@pOކ :ŽsöìЀ1cÚ¼fMö—~£CÐq:;6؆´áX(-4£EvØh“n`øð᪯@0¼A’?Q€ÛaJ2KÉ^Ž è‘,7o)aûöíDÔ¯_¿¿ë¾µ´¸oߦgÎÏŸ¿Ó«§[棌7ï/û·O7¿¡Ê5¸»OêÙELô«_½`Þ¼yR:õêÕ#™3§NžÌ9~BiVû/Bàýe¯ãç?zéå_¾¬žR´/zæ„;LÌ-'„>üðÃx< nݺ.\8sæ4KG²v„ õ^×òøÚe6K`Fa—&`ÝV qXq·‹Ãù³wb¥ŽÊ¢9%αÇä=”˃´i ‚V²ãöåKÇí°Î64é µuUnæ^$²|lrdxµjEí;…ÈBWN~h†& MpÌ7Æèt”8Ó±#µiCNÔk1¦Cèv¨ò¸ÍuÝœŠ,&GGÔØyOJt*;QÏ×íÕM:Ò¶iI¥ÂüÉ×ÉÏò&u|£±á30p÷®[§þ5 $oÚ¤Iý,[¶õ‡?îùâsíž›Úbê¤ÆÉŸpÏžÒC‡â99vJЇÅþç“'˜MU>öê•Dkx;æ$v}=Hò8,€óoªx͛ՒÒýí1кI»wÖÎh@×Á ÷¼eÞ@PQ ŽIµkv<}f×ÉÜmM÷£[¦Qϰl3n:L¾ b•¦W!ïJO›–zžè}UûOM'Í’D@s3«šó¨R¥J̬ÊÍ¢gMÕ%Wè ݵ·’Hbf\''Ž$öÔ˜rù‹Ö Ñ­{ùï·N\;ˆ3g®ê†pÔ¯Y ÌU«¤Ü+ɨžâ3÷²œÌž=ëKQûöz­5$„p{æA:€bdTí[`V®Äø¤¶“&õ_¶lë²e['Mê/"Ä&ƒèÙ£Gž&“š˜4éÍ¡B¡ Àݶ˜%à°ˆˆ?üaãÄl;fÛ¥ŽSZ©Rüü‡™a²²æº/òà<å©Óåù[Φ>¸O™ò·‚;üüêã_¼úðHε~ÚŒ,…ˆTðøÑ£G´k×î _AI4ŠÂßþ÷Û Dê èTeb¥‡<]!;€±cø^KÀ!TÉØ bРAù³Óì‰]B³ÝORSSò7®0xp%ÖýØ»w¯ãp4Úhðà›6ݸ};¿aÃü®]»óÜys¡÷îþÂÀu²¶•‚1 oXó´Îï£N4*t´l…œœÜ+•/ƒAf1b€×_ݶ1¤ÿËÒ36ï˜`âS3 ^ãI%–¨á±x¹s×ÁÌ,ŸhYǦˆðûEóUÿJÉ΀nCt]ïÔ¦«Rä7gm(¢cÓ¡¶…“§¶š¦Ý´Ù`fè…Ñh#-Á V×1ÝŠJ¥Ã+Þ[oê à¢øé!û‰@0X£V+&a D8F ‚wÞŸŸššZ§NÉ“'ÇΙ ¬Û½@œ8d l‚M°¶¡§Š’4vl8IƧŸÎ0xð¬ú[îÀ‘õfU0ÿmóÂKš&Z¶ª«t|¡A3øî½‚`XdÖL[“µêèÑ£~~øÇÿ4›¿s.fÍúŠñýÈa8žÛÝmTà$zl°7­eûvëÖý©a†aœ;÷gM‹üâ +Ô²e[LÕ •áÚ¾àþ æþ(±níM37ã¦iš–·Ì˜íÄ$¹øþÒKåÓÑ»v‡á³Ï¾ß1˜™ˆý Ü¿dWázþ®)|r|ó‚»w§ èIµG§ðÉ*MµŸU»ów]Ó˜ªî(\[ƒH'õ¯ªéÆ£;€Én+ÆAë?ÍÊÊÊúÎ÷}wîÜIà’âuì Q ""]'!œU«ÎWªiÔ¨pMîóæ½ @×õ|ÿWÙÙ'·n?®£ž“Ø zð¬&8'Ç8 >\ýiÇ6ôïõò¦m¯oÜòúÀž/3»Ï”f™4€Ë HºäîÌék Ù²e—0R¢÷¤º bA¢ßS=?ÿüóWvêÔ ÀàC¶ìÏ:xjs›ƒ5êüø§ÙÙëZô% `è†ã8ÙÙ{ê¶î|?²“·±v¸}ä°Ô9õò nfZN$º¡ëž·žø‚HCÓBQxÍí—)b j“”XÿÙ<"8ð)q¾oFãÏ*èV¸bÅ.„зi/W¡RÇ$2«U¾WP°dõ¢SO)pÿ‡Ì&‚#ñÖ[_1¸çäxý>“ȰÄúOç=ŒAçåýŽˆ4pS2¿ýír]kZXpÐ0R…Ð|prús-ˆìÚf¸ dÏɱc1'wb1ÛŒÛ€ßØ`|F7– õïýÑ«öícÿã<ýô,u*‘¨ O4ËÉ9Ç,Û·oªR²šÀ_Þ.“öxÄsÃWîðñç¯TšßþvGóæߊ&£U†}ÖWnTCÇŽ÷ïß¿ÿþ.]º|áÓ“!>㟪¸÷î½2 –’Û^¥‡cg.ÜjDé-ZU‡ øEë£P8AÎ{÷î-åþí%]íöOf-ª”[yÿþýBˆX¬ª6ëz¼[·nþ‡"‚¦é?úáÌÊé̘%KZÐým¬_Ÿ•–ÎÚL˶ÁÜíÅN6|æY¹]I…HîÏ@Úä6Š_´q€éOÍVX§r¯‚™!Û¶­W£¢„Å+³fΟ?ß¶-]7tíÚõàÁƒGŽÑu½y¥V‚4Àµv4kþÔºÏçžýäÐÓÃgNE¹yÀÍ[ù5jT\}‰XOP«¼]W»“tÃBƒap¥J° ºßù»d)ÓgÜêí+·l_>°çDÇt–)‚%jþ‘»ñŽã“œdÒ›   ‰×Ù¶iñyúõ}Åí(Ì4×~z­Öïj¡,¸¸sׯº]æ/\Ü ¡ çoŸ»t#¿N®Ýº5ÿõßs/ î_Âr3räl¨ÎB‰‹JsçnÝÊózóº 4iiˆ[æê®†Ã!ÝÐAãȑӴ µPH„B"¢Pˆ*?´~« /Êü|§_¿$Š#Á’ÙI^p‡AºüÙ­qc—a„½¶þÙÙ `LÀ:ÜR,ò.òü>E'‹oç²ø¿ü÷\ߪðð/âëw$ó÷¿ßð-ð?ÿyåvÿ¶ÒªFu0¯¹¡SËnû²woýdw—Ö=Sê™TÊ{á'EÞØáü…;¬DÍšgÂO & µ)ã„!8Q©X¿:"ûôî»k÷Î;vôéÓÇÝì£k©Óøâæ?8xðOBˆ:xïOBPŸ>Õ]·$_‰+¡Fý+eõ&Mr™/«76M<ùäÀ-[¶”7!¢ì#U]wŠTð °À,4<ÑÛ@û¤r’ÙýfÏ™3—½f¼ÙÙŸà‹ð}æÌ™óçÏžŸöíÛ¸zàV4„Sl†#Qš $çxã÷ýáeÇP„ëÄu†i÷v醰ãµ°Fº:„ÊU f˜W-W¾p™"ºKÞÉ{]$kÖ¢];wèÛç¿ÂkÈPH÷ zí݃Ï÷AÓ@È>¶10‚AöP¿Y=€‹·/Ô¨™!4qàÀ™Î]šª)ZsæÌQÒðÃW›6m¶váw¨WÙTöüQì e€Û†¢S§ÄkÆ.Yî(>µ‡•°ÆîŽ ãGÕeb- AãÉ'ÝüòÉ“6€7¸Aƒ/ؼʕ‘™I))";ÛlÛÖÅhFòÔÄÊ웺8ñŠèS5ÅlÛ6›ËþºÂ ¦ú©GTÉ„ºmÓ¦qΉsÙÙgZÄìΪúÂSìסÉ$Gõ«_ÞwŸ¶»Îæ·L÷‘ÌèÖ¶ßw´òCûAÜOáý(ÚZB:]È» Ü´E&4"Ш¨qI–M–dK Hc¶vŽd)ÙqÇIfÉáð ñxS!$‘sïžEÄUM#á¤ù/]×Ú¶m{äÈftèÐ>yÈÑ4“ÈMû÷ï—RÚ¶šIçܹ“rûv§ïï¾Ã²~‘I„[éá ÏVü‘o^ÇŸÿë× (WÓ½“ã_ÿu€úÑìÿýV‚ZÑ ÏÏ’ =lªpòàHdíÝ U£öW¯U+Mui×¶mó úJ°bÅî ZdTKÊäî^ À̺]\¸y31ê§—P$E«^_R™^ýwþç ?V¸¢àoÅÆeRЈ!Sœ8œØ„`Ì ‰;wæäoîÕ3AÞ‡y=_¾› êWIFì>žÛ8ÒÑÐ@@ïÐaˆ®Ã`_Ρ¡MסǎpðÐûDxå•W¾x¿ððÝß«?üál·Ù–ß’©T¿w®ìþ•Š  ýÚµs9vÎTŠ¢7¿É½¬t[F0m›Áz@B†r:rÄLIÑ"ª]ûQ+1³³ÍhÔéÖ- ¸Í…Ø; †ªfO«q%A™Äß%{DÞ¼S\‚Âô€‹ò^÷ŸÏâ xoÁ<§Ì,=ÊÅÓ’%þúÀâ;þ~ ¾Â¾0óÜ_ÂÎC[ÁèÞ®?—ý…d<@FzÄ_” šfN®Ù@'ÒPXBWo£$ŽÌ ˜¶„í°-¡zËHfGzøÉ,‰89 oHäÅTîÿ RÏüÅ0dÛ¶m³³³´m[~3’?tèjt®ð½´´ñ!ÕW­: 3³TJÙ!Ði=î›pïÅí›øÃŸ^ HT2¾¯\ù¹Z8vì?ø³¤ÄŸþ<I—ÌüÌ´ÙÉ]jÛÞ—ivò %äî/,*z¢a›ìëi¡`È•S¶oŸ$ñêÁ{DH¯šêzá ä_D€Y]C\ºy3˜¨W¹˜RSDµzzÁ½þúÿËŒŸ}çŸÜÂxÙú¥¬Óè'§:1ÈØÁçŸï7ãñš5zWJ̱Ò4ܾO‚¸vít•¯#õž‘Àä¾³4‘èXyàÔf¡¡U—ABàíwæü%üys港¯öë÷¾;+îÞ˜ºäöèžY=¸·jEÑS¦Û›×¼§ Þ¶*Ü&$›B#¡S0°›ta4{ö”†ÃzJŠÖ´éì¸_·Ù·/‹ÙѨ5lHºÚÕ~NÕÕâYá»§Õ¸-—Y}Rbïff”–2K°¤hÍ€k¦*Cçñî»óýýæ8²eËÞ_Ø·`É’ jaÊ”¡òí|éx`ÿ™Çâï§íÉñ˜@<€ø%l=°™½ÛbÚjÙßÌý©Â !Þs¸·+=#¥ÂC¸þÖ]Ę6š6!·HÄãGÉ⌢JQÙ»w/€îÝ»oÝz@ÿþ c €ÈúÂÏ•í8 Øe—.lÚt ÀàÁµV­:˜‘Q"õéÓ§hOwùwño½jÛ–®ë>¾ûà>~|WªÇì÷¾;‹½>ï¾7e!fâøÙNÐû·î  ˜Ùá“™P÷‰ÇÕ!OåZˆ]´ò ŠA"==¢öXá%¥C À)†ÝÈ¿N d¦["5E¯ReÑ’…—/Ý [üãóCüôK?Y ãFNs¢pb& HÚ”}$;›q³zõ^ç£n¦.=…EÁ™™•„M`㦲=K#èwÝ™º]Ãá³[…Æ-» x÷½yDhÙrʶ€ôP;Ym¹jÙðüó¯$,>aç„òá7™ñ£®cº…£Âó>%á{â® ŠÆc$„Ð)F$`³Cmß^ÊŸ˜^P€’y?»ß¶­(µbQsÔÓ5’“« ñdžè6o’î]r?/“w&‹–2{¼žVý Bø¿¼=ßÿà>?PØýàþÆÀ§úûÃÁÀèÑ-¿]!Þ`}ôÇ ¬Ï7n?´±oÇ!n–¦ŒˆkGÀµ“ÌåêZË™ #B¢9ñdÙ½z&AÃÙK|â$·n•TÌŽ²’yRø]4͵rv@Ö <‚íâXVÄq´íÛÏççKÛ•*…7n¼eY•"aD¤ÅÒa0œR©Ev}-4 Ì.qÁ]!{™H’}§?7+yÔ€å+ç‘jΕ§NòaÙÈ»©Eut\çà“ˆŒ€ÁT~øx¨‘ž—ZPPZPTZ¥J¤è’ìqO‘NBF8U7,Íš!–,[tåÊæ°®…”pïƒàäáS–nX²rí¢ñç¹þÂé³çRS[¦„õx±8jHÜ @JJJu]K¯QÅñ~q €‘OÏ"Íë]ìÕ²8|v‡&´n-{‘;÷jâÄÞjŠÓ—€øŸûEJÛ €¹sçª×÷ÝyfÌx…’™<êꃻï(M½'B$5Q@àB?{¬ž—5j’â鈄¤‘¿þš5×B‘`JJð~p¿zUÞ¾Í5jˆÊ•=ñ'Æ·o£´Ô))qÚ·OL<ï×/ kÖ^_¾òR<{fR³ò<¶œò®~’Tî0t78¦X”YÄ\ŠÿqÓoàÕÊIaS¦ ]²dÃ’%*„ï/Dÿ¯6Ö?òñ¤ðÞ6òñañ/ÿ”»>¥f(±Sv^ŒO6v’(üÃîñóf9ç̉3lÙlKt鬓{ J:ø}´8p€iVг§ûEÅ@í âlß~¾ À±m‘–¹{·YT©’qõJ3µ¦*D(­èÝ=é2&%‰B©¢³’ŽèøíŠ7Œkþ<9LµšQ>%=oÞ</¾ðJòNówK,]^ƈ=üÉYjoWŠÁ²a;¸tëX\GZZs)QZPú†a ÄîÝ ¿bE‰¨RåpñeòI(i .iq§Ä ¥j[v¯”³œFCì¸]ZÒÀ÷žëꪷ*¿Ê¼dÝ"š4ò'–tõ2ÌÔätÓtûèÞ ƒ` Á)©AM`ûŽyð.M†?5KyRZ A÷  Ö5¿°[tmÙó?VÎGÖøã>òÅŒÁR;†*\Gísv+ ðüŒ™ð0З¡ÃWâê”IIšš$ˆ¼ñIäOkRg& *. M¤¤…ÈÂ"Z²ò\0 ŒÒè‰ræ÷S§lÁ Ð K.^”,‹KJœhÔN>%¨šÕeË/Åã1ÓŒ=?µ­«Å3CYQ¥'¾KÅØ]p{±2TRÄû«KƒøÝºß¨·øÉÓ?W»!­Kù½W!ŽÃàŽ‡ã;?ˆÿ«z㱂ø?¶‹xh§U¿@^L™™@|)qâvvnî-ª?¼v €yÅråx:rÜQ|ÏƃÄ?<È ÇIéÖ­…z$´}d|°}ûyáðÇqºuë¾aÃ="‘w9¿Æ]-Ü¡ª¤‘ÐtAº Cƒ¡¡Qòñ=å l>sê9Dׯ[ î¼ –`1›Ì&ÿGÞ›&7ý™”Gú¥º;Í?A2V­š[nóÆõ™e;°$.äX¦]%½…®îÜ…®A7`è0(,Ä©Só˜f¹s_4¯ÛEÅQë† Ü“¢é²T4 Õ[?ºtõNc¹Ó6åWqŠå; 2ã…ií\ùÂCùEk>0m³N ׯD‹cujÕd‡ØÁéÓ°mØK‰’T'Òwíšçoö°¡¯¨äªîB°²ßéw¡ h¹÷ ŠÆvžÛ–L$ïÇ÷¢}QWÀ¤G¯0ÁÒ[5Tp7¢óêÈš]Wg¦ª°•”yñ…×bÏì)k›Ø·oã;†0nF®Éoû òP߸—ÝÓ÷ Àþ£!þ•Q£@,6˜·[ßql‹€!` ûüf0š4¡1Ìó]˜ÿõöç†YþêË“-­ÆŽSSp5š,Oðz@À›ŸÌðêØÙ€íÚ3IS+ôÀ£}ûN®»¸³sçÖŸìÒj5¿é›EüêÕ'º—ªÔGñË–-ðì³Ï]'IyyS 7™iâÆÍrùTJxÙtðÚõ2ÓD½º ¦åà?1¶¦2J…3sfÓærAbøðÄÊÎ;€K—.5iÒ¤ÊíÁ0o‡ø½{0éÙÓ®ŠŠò[·žâRÜ€ ƒ‡„Kƒ[ƒKcML:zè#ÆL´k݈L(²Û|7ôtbåUˆŸõšÓ…‘ãÉÒÁ.?JX˜ˆmÜø[ù…—g¹'ž˜S\Æ1yrÅíÿǬiîjîõøžèÝdß™]àd Oöy’Ç€³åŸ€±g'¤Â“ŒP ÜIËV\aÒ„&*sÐÜ ¼ûáÁp¸¼Aã+º®³0çÐ:4~<6#!óÒ¥‹‘HI(;n\¢ãvàUà»MvïÅ”±¼¢ÃÎíM¯?¦×8á6£ ±õàð¡óDÔ­[K‰}ï’ì¥_£ª;³²Á™ÂœÅw00ÕÚÁ’h¬;GAa)×Xº‰Î,Éÿ˜¿\×~ò½QzJ4 R„Hž!>ùüºbܸºK=»uk€Aƒì…Õ#ÈÍ—¯¼ C¡à¬ç;q_Ewf޼ņQ––öòK/Uóœˆüå*A^^Š^È3Ü«c•/O­V$y•DûöŸ¼råfÃFu{÷îxwL÷hµÏ÷ ~õêòÎøñíïqSâ—,Y`êÔ©Â$LØÚ$å ‹‚Â@\‚OXCŠ…‰[·üB 1!Îáï+÷È3Õ¾–¶ò¾~c)A0fŽYO>›ŸŸÏ,“‰4iii•7rÃtªñ•Ý!¢²¯‚ ½¼DØž{Šq½¹Î™‹3]CóHÄ—î†I‡^„'Ö>= ‘ Dˆ ŠïWUOÎX“ˆéÅ×b™n!ÞrÞ]åÜ âŒi·nß³g}Ó¬Ù/ãâ|Ó¦UÜxð>Xò©®ëºÎ7ò ˆA=é 8,dœ}òe ž[yâ*'WÂ'Ÿ^áé1 ¢ñU¢œB¡`8xéùnZ"°zõjA„ Þ­U¿ë×o•—• ÔWò1¡ªÚ7ÿÑPYQ4àóbþgóuÏð2|¦ d®"Eù΀/ò¿0eÊÀÛÒÊV²;À`ç?:ê|`1š­a–oö¥óf1Óã¬^j4x›¹üæ7ojZ’®'ýò—Sœ¯(üB®˜³"ŸÏLh —Û%¿ÉëÖ°Ýyë¾,0üÉ%€°xI^( Gü?ø§J)XÀ¿ýÛÛM›¾tîÜ¿QåB¤#GH‰`¶æ×ç:(.ŽU仪YebÀƒ§äy¢gÏv_ï=ND=ÖI¥ÄÞôw0õ!#þ>Â]Ú£€øE‹°‡0ed0 ˜¦²òˆiÂírIÖ&„Á a¢¤8d ø¼ }Ó„0ý%ôŸšRõ+Š0IÝsÝúbÆL—ëZ«VñV¿$Æ#rhÖ¬a•«ß–\¥ø¾'˜ÐËK¹Û‚çòn2†íöêªÙU…÷_p]Öš7©K&‘ &àj¢[ÅíʪÙå”­^~é×QçÝŒ¡<¸~•ª † /hÚ´)€¹sç2ÆZµüÿ.\\ð«_½bovÿÖæ)Œ€a óü…¢öMzwê˜Dõ…Çëb ƒ±/7û6ꨕÖúË+k×€0rD]ÉY"|úÙõ`0 žŸÚ€Ï,]º@¨è db꘩d'çqh‘e8¶>à ô²DŒá¿×ü.QKIq×™Òoºâ;P5‰}½rrrqç|/ݰ…{RW<ä½ÜÏÆÏAV&.@òª1>NIðŸTsíäÌ&ÈäKÆ8OÔ´$ça—f– 0|øñ)0|gF;0„#çp¹u0öÙš ;®¾Ü >ý쀱£R£r¹P*øÛ ¿ÿŸÁ¨d‹¡Ü¸xQ©L’ò_MöÕ’$;cQYŒÅ>Þ´)“FåRámÿ8D AÔ³G{ùeÞóõQ"ôé«Ïp$ôxà ¿#¾Ÿ0~|Û¹?ökÀý#»´Gï>øàßùŽªÖa2 „CÂ0ah„a(w^Þ1M˜& ø ÄKÓ BÀP®ã @@˜x湊¯( Ê;—Çãœ?`Œ8Ç Aœ;w@Ë–5M9†a:VƒøC‡™¦)„ÇÔ+ HÆmÜXÎÓ8çŒqq‘ú>ÖŠLìûú<#0b];5…IžVîÒ¯ƒ€Œló]yC°>bYóæg*ÒU–æqê3äôâË…áÀÛ6bŒáÚ+`Hk˜¦Y52 š—//дøÙ³§Ÿ½Š0JÄ»ÃË3»ªg8g·¾â“+&Nl¬¾ D+V^á™§Ó¢Ç~úÛ‚m&ü?ýéè ¯²d ˆ0m𣤠0fL†ŒØpWL×¢dçî›5«ñµÌŒ,Û´Sºyð`ìøŽ‡‚øûî¶;íQ@üÂ… ˜¦É¬XgQQ€1ΫSÇ7s¦8pí I¸Ëñ•’ò‘°0LFÄ¥×/)ñ €abÚL8uê”ý*㜵jÕjýú+œ›œ›Íš%hݺîm÷ù8†èP â÷íÛ/„HѧOB`ýº"€sÆcCú@ع=}{·ÚóÕ9NŒú<ÕºÊ –~`œØRlÆ82ü$âöôì@À';  ¡@Qä:Rªr7tß,¼®ÕAƒ œ¯xáÂ…>X§iMóŒêß©]‹ŽL²5LZc€cåêË.—îñºGެü®€7x"5Wndçàs°mÛVðG†OŠX¶zçØ'{ !>Zr<ñï;“`ÛÁ\31Þ½Eo®:B†.âa¥iN„»v„Ü}¹.Íu9xAîÜŒjÛÎÜñ¥{‚v`ô`G»€1Å}ƼùÙ<0¼òÊkѼ^‚i]<É[(d1Î5ÓÄçŸG3,322/ç˜:µâ>D Í¿¿·À_êMêC(b0†•Ÿ^fÀÔ)-TºŠÀ’¥çL™Ø¼Ùa¥¨þå½/#‘2Ã(q^+,["$'Cãà6oŽ¡üرÑh6ãTA®±=úæÍc_C†ûÃO~ÇòñîÒÄÏŸ?ÿêÕ«iiiŒ±‚?ç¬n]¥™H)œ9ÌþS>å÷‡㉉^ùøøñãå6?ÿú† à¸úeëÞ“1ÆÚ´©¨ìnÞ|>6Zµª—ž^±í{•vô¯ ñ»v ë¥v#šòòö²CÂð¡‰êz™@»wä]¹\Ì1iRïÊÛ©l¥»°BUˆ&[ óßçøÛÎßøçA¿$é³2Õ¨²´¤€qV7¥®]Ľ((½Å€ºuRåb7 ¯ ˜‚„Iæò5»¼ž´FžúFwÒ¼½ (B*9„cÝú¢ƒÏþüç=ñï©öôУ®ê£°ÿ©Kzôðìß*/7d>ŸÄ ™"2É œågC¡ÀK3ºªä<“¨€ ƒöœÜÍçLëÒ¬‡«Ž¨˜«Û—WNë™Kç‰Þ¸…ð3NøÓ†ß1k<‹üEWÙ_¬JÄÇ^9Y‡Y‰@ѳªS(S‘—^³É.|ÉJí-- ‚¹]žO?‹¢sÈ !pó¦be…´%oþa-€ye¸å“Xj¾EÀóSÒíƒãLm”gÄÿ\°Â0JL³ä׿~ÀªU0 D 4pD°96lˆî-=3!“9‡VC²–˜s4oÁæÎkš¢cÇþ¨Eg‚G¨¾©J{@ˆšL•¶jÕQ&tzÐ/t[«ÜgæÃ?Äð]>È9…Bœsùˆ¼Ãóx<öׯ·JKËïׯŸ|¡S§JΜ¹™’b¸\fïÞµ:ÛÕ€ømÛò„B^Ÿï‚¤ü€ý£N(‘æá–/ß{üø•Ò&MêUy#5ØÙœ€CeL–ù0Nœ30üwîo5®ÿË_2«BòVÉMÎY½:©²Ä†û¢¹zÅåE²œRÓ„)ÈLkØXžÌR|úÅqŸÇçóz‡LJSCãXt¢ìÿ¸Ïå˜+ÿ’žtüxqW’ɧŽO¼€søýfÑ+n#b “\\ËYv!D"ˆD`¨S'†ï2Uöü±äĉ™Ñ&[‚wˆò;v¬;vl€×_Ϭé{üHõ'¨ÎßÜ¥}㈗dG5}"k6»Ñ€œœÉtç¿^¯×>=”–´1 ¤68/Ÿr¹ÜŒáæM7ç¬nÝ<h¯#­l'1 @»ªø¾fÍ)"æñÄ ¡Ñpûöí’òöÙTÀ¤I½–/ÿ¨Ö…ÿú ÄmÚa.ÙýüJ€qÆ8qYPÊévþÁ¹ú‡ý’sÜ(¾Î9«_¯´¸Šµ6eÒ²`™Ï'ȬW§ãràgT Û°åü Šx<×3ab£ ÿõ_ûFæÿ0Ù™-OËÇÏ=wëúuJIA»vѶNRpvR¿OV_ B“'4%U[+‹ ABàhþaÎ5WÓ˜Ö:­…x‹\?‘ÀÕ¢Ëáîúõ›€À0}€º¸ ;úä&߈ïׯ#)ë'Ö¢~ÕisçÎ%¬YQç]Á¤ óF± –›û';Ciøð(ÙeÞW @Í™’LŠÿ÷wü쥙Q¸#¶`ŠŒ½µ`þéÇj€Y*Þ~o€WžïQ%ÙåÛt§éï½MÙ3sôé3Çæ»i n=‹ìZEÖþy–ó½O’émÔ£ÇòúúëQÍe“Ó¾|Ç}EüCÐd*Û7Ë÷{»´>ú‚1Ö¹s,ï¾sçÎÎŽVÑW!ÐÓšH‚öîÝ{ó–WÑ A·¬6|p ôu ~×®¦©™¦^P ’’ÜÆ5•çææÊ)NBˆ¢"Ê´ÝöåË¿Žvª“âÓ,‰kÐdº·ÔŠq©Xô¾ºL¾|k×<@ÉÁÿ<ø—ò÷&<¸yý*ç¬ajCgw1ŒigèúË‚¥ ë7²j/ Ê#ðºòÌœU</Î=ꩊ!Š¥Kwöùê¥0%'º‹ú.˯_›z¤[7Í^Ffòä~ë¾, øCãF7ˆ"ù/ÈTƒ„Ä-&,=¯ø8g𯵠Z„ÊÂö— äëêÖMc¶ be0ÊÄ Õ€aþ»s™J«Q?󌌌»K§‘6wî\"š5kŽÍt»3ÄåK·lÇŽÿ’K2ÆF̈¶ó´âÿeåÑVòºÀ‡ "þÜHS¢ª£üßî¼|ýf·nIÓ¦ `‹ÿY°„¼ðDdwñž&j®“ ¨>Ö;#b(ħ¤D=w5»<öë÷É'YŽÃs3”GøÈi€zôhËÞ™_«±YñwÉwÜ'Ä#p—öM!¾†öî·µ}ûöÙ2ýÑ£W̘pæ´J¤!a‚º8zR “œ>ж¯rk-í4†HÇFg‹`€Š‹›><&‰~óæÍ€ A‚È_4œsè8C$æ ñnt šU¼#ïhNÊËG¬Ëaá­es- àŸþ‚qš‚2¸Æ¼²ª…Gý>å¼;Gq«9“”•ƒ§@öx° )¿\_èõ¹¼^½¯C€^ºtç3Gž,©ÉîBûÁâpü,_¶®]ºt'€Z·îå÷@dø°ä*z‘Ûƒâä#LÎ`Äãéüås×8ÓÒê6V³&RÜD(*¾ 9¹Aô3T³& fcÁºî‘b‚œØg[§N²‡ð!ÞòÜç£Ú@ÞΟ¿¾û«¿ÚKŽc ¡(Ùåýâb£QöÌ™°ã“¡K•žÃM(U4E=ò×w71k؈ÿÏ?®ðO³†TÞCv‰{ç„U‰ø~ý2„€\fôh¬ûÒÁtMùNY†s,[ú®]Ÿg ^½ÚÙZÍÛµ›Œø Ç6Ùv÷|Ç=#~õê“=|²Ûöð¿råa€îî{÷î…ýëe €¡'…šÞ½ºÉdJÉtÉ÷ޱÉD„ÀpêLˆmÛz”ƒkÕ ô¨lñCÿiš‚HØ#>òó“‰Ð¤É-ùÈÉ“›7ß@DºÎ‰(B™£t®LSãðx4Ρ[Þ“æt£ì;LÁ]ÓèKT“üä…׊M…00¸/†LÎ×9cðÅ)ØÙ…”V£«è¼rÕËPþbLuÒî¡©éB[¶–z½z\œÖ¥K´­‘å ë±£'~] î“'?qò¤Q^núýÆ€ñÑYBV_éÂ+‰Æš%$Š˜dXAàâµ œiñ­½ùùZbbªL'*-» )±žõªÊ[—IŒN¬Û±¼ýõoYÎß{—.#jx ÷_œã”eH`Ñâä=ýtfL3%²Ú+™0 õêF÷*77 ÄÐňúh•òKy0ö—÷×xõÕq¼âÿ8ï?ž5¨DvDS¼-b\ø2ä2‚0ªi·où*:oŒcñâ,ùògôÊËV–áïo=Bˆÿfø¾zõIygüøvwýê÷ÅâW®<,ï<óL—Z®²gÏ'Óc={Fò«WèÄñ¦ôô¶C&„@û pË œ>CÛt7`¹´Û·o?g›ì`Yݾ²Ù½{÷Ù³)µkðÊÃሔnH«6[¬qÛA!„iš€¢¼<Ô°Á$]c’ì¶8£q²Z¶2óÎ")(3qb¦í¤GÝv ôO€/ÖDÏ@šcÓfEÃàùˆ i3ݾü²ùÏ…+È’Wñ¿ÿ}ôÓ—&Å„Xd—ÇßÛJñ}Û6¬_Ÿ `ÐÀ ²ÎÃklîtà@”ïöŸ¨Oßï¿s~ðƒLyBýÛßÄßÓ|ñãÛ®^}jõêSw„øGîÍîî_}õ,¦Ks–Þ"YïJ&KoÝàèá«G]íо˜@z¥Í›A!7•wÖàœ·n­«4C™g½ˆìlñöŠ/^mÚ´Qµ{Y }ûöíÛk×Þ¸I:¿V\ðòwù‡ ‡r]Ôo¸Ñív‘ßïDäõzãâÜW®.%"ŸÏ@ήB„BDDýŸ*¹ÿÎ{ÑPásS^Sï@Ȳ¢"ÎyrR|Iª„`':“1;ÉbhÄøŒëÒÅ}æŒyó&Åűäd€„Æ!„¦IJBˆåÜ‹‹qó&%%±ãÇ`Ð Í'³ud•ò)¢*3˜%£H&*ažz.nU`ÅÇ7P^^È9nÞ¼æõÆù|IËmké¥ýä'™u¡„œœÜœœÜÚ ¾Âæ\ëÒù¥[H¦;V€±þòKU%dWÿÛp·W#E“YµÌÊ»w§é¦é¯¼{¦é'“¢…¾È®$šèþi†ÏX¿>{knöàÁ Ê_çŠfÏ^¼xã˜>}D^^|1Súìo¿“àïW‡å'?Îüógggg×f ëµ{òߥݑ/áþH‘ýA»ðîµ!ûîÝ»mâ2Æd¯]ie%ª™õ/#«ÏðÁ—…‰Î]·®ôÌ€À8Îç 0´h¡Å¦E/}&kµjU‡sÁ¹Ð4póæÍ ¢Í®]']®r½í÷[9b­B`ýºâK— [4Üž»ÔÆ?~š͉ÏÀçŸ.=z"*/Uæ;¹À±cÇì£Ñ½ëxÆ9cg¼CûN˜&ÇÀµk×9çõRS•û&_™†˜ÿD¸â¬¸¨ÿ®‚«–ç®t|9/Ej5;wú½^ÍëÕ¼^æóq¯—©ùpÙ.!çLXx㯊‹ R ‚A’|…ŒPÐ#áP$ …#áH8dD‚†4ŒÀ?Ï)ÇÈÙa@Q É÷ØÞëDD•µµ ¿âï<Ÿ“¢¡ªÞB@íêZ¥šñÝW2áw¢dïÔi&ˆuéÒZ8„xÇfE[¿Þ‚;ãØ´)ÆÉ æ…1bG|ÕúÓÓÄõþûÈÏÿ€ÿ÷ÿ~$×’.|08äâÅ¿ÿbÖwáÜ(ÙɺïKwøê+2M&Ö­Ë0th†\~ð/^'ïT9^õÚ5üíoуCDœóoÜ…¿|G­ÿÂ]ÚBüj2’ï²TZ8HÂTõ«ªÕŒÉÈê"i÷:ßwü€‰»;·f– ©8_¸$C³fšÓ3µÙS§ü€8wî,ç$D‚¦Q›6Éœ M‹hҤɡC‡ÂaÍ0ôÇWܶmyO!ç¬N¯_ˆ—= 6mß׬YÝaC`¥½Xþ¸Í݃Ñcà“O>‘´*- ÄÅ¥WH—ÌÎÎBú·èÚe”^&ûT1zÆ5Î5MÓ9×Ò7¶k÷¥»fùÄèÚU1Ž ®ÚZÝ–Ö ®._}Éíuy<.W÷xt›ï-[:÷, ¶*ìfšU~¸›7—TÁw#hAà ˜f@0¿iú…(¢\n3'–‰RçFœ¿Ö±c3ìƪ‰5 -ZV¹/1V3â%Ü¿ÿý Û9бà ëÝ«Ôa:Ô®XpÓ&+BÀÀ¬J¢qãæôîÍ΄£ÁUÄR`LH_ýŒ!3óÖÎ#§Ný^×µ_ÿúÁüHÕdH€¾¶îH6ü0M¬]› àÉ'…Vš«î,+Kªóàõ×_¯yáŠø‡Ä÷G_“¹ïˆ¿;Á€tÛÍ0©&bÙ…5DÆQí†fí`ÅŠÄ%:Œw IDAT¦tT/_`hܘ3;'áL^DB&W3äåæ­[·¸t©ÀСÍ.^¼« ,iÞïO2ME·Áƒ[ȽM}l6€æbãÆ ÇäõÀÁí7nöƒhèÀ8ÍÕ¦W/ס¹¡»1â)õàªU«„¥¥Á„„¶ñRêÀ~ôà g';aãÀ ô\ãLkÓ:]ÕH%Þ õ¡W¯Ÿ7x!íRkÅTßþà+—Ë«»´kç+,ôhÛ6ùÂ…rùùùš7Ž?%%g@a¡jU(…”–Øxކ,ê˰PšŒ‹Œr¡Ç+ÄŸŽÏ–€ÇÆ/¡»0h(&L˜`ÅŠÅÅ'æÏ?–ŸÆîÏN„¿:.xk¶j•)SîdÁŽP”gN¾ïÞM}ûFïµ²å~÷oïkZœ¦ÅéšW×£•>Ï>ÓÄ\­Ò¬$H¼öÚ”¥Kw.]ºóöˆgѪÙ.XÚÏ®Jõ-r§§¿6c6lï¦$ss0]ÜêÖå63k¶ª<ù¹Ë#ÿ£enÜxpÇŽ-\ÃÔ©Utd¬Ù8‡MlY:fL†¦‘ NÛM•´&»Ê19Î;æÄ,?}«VÁ0ÔyN>øñǧMÌ G¿ßdm\ê6þãaÎ]š& 1Œ±fMöºuÙ£FU¡¥Ü‘Û^ÁjCöm÷Ç—V¥ÿÈj2•í~¹ðw÷}ûöèÙ³§ aëìÊ[gÎé¬ÒEmT©W¸táÇ êbçzŸÏ'ƒ”ÄLAªšœHµníköìù:b„û[~Ë–-Ç \Ñ ”}QQ㢢PJЇH‹‹ËÓ4Ö§Ï㇊úèš´+ÚD`ÛŽôõ€HOŠbrí2h¸ºKùò}Ÿ€¥K—–”Ο?ØØ²m5ð½Êåk~Š1fšñO¼„»¦Åéºï‡3É醌[CA«ç» w›éKÁÕ@€ѪUìú¤*W™«ÚÎÉáRSyua߸õ§•3Ê8ú>^íÞ‘åäl0eÊ û),0Æ$§–,Ù*ßÃ]À}ÇvuGž¨¤*òôÓšFºŽÎ™ÿx8š<k$ˆåÎûZ«´T)=þx†¬>•ÕI bøpõT‡ƒ¦M8†èd Û&_ànÙ¶OåÂá³Ï²Œ“ [½¸ß©= þþøïÒ*xñß ·Ýi÷îÂß…&c›ÌÁ0ýäLv$Á¬rvËmTÝu'vß²òÔömg h  ¨š†„8U2Ó´1gvª™dœ…#!{ ç΂"‘ú•7.½u"-99nøðëÖ]—…M œkÑlaú“?ºˆaMŒî hœÃb½ÓF=‹ +À íÃÞÝÐÝ4yòäåË¿&¢„Ôåí.ùÒ.^°Nx•nËWT¨]{›={fo6dç²TaW¯Ò¶m»à {uvôhÄ®ƒÆè§ê©$ÈUž+$mÊ,_p넉ÔTÜÈ;ý·±)SåälÍÉÙ*/µ[;–pŸ:uPõ¨É¸¼Pa`Àš/²Œ:ƒk$S`ýÇBV¾L´Ÿ¬ÆÐ•OМ[¢¥Uî»iSÌ’ìÌN™!EvF¡Ni¸ É­i`– o_rG¦ëâÅëñPÈ.múôQ‹¯]¼xíýEüýôßápá¿up·í®½ø{;€ƒèز«%¯G}vé°KÏ=µeÕ«‡/EdpÇ΀U+a˜VBUµË4ŒxÀ´©ìÅãžá૯”K!Ýäñã35®‘¦AÓÐÌPÎ;9»Xd÷µS/‘“é.ýúe”—!bÀˆ ^*4 º†Í[¤æóÚÙ³›¦M{Ò" DÉ®Z øË©¨®GµÔ¶Z,ÀªÕY&ŒÏd'NH¸¿ë·|wvß½øûÌw«WŸ:yòV»vu¿ud·í.wšŒÓ>lú©cÛ.ÑÎ’Jßë4¯vÝp~Dåüq¶c×Y0ê?  ¢V‰&ƒ3à `Ë–-7o¶~öÙ¦yyyZ¶hµ~}1€áÓŠìºuE-Z\ïׯßòåûêÔ)Øàƒ6l,cŒkŒ¥öœÄÓ‚dµÎÁC&ºuà²}¹»Q â· ®YI5J«!݃CG÷˜4©÷G}@aÏB©Îöï‹õèí)NŽéÛêOB$Î VÑœÖ6 pä,Ï÷xÜn¯Ûk¥ÐìØqÜå"]cÆôRUN•L"¾G>Ú´±ôI•W‚¡p$ ‡CF$Ž FÀ4ËgÏ~ÞZ>Ÿ‚»a¨¦»))*éè!˜ñ–&sOpßûµ•±JøäÓ,ŸÉTλ]Gra(..š8c¹í @\7€½{)a¦©Òû÷Ë0Š‹`˜HM…®AÓ¡i*!çé§ívó.]Xù±}E`K4üåTZß#õaÂŒ¬°üÊ•Yڵŵ¼‡wi÷ñŽïõΤ§dw„ø{‡û‘#G„íÓ; K‰úž"¤4­vÝP^V¦¶,Âܱó,10°MÔm笲·¸wò/ä§ÔÉ;{fèà¡g´jÑzý†Rß´FZ[[¿¾@jêY üúõ8õê•ØoÔÜ·n Öï=š•þ²wwM2ýØq„m$âÉÓÔåÜ]ŸƒkÐ\Ð$åÝÐ]¤y »iϾ}2æƒ>¾ü /¼P›#¹c;L˜gm£¬ÜJ¾f` ª]û—¿äêÌ£ë^·Ûãr{¼߇=™¼t©ÔdÏË3´nÕÖƒAðZÑY‰øöí{- H¦£á á ¾ŽAÃð 4Íry#*#¢æ$$À4àrG?Æ1ò¡ô´ù~_à`ÿ>Å÷U«² {í²¨óž|+Ä8$ߘ„wΓÛ0‰À4Ùš5ÙÊË ‘’¢Š`|?~`ºÝ¬3KvéÅ0•5ðX¶Ê…áô鋽 `ÊäÌÎwÿk¾'[´h-bûJÝ‹Ýÿø*úÌ·Ù…?`„Î5/¶råùõ¹¸8²ç˜ ´kÝQ‘Ý÷¤Æ5­:‚Íà¶ïH•fÀÀ6zݪC„ö‚ ‹6(¼Õ¬[¤§§‹0ظ¹À°¡ñÎå7l*Àùe®]0tè›7o0dÈ3 ¤,s2<Œ1Ž|©k¬c:;s–H M3&Ó=Í]vcÏZp®ø®©ìI"ó&h© €÷ßß–kfÍšuGvÝ—Šï……*dÇ­ô;Æ0y2üö·oiZ¼¦Å뺓Ïåòº\^·Ëóüô¶*)ž3S˜+Wî™<ùñ#G":wŽy#¥¥TZJ ,)IqjéÒùùž:ôíësdC‚L5êD ñò)w}ý·¿}+,×õ2û÷Ø¿ßû—i—Ü>ùP|Êœœ­GæwìØüÉààA%Œ¬X‘`ÒÄLÙ¿Eã$÷„ëaÎÉçsä¼C‰ñt·aÀ0`Ì4”p?xp†Ý° Ìê<ª9ý÷ M#Àp»Yj©'ç´Û!VP0!àoä1¢áBÞÙ+†a8ð6€©S3;}sã!î#â«Vïœp‡Ev[…ÿÖ™$»¤|u¶r¥zöán‘¬ž$K"´ãŸ5Ã=x4D2P8Ú¢Я_ëBødËáÊkÙ‡CûÁ5Ü*X/„(*jÚµ;¥§§ÃºV ‡É.A3žŸ_X\âBpÎ,xVóHh>.5÷vÚ†¤xßy$ˆÎœ%—·† —d‹D ž WØ™ÇFYa5$d0é!¿•…ש¼”fΜi;ï ,x÷ÝwklGŒÄ¨§@„ädI •wÀ4±h‘øÍoÞ´úá¬þß}¾×¬Ÿ›š.i(hDBabòäÇf SÁTP "r»;]%jÞ< [6—ÊÃ(3g ߬Ý<’„ðUãW¿úþ믿úÚk¯Ù ķcÇ\I.»©á–ÍØº¥öïþž¬ú¤¤;0»OºúS½R 9˜Ý1Q üTVF°¤VÕ+É’á©Ñxê©hˆÈÞY¹ŠaˆHD×uûË)àWµDª$uV°ÚØ]¼tsj׮鴩2Y(ëä‰ûðöïÎ$Ù%åïÑîßí°ê·Z“©`5#^Âý™g:ß#ܲÚÿ9¾Å XMM¹´ÀÁ „Õ•ì;p5v¹é®º¬u’I“ÒŽÀ±ƒ`¸†®Ý!„‡Ã jÛÖúÔAƸÆy4›’hËÖÀùsmZ†&Mêùä“ÃòNöß¼yó‹/!$ÛßÔ㹞È!Рl}bÔDZcsËÊü¡ Ñ¼ùÿùÕ¯¾?{öó?~aÜ¿¼4ÂΛ–Ç3«$C 7·,4{,f F$B‘¥¦òH„®\QèŸ<¹€þýãÁÈÚµ·Hͱ‚s’*(šºQÝ9sæØ”ÏÍ››«(o÷2ܾí.CfW|D¦ÐÈÈøQT¿k“ô”mu§LÉŒ¶ÞµnÊH}Ó@øI˜(©ç±›÷Ú—/@4ITýÉ¢ˆ92À§Ÿfƒ˜Çã6 31(n·ZØ{!$}ÿ›7 ®^½Á9Ú´n,ÏF3¦gøhQ´öâáÛýBü}à{uÅ«ßvÕ#Þ†û½¿„Qˆ¼s§çmZ¶“ß⸆ˆox›µü{v7%»âÉÝÂånVQ±íä!0ÙðVGç®X»vm$iݺµˆD54é³Ë©Ø6ã pöì-û«”{[BlܸQ bô==Eƒ úEë$÷Ñ´ê%ãSq±…øªª÷»= .Ó×LÀóº Õ›½@NÎÖ’’ƒuЬY3ù¯iš}ú4C¬ oÔvóyy`\©Ï=—©9¦ ÙúŒÏ‡¿}9ïO«çÙ‡…&îò¢ŽYà–ä[Û5hò»ø±™€Js”‰3 36ìNãª+Üümo®þäÏr:³öŠY³UgÍúæ]ø{Gü]ò½B4µfû¶#^ºðóæm‚ÜïÜÍbG+GÎ=Iµú Ê6•KÍ=®¯/®wMCî\8üã`ºvo¬»Ø™¼kmÚÀÑ£GOœ8¡ëºÆÇȤÛT‹.ÎΞ¾Î¯·Y§ö zwÕœí½u š²Æ6énh.|–SqÒ¼›5/J‰X¸Ë«Üç¶ý-±•ÔžÔMN Ì›×ËÌ0·ÔZ5ó}×NìØnMøÓ°iSöÆMòê~ÎÈ‘s4=úÒÁs;¸'Åñ’â ÄŹã|5[„EKΆCáIšHwÛ°A2P!ˆˆ"†a†jÔ@€À”‰­"‘à»V93¶2cTÈ!ÄË œ èÙ“é:4t4ãÆe<=NQþ³Ï²?ù4[¶;tGkʨÚ$þrrrsr¶_0 szzzË–-í Ã0 éÒDŠ¢¥ž·5§#Å,7™ã·ßxó“yöòZ=Õ|Ñí êÃ;Õž˜×²£¸1~7“o3!1¾0•Baõ#¢§Û?7¾Ãó2ÅþÝo,\øÆ{ï½Áytßä ã•—¿õˆ¿¾ÿ¯Œ¦ÖÆîKF4bªøèjéOoÑ¢ÅmW)][–04>~p|ü€¸Û.|ñ¤œ-§¢©º4ò‡®®XqàÈ‘#Där¹ˆH6OŒñßM’·3g®iëÙRçLטD­mr¢ÞÈ‘#=O Ð\k4747¾\^q7`£Ç‹šRpw ¾tSy•{ÞÚ®ù·_¯nR8 ®_-Vöœ ^åê{¾²…Fš#Ýa—¡æ©GéÉú‹‹ü $Ä{íÀƒ-ŽG"Áp$Óã2 M‡³}ûx[O B§Ž‰FØ8°¿¸›qóz\áp0,³â%–2cì [ AV`ê:t]–h’¼3a|Æ„ Šò«Ve/_‘µlyN¯éûPÁ—A„uëö(*ºòÄÛ·¯8&3==Ý4Í'NDoµæ;gxïý,/ÌÌä±Îû» Þ˜?_ÍY|uÜ쟛m½}„[x¢p'ÕDrõê,ãÆf2Ž~ýªx­~ý*JðÌÚ ìR‰ˆ¢úâÍOæÑøσžÞäÕ±³_£^š1,X8÷½÷æÚ~ä¹ó{ßûæ/vÇ|¿»™|ß^~Õª#R|Ÿ={(QT¢¹3ËÜ]I*±6k%ŽJ¨ÍbWNãòi+/ÍÒܹN…Ã׉¨sçÎÒïæ rø¨m®T}ßÞ ûö^pqtíØðLžpëèØ†AË‘G¯YâéSO=ED@@wCÓÁulXYqêc£Ï˺lk A0©dmY•o¡e_ë‡*¡i¢~jr8`^¹\ºJ¨Þß·×ê ËñÅZEö1£3ÆŽÉÐ%"5Õ(8p:¬RK –“ JJðªDiÛ³&¼óþþp$8szîUKº™†a†i‹3ŠÚ‚Œ¨ Z<ÿ—Ÿö‹ÿ~{S´ÜÆöåa¯«v†YrMù±ËE¼fÕi“&fLšíw¸dIÖ¢ÅY§kýó"ÂÖ-‡/kÝ:®OŸö;W}IÚ¾}{Ã0Ž9"ÿå—ûjY-sñ"Þ]àÅ3™5ÇœsZøÞÜ ßî«cùêØ_Ê#ðê¸ÙþgÃm¬ZµŠˆR’Çs† úûòåû¹`\°1ù4VP—2cœw:®%O#FBºðšŽÍŸTÜ1y­×Û'/áN&`Pɪ’*ßK³Þ…špH„ƒ¦0¹FUòýàõƒüìóìÏ×d76ãéqš.3gHÓ¡é8¶Õ^*¸UFÉ)qÉÉqd'›:Òi #h"¤Å;Žˆ„iÓA »+1½z¤šF䫯®8wïÕ_ /(¹ùŸo¦¹QVMLÆ„lK¾ë.)‹‘¦“̈×8¦LΘ6%S¦løà£¬ìììsg«ù~X¾ˆ];O\¼yªgÏ6ºwï^ÃÂ;w6 ãÀ10ˆ\¾Íöápä'"÷ çJWfΜ9¯ŽžŠÚ²”Üõa–óšhûØcÊ…¯¸ 1ù‘†)¤ºd‘½‚dQŒáùÙ3ýö;ÙŒáŸþé›wáïñ‹­©-YîHp¯Î¾]ˆ·KXo[ÅZ{0 šC_¹_|¿qÎê2hÃ]w‘梴ÆìàÁƒD$›Z”•¥8#wÍú¶/_¾@×ÎMºviB&•–P¼Í&!6'kÐ96mÇ#„øôÓO‡Pmd4Û>‹Y>p ÃâûÅ% ŒƒI0ƒ`UÌ6ŒZ“žÖˆ%„¦Mê…C"ÿ\0ª8hGƒ1¬\•µúå¶:ƒ[”QJ]‡Ëÿñ°ÓI¼y«ŒuRâaÕs©X«•Nó—w7üèG±º¦a˜† 0PÅ›i¦Yñ½%&¢¤üVIq°“g¬íÙe>ÑñЊúÉ¡ä‚ËW޼|wònFæóÏ)Ê/|/[6Ë­Òrrr÷î=sîú‘V­õíÛ×9Ú·:ëÞ½»išmÚ$0°m¹GnëÂ_»†·þ‘໯¨yÓ Î]¸p.cÌ‘¶ÃbuœQ)Ä[Ù22w~ÒÄLÆÑ«wU¯g™: ±^žA.\¸véâ ú®èĉ|ÙYéW1Ù\NÊøû[Ùýköü-Cü¢Ek-ZƒZúï÷QpÿV ~Õª£Õõ'¸^„À}àŽ„—k×®1Æ5ª~Œu-¬ ·ÎWw¸Ž†Øˆ¨[79âœDÔBÂÝÖß%Ü'Mê“`LjœJSAÈ„»I´5ØÆåИÊ$¢QVºGñ]Ó±k v¯‰îd=ld@†Hž@Üɤ¢*•ÙXÖ¨›CT%4o– ™gón±`Œÿ~ür–fI˜øLÆÄg2,BÓH× kÐu4§°MÒë×Jn\/… ºuâm%ñ€»¾.XÀ0*Îtæ¾ë«‹¦a<Ö³¡ÃsÞ¬™i„ssO;ך={:c×ä¹Ó\°YV•2Å=çcœ±ÄëQÄs šF\#îˆ2†ff¾0S‰6YYY•y”““›žØüÔåM›Ô¬sUJv5&§!ƒº3Æöì¹ÍWŽ!ýÁ÷3Ç;ó³çÏÏ–Ÿ 3mÉÎg¬œâ-³"™ÕFB-ÍcEUY÷î±5S ÈË»dšÂ0ÍÆSpÎIбãç%ûµ³1–a™üóÏΖ;ü­@¼$;€3Fßžï÷¢É|MvCõÍgîñÜSé‘{vÞ /DÉnÃ]ÓIÓIs¡~vàÀ8®Á‰(>ÞÝ´qw#lHq榱? w( “DãˆdÄ5fŸ­¦NVƱÏç#¢U«V=1Àrá]à:¸†½Žú»ºØ ñIc%Üa€"TøNauo°AǨB« "!³´$dóýäI,þXýê¦LÎ|vR†=}T·2Oä­Nqˆ„d(]¿VLDT·n¢ÍSù¶rân¨Ï›·È0Êö³ñ•wÌ0"†©vÇŒ­þ·""ç¥Øæñ„ N¯‡d IDAT/]ºÃÓÔåmæ²´þ Ê 1»"ßúcˆ»’½puÍŽ!“U·Õ¹ð¥3^y¹ Êçää^¹ròjùɆ ëôí6¨–Jº´¾}ûš¦¹ûàŽ'úvbŒ/Yrû& oý#ûí·³å.9« ¬÷gÖzâc^¹À‚oØŸ¶³NUãèZ‹U]º‚qL›– `ñâ¬'Ï›¦hÕªq‹æì·›ç¾h…=H–V•T•­+-###3SLGrý£kî3fŒž1c4në¿?¸?Ê.¼Ý6²fMæ¾ñÀW+[É%_ŒF˜‚;I·»P·Û¿?9Võ5ˆ8CQ¡Jb‘p`ûïöbs£¢‡Xúû„ ˆhåÊ•º‡dº¤æ×À4ì_]·.62 Ã$?›”25™"ƒD5é’ÒR;qÕP€pØ$âðó³yÈÎÎþè£,3¦gNŸ–i—2q œ+÷VzîÉ·B £×®‘ ©ÉõS“˜õ Òd xš¸<]„(¢ê °0#ÂŒÄFVíM©›!!bú1ŒûSÆÂG®[ºtoK—·••ÐC±î¬­Ë“ÕÝ™Áw)¤TÝêX`MÉpVêøþ÷2ø¥Ødee½þúë—/o“Ö„þ݇‘¨Òm­ÉúõëgÆŽ[{öHPâ³²²ˆ,…År„+,Sº;¥ŠÃ¡Â+“ã ˜ê{j=Xk/È)Ó !Ú·o&…ü Þ0gΜéÓ‡Ÿõ\:zä,ÔYæö¿ÀÌÌŒŸÿ<ƒ1~׿Öûe5»ð6ÜíGª=l«WŸ^½ú4Œçþh"þŽzßÄßÿ^v¥—ÕÕ«T`-MÆòU]H©ÃöíÛGD=zô°WÌÉÉBth?É­k qóz 4iR(Ü¥ÿn€"ѽmc®;4»>%¶bP‚¾go¦»-ÄË ‡6FW¯ðk®3+E·þãV o¹nû¨ß¾}cvÚñÞûJe~þ¹ÌèEŒ<ýh¤i*óDבx-ÄóùØåË…—¯¡aÃd8CšV¬^«Éå¿þ뛦Y6bÄ÷*ïÏæÍ‡…ˆ |¢]Ô力7" ÚCˆð† ÑNŒaàÀlÄðµqûÒÝveT3°ñxÔüRÆà>ÒZ¼j×eÌ,œûïÌŸû·³ÿþV¶c+? ‚‚‹)Zê ^#ìK;µÇ»2 cë×Û×iY寿þ:1ÆQ ÙíÃ@J"%öqÖèI䂜c‘,š‘Ù±ÖCÃ;tÀ¾}*W´KçVJç‰]fúôáç}—9ˬD©ÛZb"233þï/3k»̪C|e¸£ºù|’ìx²ÌøñíV¯>±zõ‰ñã+&Þ>|sh2¯+hQQç<%%åö‹:¬üšãûjk”Œ˜j hˆO”••”M"\úƒDd ^iôÖ£nJ%Å)˜4iÒ²eË–-[öì³ÏÚwŽî'bŒ8˜©üÐc›Ñq¤`c1†cX2õëý .€›¼yãßoÔÿ¿ÕögHi‹’lë¾u—KNž%K¶T9‹Õ4jØ’Ìl'¢¤¾ÊôÔ´1ðè±å·oEµ?Þ@h4sfæûïg½ÿAÖ¬Y™¬âLŸ>|ûÛG:Ó­k€•î$VÜ“*ÌÛE†Í˜ñÔ¢E_,Zô…Åú¨à^aÉ*øn¹íéx'1~|ûGñw ÷gžé,ÛÑÜ]E+c¬´´@-ï¿aÇžˆç#ÆM9òÞx`ïÞ½*dGH˜† 5m’rùRñòåûþ;"¤Y²ª ºìœ ãð!êÒ5:BàÙgŸ]ºtéÒ¥K»vz– ƒ12œÌE»\ ñRžzý77j>I­‘••%wîñnÓ|ZÃÏwïÖB6»”ûÊdnuqã¹Rž/@‚š6«(jZù ñ¾¶1pŸ;w.€9sæìÛ= ‹Á00s&„Ãe Õ#~Ö,|ðÁàcǾ‰åËWèׯ—q-Z1ÌP=ñ–-áƒÐuWX IhÓõ¿woüx皯ºu~ΗÓ°®ªÌ¸^>Nבt^5žF~žÕJú+¾Ì"d„š–æÎ] #c»J>|8 úœPd€%¥u%ìÛá¡å›—üûßÿ~üñG݈/’ì–H’Àa!óŸÿüç·ß~û›oÞ~æé?»îÆb’";€‘#ûp²«H¨`Ô¨fÍZ!wq›6 b3Yôw~SŠÈª.¿ñÆG-ZÔJv¥þMwî‹pûCøRôdn²?_ß•‘í,TK­bÈÂÎì"{®¦ÝK¶nÝ  C‡aï2sæÌë×óô'ýÂô‘ᇠ€%Žd“gd¯ËµÞ¡L·ðZ;W‚t„z7nFsçÎ0bĵœ9stî8Bú”ƒOVq1¶®u;[»ÊRé’.ÄG•›ì/¿ô)‘w ÇN!!435‡ò¶lýÚYÁ5Ý‘ !Ù"ÿ«ƒ_·/ZD„Ü|ãZ¹8Sµ95í>A&V¬x@×®VOæäØW ²Z¶.[¶ @ÿþý™áëaÐËiÎúyyý¥Ýy1)18@ºgÃgqz\×ÎÏxòʰUÍX·ÛÎ ÄŸñ#!Ìøàßïxå‘?Z(0€äŽ!”Tp?vlkÕªœ¾(¾óløaøIÚñ{…˜m¥Ìà ‘:0^ºs€Áƒ«Ö¨FMTÝØœçœ¥HKäí·ßfƳÏüÀ¿¾~ ±]6Âà®tú4¾úê-ÿõÜ_¾üj"¢Ž¬ù|7€¶m"âLFUæEÔŠÖßyçúÓŸŠÜ0ëæ`æÌŸýõH‹ ”KU– çM½Ý‘»"»¢ümSéîŠìŠò%Pùòå+V¬X©R¥*Uª¸ƒ÷@6Œldœ:vìä‘£'9~ððñýä´¿Q4<‰T8Üg̘‘™™S¥J[]vÛmkr“"»J‘ÀfƒŸ‘G«FV5]c•’ìä¿[{`fæZuH‡î…æak¬ÕîCtr‹µf4_4\*C¥dÑ'ŸNüìó‰ÓÒ&®ÝòýšMß®Úöñæ-ÿR¡ý »ÿK¶]"~|yÈËãÜ\°‰ëå⤴ü Ó´úp*¸wïþgeësZM›â`è/)9—Z¶nPÂÒ%•ú÷ïíòó—5ibm iµêþbÀ46nšfH$fSR¹Ê4‘_=ÎW;.7×ífØpgäNï›;w=€#zTª”êîX«>Ž•ÁÉÅ\ vŽx¤Ó Ã0æÍ›W¬dmÉ#ë{Šw¨û?æ4ö¢4{v:€‘#ûºáóåIDß•z¾ÐŠ™·o?cy)6aÚ—õ÷ß?/¯ÀÜÊXôÎ;ï¨+„RŒÁ»2ßüío¿رh"%2šzgl™ÛŒxWäë±X°ªU«V£FûRkÖ®^»vº°p•"rèØÞýG~ÝwhϾŒÝ{ŽìÔIÄY¿—[¶laæH¸/X°+33—™»uj-\É NLâòÕ¯éÁBQÎܨ°¼à‘#GJ)gΜY£6yâà‰ƒîa'#^Ù&§·@¤ÈFŸÂO‘ (ùq줷µlùpóæ©$ë7Þxã7Þxå¡?jòôàfOOøú«½`Ê¢÷TèÊ9•½YãLFY’jÅiíÚ•O@³fhÚÌú ޏf,±Ã;w_çÿö9q{Áªø>;›̨Õ|tÀ46nþ.`Â0”kŒ:`SBJêÄ;W÷©af²3¸¸«(þ«lJ«†‚©zŽ—ÈŸ±® 6â‡nš&óyœ.Mês SwuøB5{vº÷ÈW«Ú½-‹ÜU¯[3óÖ­²¶æ_ÿ)z:¬#)Yš¸z“'OB$$ò®ÜÜÜ÷Þ{ÀµK1™?NæŒ Û•9Sâõ%Kþ2Ü-Ú¯þ¸J샷(±&#;˜ûL@½Zõ,³„X+K{÷î%&+yضm›rrÔ¶:u Û›êÏ'¥,W.QÅ›*Æ >í_ý¡CÛ¤¥íTFü²Ô“)$‡íM8É3®?ÂÈüÑ£GϘ1cÆŒúŽ6$s€Ø5)ÿìNTkƒ2HÏAŸôI*À¥)èö|çΚÕh£¦}.^·eþü-Æu°}F†šWP¿^küT%©KÈ«§âb‡¡ î,±båÛzôø³4ƒ,#Gaß>¸î‚Çà)¯®¸þ"ƒ§x*Z°Ú*//ûÊÕÓ¦)+V4«U7·n•S%̸J 3Ïí\¾þãš5ZI 2}Âd–ªX³diJ–Ì5êåØÏ(Øœq·h =z43ϘÒÆIõª8!<4/kº3ù‚pa7$!€Ü¢¢ø0©cÞŠ påÏmûî¸zïúõ«¨?Û)ÿ~À‹}_ó׎Sp—&¤Å;’L*°]ºl"3÷êùg¸‚ï'ûuw=wظÀ©Sì°§O£];ØIP¦LBJ•–&ËS§÷8A^W3#š6mJD'.0sÃ÷37| `ß¾SêåkE)&cæ²Ev¶<Yð䦋+0…ÉÙÜZ&;v¬išÓ§OWd/:Š·Þ‹ŽyU‘ƒ‚®îvØÞ§p¸«Í?þä­"¯g}~Û–Í[ö¡€ÁX·¾ý!‘3ü•"Ö+JÎôÆ &0‹©ßM)|RÂÌ™?×8U¹·=RbàÀ;w¥[Šx÷AƒšÞROæf/ó,¸COž ½,´¢[Ú·oß¡C‡H²Ã†û!­-ÿ„ƒÁ»jëvßê¸4`ö3"wÒY#è—.¨íð©ÛcÇŽðý÷ß{ãá‰ƒÇ Ýc5ÁÚX„K{ØŒøð#±KHzʃt êÓÀO“w1£aƒû6Pyoä 7P'N:1»UH€œr?ÿ<@ß>oºCð‡±)8ƒTýñ߸a¯ê¸;Æ‚*7¬C‡þ<¸¹JåZ÷U­[½ZƒFÕtÔèUïkU­ZÛ5;´lÑ»nÓÑIZÙœ+—š×éÓº~¿ötnöp¿V>ÜqPîCzõ>räHõÁóûgŸÿÃsBÐ×éSܬy­-j±š xæ™g„Ó¦M3%L “­ŸÖ(€#QBÑ”V?¼œÀSO=eF,íp³¶æ‡ßESá·.O¦xA‘êûR[f^þñ6ÝX½9€>úH3쿾ijÐÝ ‚DÅÑ÷£ ÷gªôx½mÔ riJ§2mié!Þ{éî6ªJ€xé‡Ì‡H€–­ ´”›=† v9p‡ _b8*ÉfV˜†mð¥³YìG!ñ»fW|u—ŸŒ”zߤ+„Wˆ·êôÚc­D¸² HWœÞ=õWO騑]^9»Ž `ùó‚@0m#ÛŠC%9eÁT1á~¼ XÜê׺‚$·9³}û2:ö?s†Ž÷ðÁÕsçXšåb1E˜<={¡Çý*¯5jT,ßà€anÙ¶`ÛŽ FüH1PŽpò»/¥×ü¹ÝhÖ¬ÁúØl'˜;²ª;W5û¿QEÞ`] Õ×J`À¸òRtO!¹C% Ae잌[êt©ä™ü¹ »4g`ïÞcÁžßB’™K/QòüIŒþ"úç?ÿùªc¸·jYX,ñwßQÚˆ_´hÿí„{É$¼!E%oRŠì°á>mÚ4cG=¥2=„]6RÐ-aèжdàÊù¬rcR#_uœww ˆ¥'žxBJ9}úto<ñðÄAWF‡5=Â!ó N@~̈w—€Ÿ?ËÖyG„=º7ãàÁ³Îý€C“àð J·GDÿýÃD°Ü^"ô‰«sj’{Ò‘’›Œ0»ÆY‘T?ÑCخݬ5MÓ,[ïþ@ÀŒí;–*Ä øà÷!•ñ?/¿à­·Þš;w}íÚí*VLIÛ?;¹SA•}!gmÉËÚ’¯ÂC÷-‹eÎHÔŒVh÷ʧWÉ®§·»ëÛƒaݧ˜:u*=ûì³°Ïs欎º“ïш#ubôd¤,¶ø$d_+påävñŸ¬zD7VD¹|öÙgBˆQC^C2N€×^{ˆÞÿýØ €S}íµ×5Äc†ý^ññÇ»Ws ÷G*ö(òÖ'ñwßKQ.Ãý¶Â½TŒø’É ÛܼÛõÈDÁIcå’a -mGäK…XðZ¡-<õÔSÌüÝŒéÞdòÄÃã ‰â…fí׎ éøbC¼Ã÷ùó·T»^ŽÆÍîc IãªÌ8xðlrÇ„äŽñûÛß<=î ;t%wÄX´d"ÕºZ™ž½¢¼—UÈ0ô“pþ<{«èn CæŒK—ìáRñ»d–NT¤:wAÇN¨_¿ªiÊ”º][·~,`;w®0L2|ðû‘“ÿó»ñ/½`ĈîÎ=~r§„”N jR¨Õ–.DdÚ93¦$ç&&ª„‡(.ú×ÉÒÕÅÄæ{T=þx/‘ˆÏÞžO@rû˜…c·ù~3žL¤r½à‰Òý_îPîrʾýLj•‚ø/¾øBñâ‹/K.AšiäQ>§ÜÛË/¿,„øôÓOÕ:Üo¬È8¥ÑåÂ7ò½TBx—á~"÷;‚x·'£4uêTf~úé§µx"L™qfxF—)šù8]’í@ž‚SX‹¬Ó¤—¡¸ ¤BxÝ]g·/€Ç‡tü1 ^ù3óço¹/³l³æÕ›7«Îö€å©¤+'­jeÌ,%[¶Œ2ÆaáxÁ¢‰}ôMÇ3ém´í")¸t‰#Ãv™çÊj%¡aÃ6̼mÛRQ¨Ý®=7®nšòÀåS­[?âví\e0m£æF¦//ÇÈË6Ð À¸qã`ߥHî” byuU‘öàªtåÏH%/‘—2§E‰oo'$„Á/½rtÄÊ´‹õ.ÕMä›7Üÿò—¿¸/eÊQŽ [÷–íÝÜ}Eÿꫯ„Ï?ÿ<€Êµ­£“Ã8F9›° –  JmðÛßþVñþÏ»á©21ì?çŠø»‘ï¸9Äß%žÌíD|˜áîÈ]‹ì¡Q|T±Ÿë¿QˆÂ;’5â°É‚ø>nÜ8fþæ›oÔ=å)þ>Ò=ªÀ$»x²3býÈš¦­]{¨ÊÕÔ-k¸Ýç”N *Krþü-ÎÊjt™íóÂü zìMEÛNÑ¥àvÁÞÍ™0r²±Öà§Ül>Ï6³i'ìñ§Úº F®)¥¹ÿbFëÖùÝ»× \½œçËçü#/ÇøwƒÕB|ûí·Î’;&$wˆ·ü“Ýþ»ŠÁ£Š<¤‘/I Þ®SLÖuE]cJQ‘gÏN÷J/nÁhê•s¾T¦M|‹ž ‰°oFÖê\_ýµâ¹çž ®¤Ns‰Ë¨?™ŒƒÒ©†d)%¥6@{ܳÒs"o#uã ’"†î¢×¼T²êcwÊ“¹ƒ 3ÜÝzöÙg§OŸ>}úô§žzÊ Þ¥ ÷‚n±¥ Ú6-mGZÚŽ¡C­!û³;ìXÛžÜä˜øãÆûæ›o¾ùæ›§Ÿ~:r‡ ýÓ¦MS¦ŠÂrWò… Ízëi±÷o­zü7Qûã‡?×l9›¨…Õ«ú|9u}µ[v¨©g -f2lXÇùó·¸OjŠ)‡’º&´Ÿ.e§XY4Åf™‘›Ã ‰´ÕO‘)!³€$«àŠª9AÑöU£F×™5óØÞ‹ÇZµê¿gOúž=¿T«ÖÂGÉ)ɉžø¬Ü‹—/^+W©¬ .“=xT cõ“þÜT¯2g¬‘Õhñûõ´ŠìQ/ÓÒž K€ 0 â Z¼×œ9«çÌY}LÓ´Š×î{äbøÐ–—@~\º CFw}ÃU¶®œÅ¥Ó\ÉÎWùä“OT=(ç§ôsnn>Àž›ô²ZØ/¶ `nÖ‘½˜0aÂäÉ“'OžüꫯÆr“&M"¢ &ß“+:Wa{¥JåÔ³Vç¨_—”>…™3™¬ÞÂJNFü]¿+7Š¿³žL¤nC¿`Án„îazê©§4MûþûïÉ©°h÷)Пñ[âŠìNœÖDÁYNÎä‘BBøgžy†™Ïž oÌ,¡•;£Žê"´oãÇê5z1êÞV­: N~펛(sˆ¢U2QQ|°@®+š3÷-ÇýE´)ºÿ(êÖ µà]ƒ|ã òò˜JQcîø]¢¨éònéžóš~>¾&Ê•má÷gÏîK)“¬†[órŒ¼\3àƒ?žxâ "úþûïÝ›«ø=¹}¼Œ|–…&G’‡ÈKäðDyU¹:Œù«ß~û-¹« ¸¥\šK—®`ðÌ™3çÌ™3oÞ¼ ,^¼8r}ÿøÏÀþ‹X¶bëüå«òD^é†íï¼óÎ{ï½÷þûïOžþ—¦Å™a…ð^#ƒ¸¯ ãªáËG\<ðû,þAH0üJ…”fݺͮ]»¼eËÒNc f‡ï6oÞ/ßϘըaëV-{îùuÝÞ½[š5ïÈ@~ž!~Æå \±J”Pz̘1³fÍš5kV—ö#ÙƒÀ5 MXí#¥HPÀ=É É @¤Štœ³³QBBíGÇõš9s¦û¥~øAÓ4ΚÐ5¡ukÓªaùŠm9Z6€‘#{Çr–nF öx饗^ {)k].ûdÈ^Ÿ[¦{H¡=¡±°ýM–8°³xïk9ï$˜¸|åêÊ Ûà2ÜŸ}öÙ}ðõGk?¬œ\E²GÉ]‚ð·¿ý­Zíòi.è›=ú!*(æº{´xñ=Ö¤ -:`W¸»àîè& LFÊ÷ºRªÔ#‡~H“LÓJ¦«PàI JÅïC‡¶½ø«mÜkÐ<¬ò½ ÀôéÓ Ã(âEÊ ìIôÊ}8ˆŸ?+€þµZX†eQ3!ß~ûm)¹}»§[´¨­ÒÞ§÷€1£ÿÒ¨˜åJO´‚îÍ[–Ð¥ëALvž(®B >Þú+ËÏg) º”—a0¤¤ýç¶Èº‘É@çΪWé}çÍûeß¾SÍšÕ>¼Û¾tüúëo|\³fíHÃ¥ÌÓñIÞ*U+ /ªÖ&Š¡£Gv6·ŠB·iäÁð‘ô#ijI-C\9 IDATZËа8keàÌ@r¿ðÙùûתŽíÐã Ç±'÷Õ´nÔD¶HM:À3Ï<£ZU8À–¥Ä²õ? Ò„¦iBÓ„&4¡éš®k{Ž!/¼0:ê>o^îrçO‚Mø¾ºÑ/TÙës8x@‡ß„°%ó‚ôå‘Ï€†A’±tõãÇ/ò&Ož àá>¯ªFcº×2¯î?®à¼ðg¯ÏcÎöÙÌ,R)¬Ä¬ó3à—ÌLÂzF%\:ºøŽB¿h‘êÅÅw-Ü•Jñ¶'Ù]°ÐÌá]‡Hú×.3ãâïoÜ*}^‡æaÝ‹„j`ÆŒ†atŸ^΀ åû/ÒÙô¯Ý¸èi2&N|@ëVO2£M›ºÓ¦OðÄØ7ëÊöéSÖòæMKAèÚe©–xäæ;Ò7¯£kë¾Ru)K0$¤¤Ý‡7Qrr®èÜy@µjE¼ã¼y¿¨…áûمúïMÇÞ½›½ñqMš´>wáx|R|µš÷‘‹u6ÐG¥¶UµšÛ¶aäÃô‘ôƒ ´Šð<²×æØc¦` ¹W¸Õ»o Èá{{°zÝLcÆŒ‰zðj8]Ùsæ¬î×¹Cb|"& f;Ô!€°ù×uº®íË8FD;7 w©W¯^'+f¹ù~çÅ!*â³ÉU·hž PûgBøsé”ÌÏ'ŸF¦$)±b]LˆŸ2eŠ4ñpßñšÆºŽÌÌ«Ieô&­C¦›dÿ’ë|#Êô/ÓýÉ'ŸH)ÜÝ|WÓPœzÅwµÿîH‘]QÞ-÷AƒšÜåp/E• î¬iúòK"§P)eÄïÙsÊ)æÏŸ?{öl¥wžêðV|=¸xÉšÓ¿N uرÀ°<ñöíëJi~3m"€qO•î°Ms•{-x+ÈCú¦•ê¯qî•pÒf*ZiK,M#ËJ¶(|ÒBáXUÃæÎ]ß¼:tèä÷þšŸoäçŒ>>œÌP#ÆQŒ“ƒÉïf´pŽt" ÔB„¬‰Q€JÔ®V“ Éœ6m=ýôÓFû®ñÀÞ=s³Í³§¯~k®–iØõݬ)Ƹœkœ¿á+_¾ú‹/ŽmÖ 8qâÄéÓ§‹8e±éOú½ûֶ&3›ç3¢žÀ9¥Ù›BJ ×=.n?[3N¾páâoœV©Rx!¿Ðt·(;x饗†?úò¨!¿3üþøÊ«¯¾:a„0¸ã^á;"¿hÑîwò°bÖ͵.X°»ÄpWÒ5MÓt²GG‹¥¡CÛáÀ³ÂN¿ùqõ"õRAAÜMÊs<•°pñ»‹aÃ^P·Ê´‹qŽÌĉAxáù7…Àž_¿cæ-ªS·„#ìä¥`b;œÚ–útê `Óž•°`5‚FÒdi–ñUDQ©Ì îÇwSpS½.hÛ¶½Ïgøò¾<ÃðÛ³[ó`äÈ‘BÕq6›œaUÉÑ+Ð’Nä!çgØ«»VØ“›Ô´Uמ#wõí·ß !Æ—w}yä#2‰Éù¹æñc—LƒLÓšc¥àž\?lZ¥¶u ÷–-[6kÖ¬qãÆ 6t‚÷sçÎ;WpVcÌzýõ׉HMUSÞ¤ ÓÄÅáˆ/Ó5Q·hܤ6 :|Òýª3–—–1eÁ«Yn§NŸ÷zô¸8½R¥T=tL;gSžuz ³þ•Îg°”j¾žõLÔN#÷ ßݲ=™{îJ7ƒøâîQõ與u¡­Ù¾¬ )”E‰a— Y¶á‘@Æ 9rd‰§H)[¦Bå-ˆ¯? LÛb—q_|ù€¿þõ/æÏß\²#Q4߸i)€n]\&”käMß¼ îl!~ݺå*´—•@–&›f³&í’}•7lXõ]æÍûeÞ¼_Ún­Ñóhx_{'„аF¾Ü&à7}ùãÇN˜~2|8¸?<„WËM›» ÊGÄ©¹;óá!XpEäÏH†©‚wEyÂìÙ³‰Èq}÷ÝwDôä“OÞ¸Âþ<øßý0üT©bE_¾y`ÿi¹« œÔú˜={€‘#{2šZ»ví5j˜¦iš¥SóE™Ôï½÷^ÍvÙ5ÓÄåÓgGÝŸ 4n\›v|Ìåóx élU̶¦`Í÷>ø@£:£<½jµ •*¥ê:ë®4—œ-yNð^Ð/œ`«™£jûH£­|/ñ]…ðï½·–'s/Á]iðà/\¸§X[9aûÍÀ]©OÇþº¦ÿ²+½˜³ípãÚµ«âÇϯÚú3 t“SˆæÏߪà>lXéz’³ØÙr±·*\¼#hר|}:öu<Ó>ûÆÅaýúåʵ×$LÉ&š¦G½ãv<™z¯EqåWÂVp#Àã/µôûÌjUj¨Þ{††NDóçχëoÞpU‘ ÿh:H³ÑüÓ©J0P¿atsæûï¿'¢'žxâê¶ÈîCÀÃOf¦šµªúýæîÝÇ(ßËv¬rà^Ôé€Rä;ÜÓG,É4a˜ÑÚ'¹|’FkCÐÎY‡Ô+Ie…îá‰{túƒi²"xA’ öx´ªÕ*¡CÓ‘TÎõkJ®‡ˆbÎ\<ɦ¡îÈ1…Þ{ï="ЬA/ñÝQqÙtWiðà–bGüMz2a"Bv}t]Û¼'ö†;Ö†$Ъeõý§6]¿žûØÈGKåx¢ÊMµà^"©¹'òìJ&ÆuBICxÇ·rÞ].|Ù² Q ܸ8k+YѪ?#¡•õ‡gÏ„y2_­®~q5óëL÷jˆoÆ Ó€iÀðÃðÑî `ذaD”––6lØ0!Ä‚ T(ÓŒ–ü®tR!<ô(A¥„ ˜ gjT$ßgΜ)„;vì…³°mÃGf€Ì C•ÐB `^ÑNUlj…íˆîÇŽ3M³víÚ±¬‹þøÇ?ѤI“œYªÝîÅS!ßc’JÐAas˜”¯"œ9ØJ…‡ð³f-@$ªV«¨àîѹ\• „s¶å‡ðÔ9$Cìò½-U¦{†ï‹Pæûk¯õ`Z4ÿÁºyÃ=ŠT8©éš^¼© pö¬ªP¡LÛÝ£V+¥¥Ya{8ܵt0`Ä¿õÖDÊPr×)1âU’²óWœ†JvðÞév— ú÷ï`ëÖåöü&C²)tG×õ«®ß_î!ïèóÌœáî'ŽøÖ}! ˜ÌŒ íØâÒ¨³ÿ=ÿ°t„=ÂdMŒRn5aÞ¼yDä´S0kÖ,"=zô™ÈCÀC…팜²ûÌè7¶€¿þõåÉ„©tƒw¥ &XøÓ$–0M;„7qõ\è5PuQhÔ¸íšwØy±j-!t»Z©YÑù>kÖRøZ‘¤’Œu •k œ»#ŸÈ}{º§ÌsÒ0`˜Áà½VC¼ÿþûDî¸Wøî «*‹F93÷.âc á,°^-M¸ÛÞBçVÝt]Û´iSìnß·qó¯ëôîðÀÌÓ±Ï;‹]ii[¸G_CO˜YƒP©dˆ¯TÙT¹ªÊ”+çΣqY7 ؈_·~L¯d˜BCݺ <}߾×÷Y†;"à üóåí„\O»qcႎ­]?Hf¦†2d-\¸ÐỊßPvY†Œú•»òLËVáæŒãÅŸ8jEî†OFuËb‰êmQÕ5/–qÈcˆV€úæe?[õ5aš`¸qEºÖ Ieiظ&³f-wV¨]ŸT.Z!êaªÈýüÕ¹DÔ³Ëï…MCz¡'Á~‡òIB‚÷€AFNwI« j¡w ÷ßä÷B¦8Ýs*ñ î¥b¸‡É[ÃJžéв³®ëÛ¶m‹e«-[¶¨ß¼ûÛö&¡CÛHKÛ^ЦÈ`èÐB=O:ð†øü|¨uw+âø\·Ï*xïß¿\MOXyE¥þýûÇÇÓºõËLi2LMcMƒÇ£{<ú¾}'Ëù«#Ü•Ê=U–tN$nülµ~ átiZˆ7ýøe ÍÌŽE‹nEüàÕW_%¢WNQþŒz¨:A¹ïÛQ£FµšÅÕq#¾aSç¤)¸W­0@zʲ×=!RîÛ™qQÞ­+NðnBJ’õšaÒ¤I°oD¢ênç{A3›îõ#Þ†{Ë[ô¾N~dûöí5MSª ÑŽ;tlÙ¥kë$W@)#>-m€¡C;÷Ø^¶TrßL `ëÖå°ƒôBÔ¿ÿ¸8>KÍ¡±Ç£åçû=ºÞ¢Eín ¢ÃÝzSîЉ4d­²*’G"¾ËCªß“¦'U-‹ÈÊJ4$ú îÜÖpFVÏ]ÁÙ+Ñ-xg|µS¨‚!C†A£æÀ^6òð‘¡.0HòeêvFN€«\°šÈªœ™¿gÏÓ4›5 ”0³bÙ.&?ž™X1YJR¥ö WŸØ:Â"; âj6K¬;kÖ2g?Ê¥éÜþ÷Dôá‡V¦Y³–)¸5°¢r! {P«^½y¿æ;YöâCGV¯]’êÆÂm¾Ož<™ˆ ;±dÉ‘%KŽ”ÚÙ*=©â‘(¸2Áâ,Øs«áXU>Tt ëº^¨¿gÏ;v¤ˆ– ¥…xî±nàM¾bçÒ¸U‚^8cª¶?KÕ{¥0øÂ…Óš™×oÒŒ‹÷4l\M•{9SðXFêÐîЉ4Êþ%7gS¢!¾û£0 ȌҗÀ£>*„ðù|}úFëì¡Yñû© ÈÎCJ2‘ D¡|g¼»'ûiiiBˆáÇÿº‹ùdøÈôÁô[ƒ½ÒD£nh`—\vJ8U Š¥°àÝÈ„1èfôꫯj:ý”>E%«n*W X‘ݮϞåÑ´ ͲVÔ)rè?jÔ€•?í³'t4hæÌˆAL{'$¶ füÈ0Ô™?®œBDE–«´úkßmˆwjFîÉü Þ‘Ëp¿•p·ýDo%hÑ¢…¦iû÷ïºæ¾}û´mÛ°®Þ¥y$iiÛl¸GëWˆâÒÁ@þÍ"žóæÅŠx•MçôPݼe)€b-{{ß}•™yêÂïãµ”²åÊ•/sáâyÀ ÓNì“¥<šìÀH#ÈÝ‘hˆïýLÃri–/S‡máFYð!ŸHXÁ{ÀDùòð›|ü C´ÕL»sÓâÅ‹‰hРA .B :tûVòÉȇåÉØ‘{“ûƒ›‡ÕŸq{ïðÝËàÈ,ÆÒ “Là áýî7‚­KºMvhаf‹”úî¾u;rZNΘvÀ¨QýUäÎÀîýÿ¸G·—›· {þŸkç.ÊÛÊTÁ{ÊŸ‘Uˆ‹&ànC¼¶7Ž¥òû½ŽxÂÿßÿ»À!-o5Üxî ±öš6mªëúáÇÝë;vìСCZ¶´Ž'ꔨ› áÙQ¸+ŧƒ¼›Büðá€XOÁ8¬]W<¸0MÓ0€øDY¥j²7AóÄi¤Y|gàÄ–·MîW†48pWá|Þ¯ùyû}‘+?0Ò€é‡J‡wR$•E" Ñþ#ì7Q¿>LLòEÿ4¦„ ôîc]*-ZDDƒÞ¼ª4‚òd̤‰–½Ñ¢·µ­c¸G Û/N¼#âMÓô]3}0á©òªš·y“?~y(åuü~ RW¼/[k-³»Îwà^œøV¤sÜfÝΤþ0R7lØPÓ´cÇŽ©ÿž8qB-8Žgà*@ðV²«’!Þ ÛKw¥„tàf»‚5 qqQVˆ¯å‰º `Þ¼ gÏ^¸aÃJömxto¼N“m¶2p¬„&Üp‡4"m:"4„0`¨•1¹d$$$!FÈ¥qÝTX1$’!( 6]kV”t¥j¨ñÕõkaø`†Â½íÁý‡în]þàÊå÷.W~³2bˆâ7lØŸi¶kÒ & á©ewÞyçæ)ÿê„W@X¹îCÃD ,~·â§O#@¬p?¤C/¸?kÖ²J•Ñ…+Ó–,°¶e€ˆUfdø»Z‘)Œò -ÂÌË|—&V¬ý€ˆb;¾ß !¼Ëp/^ñVU\ÌéÜtiáÂ=Ê|ÿŸÿy€9hÑÜrE\Ö#þÌ™3êիרQ£àË…Ö#+.âKèÉDÕM_ÙcáW¯þ™€Þ½\³¶xÁû¼y0ËJ•RÔVRøâuo¼.< ­™àȆè;)Ó-1îÐuèX;¡râÓ# Ê*é"(Aضۘܪ¥F†É†Éʱq¤¦­>ø~øá'¡>9i é‚»4ÐéAtx0¸UTÃ=súµÌ¯3¯|~•tªøZÅXN]Î)ä]5MÃT_´7ZÑM!Dnn.€wß}WÍô)±¬±h5_,¬± ;¹…íDk,´u%«˜„CyM ,KÒÂþß A/¿ü²{—¾£~Uß& å]òÈ6g(ý—5^yå•?QpOwñ.ý˜•¹Ü›ˆw†U•E£œ™ÛƒøH§¥V­Zš¦)¸GΤBùŽ˜_rý %¦ƒœ›wixÞ¼"f¨ÙLDðz‹±s÷áûV¨|æÌ™©S§8`Íæ5ñIº7IÏ÷ç ¤34@€ 8¸.ú®;&DÂ]-´nS§VR¥0ÄÓys „ˆ7yù®¼?hdHÒšŸéòîú–ª«5N+„ˆó>${ÝF×GBŽ3Ò“¹¾àƵ9×UfiTáwåÝëÂge€MHSJS‚W@Å|"JNNvêj½÷Þ{%¦üøñ ÐÚÍÿ „ºüñ¼Ê…·È®îº¨nýêÞ|ó3X†{g˜4mŽj„‘u2|§¥I$åÝk`˜Xµáÿáˆý…¼íB|± ÷‚to!^Á}ðà– î·YZD^Õ«W·Ú¢FŠ Ço¦›5Ü RR:ȾIÄwPâ DX½ºÁ»wÏ>ûlÕªU|óÍ7—¯œ+£{D\’.<ªu†8ØWPñˆ¸_ºF$AmÚÕ«Q¦â†ŸC¢†á‡ÏR’´Û3™y’~Ùä3¥ìÒÉ£º©tìà1$oØêwSAe¾ðûýš¦¶àî‡@ÏÇBŽnΜ5n¸g­ÊÉZ–}ãÇ,v¢§FåŸ+ù±Âí®L€!MSJ3¾&äÞ@NÄ4/'ÿõ×_w¦q¾ÿþû*7¼¸êÐú¿¬ÛøO#€ý¿†Ü‘e¦‰àX«t›ªÜk23C /Dxð‘ðàÈw2²·‡Q^ ¾q0‚8r­àÝi³Ï ×÷ßKæÉDÕ=xÇ“‰$ûí á£ê¾ûî‹Úc/–b“…‡ð¥c¸¤2éÀ-G¼®ãÔ©OLpŸ7oƒîJÏ<óÌ3Ï<£–gÌþ~öUqIºáU…¬Ü``Oz”Ý&´ˆwÃýF.eçâÔy=É$ÀÄL0®…Ìõ„EÀÑc¨fýõ«V­¬~S²)YíJ=$³dvÝ&cèp,Y²DAäÀ½ß<04äØæÌ±.J?Þ+gS^öú\u¨¤¦P @£rO„t±pËAüÕƒ°*™k·-—ÒìÕÞúfS”‚ë—qí®]BæEõQB²M^{í5‡ò“&MRÍ’bW÷>V–zÀ@È,'¸&([á6H ãÄÙã'ÎBàÛ¢y饗ˆèô™Ourr¢¤=N‹ÊGkÌà·Í÷_¶ü“@Å ÞÉ÷Û—Øp¿wå.(l¿ãˆªÓ B|){2QU& dݦ±Ö‚¤†UW¯¶¾e7Ü=ýôÓãÆ#Â¥K—þ{†á…ðª‚3v¯v®ˆöã[&5e’éÊuÀþ#Ò›Tà[YÒÈ zíññ€Þ½{ÑêÕ«!°jmždîÙ3ÁaC’’¥´±L‰åË—kšðTpd~T î›wظsîÎ| –¡hTvD´ÛF—ú´è]Á¬±{×QØ •Ò”lxk†¬–Ze+A.ÁØã¥)&½ÿ¡{?þñÎÄŸ)S¦‹ò­›þžˆ6ný8Ì¢Q#N ŽgœUןõªCJ˜Qˆ'¢¤„gGŽŸ}ö9Sýgîý)oïß­€¿lý'¯ŒÿïØ?…uÈ‘OÝÄߤá^îæÞnÑW„'sw">F…!¾ô ÷BT c­E„ðuëÖ.2x_µj/3†ïŽW‹–|ÿóÊ".ˆx‹‰,°mY”mÉqiù\±<ê׳ [iñõŽžˆß°q?Ì< ñBˆøxá,§”I%‚d)U§ {>³ó¤Û/Wq¥Âëõúr ÓAѪýÏ™³¦nÜ}µé첬1}ËO>ùä“O>©–W®šµnÓl;gèAÄoþ9|ø:^wÔªI>G2Ь‰hÕRQƒÕŽd$~óÝ L?èÕ«­Y³Àý÷ß`×î]ÊZ«6n5ð°ñ¾jÕ*ua8wª÷è'õáO„ɦlþá`Ä*-[Õ°íi²³M”¤!¹xÃn·ÎîÀ¹]Öróæu¼qzƉsBc¡1Š(;S©¦:DDNõ–8¾G÷âðÜñãÇ;”ÿàƒ>üðÃBv ïC0M´hò{"úè£Ä«/%ã乌Œ³d‘=XNÿüµëŸ«B,{T”•²Ý‰¨gU0B4yòd5üp»ý÷Å‹9p¿uïrW!~áÂ_‹ ÷ÿ€þïÿ7ná^RÓ[ˆøB4oÞFµ#Ü=ñÄcÇŽU˷̱y†hA üòCøVÞêË¥ðX©íÛé]:©ÊU¼jm^úê¬eK¯IƒÝEÁH£ƒ‡Ë0äýRê÷í›ÌÌ+ÒoX³f¢wïÞ'õªQgmØ®š~Úσ[·®w)“²r`!^ þ$2=ûu;:±§¶oºã5ªŸ¨]¸xù§m MÓ|ì±Ç Ú\©R-òÄYŸO1 ÕFXìÄÞíè×c|ŸnVÎxá-8˜&LQŠý2NœSoS·n5§´$Ùß…qÅPˆ‹[NDŸþ9\æ»qÙpÂÿ‚(ïÖ¦ ¼uçÇDhÖðw…°#÷mÊ„  äû­áÙ<öX£Â×¼yÝ%ˆwîÅû³2¤%ÀN[¾{S·qÊ–£Ôt0pí¶"^Á}øð.Çw‰,Û‹F­ÚÝmÙ:w×Þy ò2é`ÝŠâ×. ßÄ[EWLoÞTøMÞù«„K>ýTíºu²32Ϋ5—¯Ì”2X’·M›Ö€yõê¥àˆ^ða…ðëÖ­BôìÙ@݆ëœmidI㺹yËŒ³qI©õS+Ö?}Á&”óÐHÙîI]£ôU:¾ÉöÓÈ®” CÓYè\«n¥­{;cÍH"òx©j]ÒthtMƒ¦AÖ±°§#õêòJŽS¦|øü£ >8†F ^"¢O>ùÄy>mÝjÕ­Wµn½jadwÆ0Œk¦B¼Âú_|AD/¼ð‚‘iZ“` ¢¼·fÈ´¸­Û?!Pãz¿{à¡¢O‚›ìªÝ6 ßKñvØÞè6Àý.‘¶—Ì–QÅï-ħ¥mWæûÿþï£;ÍmÕíÎìÀ]ý7®FDÓ˜å4³Ý»þáãið2y ¬ÒEl`gì& “ƒ…‹õêݲNlð±HU+J‰åËÏX¶ìB… eëÕ»¾uëfÕM×õ @&$ìBôèÑÀÚµk…½zõ2}læËu|Ógd|?ûÄñÓ ×´BNòÖ€‚WbëøÄv G ‡Öã膠§¡à.tÖth^è–Í1Ö|wîNªÔ¦ªõÈãîÇ]‡®C÷@Ó¡k¬i¬ ‚{v~€”øàƒ|øá?WEë†k˜0 jXÿ·Dôé§ŸVÍ÷ºõªÛ1£Dð G‚8YðÁÌ'e¾Ê»ôÉ'ŸÔ°þË…_ã>üðÃ?üй#?~üƒ½ÇÜÿâè^P‘ð–,9 `àÀð†¿Å’÷›ÙI ¤âwËßN•‹‹5 IDAT8lTé·pºer†U•EƒÛ“9UWû@ùh9†1KÅï*–/`Ë“qà®ä;P”ÿúë¯+pv2#c{ëyŠ C‡ݸ†?8Ÿ¨_hV¢qÃTÙu·øAèÚÙ gj%°båN h»qãFÓä¼¼Úšf^¹([6¾lÙ³:t/ÛùË/tëf}¢íÛv<”R±B%Õ¡.#ã14BÓ&5ÚµÔX’Oœ`0jÞ˜€d6ß À9`ÖZÐ5 šî<ر<å ÀûïÿÀÿXô¸¢²A^xáç™+g¤”dºªºK Īiµ*4U^ŠÅßf §Ö|Øî+¼x<^Î8ù3—/ßÀ¨QýŒK†uf¥KZËD¶ÑN ¿f.ÉÍͯR¥Ü¨Qý¿ürKüfô@°õÏ~Xö¬za O•`1×O?ýTJÔ©ùÛG 0¨þñ(zKÌÜ·Ç+Zð:Íj¡h¾ãæ;=™¨ºýˆ/E¸ãÞỂ»CvûÉ;ÄwÜrÄw¾3¸êá|W¥Ø™YEšªf€1cÆôîª/U(?È]Æë¡á!«9’ë6ø@èÑ#Þ» çÏ_ fM€Î;/\x”ˆË—']7<žlíÛ¿-[¶¸~½æµkùeË&\¾œI¤—/WQ…åÔɹ¿{ VÍœ$³$–ÌhT›X2 Ž«î{Ò•gÕíHwÈîóðxáI%ß}÷§€‘#‹pÛ¾üòKÿõ_ÿöüµ R1Ý0Iš°—­*Œª+¬!±iÛǪ! @ê½lJ«ÌzN’S‘›¿<3ó•àh\1›ìê6Ù…5É„ofl„@bb€Üì$ãFuqÈ•òzŸ¨Uý·f ÿ¼~øOfk/Ý;þAÐ ß…[Í÷;w¥Û‰ø[‘'s÷#>*Üí—îâ¯ô€ ¥ø0O&LŠïÓ¦M0nÜ8õä´iÓ¢òB@1l˜õ7­úi(Ê'Ä=jø¬&M]yŠf®TLY½.Ÿ½z%e„ ¹sר];@ÇŽW¯Î i¦®^o.‘UÿÀ–-[äåU¼vÍ Djjâ•+×ýñõ¬\±S€zõj ¶°®ºöíùU‚¹E#Á& Ù[=º=µs4 B·~*¸ëÖ=нP?½x’ ÀÔ©SÏŸ?_­ZµqãÆÍžŽÿÕW_xî¹ç"_ʺ"UÛ#ÓêD†S…ó†…{ÓÀæí+ÄQ½:/ªÕü~6MÉÐnd}NdÝ"™fÙƒËÖãû9<9®€Y³–vi×rí†Lc‡7á ÖÃ)¯W°øþÙgŸ¨YãÃOƒ†À ­…G?b¶tmÿ{Í¡¹MX—Ow5üßQ"Äß)O&R·‡ï¥¶‡é®E|¤'m;‡øË} b©!¾°Ý‘ïŒ1oõlNbL!š9sf$ßÕ²ZŸŸ¯–‡ öãü ܉™/!hÕêúôNrà®^Uˆ¯[7AJöùÊõèQ_=¿uë^¯×lÕª€íÛwÈËK6MýÚ53%ÅÓ»w-Õ¡÷Ú5š<ß·Of ë ñÛw Ѷ¥“YÂ[%J¯˜-?ChÐtë§®Có@÷°¦Ã£àî…× OfÌ™î´LaægŸ}Víaý»»AèþZa¿üêVÉÙ¤Äúä“OÔ9WžŒè=öIëC9.qÝ$ÛŠ±8ÿ]°ø,€!ƒ«<$ý|úÔù•«n¤¤$ XÍ"{4Êëå4ç]R“_0 h„¤$hŽŸú„­mÀŒNm_Ö\X×Õ`²M°½`á>V¾£˜ˆ¿{à®t«Ká®t">¸ÛkÞ!Ä_ê•J‡ï±À]iÖ¬YTJ ™Ï&[mÕ¤DRJ6iiin¾«eEy!ð˜?O|²Jµ*…€ 0+VÞð@¿”°R´s箫[7 @ ©K—ðÔäÝ»wP °fÍq —ddä''' ¢~ýÚZ<’3$6n @¢s;]¹ðž Qà¾á'ÛgWp÷Xp×½Ü=qø÷Oßs¨ÂÆ*Tÿ`jGËãVM”l°&÷N0uêTs£©œ"Š÷>ûÌo¬bÐ_|ñ ‚@ÙgF÷ŽfÕc Z¾òýû•µ^b€qêä… ›|åʦôë›$»+x×R­ºOŠï+¼ tOùLì3sÇv/kvvº¦AÓX=©+¸kÐ4¤V@qøŽØ—x2‘Z´h€Aƒš¹fqu{æ.Ým|/Ä“)`ý{ñû÷ŸnÚ´z‘d0wî\#FŒ`\·ÈÎÒªÅ(UEu ÉVO%µ\¯AÑi• ,¶²³ä›Õk¯SÁ~rR**C@“¦9~¿¯cÇŽîmwìØ  mÛ6E¾Ëúõ‡ÄÇßRfdä?þøýRÂi®¨´~}>Ý;Å© ÂzÙð²tkY>Œ°¬õ`Ý º«×Í 2dÖ9½‘r\šËrÂø®†—çlŸÂÈö£ @DI]¢§îÄ"•'3jT?哤$¿˜óÅóÏ?Ûè'ª¯QŠ×S&..)1!!))®G÷D€H`ó–|:uŠ'{è•%>}ê$ΜO£Sû80'-#ߟe×$2‰.+«÷úôÓÏœ<0§¶Aáʺ*5 ‰©Á¬ÈRæûâŇÕ1ÝmpWºˆ¿SïÄ.ÍÅ>P¹äˆ·ù^£t¥´´4C‡ \b“Á6Ç¥+~—.Ö«eE|SÂÊú°¹ßµ{”·Xºt© íxào§¡fíÕ*ÀOJ,C‚J2M³Aƒju1hÕª• Òº1bD"?ïÆ<ž\€Ñ×0įY“ ÆýÝ ¡'‡¤Y/ŸbÅhvVŒîeÝ‹»ç*¦;?Ÿx"b^l4…ñY+sTr!@˜»c6€Ç;ŒT³’¬ÄokŠ’‰˜Ø2¾ ý˜={…âá¨Qýœ'§N•ÀWйÁ'ÿµÍâ{bBRR\JŠ75U+WN\ºbQƒZÈKœ>}þä Q¹ryA”y-pãº/;'?/7ÇçÏ Ø|WCÄŸþ¹cRÅHö‚T<¾£PÄ/^|ÀcÝT&å­V)"~á½¾Uî8âÓÒv¨Kx±àno{ç¡T)6âYNŽ?SH¿xñbÚdvíà=„òd†±Þ„d¦ãÛ›j&[¹}RbØp¬X±Bá[ù3JRbåÊ‹úõ«¼mÛ6€„š&”÷¢Œø"ù>gÎÚ ’ì«8[ñ}åÊ}z&BBK ûOs¡y\p·|>z<ͱ_Ùó*vE޵f­ÊaÎΙì0Ê6ĉƒE]ÈI3·ÐOl¥ ë2@qõ¼³g¯ÀÌn¸; KÑ‘>©UÕ=Ä®ÝÔ²zùr"µlÁ³Û§N^ÈÊB PÑ®_÷gçä |¨aWúòË/³ôâ‹/÷üDªØ|Gˆ¿'àŽÒã» w Üüf©˜ºƒˆOKÛàÀÝÞÃBüù>p_ñøî†»ýLˆÿá‡Ìlî×õ¶9Î SsÙƒÄÛÓ´~š ic]Úp·&L‰ò•WªÚaÎt$¥åËÏefæW­*uÝÐu£C‡à¯h‘ˆŸ3g-€ÆSárä•T¿bÅ }{§@B‹ÂlÑL+™]xàQ>»—¯,’Rº½ue[•LQsiÔÈêã GE0y18ÀzžéEÊÊW+?qŽêÖ«FlÏ#ÌYü⋞~ˆó¾`Çs·nbÐéSX¢FÊ.³‹E\ßÃÜùlßi±,ߊxEvÜ pWºyÄ+¸ß~²;º#ˆWp:´íMïçÞ@|왑þS¼|ÓRfôéð °;?¥½lÙ52ôfôPÖ›öj?ý8U¡ËwíÚuùò³ÙÙyW¬HNªŒ[… ^ÁýñÇïpöìY7—ÔªUkéÒLýú–Cx‚pŸ?ÃÛ=нè×K–,q¢uf:thäÛ•@‘ˆÿ׿þà7¿ù €ì ¹äd¦;ñ»;x¬üzk5";q–Õ«WÍÚÊž‚ëqS†ÏÔ™k<;î~…c69ø^ÅѬYK»wk]½Zç~ˆì3yqËw"¶§¨)*W•’TF<$‚9N|Ó|¿çà®TbÄßÁ°Ý­ÛÏ÷Ò‚»½·;„øs} jш/rf“B¼ï”UµpÕÖå`ôhÛÏ!;»È.CCøHÊK—Ó±°"•Ñ%MÞ°ñfBäæÖÒ4“Èлw¨ëGE¼îΞ={ãFN™2 îœÅcÇr˜¹Nà“{v1ƒ˜DãÁƒñÓO?9Lgæ"«ƒ•@nÄGÙ”³9ÏŽßCÌw²so¬à]àØqUÀêÖ·‹…Yå(]UeTÉïæìáÉ1-Õ­»„âkÖ¬¥F`…ØÈ|ùõôµžu?«¦·6ÇUz’ n'?•ÁÉ6͉YmU|°dÉáC‡®6lXþÞ"»£ þ.»ÒmC¼íÉ”Üqgø³} Zˆôd¢­³±š^©]ûúŠÝëv¬£k«ÞlÛ2lç̸ùÎ.š3£A·›ý(¦U¤¹båu†$’IIGt]!òóˈ¿+¹¯ÈÜ•Nº`šÂ4Eýú¬^½™k×Nt‚úݻв3óÑ£G¦K)¥”ª?»ûÚæöÜŒâ¯];ŒÐ²aÊÙ–ï”V·ÌDZtìøEêzõ«Ãt²æÁ¿«'ç,Èa䈺DÑYƨÄ›j¾;sO oi“š!­@ÁjsîZffH²T OŽ-s³þ l¾7jTþ&KÓÜA ñwÜ“‰Ôm@ü­€»½ç;‡ø3} zÄÇwÿy@â— ‡tîÔhÃεÌèÔò~¶Ãö å%$£Ji¾YÒ.L«Öä2LfÉ”°yóf{†”ðù»v^ˆÛá{ApWÊȸdš‚Y;q"ÀTs¿ºjÕ*'Z÷ù|Ì\¿~}èå{AÏ«]¹Ë$©Ù³ÓÛV¶l™XÒKrwåáæû±ãg!P¿~u»@XèAs†ìÿÚO.ùù ¬LöØl‰5sæR£G `‹ã‰ÏIâ•»î÷üöºI|œŸÇ7öóh;¶™g,b$!˜I P«»kï÷~ØUÕÕƒF„œgõÒ*·ª«ºKøWo?ûòò†µœ1yà@%€AƒúWT\‚¤ƒ¡ â»jàô »¡UTÃaÍÕÑ0¼¾©Ñt<~ü¸›ïj£9Ê«±JiiyhCk·Ö­Û5&) Ù9CÃwMÝ‹0ß]‹«Ž{sòTS¦ø:…ïV¯|žíÙû ‚üdèm°àÉÞӉ߉ )qB¸¶«ê›z4ßÑ"â»9Ü•:ñ]w¥§†øšBHß³qã÷*£¿ÕÚ¥(•””œ8Q™ššÞé|÷—˜(¬kŒÙƒîÊ.“))$` š1݈Êÿ‹æ{sRæ \þL !|IÉ )%@µµ^€K‰Ÿ¼8†\‹&ÜùRpf!> ô,¦um$ýí¦Á®œœãÇ•3Uõò•ÂZ—þbý XýÊ‚P~éš®ñáÙƒaØ` Œ“uF }ÒÂWDe¸¯\¹@Óå }_dqÌ÷pc÷°ùîÐÿô9™œ‚áÃÀ·UTômþ¬ C() €0y¼ÇöÜc,x‰ÕÍ÷ıިãwNÿ™%â»­'«ND¼‚{×]éiñW ïÜy°ÿÊǵ¥&NœØjÑSÛÕXì‡Î˜è,Ìw1ÎjnQH h”4~œá·“ïn¸«òd©‚»”rÚ´iD(/odà99^¸?ˆ½tŒ‡YϹ5/ÛzŠºhöê Ö1Oœ8`òd«ÒâþmH¿ñüôÕßJABYYµ¦i99CpG{h8q®aÀ[+¡fÌw'lwñ±Š*1`€Þ»oë×¹ºVsëj58 _VƒíÆ8ñ{«K¬D”02ëJEEÿlc`5‰ZëLð#A|Û£Ô)ˆïz¸+=-ÄoÜøýÜÌ_¥¥¥`XûêžÎœ9`ܸqöq:Žøß40ƒ1LgÌ`±|ÐÈ~¸Ëš(µ/²³¹c0Þ ßOž<©i"˔ڒéèôéÓRʉÃU¬ ©ååÆYÎpJßæžf­Šªóa¸[?ÐÃè'u'àšëI ZSw0VóçÞÿý›W!…•‰têT…ÆùèÑ™Vu²(Ov8  7 žÞ|ļr_ÌóÝçwî­ïÝÛ—ÚßÈÍÕßœqëü™[ÕÕì…çú#¼šj#>¾OðovˆÊýÛf0 Ì hývïÌçk9OF‘ÝI‡ï¹ê‰p|mÙRºeK)žÜ¼òÊ"Ú´éxWžtãÆï¤õOiï0¿²²2]׸Ã&»Ó]²-ª[[_¿ùÁƒ­åZ{ÒƒÚ··õÌÆ.^–gÊäÉÓ¢øDÈúE3o¾¤¤@(ÄC¡ðÿãÍU6)²;‰4ŽÆ wët¹¹^*+Í+Õ&²I üAzYyÈ…a#dµx)A$ #“IÁd„4!MˆÌ‚kðž’ʼ0hgah6À0n\v `ž>])%&3ÌB!„póæÃBõõ¦ßÏ/•5û—ö 5<ƒ Ï €ë~ÞaËŽj#'[ë\¸70)‰¶ï¸ ÷©ÝoÀÕ¨Ò›ax3=ÍÁýÖ5YS¸/T_gkm}UÁ] bm‹zt¿mÛù‹ïŒÑ¿‡’½Ã!¼";€eËZo.øä¤ø¾|yôĸN—";€W_}®@f›Bøòòr*¹;æ°­Gñµ¹Ç= cÖ0X8r×­@>ÖŸ†ªkI˜‚LI¦ ’‚$¤ÝóÝŠß‹‹‹9ç¡P€3F!ž'«Ø(>ªo°sûQ ¯¬49ƒÆÙÐ!Ü.§„Ö«¥ñ‡K`ö¸QÑ[6sù9Œ˜mé½™óUé÷¿ÿ=€¤¤$X Â?÷»ß­[·›‡ŸãÔÄõw4˜ªLµ‚ÊÆLh…Ðf­é6ß¿Øt!1!eÙÒ!ÌŽè;YW«ož<)5Mohx°répŠ·Äêy&NfGÕDÿ£€iФ¤DRC_Zà{{áŽî×ö½]R|92íIôîuñvØþ4É®Ô5|†»RU!dµ‚øŠŠ ÙÙñ‹†šãûíß߆‡qƒ‡1ãƒ'’ï³(¯°®6Ù5÷*+¬²pv¤8(‰¤$©’$mƒ¶®înVÖƒiÓ¦©6¿ùù¹m»RâKJÎ2rf<)sƆý £¦FpŽÁ¹;]/¶]pô5©ca²[Ä··í âe—Îi‰ìË/¿lllLLLŒ"»ó€i !ˆÈ–œ¢ ZÉZ‰e*ô½÷ÞkîMŠzñ×~©ký £Ïë«FÙ‰’O€ïtµúÖƒº.›šî½ñÚTg‰U×Xß­‹ç­{@Cƒ_„DJï^pÂý¸|wy2퀻ROD¼2ß,Y’׃–Uãª]ˆï>pWz҈ߴé{¢H²;jâ¯\¹ 33³¹ÚnÄ×ÕÃ`\oŽïÜ™ÎÂ|çˆJg¶e¼koƒâ{mímMCjjoÎEcc£®KM Õ××µ¥'°’ñÇŽ]дGŒaÊ”)QÁ»{°ÜõRטÎÑ·ÅCBOmSzɽ«®¼ zÆÀ9]¬(Ó’ØèÑÍçùàƒàâ{(d*¦ëº&%¾#|KpÆ"Zê~» дþÿöÓÙ;Ü– %‘­_­¾I{Ô„Ä_¼ûR«¯;uÂê`ÓÐà¦B¤¦öV·2k)!–ïw¥ž…x7ÜÕÆ¿⻉'«'„øM›¬°}ùòf g* `x³|¿v퀡C‡¶|¢NL§q+øƒÉ´ø|·ºÔr”ž:øgΜ¹sçMÎÅ£G~ÎMÇLNÖ½Þ&§Kpsºqã†üìÙk¦Éòò²ˆxEÅ])Áy²²²ŒÈõEñ]õ·o‘®AçHxûâªþz$åFü媋`3%~!®[ëÖí>w®j̘¬•+çGýêðUÝi7±'€ç/þYÓšå»»³ …è1Û´ UÐD’Ömþ@Cƒ üànî†èb`¸]/¦~Ë÷x2qÕSßÓ3#ãªU¾w[¸+u:â[‡»RE!dÇAü?üpëÖ­>}ú´¼;zBˆoAûöíSK‘ùùÖ@uëÊdd<Ðu=ª |sºqãÆƒ R¢¢â¡;6ûÒ¥{R"7w@EEcZVV–Š ­º}Î8ƒ²à9ƒÎá÷£O2|W>ŸIò ?t;®nZX‡+¥½¢ºœ¹ÃGè}Zzm |wk××âUO{0èÅEmË©Ý=#[= Ibv=AQÑ7jšã»jA^µûQ](U3J""µ£öŒà{gÁ]©û#¾Õʦ%â»›'Wˆx÷VÈîèr!äÄAüµk×LÓB4ç¿»Õ•ˆß»w¯jÔ×?BH!Ÿy¦ß¼çâÙâûã{2±êÎ|õdbõ£D|€;:ï탻RóˆPQQ!„ˆ›?¥®Aüîݻէÿþ?‚8皦qÎgÎŒ.ÚŠJ‰P\|Þë5ÝOÖÔÔH)/\¨yø0555yþüt"”/aÀ¤É“cÍ™p£+§)J<¾C77~%N jªµxv¥¦’1d ÎôÞû¸áî~¦-ˆw´q­õöGŒc˜0޵r^Kf,Ic¾É“Z3=:k•U‚$aý†ª@àaHÔKyOÍb%"Õsã45"9ÅNâ¸W[§'°zõ‹-›Ñ“€»R÷D|[à®ôcB|7÷dbõ˜ˆß´©@탻ҥBÈøòòr!„išîøXmÜxÀ«¯Îl÷Ù›×Wë!%$¬ÁOAaJpb|È=¡÷z!i7 ½¡a$€çŸïu„ÒÒR"á™:5À‘#åºnF€;Õ½ººúÂ…!|^ï`§gä¡C‡äççËSo³>\RmÎX”€$€€oL») X‡êëW™ž €Z Ð<Ê;€x§KqîLœÜ#°×cg̘àØ‘*]O0 Ÿ×ëñ%ÆìñCx‰’Mê‚üÖ,ÖPÝÏ1Àš5k{&µÏ¿k4Že¯¶³zuëƒØöí—êt²;ênˆooÛ÷x‡ï=îJF¼ w,_>­#'./€Í®µ–••)¯ÆÍÄXuñ¥;­‰Nžݪ‚ ì™®Bñ¨wßÝc¡ã\2Æêê¼Ë_™µsW€… ûƶ+PEÿ@Rm-ëÝ[×uÓëm€« €[;v'¢1cú >ÀÁƒ¸:2 ÷p ï4AŒæ»õ3arBìqZÖÕ«W î;LY7_}uÄ?šxø~ö´j|¸_?‹ì3gZ;Ù¤òÉÙÙsõŠï‰‰<-í±jêëp÷®¨»ª¯444½üBšÓ½]KÑ|ú7®í¼g ý¥”¬ÁßV¸Cñýå—[÷GÝñÛÔÓáÂÍQ£ö,²;êâÜ;HvG `dKé’¥¥¥Bˆ–—ÇE|u±k܇k ·µð†pžd"àÅEغu+çÜçóq΃Av³fnrê1Ë—ÎîÝ,X˜ÛQRÕµVW÷1 Ù§6gÎpõLÜOñÝww¯èú£Q£Ò³³³‹¿‡¿i?§md ý`ZÁ»°> ©ˆ^…x 2)ifb«‡r«¦¦@zzúºu{ éaŒ–­j6õ³]ˆ/;‡Ë‡£Ô~Ý´UbIDAT À¬Yáñ+RUçª+ɬwke¯dÎóµû> Ôݧڻ2+“CFŒÖSMÄô¾ìP½®®Šˆ¤€´Ô7S’’^\Ò¦“t|¾G»ôÔßvO&V=—ï*~¿páæ¨Qƒ:½‡p—©]ˆï¸+µñ%%%Ê«qã@Ýe«Ù!I”–^aŒååe8?¬¡N°)o?œ¦Âµ­ÂbÉG;8ç@€sžÀ«»÷\Ó#3!åØŠùdÀ°{ïCóç'ªÜå‹‹‹««SúöMÖ´€‚‚ŒââbӦŹbß}WK¢êP¨~Ô¨t"zø ·¦ºDK_ÍìÀm:×ä á-Ö'ÍIjû®_¿àСrØex3–Oñ<.Ub }SYÔŸR<’ œš*†Úû ýûu$Š'ÊJ‰¬LѼ!²¬É±bŠŠ¾©­½ peŠþÏ-jý,ÿ|ß¶­L­w¸0µ'"^Á.æÇÂ?®'W `T|Äî€$JN¯¼šgÇÍ3|5rÓ… מ“3˜(œ…máÛÞ¶@ïôeö¦ÏÂöíÛ0ÆÜÕ¸ê(Ø(ŒD îD€À°wÿ#VÓD´†/*º¨ëX±b$€Ý»¯'$ÔpΦO^†U¡âÎ÷,\Ðg㦃>_¨ÉŸ>a"]»víÑÃ쬌 ź¼Çû7õhÿ#2 ½žëÕÆ—|öÙvo¾ÙÖÉ®­"~û¶ƒª˜é¥—óc+$8ÂìVn ¸vÃ04]ow:I*9aòO¸§XøŸˆ`¤Yˆ?{¶bìØlåÉ|ôÑ9ùï¿ùͶ|ž.â;žâ·m+°dI³ÅomTÏB|lòÌñOîÊ `ô¨)áf­6ÄíÈëHéS!L5b›(úQYy‹1–ž1ÀÍw' [qp™Ø ¨¬Ú¢ríj{Î[´h€­›)ä¿øÙÏ­ØUø%öôÌšûˆÖT;wÖ1&RR*(¦:t@~¾í2[]¬|jËç)LÙ¸YµÎ-//'¢ÊÊJ’y™Ã2ìž.`@nK‹Í#5vµ©©á…ž8p`{^ñÛ·tj©ÆäågdECZ<°ÙÅ3VQCNŽ·}é46Ç÷ìy `vr¤3ãÌPIxéQ|/>JfÇKå‰süú׿nîT]Çwt9âÙÑpWêˆw‡í1¿úq"¾3=™X/€¼è>t3<ÑIñúà‰½¦0…S_ˆ€;@@MM-cì™Á©Öôc{Þ½~ ³¶OŸÝ›æŠïK–,Ù¾};çü¥—^ðåvôK ú’•á·d>’ŒáàÑfÏò9ÍÐÁ°{Ï#5¦uá¾§N•65ùÕÊááÇ… ¿ÄÂ…iÖ-vín° °7ØúõûçN›qÀåË—½^oúà…aÜÃð'ÖºIÁÀÊ•…wîÜ––Öž—Gô?pà@ý}ËL›—Ï5¤gÄÀý`'áÃ}=Õ“ç/40 /¯8â"¾¾½S¢Ÿ´sаã+×L>rFî…ù^Uq'k.(ö²êÉb2CÇNü Æ Ào~ó›ØOÝ¥|G"¾Óá®ÔÍßÜÑÃùŽfÿdá®Ô âÝ Ô¨Bì:ö)Äs3Q¸VÜZ?»s·ž1Þ·_2i£,Lù¡™líÚµ è8çË—/WÇß¶mclñâžýZ¥ÆôEËÃoÀ|¨`ýúýfSþOßÐlß¾ˆ²³³ÇŒSUFv@<óx©×î°Ýýü½{÷¤¦¦ÆyM³‡ÚUWwcôè̇õ;fc¤f‚k>Áwó¾¹Ûˆç° jÝÏ ääŒaêô!P j€Í[¯÷ê•Ô;Å—šjdgkå—ÄÝ»ººÆE?é€ € ÈüÏ~öÚ8÷šª î7@”™1زnˆ¼ž¨´÷s§Âˆ±ÇþÀ¸ÕÀàý÷ßGø=þ¸ôäà›ìÎ n¢-[Nµ±m¤"»“ßsÕ¥p0f8[ØúžÀ¢E‹´$ƱãèFßP–0„>x|0|è7011Eg†éI„' _ïüüŸ;¿øvçÿ8¯]µjUÜ7mÚ@Á}@¿fPxuQÜkîd©©klÚ$CyGÇŽ…@47ßg !p9S2¬ ÂÂù½9ç»÷<„Ý9Ý6‚‰«/öôŽ×^›K¦Ø°NX¼x±Çã¹téÒÙ³gÏp\ÓÁ4ûÁÁnUâv%îTuäª7w m?Ô¾}û 2\8{üØ9'Mƒ®ÃÐcà^kFÜçÜ_h¢ºÄ’¬}%}^T¦v˜6Å›Å!iD6Ÿ5=DëÖWXk,÷ò¨™ukª"\©ºQUw€¨ *²+ÊHH€Ï ¯†M§y³~=o†¼øá‡~ø!žJüŽ'Â?!O&VÝ*ŠoÙ“iæ%=8Šß´éxYÙÑ£Ÿé"²;:SãÚÂ+mذA±jÕ*÷“2HàøÃŸ>î×/Ù Ø_ýõ掠\š 8ƒBÜ A!M,x%¼[è®iUß\”Æá`ìÄ)3$hú4ƒÙ‘»ŠÁ÷íÛWY1wÈÐý^/æÍ›§ŠQ Ø»¯@Áœ Œ°CÇ‚ ÌšîÑ“¬XðóOš<‰š/ÉX¼¾þúk¿ßŸ›› `ܸq7®#‚eÚóµÁv8R‡µéêEy2ŽjkkÕÊç<%%ÆÞŽÑ¾}û`½–“9÷رsºÎ§MÅ5p ýDÂݺ’p¢uD¬¬2N: OàGž0cÆØO>Ù¦±þ†Ñçõ×FEœÞæëºÍ•M{¦¼óÎ;/X³f €_üb™Y'œÈýJÕ ’€¤ÌŒÁÖ­%œNC ør¼î(¾ºBÌ4­á㪥ظ©Pp'¢§Ãw<ÄwÜ•º âsfSOD¼Í÷Á]0é)ZíGüÚµkU‚ü›o¾ àÓO?pÿþ#ÆX¿~Éo¼ñF ¯Ý°a€+VlØpxØâfЂû¼Èò–à-SͽXAjDQÐD0$'O4KAK´ýé'ffÎÁ@€<BeU8p¨ Àœ™>eÎøþx„g§zrø»þúÏžD}é À—_~ ॗ^:sæŒÊè4p<³–@p~:ˆÀz‰ÿÁ×­Û  (¸ßºu‹ÛêÛ·õ×ûöís}Ì›7ïæ5õ4•––>iRަSï¾áϺc2‚c»»o7YOjöÝN5¨9{ö,0@cýÿmõŒ€úÙÚã‚,¾òÉ'T1õ¡ªòÞ±zûD,·ªez"߈Ä_«’¡ ÌÂIbŒaôD|ðÁOïèTÄ?QO¦9=]Äw l9BC¼3 uùò©]±¬W§ `|;ÿÅ_¨yØýL8çII¹h¾uk•u0€áæ“©‰ DÂÄìÈ–àSYŠUWé‡;Hë`ãò8˜Õ ^wÍûÇßåàô½¼^ÌŸ?€h’Š)‡A˜9Ýã¸%'C¦LÐõÞáÆç_®Gˆ‚ÞDý'/sÀÊÍ_¼xñéÓ§O&L˜p÷61*œ·(ÎÕŒèÜkPøãØpÇÊ•Γ7nÜpbö6¦Í¨¶šDTPPpû†e³Xž;Çù²J¯—?;3ËÙ?tËtî…äÄìNïZkÕ’5ûF> `ÆŒŸ|òÉ;ï¼# ´FS-EsÈþ׿þÀ»ï¾[Tô-€)Y£22[kñv£‚pË0'2a´ÏA|í- €iB¦ê-T:ÿˆqOÕWdW”=¸pYq]­Ç‡{“î°Éî¸ð]§ñ{@À©6ñJ¯¿þº Þ¼õÖ[o½õÖ›o¾©È®ºD©¨¨ÈÞ `Äðg5hNb<ùGÂjðrý:<`CaD;’[Ö­$7Ü×þ?èFDùýÖ±4WßîgNóhœ}_²Ë®HטÎENÏæ ™b×w–ODÛ¶m?~ü„ ”––Ö\/Õ h¸NÖC×ÀÕlU{v‡ ‘ÝBãmøïXp_¹²ÀûÕ«WÕdl Ü÷îÝ»wï^óæÍ+((¸{ÓšÅ9išÝ€×˪®V:/ Ý4§)&ÂÃÄ]Í2¡¥h î¤8BDªõ­Y³FKÖbë¢pµq÷Ýw|¿øË_þ¢˜;}˜gn7à„“Z«uØÜ'üçš/¾ß@ž_<^é:é8'ƨâ|__ݶ­ìéÁK—ްuëÙ.>ï–-§,[6á1áÞƒÖZ´™§àÉÄjÂPÚÄxûí·ß~ûm÷3qÿÅ_XµjçCŒ=Sƒ&¬ÔÃY±SÛ$©NM~òèö #a•Æ’„;î 蟶«©©IJÙP·Ày^OâêþñìdÝÐØÉS$¡s¦sA¡»¦³óOV€Ò”dÚQ®1 ñªªKûôcšè âZ èmÊؽûd¢L^²  pªªªª««Õ¡iø|ðx ¤šM2öôüwGvi¶m»`wx pw¤ø®Xÿ¤¥È`Ù²–ñ´ó˜ÝÚ¥‰ Ûc~û”\÷‰ípiš“»Ù矮ÖZ7l8 `Ò¸™ffS¶Œ0AÓ"û6]‚ŒáÁ#–’båƒg@Äx¼ÍE€Ž¤”o€×ë}xïùWW‡kÖ[öΕIÆŒbåˆ,FkØÞ®-à:Bô%isçk¶lÙ`Ù²eÎ>'OžT,Ë5ÙŽIíj.(dž©3:t†$I¢yó&0†ŠêKšÆççÙc2ÚrwïÞí YU‹ îß±mÏtÕ4h’­=×®Ý5€õÍŸ=.Ü`€!bƒ±®>\v?g€¿ýíT(tL5j7kí»`<¬êý­ã¨˜½wï,šÿ³7ô?ÿùÏ~ùË_ºwTí(Þvfìnû6èi[Ù>¸ZÀß¿¥¼x)u‡üÈŽ¹4Û¶]°dÉè§ wtaÿ$àŽîÅ· w««{T_ßoôè¡ çÏ úò#€d"‚ ãb 6žnSÉ3Luà`Þ̈a§ßlt@§^b_}õUSSSBB‚ ½ÈB ‹\UÁ[¦Mº}µ÷ѯ/ÒúoFĘìƒ_‚ëà41CV]=ôÚksœÎèëTR¢8è4"vòdFŽìëäÆäåu¼J˜ ˜*ž69_Ù2½úFcÝú¤5!ÇŠ9tè ògc``0Å«¿þê”ÈÉž%U•Xò ÜîJPYùQ²»i¿õïäþý Æþ2dŒÁ0`T^ñ1cxï½÷âžÚ®)Ü´D’¹#úF£n¿·QÝîOH]÷î£À½Gëïÿû½{‡ ™à…çfˆ(€`áàâÀL‚aÑ(g& î»6[ÎŒî"’R‘®[ëœDê!‘Öƒû“—Slü€«Û “!1*w6€+VÑúõë£vž2eÊÔ©S+¸ß¿#7×¥:vìØÇ;€‚‚‚i“ç9ÿYròðÙ Gšƒ{àjÈ]‰J+Aqá~í*]©$)IJHÉòÆbÜpŽ>ú˜ˆëœÉˆÞÉz‚ê'X½úù_ýêW®\ý³¦aérHa²Q¹¿’’þô§â¾á„1¾„±^¨Âbv.޵Àˆ_3ÖøÞ²Kãx2ÝîîÒlÝú=™Xu—fóæt(O橹4Sö€€ãti>ýôÓÚÚ‡éé“üïÿõZÈ/›¥ ë >“V {¬kT³2Ô  ‡ò¾OÔžLå«XðÛ¾}»”ÒçóI)¹f™$nyNâIJNDr"Ô襦ò€{ÏY‹Â=ż†!Ìf]G*x?qâÒ½{ׇï5uêH&Lhy’måof<;gƳ³<7Û“GŽqfd; TáDÕ³óǃhóýžgŒ¨oÞ kWI˜8wþ‘›?Ú®xáZô‡5†á5d—3SYq$V¯~ÞÉ“±ºû2xy™…ø1#ß#ÂÿøQi •‘ˆQâxˆÙ.õþaÔÈ̺ۥ°fÍšnäÏ´¬mÛ.t[²?!mÝzzéÒ•°]ióæ’W^iiÜÝLŸ~úiròÈW_i)؈ fÀvfLÛ™‘gì52ÍŠ¢·6Çû¢vۿƹó­ÅŽ;$%¼$ƒ@ˆADt;ÐT Û¾.·7atÄœ¤âo­;‡fæ…áȼøñ²[ë×ïËÈHàœ«ˆ¾SÔôRRbrôÙ9ÂSYêJ+Ápû†I5hŒRímišÌ¦•‘"%r]úæK\®ú/Æ#L•ÀÕ`lòLeÕõáYC¼á[ï}h‚b/¾Þm×?¡ë8uþOæÍú/eˆ…?*ñ[yœdw° Ñ+?ܨrÍš5ÿÆçoz¢IEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/Transcend_info.txt0000640000175000017500000001352110304634070026245 0ustar pabspabs------------------ Transcend Credits: ------------------ All programming, graphics, and music by Jason Rohrer The PortAudio library is used for cross-platform audio output. More information at http://www.portaudio.com ------------------------------ Transcend System Requirements: ------------------------------ Required: -- Stereo headphones or speakers -- OpenGL -- Keyboard Minimum system for basic playability: -- OpenGL software rendering (no 3D card) -- 250 MHz processor Recommended: -- 3D card that can render OpenGL -- 400 MHz or faster processor -------------------- Transcend's Creator: -------------------- Jason Rohrer is a programmer, artist, and activist. His most popular software project is MUTE, an anonymous file sharing system. Transcend was his first video game effort. Jason lives with his spouse and child in Potsdam, New York, where he divides his time between parenting and personal projects. ---------------------- Transcend Description: ---------------------- Transcend is an abstract, 2D shooter. The morphing graphics engine creates an alluring visual display. Using musical power-ups, players simultaneously construct churning visual collages, arrange unique pieces of music, and strengthen their projectiles. Transcend blurs the line between games and art---is it a video game or a multimedia sculpture? ------------------- Transcend Features: ------------------- -- intricate, abstract visual style -- dynamic graphics that bloom and morph from one complex shape to another -- musical power-up system that lets you strengthen your projectiles while also composing a unique piece of music -- novel collect-and-defend gameplay mechanics (no lives, health bars, or "game over" screens) ---------------- Transcend Genre: ---------------- Action (2D Shooter) -------------------- Transcend Installer: -------------------- Installs all files into a "Transcend" folder. Does not touch the registry. ------------------ Transcend Support: ------------------ Web: http://transcend.sf.net Email: jcr13@users.sf.net ------- Extras: ------- The motivation for Transcend: Though I was too young during the 1980s to have participated in the golden era of video games as a programmer or designer, I am greatly inspired by the swell of creativity that occurred during that time. Computers and other game machines were quite limited, and this meant that you could not fit very much content into a single game. Thus, most games were distinguished by the novelty of their underlying concepts. Having more than a handful of members on a particular development team was overkill, and many games were created from start to finish by a single individual. We saw some amazing games during that era: just think about Tempest, Centipede, Pac Man, and Defender. Even if we look at those four games in isolation, we see four dramatically different creations in almost all respects. From concept to gameplay to graphics, Tempest and Pac Man are about as different from each other as two video games can get. Looking at the popular games of today (August of 2005), we see a handful of narrowly-defined genres. Games today are mostly distinguished by their settings and content. For example, _God of War_ was just like _Devil May Cry_, but it was set in ancient Greece instead of in a Gothic castle. Imagine a new board game that is just like Monopoly, but set in Miami instead of Atlantic City. Would that be a unique or interesting game? In fact, Parker Brothers does market these locale-specific Monopoly modifications, but no one calls them "new" games. So why would anyone hail _God of War_ as a "great new game"? The main reason people plunk down $50 to play _God of War_ is to see the graphics and experience the world that it creates. So, they are paying for special effects and a trip to ancient Greece, not for a unique or interesting game. But why the short list of narrow genres? What happened to the Tempest genre? There was never such a genre, ignoring a few Tempest knock-offs that appeared over the years. People were too busy inventing their own new games to worry about building genres of nearly-identical games. We all know what happened: games became very expensive to produce as the hardware platforms became more capable and the focus moved toward content. When you go from a single designer/programmer to a team of hundreds, your budget skyrockets. Game design became financially risky. I do not need to delve deeper into the history here---the simple result of this trend is stifled innovation in the video game industry. But if games are boring today, how can we again start making non-boring games? We need to move back to single-person teams and focus on unique game ideas. Of course, there are major limits to this kind of process, especially when you compare it to main-stream game development: no huge, virtual worlds; no motion-capture; no highly-detailed polygon models or texture artwork; and no support for the PS3. Still, given these limitations, we can perhaps design fun, interesting games. Transcend was my effort to explore the development process of the golden era. I first came up with a unique idea for a game that was unlike any of the games I had ever played. Then I designed and programmed that game by myself over the course of several months. The resulting game is playable on a computer that is pushing ten years old, yet still pulls off graphical tricks that will make you say "wow" from time to time. Transcend proves that there is plenty of interesting space left to explore in the game design spectrum without a team of hundreds or a massive virtual world. On the other hand, just imagine what a team of hundreds could pull off with the latest hardware if they could just get out of the five-genre rut. Jason Rohrer Potsdam, NY August 2005 http://jasonrohrer.n3.net transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen4.png0000640000175000017500000041227510304371203024626 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ G?„2 IDATxœì½yp\Ùuæùs_&ö•Hpß‹RUI¥Ú$¹$k-­¶Ë’¤w»ÇÛÓRËíéÖtÛ3îèè‰ÙÂ3Óm÷Èîì¶l·Lj/•¬’¬Ý%Õ¾káN‚Xˆ…ˆ-ÌwÏüqî½ï%‚H ‘Hø„€‰ÄcæCá÷¾üιçÒÇÿg°Þthw€WÏ—ë€vÔë“=Så:æâÚ·½Àé‹“•ùçÒÚÕ]à|ÿJ_éÎîzV|œUÕŽ®:=ƒÓkýD6´¡5¯õ(Ye‡;RX _U­!Üáɾ«»^A¿<­ ¸Ã“}GW‚~Cº©´þø¾J:Ù3¥”¯ â×VÁ¼/ñëîª`Þ7¿¡›MëŒï«aÞÓRÄïßѰJÇÇZ›÷ óýSÁȯí3©€z§ƒ‘_ë粡 UNë‰ï« wÕª"¾Jà´ į/óž–Cüæ2#þíû”÷€ª¨0»‡ÝÐM¥uÃ÷ÊÀ]uªgÀþ û·—“òÕw•"~çÒ¿~á®êœ6L»Ëú–…ŸÚý‹e< ;¬-”ý˜ºÙ´>ø^I¸«ñÊ…øê„»j‰ˆ_ïp°½«¶ÛBl·wÕ–ë˜?ÝECîBÙ>þ;%=“ûîYâ#ã±½aš;øîä(DÒY͆nUßׯæd5ë×¼/w•sñ›ëÊ>…±\* ÜUŠx”bä5s'—¿KêDèý½•?«´â+}ñh?Ó²Ù4w-ð€É+€˜†Öòþ»Z/ª¾¯GóžÖé‹“™ˆ2ÝÀpW]¼ä+®Õ‰xÊEô åúJÉj¾sé('áŒø,>é«Ù<¼$Ä·L ^÷1ñ•^½aZ¶,ü€‰ˆ“Åoè¦RUð}½ÃÀÞí ³™ÍÛ½ÛÖe‹Âá®ê¹4ÝsiUf©¯PÚ÷Ò;Tóž–"¾»cIˆ@§¤âê"%~ç¥ÞÖ÷‰ëD ßãË=ñ•‹LÛVÓÚ}ÍÇm4DÞôZ{¾ßppæâä™Þ){·Õ¯SÊ—¤jCü*Á]µtħZà‘ªµâ;Ü·ô,†øE¯ÚË=î1mÛ9B|u"¦©ãºOxC7°Ö˜ï7ÜçH`ÏúA|Iæ=-Eüö®ºík½ïݪÂ]Õ?¼$ħcwJyát"a éÜ'¯u›gìÈ;r€ißaÚ¶/ò⫃€,XnÝÐM¥µäû÷`ÞÃ=gz§”òëñˆ»ªÇÇñkˆø À]¿õÚ”ÿöð±»§RødtAêCNýþ‚™ïßíðy½Áí;’ñØÄ.XnÝÐͦµÏgÖµæÃ=è¬"~kU#~…pW]œ¾8¸f­“ƒ»J`Ä'™»àõÏL#xŸÛˆP÷ÚÌ?Bšï2|ΟÀ›vò¦‹?½ý›ÝÕñü®M/îßòt©/mC7žÖŒï7€y_îª*G|Yà´Vˆ/k¿Ì’Ô?œëÎáÚˆO7ÏÐÜ^B‚ë¡$’ì+s¯|—¡³2|Ö³c×µžÏþ®ìïúÑþÍìßüãÜÈ(uÝîÖŠï‡v7e3|cÃ]u¶Ï!~wµR¾ŒÒÖÉJ"^Ûû*eÞÓê»vÿ­ÑcL;~\ø†Òp1îˆ^úWé#°1tFoSÇnîØ=çŸØ×ñ­}ÿ¸¿ó;û»¾G‘V¸·85p×j¼ê ­/­ßoÙÝ$ÀÌl|Ë®Æ[v5Vþ ¬\K„»J ª_^ó¿ÂqêKÑÂ]µâSÍ3Å-ðs"šÔ·ÌóÿÚýðЙÜè%Ô¹‡:ö„cîi}pOûC{Û¿±oÓ7 B€ð™ ¡!’Võeohý¨Ò|¿ew€×οv~Âݳ>_’ÎöMë›BÕ ~•àn­µÖ†P«Šø5‡»*T\»7½Ø9]ðäšß(±’‹~<ó):ÀHŒÎ½v6~aWów·|yOëWáSÏô"ÄO ]PßÕFÀ‰{+x6T½ZËúêkç'”ò×âK2ïiU âW îbcØA\¼4Ý[ŽM‘®¥*»*T\Óˆx¼h!ëÜÑ’©.IÍmˆ@ùzš­•S…ν{ZŸÙ^ÿù GÓV<ñîaêÒ( º6–ªn¨Hå{0ïé;×â— wÕš#~•à"°6½`r•_UpWõçúGr(F¼oxw³àÉ·Æ»«$.ž!4ÓÐÜ6ØÚ1ؽ÷µ-—~‘m¬zúá%÷L_% ~s|¿¡ ©*Ç÷á®:±N¿B¸«â+OùU„;±°ˆMß·ª.¾Ú¤ˆß⟟ÞÔ)Iä’m¾EùšìL}ëPMópmóe÷­§ •ç¸cÒ¼Ø\ìùÚÀ›+ûº7T½ªß»* ¾j)_¸«Î­QÅuõæ‘ØüŒˆÀdæÏˆ_ÆŽóµµ£R]æ=­4âÓ³àS½’˜ÄÅ™¨'®éᆞüø6íî¬kÒ¾ 啸Ì× ÍŸÁË£ñS£€4to$3Z@•àûuá®:á+®ªñeÔ¹ŠW\µm1?Ë,±ÉÇ<…i½+D¼ö›÷ W)ÜUñÏÔ?²ž¿Ò×7Ñ;œë»RèÕ; “»óã; ¨kî¯kHJ¯$Ó³Æ  §r½©Êjè†$ÂTÿ(ÝmZöH;ý màûá®:q~B)_mˆ/£yOK¿«{ÕÆWî¸þ[ƒÞÁÒíÎ׺€»J?ðò?ß3x¼oàäP¿ ןŸØ§·k›z4‘O5JÂXk¬°€ ù_w½^ióžZ&á•þŸªèKÝPu+Zë'°€NžŸ8°«ñÀ®Æ“Þѯ­V îªs}S»ºëvu×ï_-ø®6Ümn×6ؙ뜢¾¡œîˆTjÀÒÝQ+©N•ªÕÈ+dŒa¦É³l qVˆ ©Rêìä-K@Mã9  ‰+ 3¯~½öuÌ/±NöQÃÖÖ$_«—½¡jÕêú÷’Ì{ZJö;ì\ã}ïVî*%û*¹øUwî` ,)à_Þ¾wU®Þg?5ðü¿zé]?ŒRÎÞÃ@dˆxú¶üÔëÕ¤×5žr5H2wWY%1‚Ȇ;…™— >ÀÆÜ¼Üßš¼ü U­VÑ¿/ç'îv6œ¼°6›"UîªóýÓêâáq_UîHjªK«Þª‹°D#¯ D«Í¼¿ö£OÃÆ‰Ld(©Bsˆ$ž¹ƒ`‰PSÿªÿ¦…”E\#`DØ “XWIåŽ?XwÛúõDßU"nÜÚBw” 뾡…´Z|_!ÜUŠuuñ•G|Åà®RÄØÙ]waÕ²š²+ž Û7ËBÅÕ\|wGíâà®*¸?õß5†¢ÈCÆ0QF¡þÉ€ëk$0 ù» ³BÚÆ—d…e¼ˆæ3"î ÿo¬+‚Hý2ý⃠oüù°‚•Hó÷Ä×ohCs´*|/ ܃N^˜\+ÄWXêÜwvוñ•1ïE 3%v_ö åº;C|5Àý{ÿí7£ˆMÄQDÆ0$ žÓÓ¤OµbëomŒ_ÎÖ=Ç E±ÉºSE:‘sïD"BÄXÊs€?‚‰ç¾F]ï¸i[3’*«ôRÿO¯Ù9ªn½mß‘GN]ëg±6ªÆúê|Uñ6ïi]èŸÞÙ]·sKÝ…,Dª Üã‰ËLcû²Ð?|Mį!Ü¿üé_Ê(Ð#Ž #5]À¥.EûíùI2”|·;)1ò C5³Ï0 B$ºªU ‚óïÐêÓ!ÆŠ±–EîaB°3ã ¼óïCzÑSÅ'$¯#½óÀá|áæ}oSþújyÍ{PÈjöïXõŠëÂ]¥æ}ç–eV\+äܹÓׯÑÿ¾¸’]©AŒk÷/üé…5\Á3¹„Ú)©‹¥]¼8Ä·ó™ˆòµ¹\œ*¥V˜$bÒÑœ~àF k>ƒ0LØ5Vê:)bÆŸúšk÷”©ÿ•?QëB6&oð½LZ%¸«‚y_Uį9ÜUjÞwn©ÛQ"å+÷¹ 3¦±xb¤ÔÃ$#º–°õjËA\„‹È J!žO*F|gæà¦h…ÚÞ™lïôLL„ ¸ßõÉ ,ˆ‘Fö:+Ø1ЃD"&Ö FØD0=ÞÌ †¦ FjÈLª~Có%Vg7ܤ*'ßW&OõLbÕ_%pW…|¦TÄWFñÕ!ˆ˜¦Ž¢{—;¡8×ï7EZÛØÓLO¦8/:Ð9Õ©›ë^§ß©¹@@nÖ²ï aD"±0!bd˜8åÄèlÓcj@@n¢•‰êÇ(Ìö?¸¡ké{Žˆ%Ùðï+Wà´Jˆ¯*¸«. Lë8°%"¾¢æ}Á­ñV6ã¦8ÇL†i kª©NÇ$I »óòUO¶7¿±»ñvFò¯åÌYr³±>†gù“±V6bòßu YXc‹&ÅO·ƒ¨¡ñJˆûÝS…ðBÿ»×â<­‰…aKŽ o•‡ï•„»jU]|µÉ!þzûÞ-î-W—ù´FùŠöCíÞTÇÇÒ½iîŽSQ£s](EyIܽÒvgÛú#C3¯øúª¤ê®®‡ÒíÕ—¾Zˆˆ"da<ÆBûßÃÄ1fbbÄ'‡Å†¿¶Þ½û°¡þýŽö_]ë§³6ZËý=V(Eü¾ û¶—òUhÞÓº.â—íÜÇš»–ñ|â±@LËæùßÚ¹éÅe~xzÿˆ›¥Žâqê§zHÆQÔ)úÝ}›ßº»ã^}ãÏ]š|‘ºCÀìŒEjf$Κ÷§® B‰XK]!¥Öž­üäíä7nšè¸±q˜Ý•F8d>„çúß[ùS´.dc’˜lLÖÞ¼%Ö2ð½òæ=H`…ˆ¯r¸«züÖ¦ó7°®l,àÚ9Œ‘Ý¿åé>P¾òˆm0©ü]8»CüÁî·)3úFŸV§_›effc%pÞ?„õìÅÁ†ëJÖXÈÒIFü€01¾…‰šBN5ílhq‰…µ$–$†Ø›ô„­”ïkwÕéžÉÓ'±į ¸«z<ÁÓˆ/ ÜÛÆúKü‰…s˜]Ïi»v©O ˜÷ôsv̨Œ(ÕΘ0=Ìw$ܺû½‡v¼“€³ƒöŒ<¡?U—5 Ü s®Híõ‘6ïþ_‰ßïÎÍ_\ßÊDM ýúŽÁ÷J}lhA½gûa±€…8Äߤ§jE|_s¸-ñë—¦/z#_ÆÃ^iéÞ:pb‰ޝôA`Z·Î¹ÿæŸx__Z¿ ÜU•G|hMAÙýhß;}ø û? =ÝÿOÁ˜×e™3³qRÿ £ <‘Ϙ„X_9iØÀmc+ÖF"Q’û3ÀœtgŠ{ g˜ðlÿû*vZÖ—4“±–l¬F~­ŸÐiù|¯¸«ñ{·7ì-G_åRÄïðYMY’™¾-—üؾó#€ø%NrpËK<Ö"pW¹3Úk·´W‚ò>™NYo"Ü}ëú€WÎ}çäŧ0‚åOuÔp¨Jªkž@®Ðš¼?`JW±5ÙˆÆ'vÔÒx1•ê¤kæ}1‰¯¬Š·ðkýŒÖFËä{µÁ]uÚÛð%"~Ý™÷´.^š6†"CeŒÝ7]¹¸¤Ç]£s† €%XZrÍuá®ð¨â?ôñc>ýpæú¾»¼ùá…×:~úátƒ<“Ôe’ØCõ5EyJµÍ¤1Ni´”Ê€µ£W÷D†ÛÏïí'\ßohA½gˑĿ[²1•¾ªúÑ:îŸYPg.NžYZV³®á`Çæº8–BÁnïªÝ^¦ ¬GÚ¶ïè{eñÇÄ—{1íÛÓwîïúÁñ¬¿Ž–wÕÀåÜÀegä—òø•(pË;Þü+zϳǿF§v²¡&ÃD2ë–2©7¦ÆWhœÎ|“ž·L)¸xý#–­ˆˆ²e ù¶™ôð2xºÿ«}Ö©Äÿ‰Iýû^ù­µ~^k åð½:Í{Z×Eü w=—¦/ú­MË…øž­¯;xz±he¾sÜßõ]‚¸²ªèF [º/ËSJK¿y•¯À}ÿ;ûÝ÷ý& =}ô©¾š&CŒ^›a"ÌÎÚ95ØùõÕâR­°Ÿ-“M£Y¼œn'PKÝ)ý‘L„äb@þ’°áÞ¯¡÷m>¬™ bø«~YžSFë*Ë/™ïÕwUˆãçk½Ã}Ž.æ”òåBühÛ]퉊Ùýÿè ª¢þ]ß ÙÅ»hJ2ïi9ÄoªÝ¼jEW&|𽿀€?þwá’æ'Ï€š,!Ÿ·:kŒ(¡9 ¿Ò5Œ4ÐØÝ3’pFˆz󷨽á6‰-Kì„ Oõh•^ûzW’̈ gl\ÎU¬Âë)ó(í¹®¸«¬¸Þpæ=}§"~Ûæ2 o°}×®¾—ü–¹€7íÔ/÷u<ìYï2wÿÙ&Û-¤eÃ]ué²ûÁÕ@üþZüì?à»?üÌò_‘´3é5JéRjjLMÊÈí¨ç®>@OYòÄÂ_™¼•À‡¦žK.[†ÔD¤ãÆtÆä†®%·fÕ·½‹¸|ææŒàKàûú‚»êL‰×êׂpW9Ä—ÃÅŸßzÛ¡S.ô9! Í-Iðï‹Eð+„»êÒHîÒHù³šõŽßþΟ_sò Í0ù¼M3Rc ª ¦;^NÕ|(©ÍÁ]BÌLÌ’ª¦ ˆÇ–€šL²KÔ†Ôû6ëf»âªïŸ±7e Ízz¯±<…Šk0òë×¼/wUï%‡ø•S~á”&Îìmÿ†Úvñ šâwcuYüë»<çe{"¾«ˆ?Ô”9Ø`œÊá›ÿ™›Îè™Î¾žš ³&3¡m1µ®µÈΧšg¿ïs¦0—D¸<ý&9× ×óÎÇd#ÊFôÄcÛVþzoHÙ¤'I}Øàû"Zæ=­3'3e"ºá®êõ×"~`ÓÞ=½Ï§ï‘áspÇn{Ú¾žîІDY¯÷BdçG4+?¶4«Y!â_×\£7NNi}8™%P¹ˆ$ÎÝçìH9ñ°µÓ"»÷B·{¸_F¦î"pGý³lñÜÝíÅ”îÁqÁ@‚ºì`˾ðgGÊ~Löž„3Ö³~9Ï$j[î ¾µÖ’þCYïp°w{C¾ ³y»g[ýžmõkýtVW½ƒ¹Þrd5g·½ñõ§™ÿîÖ¯B[eD¥åbJAþBV]†:P&ó4èßÕ^SêÏÞÖÖpkk€WÇó'&c½³ÈnÕHSì‹Qý¡¼Ó—ðàdík:¢©ý™€ï„ãÄDÌ¡ÿ’D"]Dã“ú¸ þ¸›m%¿ÒªÒÿôˆazð/~ù¡ÏüÊ?üUy†;¾¯õHHf$Îø †ºF~o‡mkYÎ ¾jÐõù~cÀÀ™‹“g{§ôžu‡ø%š÷´ñ[W†øÑ¶®»Ÿÿ&> uìB ">ˆw«œt·h‚½µûŸôP«wÕ ¯¸–„øÛ75éW®Î¤ïgÌérq7²$Ÿ·œÊâè‘òãéfÊù.pw Ye8÷V"Þ\÷„òÝSÞ•Rÿ®k_‰0;[Ö1â¿ô§‡#FdÈøïüío|ÿó¿¹ÂÃJª²ê'‹éGðò%G4“WÆçì`³®t¾ßpŸ£³½SJùu„øeÀ]åßY»µs™”ïÛtðʦ"ó²«ù‹>™±!¢qÎÝå4ÐwAü*Ã]5x97xyKCü]moèlðÒ•Éã£sOlðé „I¿Ù 1P(عûkûþH^å©d¦h3¿ÂͼÀ[êÓA&™üŽZnüA€Á?ÿ€Î¶šõHyÃE1¦ÈPÄl ¦G¾ô[?ùêo/ï˜h>lm²¡‡ËdBóL¼œ¾azt¢¡myϧJ-ò½î ÖTÏöNíÙV¿{[ý9ïè«Vׂû;Žc²1Å1ÙÅ1Û6¦Æþ&ý°¾ÁœÂ}kgmßÐrðzzû]·ŸüÁ‹­;ìh<&B 0ˆ þ,„A XûΗTTîAƒ—gºÚk:Ûk†.Ï,ø€»¶v‰µÖÆ^Yø¿í4—µ²šÍ ±5'ý‘éžw·æ4õâ©ÂŽÎD„õ?shê!€g~šˆ6×üH Q?Ø- ²þY=ýø6?ÐF†®Ì(Ü;Ûj†®,üb«Pþç#‘!ÃĆÙÀ0CÌd"&&ÃôÔ7~—™˜éŽ÷zé‡UˆÛ8tC’޳ê:Jo¡©ËOÖµ–þúªK‹ñýÐ" 3g{§vo«ß½µþ\_õ"~çþÓÇüôÞ#,fØd;š-Ž)v+;ÈÆdû)¶”1fë–¿^â˜n9óøä»àz¯ ΡƒëJŒd÷H!!…p[sfx¤BpW ^žél¯Yz÷lßb­ðÜà•EŽÀ$zÕrÝ$Ža …L†HÈ?ÆwÚ¤Zn\d/ ¢Ç¸ÁúÆ™„ˆõ$¤37ÆäJ!×™ˆk²"è T¿.ÿà>b Œ!6dlØß ö × ÉðñïÂ0zÇŸ.åÈaŽ˜XøX&Έ¦7%<ÕÎá ¶ßºÌ×Y5º&ßoó~ÝnÈs½S»·ÖïÞZ š)¿ˆ~tæè;ö!X "Ž ˆ„  [±Sq!nìÿõ}1çrb-Å1ILqLbÝÅ`bϺֿ2Ô¼£Ã>z÷‡Ÿ~ç]Þ¼ZmÛ¤É;ûXFï ÁÁöïŒÜWÁ³‚¡Ë3!»Pð½e÷k­Øøé¾¥õB„.FOg¤úÖÓ£`æ÷É 5– ¸{2uƒ00{?ug¿ !ýŸ°uoHܲ@˜Dúþ€!0!/öÊÌzA|d` ;ˆt…;ƒÕÔ3♘ùôOþ%1í}Ë\ä°l8œ9LÓÊjReÉjV#KõïÛû_½àŽkñý–ÝM5~áäX…ŸMµÄV÷s}SÊ÷*4òKŒÝxöè»ö¶Jcƒb‚µBÃ’! *ÄÈÔÕR.gIØB=6`‘æ³ÿÂêI¬,þ¯35ƒ·îîÎæ hÍο‡Y”º¡|ÛÖT‘ÑÉÙU;I×T:¾8Ðælû“–ò³.!˜Bl#ÃE†H/z ½Jüû¼ærG¦ãôût¢;ó-÷ !HÄØ×4Xß Áå|ú±­,°®[i[åˆÿæ§DΧ3°Kiˆ™ÕÑ+剽©g"ã@ßóÔ¿ÖGv¿ñÿšdß黺R›gê¿l<ûÉE|ŒjÛÀ‰‹Ý‡VçTZ ð]ûÌl|Ë®F¯Ÿ¨ô“Z±JZǤXW#_=ˆ/©¦úý³Ç¼sÇ ± ³ ØHâX(ÄÃÆ±DlêkÈZär H, Y&H,tt ÃZ!b¢Ùº–®Áì쉱™™[¿õÄ‹[cKDnŸ ×ßúçå5S€ÐÚ˜dlrÆÛßJkèÊÌÛ0dÅÚ9¿ô qy6" ±D%†=µú4Ù 5q¤Fð#àS òvzmË> f/D!# +!"lq!‹X ¬^ æ½Ø9oYªJúH¤L7d عxxÄ;#¯_jÏLDú-gù™ièø2ó¦×ýoáȪ;\±{ ‰ ©žwßPvé° IDATOKl¡é:Õ[ÂFÕ®¹ý3!– XWÊßð ”_ë',·aæ=G##‘‘ˆ­‰lÆØˆ‘1Ö›a1l#¶6Ž‘ˆm] "–ˆÅ±5,KDV¿kH ‹aiëb‹ ´ÔL´š7??¤ ²LBdsÇ¿:ýâW'_øÚÔs_ÉztúÔ£WŸújkCÈèÄŒvL¾±û»«sªÓ;Pˆå•á¾’úLÞôß#BÆ‹èl€T†.¾‘òãá6ͽgæd\·»Ïâýr*aIbãi„€çï¦ô?´ÐÓX¯¶¦šoú°&3‘·–UÞ`CưñÉŒ«¾2-Àê=OWOþñøé7qöß¾v:o&A¸m“¦šëð}óÈ™þÎý:)ÑbõUEü-»oÙÕ¸Ž\ü²‡hV³æqü²»!|¯ç€wm=‰ kD£ay¬>]ÃÚXlLjä§g,¬À?˜)¶–‹oí:?{µ“ †ç7××ÍÚ·?6øÈ[º!â°€‰ÈL$®ÍbÇ&c€\Ýuôȉ“z£To›efRçî©tÄ ƒ8eÛC˜ƒâ H²ø°ï‡Ì¾Žˆ²u¯Ñdh•qkÑþw!½ÇR2ž¡_Š\cpy+®ï8tÈÚØZ+bÅÆÖŠˆ}âBo©ÇI1‰C}•¼‹ù»¶Ð€|@¯þZqe»\^GõP®ç?˜zöw2©©w¡!°a&2 v¹ 펾nj—û/~°[Âê(/a9ë5ý{ÇØ…ÎÁ «qºÖV‰_¤aæµów5ÜÙxâBU»ø²Œ[Û8~%æ=­ïöðž®#q¤EWgäÕ­S Ë 8ÃQ} ¦gô1쫲D1g7]˜½ÚMp. ´ÖÔÍÚ7¿pùÉ7´»ý>@CóÕÙ©¶©«™º±É§¿Üx÷GÝߓțºÿñÙþûWøºJ’»5çm[k:[k†Fó¶­5§€8–("&]aDDivKúK¿ *Õÿ®­sçÏhAƒÜ )kÛ#\$‹œ|]+»UbBòÜãÝ⇢¶]`ÄôóÏZ+zÀðèrêÛo?xÐZûOþ PÐ=Û»—x„ýu¸tò‘ŸûðÇ!"C¬ˆ\WW(†‚䥳ßv†=1õ¹¼ž3µódˆI3z0‘!ß‚ œ„Z£íé5«â›âS‚7]ííê9~ËÛ—q–ª\Žï×í†#v}Pðü[Ø]E…A.ÀYPs { |©ˆ¿oß^±öǧÏ\ÿ¡×ÖýOG´aƯcb6LÚ’wÃlèõ»ß[ô’üiã]Pl…пjñ&ÑÙÔ"+®³‹† ²ã;•òÖoÊÊÏ}ÊÞñ'áŸêéyuï[Vò2«YJYʤXW#_Uˆ_¥Áîç=â!r¾¿Ìä­˜¾=t ÀýmGbMT¬6PÆpEWbK‘1õ5t¹pÁ¯Þ$X‘B6ØUZkëfmm§QŸ–Œ‰ ¶PпMò?ÅPÄßûѵ©²^Ãæ{Äëm½3ýez0æßÆF"ñHšgÒË\AcÞBDÀpÎ=ñõzR„D„ß]±ÂµÀ“v+¥ß+ˆØRÊã³¥"þZæ}éúñ9l"bC&A¹_³Ê¤•R6¬™ÌÉ‹ß×övvý‘É€˜Ãgfj¦Ý©(L¸ô„üÀR(?±sQ!223Ùi­¸¦ø!wÝÚ:|òµÎƒ‹3Öµ"”¾HUü]'«£©fUwíPÄØÕ]·zˆ_%óžÖ?^9úþ¶Ã\°±5ƒF±Q‚ØX GA\ nxÒ†”Áù®úÚY{ßãC¾¹32 ØBìG°øÆp@×]Qî‰/×Ýû±»º~ºÿ«÷çkN)â,hä=µˆSñKQ§#~Ì‚•U‘™»~5•ùˆ@!έ>ãG@°cºk ´^h)æ"i{@üµÖ[wï+ž;¿øÃ—«‘úNN-UM¢vmŒafƒp PüûVN"x&fžâ¥¼‡¿ãN‘¸3GB®N!D¹«»Â¬;€ê›‡é —üî‘S­—{;ÊðÙÐÂäß5¸ìH’/ Óºu%ç¤òZæü™&ìj<°³ÀÉ55òØ’Ió™]Ýu«„ø À]õ­+Ǽ¯éH a Y!XøU¯ÀpîBdˆ‰fÇ÷¡®ñ,»r¢F ¬SUÁT“™ì0D¢.ž! &„ ¹?<¯,Ö"‚_܇*Öƒ‘GÊ˧ó™tóÌüyéaà;(9H¦æDd p:ÐWÓI8Þú‘Û®|BmŽI-[=þÔfLš‚‰[;ÿU]—ÝŠx,!Ž_¹yü³G"eº!Ö†HvkVÄ9>8Åñz"b×+Ézƒ¼£'"£»É5Q} ‘^)ëš/ˆ°¢_À"$ šæË"<йwÏÀó›§§»ï×È;^’âÑþÔ;ÛÔgƒHßú‚k«¢ápùóÅNžŸP¾ØÙ¸Vˆ¯ä~{çû§wu×íì®»PVÄW îAß?ú¦#®Þ-`EHx`¢ˆ¬•ˆ™ˆØ¿¶=í߉ÐßZS›·÷¿üÜ6űebÀ²·> #+óO~){ÏÇ*ö2Uv¡ü}ކGgæ·Æ»äÄ·ÇPjjKñd±dY“ûArïÙºw‘ý7!2ù)Ÿ3è¬1bW"ts|!¿KŸNž±*P°©5+ÀÈõŒùRâø7ïÜ&b/½É=è‰ÏŽ↓å©~ͪ1 ¿TÕÑ_ ô‘±Ù‘±Ùô=ÎŒ'‹Œ’Ù¿¸:|§ñ(*Æ¢¶éƒL&žþNà¦ûËñMcB{ašÄí㡯>½™µ§O$}bMSÏ¿¤S’6òéûïÙ¶E¬<ѳT—:_ÏþÕ‘â†)J ˆ´¦šLpã$Íü‚[કآA4Üþ£èbìXïÌ3´Ò“-ã3íWò/6ÕeúyWË—w·|uÙ/y¾l.É3(Ê’‰Ü`ÌŠ«<ÿdå_yóò™"~­Ì{ZOŒ#¯ô÷ûmT5±T'*b-ÈFLîâªd ÖÆq_kÍt‹¹ëøe5ìä»>îá†aËb™0û…?®äkX¹Fkæ8ÊojɺP‹•dá¨[bй@OOž©kûYbÎO|³¸ Ì[æðÄ~(vSü.}_nþ;é_¢B>“Fü"çj)zî/G“_¤ª†‘Ñš f<™<ãn°4æìyªß†àš*Ó£iˆ¼y'µ+ á} Ø¥$蚘m-´_ʇ˜‡ÈuaOëƒ{Ú¾¾·ý¡e¿üxj,ž¾jsEí*ïË>ìJT¶KJ@ü å:æµ´†pW]蟾00 `Çr_ pWýCîØƒgnuÎ]Ÿ/AW@ ÁFd4…†˜ˆÇV7=×Y7ÕÎoyrHN@0.dK$†…Hˆ¬!ûSoí)«âÅÚ»à¯%E|†)EíbˆÏéyO-z ÔnØôf3;öP’ê$õØäªà/.Ö×%N$L8ùlgò¯»Œ(y¥WŠå™÷ áÑÙtVsWw‡XûTïÒæã/¤ôtcÈïÓœ»ÞlÞÈÃpêÆø@&å߉‰öÉ·ÙÛsÎÀ}ö¬ßþBS³íc³mƒùŸÜÛá*¡_ÁM¢}›¾¹¯ã᥿êxâr öøÑ÷CDns +§Ü>Ý®¯ÆoÁêÜýö¿ÿÔµžI ˆ¿Ö‰Z¢Žÿõ‘Èè~ªa¼ «wûhû}VÓ95|†ÃL`gÕ©OÑ`È'ì⋘¾yŹl-n¶Ú ßÙá/.ÃApñîñ¬?µ¿ó÷u~g‘×_ŠÇ‡ãñ‘p©o1uÍ\Û´ìsXv•9:yaâdÏ$V ñÕwUGüÒ)_Up‡âÏÂZ{Þ¾‡ ûéÛ_ñ¦^ˆ´êf ±d Ü—L° ÛßR;ÕÝùòeý»3äòwC–!FS²z¿!k¨BËYŸÈ›ÛÓw^×ùZ+d 2fÎ䙤:'·a_;mÜò‹Ä&7ò¥9ÙNr%˜Ó Ox±ãcF0°³ÉGXÀ©Ø=IrDRAVwÕðèìÞúƈåBn™ûµ½üׇÓY)OÄÆs%µÛ܃ÓáLqÃä£_nåÔJ(ïߨo1Á _B-ŠhR™;`ï¥ÉÖáüw´p“(øv8+ï‹ð´¿ëûû»~°¿ë‡ú2ãÑþxt »_MÞå¸új}5nÖº*‘¿"~¹_mpW)ⱂ¬f 5zâÅŠˆ…@¬=¿g(÷ËD\Sû …þ<r1"Ö`%¶$nl$9§i/tÔM·™Ý&™ˆ|ò®!¨Ëjåu²¹¥cT™—9§'d)±Æ–_:f­U¯1²MÉ9©Žz÷ wOë¶_c6ÓƒOs!.éIÚr—~þ gŸëHåB’Æ=Aj"& /çr6«y¿V_Íâr Säb6†" 3J¦†ÙE4äf»Ð†Š\|µ'{õû†ýEPÒzä*¨z‰®g´gxªm¤ðÌmíιk;M:•A곩h4=|u?´Í|£è%ûþ™òüVG«UÒ]%ÄW§z¦‘ß|ÄW•yyùÄZqRÄ €Ër(‰zÉÁEo«AMéÒV˰.© ™j2ùFCDnt°o¡1ÚZㆠ۷¼¥ÇèTᣕ@¼µ64Â/=³&ÀÆ;#O"ú-™ß龫ëv0ßÅ—€øðehOòó’LÆÕZ d3LB¾œ#—ߨÑ,"Oö_Ѭ&4‰.Q'þF“·¯^R85ZeR¹<Üø™Î_tÕž*ê™Ñrk2Ø·Í„@†ƒ['ïߎã ]—§ÚF ­ýyßH£ö\ÉL`w]ðÄ”OŽM ŽN êÕœ@·:Ë¡óÚY®“¿ªZÅ–Sñû·—òÕiÞÓº.â« î/ü[kÅ…ÓVÄs^kgåþst¿†3î -`Þ X·¨UÛ 2Ð\›¯‹¶L»ø˜¬½KÞC>ÃA ‹9ú‡«ýbýX’Ò ’aY“µ–€0Nráú*@„¶=¿Íl&{?—¸ï¹M8é‹B `°áæb=/lrSõ½Í9œÎâË7Q?¼Ä/‘ò'?wØí²d(µÃîé9ï\ΤLºì®L‡1Ú ™Ú̓ˆ™¾ézÞÝo|A•˜È-j „v\™j¹\héÏÿàÝ]ä—pè§éã_'‚æðœüÑÔÀØÔÀØÔÀ(|ÔSßÕ^ßÙV×Ù®¿Ìý[žÚ·åé²ýÕÔê¶d*â¬ñÕw•²{AÄWÜ/>ó)èf<"°ÖÁÝÚ¦x@“¦"bå,î'Ñ‚ÀeîVãx†DÆÙÒ¸_iªÉ7˜»Ÿ6p«uØcõ¶Ö`#²†­!kÑ߯.â—·f' q[2†2?j6Ô<7íÿ=âhüügSµÓ9ÛòWhS¦ž¶6eÛ“Ã&]’‚CšÌ¤/ +Ôíí "ö…‘$yŸ³`qEQj“ñ;r«žÚf/d/ÌÚø¨;ÂÞ{쬺§¼^´C†ÃRVí¾%_MΕ¯¦Ú6–k½Rh½”ÿ§wt… >¤b¡¤ÜüTÿØÔÀÕÉþ«óD ›Ûê7·×on‡z~ÿK;5p÷é»ÊtúWW«ÞrªgòÔÅIû–‹øõwU@|šòUwŽì"bÅZXçÊ]¬q@p†î'€Dh÷¤ÆºTÕZËà AÎ×FÓ›Œ2]KIz­“0Ù{ßrÑmñJÖ°dVñ/ŽL@äήF”ÒpBE âXù‹lÄÞÅK˜aÐyè“Ìæê™?/N„=âƒs/êÆqÔq‹Ħ ~HÛîu³ï¸`CGMYNŽÿÕ)½`‘Ÿ=÷wGŒkv¤ÈWJ£â™R) oÜ´â—â €úêàâ95“ „,Øì~E @/ ÝWs­£…–KùÿT—zrau“ 4Õ?1Õ71Ù?1Õw¾¨ÚÐÝÞ°¥­¡» ÁÔ§~W'î)Ëi¯Œ*´¤jÙˆ__pWŽ_7Ž_}ìôOÚZ «ÆÝ5îÀW¬…ÅÜ?Pÿ!‚âÔ W÷³Ö2ld(2aA×fs-æ ¯]ÑtžIˆ%tфφtokÈF$5Ÿ_EÄG ±¥5œÚ.DZc! ü»w÷]6Dy‡®?ŸP~nPS4€,¼!x¾ûpÿKíE!ŒÇ}6"†ÄqÌþ]•ÃÀßÚZ+°/.¼ÃÁâˆ?ÿw‡ #2T4€Sííì§ xSÏ>pg_z-2õìM=Á%€ŒƒùoÁåï¡R(é’„›;³e"×:VhÌ?ú–N½$èw…”oÎ]mœìŸLR{¢ÆîÖÆ­mÝm.òIµâ˜ˆN ¼y}Á+™/VªN_œÜ·½Az]ñzšïâ«Ä¼Ÿxä÷aÖ ð–,Y¶,DÖÚM™ƒÆìC¬©»¦6µ59Eï;`¿¥3¶}Mj`E4§ˆÉZ7,·§½n߬½óÙ‘çß´ €ÛÄG„˜¡ †…¬eí¥'Ke!ÖBjoΊH>.-¢aÿfž“À‰­`²¶n˜l¹ýSDyåÿ5SjÛ&wïБ¢¼niXÔHL€qãÃ|Ê $kPˆ%.‹ßø àôª]·á}dl6dñs®ŽùMŸû©¿žË†˜A~f$¹¹Í²ýwõøÄä’7j†²š[òÿÞK¹ÕªâmvðÛ ö_šh)<õ¦M!´™¼ºyr¢sr|S„Z×8QÛ8Õ¸­‰Bä£×c·ò©à‡^xë ÏóªÒ#oN_œ<½´¬f½ÃÀŽÍuq,…Ønß\»}síZ?Ý‹Òe2¡¦ªÙ;¬u-ÅJˆ­É™š.èˆ.Èéèppwë›tbAD”ÝúW)µ3-|ëkWÈó3È,Ýh±ÖZÅmüüÿTÆ—¬p¿|uV_|I?›NÒCD“Bll¹ëß =ÿ¿‡ 8LqIµ±Ã!»§ùþÍ+¿“j‹ô}™€†BÉý’,]‰–¾`uNßÿùÑ3ŽÚýHÈð-Ÿ¶s*ŽgöáŒïm7Dìúß}Úî–5¥Ö7ùEª®[&9oÄâ! Ú3<Ù<\xæöMãã;ÇÇwLNl›œè†ï˜lhºÒÐ8ZßxU¡a†à§O:gïj^í¿ïÕþŸZñi^KUšïªë"þÆ€;€žKÓ/åôžµEü³ßúœawÎ=é‰+]u·x8÷ŠûÚJM†!vzºà¾îzoEätôÁSYgäá\<4|±®mÙ€I.¶ÕÏ´ñÏÎ-ðˆ Y#Wb•ˆ¬!‰H"’æÿö©²¿ü«ˆ‹kN9T#ðâØElL6¢àÀ=Dq쉑2ìHÛv¿èioáE¶À_Þ},Õ¢A<ÒÄ'JäWrB50ľ:QXúÄ'uTJ (jo÷žSw‰ænÀí,öÒìýwCÇQÖ“hkæü܆V–¿/oÚÌÈØìÉÏ|4›åš¬qííó­z(¥&#ÃÜj&ÓÄôJž º$©è@ćf¾áÚ"Õ°'«\CäèäëxhïÐÕC½›ëõ×ÔÒØÓÜÐ×ÜØßÔ8ïîý˜…ð«™à‚v·*ùåþ·¯äÄV•ÖŒïH!>Mùî×ÒZ!þÇ_ùmI°î>¬G¹²^W†ô¦&C"’›.$Î]§¨Kr"'k>H¾O³èÐVÇ_j¨iæ×Ÿ¼ÂúôrÖ»ÞÜç»U#újZÿnEˆOÃ]U*ÑRix29 ¤ð{ßõÿ›ÞGÿˆ…‚6Õø SuT71ß[{_}-rúDbÄ~¶ãOôMØ3 lQ2ƒt>³’?݃u‘צJçûÑ#€hÏLM–]·ûœÎH‡ø"Ü.B9™0RF[eÒkVáL½§¼#rÊüà+Sw\™zÕ©ÛÛ¦óµSqg][ã™Ö†s­ çÉÕI Én‡IçWÓ=qo ü¾ Jù—úßy¼ÿ§Wp^«NkÉw”^q]Z¤Û½WßU9Äÿàè?s}&ã»õ7¶·ÝE þ±g­ƒ;‹ÈôtÞ›wë?Y¤î¬œ¨ùðÉÚû‰’ A1Šø K_kýL«IÍŸ‘°“D>ˆœ©—ö¿]&âçÃ¥ N¯õ7Rî›#æ{˜Ñš IDAT(¬–)„5e#šƒø¹U¾£øàÉûì[Žet«ÃØjDãöM_oüêªåiÙ›41#2, DŒš,§‚u?÷Ñ—I s÷“gý)lÖá™^ôeê†Ë£Ü‚áÜ}#¹7Lß32}—žÜ–Üìë'Ÿ›ÙÕÓÖxÊuØø†wÑs/þ}&¡Æ»?š´ÐûdæÅþw½ÔÿΜÑ*Uåú#¯¥”‹oÖ¹y¿îR¦ÞK¹m]µŠøÞÁÜj?gÒuskXÇn€%Ë.r€X!›!±’›ÙØ0@  b‚µ&+Â'ë>|(÷ ‚Õn6¹åb‰9Ã’oæ}¯žÝÖìžg!†][ƒe! ˤo'¬]fÇd{sËWó+îd2…¼ V['“0'5ð=Ey·†R¿4puÎÖæ q£+Ö…E?’wËþÓ]žy¿ú…#©Ùaˆ 3“ˆ¨‹¯­1Á˜'+˜’|†9½'³97‡¯¸º¬ÆNïœ}ÿ`þþhm®ýIWí]uOvÖ>³ej¦q,n*<}wQhPò{ëvdaí„îý¨/¥2žïï wTß÷moÈìlÞîÝÖ°wÛºÌjJBÐ;˜Sʯâúì¯êÜDï.âæ¼ë_w(´îÛrŸ»ô“lÄVË ûk[74¸˜õðõUꮺ ÖBðjÃÏzf݈`¸’ì¥ÖúB»qs%ý `öÓá#cläã_kEDvËßþK|É‹ÀÀÉ‹Èþš¥îtœ”äR.þõùkbs替øKÐ9šÉ—ù¼%’š 'úîTÝ5Î|ýê¿`)êcùÌ}ÇŒ*Ù“Œ>´YŽß›™‘Ó³™’~jü :ÞIm°gÈ0YqkMk³ÆM pñƒüjÕ¢®˜Tþî»Ý“¬F ¯·3·ØÜþÎüItg¿¿¥æ‡Ý5¤›gÆ #…gïØ¤ïÿüEVÛiÜ=”d5TwïG]Ï @Ïõ¿w'p}©*øµžéuæ}Ý!~yfúV ñ_ùô/ÙØZ Ÿ³‡¶HØØJlC/<Áì;g¬> ŽÞÖ»vÇz½ZèH²PnÕÐ'@´%Cdò"Í>¶;ÛH{ûÇ#«dgÁ–ˆ¬«µrZ` ‹aé^2âWIÖU~þ˜7æ)Ä“‹ÝÃÂ%×Î!‰‹g Ÿ"©ÉPMÖ8. ž“Âûö bxó>z5ÀYuIܺG#º©Örû#ÝmüZJÆCjÏL˜.`Ø0Y+±ˆaª­‰tvX2ŠÀu»C‡Dú]³S#ÒΙ»YÞDö (ÜVmÍ<¼5ûí­Ùïúi3>/ížl)¼p{»?¯šÀ8Ÿîß#iY$¨»ç£¡gæÙþ÷Ý pG5ð}ÎÄ‚3½ŽòëñË“C|gíÖÎrR>QLh˜Ñϱu4ö‹WuxdH¬ÉÅ}‘Å„ŽI«QŒï¿ášâ‹Zk #ÙƒÃ5·ø–m× O$CuµùfÞwúŠvÇóþ†{ú#¶QXëDVáâÉF$[ÿæÉ‹›w§0)xi┋gà ¿øEeN|íט@ÞS§’tßCàÔ”ÇlÆg5¾Ì7ç§)´6e îÉc6ê ÓÙ|?~鵉=&'"g ¥m :ý¥#‘q\Žü–L~³Ž´‘WÄN}Of@Î_²DĆ²Ù·GÑ[¹'ic!ÊÔ¾–­?™m8›‚N íº<Ù0R8þºvxŸ.â¯ÂB$:ƒÀeî"$âÚ)ŸéÿÀ3ýï/ùÄ­[­=ß”"~Ï:Aü ÇCöù8¾»|ˆ—8YÇšglìa;€ßºû~ŸíûžXÉÍÄfK24¡#Þ߀[ë$é€Þ{y 7Dž^iú¹â­ÇMu3­Ì$».oÝôC™ˆ%b ¶]|Ä zÙ~mÄ/ î¥GÏ!|WZH\°¶Às`æ4DzÄ(l>oÈfÓˆ/b÷ƒã¿ 6cOžÿgÞq4ìŽâ »fAËï_FÏûÌ+Ó#SËðœ5«ŒšŒÛl¯6ÕÖ-¢²/±&‹W™˜©¶ñþš†wek~ÚWE‰ù=o²Ç35/kgä­_#*ÞÐ ÐŽÑ©†Ë…—oióÅ  Óð]JC©Ê*ˆ@5÷~ ÄÏô Ô3¶ÞµÆ|_dܘCüÖ†=[«šòe™ýÛ7”ëÊ¡|ˆÿ…OU²Û9ˆ‘ØÙw±Ä:mlf&vKvtëÍz1Ä‹?R òÖý%Š}¹éWZp3 ÈmRhä½WÉώ׾IGv—¼#b­¸Úˆ   t€ÁŽÏ-€ø¶¦ŒÈõá}ÛQŠ'ÿYCv±1l“€9{±rÊ_$_°ÔxÏ©±ª–¦Œ‘üØøÜçÏE©…¬êH;ÿòŸ/ýµì¢)9gK›iZ¼;8ý¥I&Ôd1d­Ä±(Êë\Å5µf•©qÓÏ5´~¸¶éþ*H°’<ÁüTÒJÆŠù¹þ·@m›®¿Rxm_k*ˆ‡wînð¯îL X½÷cO|èé›îX[¾_w–äYÇW-âË;ؽ¿¬ˆÿè'ŽÚØyv›j\8#wÜò3†ù•ÓÏÌ&Î=4ŧ@Ÿ ±HÑ’„3a]+¬k¡ йåV †#5µùfÞun”H"F†A°Æ¨sWîž]Lâßà ƒ¹î»­)àÊ<8.¨R§„QJm‘Xl|:W_‹U© ªä󖀚lª©Æniʈó <^hDA(´ê*²%¾˜±ç¥~é¯@ü•#É’%·jzÍjh’1¾ŸÝPKÁ ×Õè>LÔÒ}¸©ë;>âÎ QœûžÍÿñ#E=3I‡$ß6ñ5×>äÇÄ0°mbª~¬prOk@¿OÅà>¬P@ðØ£ÛŸìÿpI¯úFÒšñ}‰ƒ‚ÏöMžísF¾O«­Æ®ñÝe üÏü¨ËÜÙmšõl ‘™™%öö< ™±"¾gF¬gwÈg|>ïë¯nîL-¬ŽþÇ[`·tS˜d¨±®ÐÊ$º—“ÍzýêÙ3 à3¬í4®ÊêR¶{ÿæ÷à , îÎÙ:ÙE ;Ÿ¯4µÉ¯JæD·ÁxÞ P¡ú ä @m–‘'!à«î툑æÀ8«N~¯WµðוFH–úÆ€|åpj˜;9÷°!ªaÃT›eCd­°á0,¬yço5ïøö]¿Þ²õ—}ƒüÕ‡ò“Ûéo+ÓMªgÆÏö”×.[8'΂ÍSÓõcñé-¾[Æ×N)ŒÜO!„lGÍ/ü¯%½êLkÃ÷R§ÀW!âWoK&E<€² ަÙ¤†AÄVÈÆÉ¸™Ð/.ÁÚs½¡ì Š ïæØhÄ«£(uMTè§ÄK-¼ÔöÈØÙFÞ687’ÌFŒˆ$2bØF$]È:&ÉFäŒ|Dvÿç>¹‚SRB5ižÑ .ˆ-@’"§Oá¥xF¼oœ/Jꥷú¥7ò¢æ}l<¿ ßÿâÝÇÜa‹¶ûR[àwØ«°Òƒ¦%¾jUѼ…wÊÖd†™Yûg Ñ–[ÿe×-Ÿè<ð{D°¦Üðg.efìAf6Ç»O©UM~m“RÞCÜèÊåêÇâ3Ûš‰Bã.©:HÜŒ Î<þØŽÇÝÝÜpÇš¬_]Þgû&÷lmؽµs}Kõ_ëTÎÅwÔvwÔö¯hëŸ8öП¶ kE—­ÚØ pï¿H„'Ÿ;f ‹n÷A¤‹[… ¤7¬[ÖÕª$b-ô~­ù‰õãgô†uÖ‰\>kÁ Q#ÏûRÛGÞxå+ ÍÖdš¦w\ëÝÑ"b qm³³Â,±P &ÀÆb‰bbbXH fAL ’ŸûäðÇ?RÌ»ª¤£¯¬Šâ]$F¬'ˆW-Xh µY5ûù‚¥µµ\›åoÚ @c÷ÿ’ù ?îÞÞéöÛZÂK.½“Ò|í2nUª.O-ÞðÚ01e³†™ "Ûïú_DbˆA€«>«†¼¦6ª«‰ˆi¶àãªU‡ÛG•‰öþ˜AtÛÕ¯ýÿì}w|\g•ösÎ{ïhÔe[²%¹'ŽÓ;-t–ÞY6hÂ’&!$BûX`wÙ%ÔdÓI¨‰Dè,!z ½9•Ƕ\e[–dõ™ûžï·Ü;£™ÑH–l%á ¿ñÌrÍ3Ï<ç9Ï@ÛÆ)Í­ˆÖ·68+$¬ ã’’íb& îº}qN›ÅÔ FϺš£þ™¢eX<€íS“g¼öÍ>Uƒì{ÏâßrvWb I‹†bb°hŽMñ–çÇP ûcbG«•à!Îail:ž§[ Þà»?Þ ô ²vþ??´à ôÖVçšØXEdÕ1ÛÓ¡T”„¶¹j“û 2c³A:@ûež*¸cŠ’„ÎL5)»ÃIó6&ÓS¾j“ú¬³NpkýÅ¥^€ÛÂê4wñ«û`¼ð“žÂâ\ŸhÙÄž2€à§J!pÊŒë£Byý]‘bªJ©¥/úRÛó¿°äyÿáÏq÷õ=uùž WÙLL¹œ˜„ê*Ed3#ó¨º 1@O`îTï‰Ò½‘ ËûôtàîZ¬wݾD4A Õñ\'ïØ÷ü}/÷ó­ß2`E{ÍŠöšýÅâ÷å²ì­;Gg„Å¿ñÌΛ/Ëhh&B3ÎJÄ$Z,['Ö$ ÇÓÉ0z­&  ÷%ØäK‘ÁtmþÍ€€´Ø•Ú$Ý'°ƒ5&”€~¨ùGïüq®–[wîéi®AÀšA !-Ì@D"ĬšÁ"’ŽôX„C®ûÈc§]4¥_…÷ʸç; ­·Òé-αx.ïÙ*æb‰?%tU¡ù6 uJˆï¦.]×1}imløDì]:Å”£”ߨgf‘ü&R´ú—‰Ž‘ˆÖQdN¹gíy‰™%‹Ô>X&—ÕÄœN«š4çItwáî ]†ÐB Ò@{ßpÕî艃›HˆA‘‰(5,DBZBwݱ8Ò¤5‰Ö„Ž/Mõ¬Ÿ•µOùûL-_5Ⱦ_Xü¾wSÙ[›Ó­{Gä_f§ÖPD:’—¿ø}ÄêÏûvµ&¿wj9iQ :a”ôîoŽôÞIh 0ˆà™»ÖbüڢŠ֋ˆÈÚæw„UQ=·oPd–@iˆXWÊæJ†Œ€t t@º*¤€Dt(Q$‡^{ö”~›¸%º¿’ƒc–hÌ*:'Q.Ñêt´Ú %V"w°›üx€;8"~Ðÿ‰~·[£:U‚ãÀ÷„'§Âˆ‚öì.Ù̯ädí‹ùEF%=3nfõÈùÞao¿ö7_iÍQ ¶Þùùm÷þß|‰}ÔŒÑè)<`W2esÚ°xN|ørŽêû lÈ;µõ§{£u«šœN†Ø)6gæ¾;Úï¾c±h3ÇD.‚ììKþ>³›µ×o6,î÷Aí{p7µuç¨÷Öém»¦OäOùÜÏnüÊ;Ƶþíy:4i&Öî‚›±4 $ ­5› IÛ,LT¹fhÛ7¡óGZ¦o&ê †àƒD¼¶ù_ˆéŸrßaÒ,,ˆ"m\ããÂŒHAHGà"­Ç#L‘ÖĤ§þfžŽ>cü‘Z'ÇšLYò0×ø#ÏåÓ•zØ&`Á@<ÏÏ5Öý%V)å}ø”11ŠüŒ©©êŸwØÙŠ­Ô®ˆï¸A¢Èü1¬ÿýÙ©P±BN“R•ãHÈü”'¿¸ðÈñ¬T§UMÓhÎd´³ù?8ýÝl¶ÖB‹‡«ú¢§htÙ2@RsDèÞÛkqDBâ|æäÝÖ>âï3 î¦<¬ï"¿¿ÀÝÔ¶£Û ‘_0M¿pAÀË×Ü µ˜=Æ:ÊË…×nTU'½ð‰9Uc›Ñ.uÒÇOZceÒo† óMñ2q öÖ‹Žmîd’åGî0j;AÖéPª‰åxv“PÆ1éù£®;kj¿‹Šw±&[š@"‘(ÙA-°ÀÇL?ÑYMÞjÈû÷òvÌó»Ñj&ÖåoìŒó#cýä(óâ[ÇvˆÈÖTs…¿ñjbcê‘'ÝX=oå¿8uÝMØð›3«BV Z“bDãÓÛ‘·Ç#?Ò ÷xV9¾¦*°)Ž6&ÌtTï^³pd$Ým\Ö`{§Éi&€@÷ßÙ~ïí"$ÚüÀ|Ÿ‰2ÿ]ùù>ëk_àûl€»©õ[†­VÓ6»¿ÁÝ—!ïÓ†xS8ûz"õëß\b‚ ¼8ãŽ1Äû ±}°qeÉp#˘ -†¼‰Ï›ÔI£¤i´Šód Džª=bsë´¾Ë,÷”¬It@*Ib“é@¥¡#FÆÖ>æÚ)LrV±•hš/!´æ[G µ 4wéö yGàFX³ÙÂ%9CÞKA¼²Ê;8Æ9‹õåNsŠž÷†ÿµÊL`!›óa7H”{ðÚ—˜ &³Ðø¢˜ÙˆòþG1Ùû2±ò˲ã=«`¦ñœŒe…™jÓŠDcµ šÇF«ú£îö÷ ‰5Lxà®öûïhó°mÍ‘¢)›ùråçû\¨g’¦TyˆŸm”Ÿ e ~Ñ‚ô¢© ¼!ï=»F³~ ^œüñ™ïnfÕzf´ñÂìÖðŸæxgÚˆ1Åk#LÎ ïâ…AnêïMDZíÖûÚÞ²¤CÒk"+ǧC© (!,¡ÙÄmòj\¤ðqC|åÀGΑÇ~~U"¿róƒ!óX<å&¤"`<[äyåചƺ`"Ê;}Æ)3§H–ªE£Û!²½ja…¿“Æÿí0Yì=3L‡œø]èÜ“?;Å̬V¥˜k-.ÕÝ‚¸Ïygb›™·’Éê.‰´HÑØ¸SM:pìžÍê‚ìhÕ@´¥µÞXÝþR´ö®67‡gþˆH ‰†é¬Vx²Ïšu|Ÿ=òž¬õ[­V³| ~Žw_^‚¯â“à€ˆ[|‰Í-°ÄyIñ²ÍÜ“F"ˆÆÍ²º9&— ¯!64XÌ’#¸Úkl@¼y"¸EPîKuŒõ#õµÇ¬ÿýÖ×¼æ»/è XBçŒ$ÑKÈ1sW¬C¥‚›{ÒC‘>þš3+ù…l @¤m|ç¤G:g¤u:ÂåÏÄVwoˆ'"ä³xsÁ,íËå"~8öožÅ*ï^‚oȇø|}&™Pú•O5ØÒöüxèHtÎ0wI°A46fìÔ³æá8ÅÙ¿Þ/à •cãÂL5i3óDÇìüѼÜhjÞÖ\o¼1nù8‘ÐÃwµ=tg«£ídh»DŽ<ôò^X³‹ïûÜM­ß:lP~f!~®»©í»F·;"_þÈp_]«ˆè‰aýÒÓ»Lt¶p Ý]H¾‹Nðô¦ÑqÍD5ÕAmµjÔc©=ºg~à’ 5zôÞEÝݪýѰMZ“6_ >‘ïì’lLûì´üªC±ñAÆü}Å›.‡Îmþ͇˜¡¥BfEfϪ÷AZX÷Ë:bænG–üš=ŸC7¾ê>FÇÅHðUCzgS­k¢Z7äc÷,B‚¤{þîܶ³ZáÉ>×j¶ð}¿€») ñ­ÕË[§…ZPsÜMˆ_X â'‚;·CÁÖ ?Ði@\GùíVâ£ØQcBf´v^xçŽ7Žy‹×fÊÅðzKÛ-ú; >á…‡NÂýH}ígße^Þ·^Ôõ£×þȈ3i‚f+ÇôaCCó]^¼zô÷V¹Ï ]´†DÓ1ÁãEye* r¹xÞubMÄwxˆ¯ j$&WÉy)Îß›‡¶dGm[%gÄ»–\¶ /{Ã¥"zë-g™™Õª•"Q‰ÃØå‹¤vWæÉ/É­{^b÷3«>’€ˆFÆõ ]×ÛXËÌÞqúø} »g‘ÖE“s@Æ?0BÐÐÉ_©ðdŸk5+ø¾ÁÝÔ'Ç/›.Ä?#ÀÝ”‡ø¢(Ÿ¬ÃÓDüèÀxòÊ罿3±Ú)Ï<“\îa±¸ï±Û)5^u1¼Þi­5Én­Ÿ{òXOþ觪Žm´_¿Ì¾º«^tCÀ(›2F"ÒUaÂ%É:dm›®¤ËK¯9c’ßZÅútÒ~™|v]Ó‚•{Îÿné|*`#»'§œ&Ãw8ˆÐP\üŽ.ó, I—d‘S˜yo¿9cö_Û&E$Aç ‘¯ Y)-®ej©ºÛ½gäx;³j©º7Gºx™„I&ø.}†Gìºkwmh°@1³Ð“÷.ôñZ“¶ß÷Ìõ“¿ZáÉ>kæñ}¿ƒ»© ÛF6lÁ^@ü3¨Å%ƒ—•zö¡ÜÀ%ò (7ßäY|A-س "»êWrj‹oî0òK âTÈÖ×|StnÇŸ?ÎŒTÈÌiñVÈä&ÙÈ/æ@11³r3«v'Ÿ³·{­Æ˜í»gÉæGH@BZ°amˆµ EK1^„1>H¯ÌLsÇøs¨ž þÈ2µqZÿ "ï¾zvöäùž “®æWôîGŸÖ)‘LôGZÜò°Þ;¹­ ÞÑ—¿’é[¯èT F«a‰B–ªPÒm±ºÝ bI½’——†øžš6´ môëÃň`£q#•(@|?¡ê‰ò^IˆÙÈÒF²¹GN‰¼'‡˜Œgf᫾&:·ûÖO™‘¥‚ «^™QVq·q’Ь¹Ý9ᚪ¶J±J—!f?¸·®w`Wkºl^ËxV´6ÎK2 ]kh×Põ Ä÷Ÿòµ½ø½>ûk†ñ}Ž÷dyˆ¯埉àî«gר™‚™îG57󃽃¥î{ä©Ée ÎÒžpuZ|5Þ™â­8äû#‘ž²S¬ÐØ÷®Àv_䌒éuc=é–‰/òêW8뤒Èèïé@BÖ²»Al¯•$dýÊ«>Xê|+”འ†@ÛýM. 2PñS3ÄôT]JÁ^ÕÀPîš÷ü(d矱r|^ÍØHoÃÒJpùo3‰5{PL ^ñeHÔÿ×Ïnn‚Ýìî2dâ&ªŸY©zrfÕj1nY‡kª:´·3««‡î­í¸÷ ×B¨{m³÷ÆŒg-yÇMÔøÇ²øÝÿ÷Éj&ñ}‚»)ñ–-šâŸÑà`á‚´DZZæWµÌ¯JÞd4“òww<ÝìhuÖÉóGŠ8\O8ˆ7ÿ0M3N.÷°<Böc@‹±Öx‘íE§CˆÈØXîÇü‰SÆ¿0ñE^ùÊ®«_mP^“D,:`© ì”SèV¹†d"…åU% ¾Bªk§F!DAbýºAü”"rQ¼K€ïGÿYôñOZ\iXÊÀPŽEBB˜H£|ûåï›êXñ;ͤ”5­ÏÙÿƒD{nûœ±·§RÌL"â·îù]Kv¿vÍÉÂ}Œéd°æpÓK.S’‰‰½¿®oà•¯n¿å–ÍÎ(á€Ìfe|¢)`.Ðj !;ßýõ oÏåb«–ÍÀ^¤9 î¦6n1(¿´4Ä? À@ϮѽcæñÇ.šÇLìè+ÿ‡ŸÖe»CÛájàô™ëí«€—¼¬P‹1ÛÄîxÓ­Eœ+éD‚o·"¾ì®]´fèE_êU¯Žg ¬VˆsLº¦+é@I@òê+Àw’X IDAT×yˆ©HðVñù°;›’²»év!Štì/+ΘáÕ K´•Ý-‘OXhšúÖ‹ÈîÆå•<ŽÇh§º°ˆ@kfb…TJ)2àîty"R¤J­1UgV1ÖÃòô¤-2áŒdb#Ѭ ¶Ïyõ]7í>C»\_Ü:ŠE˜±qÑšV¶qcâZÿÃYQYB·—?ÇÁÝׯí%!þ™îµ£wÌ ¼x(VÉ=µËM6%UøäW‡õx&î³h´÷G*7¾$ñSawVqÆÊªAdt4"Ño«¿ðWÙ3ûêKé\ñš®«^ßi Ð!I:D:°S¯‰RÆ)_Ü1Y¡>ãAœa÷sS¾&“4Ϥb —K†J €ïË•zü¢æÈRuQ¦Ó|ÃbB¨ "à+Ý;¸êwÞ3cÓê_øй‘{þ‹a†Hn!ŸÛ³ê¬îŠaÿéâeœ#>|ñ!D "> ÷`íÀÀWªo°¯ÛÍ+ÙHÃÐ#¸Ã⥼ÿ}Ç{¾Qù/í¹\ àÉCV-«Ê?SÀÝT·ƒø2Dþ™XE{ªâK9gŠÖêí´ú{œ(éÜ2‰&j~º$,sÛ_%V±à.ñq6Jh!7 k}*dh=6‘o´7êO¿W·˜ºâõ#$͈ÖŠQeåx ýîV%o¼êwì­_ Áüîò¿Ä>&ˆDHø#‘7ªê%!Ÿ5Vúñ§€ï°qðHÙlEãî§EÐ×´rÒ»¯þ]F1\¿¬¸öùŸƒäÆîû’Rl 3(²Á\8ÇD¬š'ð‰‘%Jzf-VOçwîZú•”÷÷Ý‘±l="íZH‹Õj´ÆØ¸a”Ò‚mïùæ”~cÏå²{â4ZÍ/OÒ=Ä?ÓÉ{)à €½c«ê¤Âàž-=•?àª÷t&4+¸›(#Ê#)¾KÓ7ù‘À¤Ö¢…tÌÖ-‡Ö‘ªÜýñä"|w×,:cìce^íåoè •„,$ša´˜.kH:P6¦æMW¾¿àޕ֯ìÈŽ¿QÞņ÷P¹Q¦Db0J5™š’>ƒxnV ‹„ Le\+±f”¢šã?‰rœ§˜Ù|1&x7$«D¼ŒqIÒ„i&¶1¿qòŒ#ììÒÄÌÌê/>R»£ÿ†êÏû×#Ætj}îù†HÿϱqÑH©g¹åof+þe=¹qÈ ü” þ™EÞ}uo1h¾Ìùg%¸›Š4˜©e^U˼ªRÇL¬•§tŠÏ óPù^øxx5ßßóðùļàs/^b½g´aî-ccQŒûˆ¼£þ|¿=»¯n’ÄKßÐuù›­VCâ!ÞN´"²¼%â+EñlÝéïâ]1Þ("@GN”O¨:ßE¹E SÒgàógDXD´0°`ÏÓa@ó˜ô¾‡ýÁ&³SfÌ'¶™Q S¬˜D‹‘_ÃyM5ªÇ艑%ë’ŒÕN0wübà£ïŠÎëL}Ö¿žÜÞáz§æ£^¢‰gV†÷ñqÙsÆE SSú=—«ðÃð £Õ,­⟡àî«{ûˆîxƒ;bι~Ô” ~ù)±?Òc}ã¸{×VMšâ­]'Þ’È—OB¼g´ˆñGºF+bŽÙÏžF…±‹ë²·˜(cÔU¡éÆ6]YÒo»"6œTÒbõöËßÝ|SR™ cŽ·åý6•4§Šï¼»‹«‡aºÙ€ ¾f’]lGü!ã³ÃƒG ÉÑ#ç3SÊ€»ÀtScþî°É™Uü›4Dç{ž½Ý¡ü/öœó󞈯þP}&ù’ÄÈ2’gœá]"«ÒØéfM¢©û_ÏßÕ?à_aù²S!Ä?ÓÁÀÒEÕ¹Hr9½tazé½Zš1g넕˘ùŽ ›vìÛ±{ S„ø%'[ˆ·1ÀNœ1`Èkñ–Œ;_¼6]Gðã,P8Д "ãc9õÂû¹§$·Þ]ÓzfnòU«—¼¥ëò·w†J‘ñMV.j˜tH¢HÞCüä=Iö ÅøžÌ!{X숇SQø.ObœªþŽ„DÃÀºƒŽv5¬ÊAü‘¿ïPÊf ØŸ#Ïh~ìBfJ…ÄD"Ä*ó›qà›è Æ &Þݳx¢ÄÌ*ãçCçþ|Ï9ÞÉ߸Ÿ.xU ¶—äî­&û×ü8oîå!þ(?i³žìžâŸà#ÔôXòûŒƒøŠÈ{¾íÝ@|óT ÞK1&\ÌÍ=Åàcj´.eŒŒ>Ÿw< uÜckË…—佟 SÆKSP—¾½3d°Df«_*0+AtÀ:d Hþù²ÓP™CÒ‘w :ùv«‘hBE D‘Ÿ ïeÅ’5Uþ—,fŸBÛžáJC¼rº¹RÌ ~6t¤¿X±í©`&VÊŽ,åmÚcënôžkvO4%ìð†ÉÿtðãæÙßžß²§°½æÖŽÄ<ª…u×S5ŽÉ¸Ñª56¾÷|_ñø‘Ÿ¬J6+<ÄODùg¸TwϨAù‰/":Šì·à¹T•€;» 9Qâ›RÍM½7¿»Ë%ÏX[dì…ñV`×Т5ŒuÂg 'ý‘Žžë0 ÓS€t¬ÉxG DÞY±\?òïï 9`©ºø&&ž C–ª@›aWÛt%Ð?o¥@v¯+ó8÷¼²Ës§ÇòHfµ¸5Õ8ˆwãN“Ö…ÝWUxF¾Îo,òøŠ£!z°y•¹Þ@|]MP—òÇþ1Ï3#‡}˜$J=yi2~€™Ì2U";ÍD eÆ|6›Yõé…ÁÌF¢ùéð'<¸¿½êîÙxEíg!š$"×_uI!˜f‚ÛÀ·þ½ÿSp÷]ýãÿÐj&­rÍhñ¨XŽUÑžªø%ùo,¼e%š2Ïš½ªÜ_¶ú fþë“O\¿s·€ªâ[OîŠAÜ' D†•ÛáU£Å‚­íçŠsÄ{{¥ÛÑŠ0 ˆŒkk­q /ö-žÐpµ»ºµÒßpá?w]ò/)VãvûéPIæòSŠ\ãìl0æk‹ÿg¨ˆ%–Ý“ë¶Í½®S³µQH™¯vi\âxˆ?þJYÏ +RLˆŒ½= ‰‰ÄbºÙºÇÊ{fò5™¤Õ 0ÒüOF>ýãáOú×óÖê‹Z»¯LŸ ¥cÃŒÁzkx‡FRsGB™™X»úlj*ý3~Ö$f£'»‡ ´šgy/c˜±¿(ŸÅ‹@G¹Bá+w̪¶ª¸³³o|gß8*†ø…'uzÂn’ûâE.ƒÌ¬ñ 6ú»s»'\!€ÖããÎ0#yâŒà}hpòeüdàã§Ô}¡’ì뢯’´’€u¨ìn“/{oE/¬Û|10Ø<á„_¡ç}FêáeG“È¡ëÖ\?8œtDfTÕní`ÅÈô’¨æékXq*`f‚[–m—7yýÝͬr"MÌá8S¬ÒÄN™ä}»zKÝ¥-ÃÝW‡E‚Öü)c´—(Ög'Ôx& é©S É»¯ù ¡éìÌ,‹ÙêÕ¯8ä|ÀýUýzˆvƒ»©MŽÅÇD^4©@DtvlŸ¼Æ«?=öØÐØØKWXêñ *ƒø™NÑ&bËùÈeáÆj83âDž¿{d×1ý·ÎwótOðaMñâO¬Î øþþàÖ¤‹‡”ªÿ9±ë’L'C¬«¤²~:ìÞ_Ln¡ñM¦ß—ˆšñYcùSN2Ûþi†ú€` þ„¿fœ˜1zài¢£Ú ×±¢0`V»lHòÞvÓ´±Cª‰41òÉd”˜éÆÑÏÜ8R(5w_CÅÃÙEC¢y÷†H‹3¦ïúÄ©–ú%Ìoôdg°ãú’xéAVïòž¹Uéá“ÝCaÈ©ŸÝànjÓvK ÄS²=,Ñz|?;)+'ï¦þòø^|@ÉÉÆ)Aü¼gŠ´DñÔ’áZnõ‡QÀ¬}rd,ìèP"Ùqó@(™,/â­ ÄŸ€¿}5•6Z“uÑI fèIRÊÆ1¢Ãïùyù;ú~©QX(‚‘Ýk2Áׄ³¸T¨ºç1Òú¨ k˼¸ùŠÍj=VL#+ßKZê7}W)N¬”é‚ÇÆGóË.Ø7K e×n5ž™Ç?ûÃÑÏL|öSÃÏ^§‹ËS§ÿÁtVm¤v‘2‰‹òzq…¿½ï¸ž°rù‹Xa.ÿåñÇÿôØc^ràä#Ás¹*Å÷UKk³Y=žÕ.±,þWSšcÚÔ3º))Ç›7vª¢õØ~û„›*¸›ºõɧœ°rù‹–OŽÝå ¾’÷Fû:cÿ{^FMrõ‡&R …Ä”ú ŒGÉÄ‚x“ÿf-Öç×½Ÿ~oS¹Ð‚RuÁÉ]ž|CÊø&õáëî%È‹n.—gb÷i¤Ö¡2ÆóØCúLÅï«ÓŽ˜ÜÔ?±ª·? ‘c7?ÄÎØS´fJ†L¢H9æ.†¹;ùEq2æ—ÉÛÛ9Žùõ˜î±¾kü³EŸú½éÿvö¿K½0OÏí]i3ñ4Ód»;âQÙ{£îÄN¸É&—T“zƒÔäû«ñ¨j@´dÇsF¶¡d@¼›—J*òÎ(©ß•>¯à5|§ï‹gÔ— -(S¾Çù*%ŠE‘VЧ^òžRÇû‚&³D‹ÉL¦Ó$9}«ª¢hò©†˜2-ar‹úŠóÒ[3Fy ˜Ú3„hþ¶|—U¹È0;˜êc óÆšLº€|ÆÄLÙÏw–÷÷ÔþçwFËìgœ!Ò$‹GÍ#§•$ïÁÝ×” þù‹[_°Ô.ª½íé [·¾à€¿>õ4€._RÉ£ÍÁš²HøÔ&‹òÏ,ˆŸvø¥Kç·5§ôèN׉èhd`Æ_d™Úp7uÛÓnßÐ àùKŠ[PvõMþ8@wË=Ü"'2 ±bV^Ÿ±Ì]#;ÙZ›™UãšâÈI«Ý[cå„Ú]=•ÆÔïéT¢Òé@DŒ(,íeŒóm !Žü-XÞä‰|…5 |Oo{‚ÑE‡~}MWÑ}^~k‡UeVŠzÛN‰šwÜ„* XD’sLv|)n¢2™´°„ÔîãeÀL7dK~yzwÃ}o¨dX&€3nép–öDgÕ¯Wu¶÷O»¤Ô#Ìk¥¸›ªäÏøø¶–ç-¶¡wlÜlÞEë¶§7xÁ’¶ãÛ' ɘƒ59¾{òž¼ò™ñ{™0³Ù @‰´5»¿˜Jcædݱq3€ç-.‰Œæ½12ˆ¯=±Kû ‚|¤™A%6úŒh´Dëìxþ0«Ý'.XQÖ@¢¯iP]=ÿvZótô Sß|o×ù§v¤ZA¥O¿ô”¢Gz㣥.œ€Mì#à§²ŠoÃMIà ¸ØoÆfH)êo{'‰nÞñc˜…|lEk•qÊŒJ¬d2-SUØG%bº>úÏëKƒûÉM_üÁ@ñ5&ñë×dGR£¼±¼¨™Ò†Èyõ!»Kƒ»©2×cÍ?®u¹|׿mwvO¾£ñŽ ›Ì…ãZ›'=xNÕ$ø^ÜMYˆŸóÖø‰ÛÜ3ºeLjˆ^Ü’ j!:Ú=3/q²Ú{òž¬»6mp|{‘íw¦*„øê»|bÖ:±£Õú#ÙF»'@ù8šD+Õ† ;/¼Y÷׆+þizÝÎ/Ÿ9oòЂ2õS;I4“®RþÐ%E ž™o:IÕ½Ûâ \]]i:9O‘¿Wm{±ÖÃÜÝ‹è3ÿt[ƃûîÖ·ºe×Ϙ‘ ؘdÂP¥B&cug»+5n¥ÆQÀ ?³Jt½|áú+¨Leæÿ÷õ}“4EÎüMbfU¯WMìôК־¯8yŸWؽgp75±ãztsÃ1-Mæò=[wܽy ‰ªwnÚjXY8¯ò{í÷*‡ïeÀÝ”gñs–ÈÏ`6¤ªnغc4;Ø×n ¾vD¢ÁÞ½äòµhAšfÜMݽy;Ïb&V¯ƒx#t–ªÔ;;µ†ï"¡ÆÄ‘Y‹hÉŽk8¦oû®p"LÞ䪳Àk!›â3añ —Ýé)L<­¿sâmÇüKè þÌKÞ]p"&s¦.Ÿ /?2žªdfÕ×Tõ™‚o2_=£«à üšÛ:Ë#+&ó«SŒT X‘@ V1±qÒ/C‘M#bïŸ1köÊì'1õ®–ó:{‹Ëñy¯?/˜D“ö´]Ûë}ï»tJ¿“2å;®Ç-¬?ª¹Þ\yßöÞ{·íšÞÞ»m'€£ZgêÎví­I÷©î9Ýq5ÛÑf¬DoÝ9ÀB|Ý|ˆD{¦ù·RI-Z°}FÁÝÔ=[v8¶4éuô§<Äïì„;»¬Z´<ôƒ£(·êmß Ð’Í:¤$²‚UÆ7`}¯ub»µ`PÓ× [?ÿþÖJC ŠÖÈÂC rÉ[¾`òÈBÒg]tròE ‚Ý,M~mSìyÃÅ®¬)2«Yª¦„ï©­2ÞvxòJ>váIñ²ÛªhGó›!Q[ÿ¯¬ÿÝùÜMÙÃÀž‚­¾n­2pâÌ÷ðÅïMî'.újËÈ$kRL%†˜ }îq&A‰šy÷uxSº¡¢vÜ?ÙŠÊIëþžÝŽœ_·—³oª$¾OJÞ}ÍÙŽë²Öj$–kÏ@‰@dËŽQí-éö–´ª_ÑÑÀŽ{Š}X†ÅSšŒôö›.Ö¼²wt&„øxÜ鱞$:·ì׿[à“!ÙãtIä9&­8SÌïëšm_=«åÌéÿ"œ#åkk:Ö)%U>â ¾¶Z±yIdæ›Ìüª¸«‰áUL™LœßµSx‘Å~œÐªßpGÆDÇ(¦žùo Ñ‹÷ÜÌŒ04\>æW „¥VªÐênô÷ïò—Ê'×ûjí¾t°d;Ôׇoêði3gV þž\Vô¾Ó÷ÃR‡5Úáí{{ö¬©×µ;ú1¯fFmV«8¾Wî¾æÄÏ<¸Â‰wˆÐÞ’V -€D=«Zï^Õz÷ >Ýì‘w_÷õìpTsÑóKþ‡«âõÛ;½ÓÑ“n&H”Ó¹œsÊ;ÈÖ²„=/ Æºéã)uˆ@Nþo©Ð7•\š2õ•5]—}ìFc<Çñâxɪ´Žÿi=4yæÈYªpËC²íG¾à³»Œÿ¦Û;ü®Ômó^Gˆ– þ†a¨ y2†ÈkK)ð9ïÆÛ>i¬±¯3æŸsYÿE•ã¸&·ÊÃÙÞ5‰¦;ßyÑ;6Õ‡R1¸\ˇԩCëlüÎÃ}£íÆL§ ?Ø;à°Æ¹ž8[ß§î¦ Ä°¤ö€ýò³î¶‰Ú²cÔyw¥&èƒZïš‘çÙànêþýæB>²ÛA|”Ͻ­ÓSxíÖø­ûùiZç–¾þŠØánq\g¼ãõp[>ò‡WP“XãWêÙ¿¿ñ?×,ùd©['-«†F¢¯žaˆmp7å ö#ÄÏ"¸£ðK²ø%«–Cdt×n@½ºõŽÕ­wìÍ“ì3p7µvçÀƒ½Co*ÉG¼#­ Ä[ý=ÒТˆ u6«×ý|D¹Å¯½$À5Þàê$xñÎaé‡W)ÉýK×Õ›¾þÑöÓ§û;(¬ó>Ôué'xö×O‰þHƒ›ùâ{bru–*Øü ¹ö#‹ÞJÀ[ïʸ1ÚÒðJ‚^>òG#Ë\ÀäÖì1¹xít›¿q]0…ÀË5­Ÿ¼rG¥Í†ÿ"“ßVÍÓÜËÈî•ÔªTî ªÈ\þû~l0zt°xccfS…é°ºn’ÍY¦~rqæ'g~rI& (é'—<ù}öºf>iݦ¡u†È/Þ?»àx5Æ_ã ~´ÙÙKƲ 9xº¿ÁÝ—á#‡5”ü»ß=µD¾¾8ÄgßÞe€› Zë\Ön¼éC¢£E¯º(^îa†Ô»ˆ`ˆ7¹4ÉEN¥]’ñ+LOsâi¬õ0@ª¶=쯩«V\üÉ?sqæ_Øå4™ˆ$Bb¿¶•eh:â{åU>Ãø±ÄÄ»3Ó¦úWdùèŸS!1“$ScÁ]‹\Åç!‘*4´ÖÎKãš¼WÒ¹'É™âýF§S¨œû»ë¾xúÊs§wîÉÿªõ5Šƒ#ÑÐHtѧnüÜÅЉÈ~üØYVcˆ¤8\첆Jð¦TjóZ@¢ÅÅÉ;àWªÒ¦Ú—‘è•c·*§Õ(† ³‘a‰&ªÃ}¾¶êkׄ_…‹œ¬âß·ô3×l. Y´ÎþyrU“ Ûö×üyÍ•EïUÜW¨Ñ•å@OŽOŒ©¢‡•ª„øÇsV¥q€ûàæË27_ž¹ù ëõƒ€ ÄÝtÝ¿îý³—¯ßgÜM­Û¼¯!~¶É»«"tjëÎQ©]ØØP«FzzPèCÚþzHÛß*èýEÞ}=20àºrïÓé* ñM ¡:éG–¹‹›cÙô›è(×üò¯Ã @™A·µîou™ÖÛ ßZ®®|z öÄüŠÿ³š½ÐæòÐHtÁ§~Ä&®Ëè3Þ6“H›E2V–½ÿË=¥ˆ™7Ö¼ˆÈþMågÿ& Êû¦kª¿Q“3XbT²N]ñÙk» ã€&9ƒ8þ×ÿ¸x¡[ëC)îËih9 ›ËërUO—5w•®)í?(_O ÛÓ¨Þð‡ß_™ùÝ•™0t°®, ‡ŠËâÉ\¿÷O]¾ìÌ8¸›ò¿rñ¬{‰ö¸C5¶B$ê+kÞ=0Ú;0V¿¨©¡–‡{v‘›­'‘CÛþZÉ#ïwp7õèž,€ƒKåÐçX|åÍå¾ìž·tZQ]kmÖpGzû-çJ”›÷’óü“_ê$‹3ÙíÇIb7„&]¶œºzòA›‰åù{c]@„¡XÀ‰ÄÊš§É@K›&7N£xÓZ¢%G½õÄ»;”™Kb2ë씢0 åV2Ån³¶×j®®þfÑÇœ°*Yï=àß¿½¾Òî«/­¡#Šs€…üæ¦?~°ÈzÂÆú@ÿp_*{–ÁFû­ÒOçöÖ¾â!~oPþ/WvüåªÌµg®»äÝLxók?†Ö–³“Rdø{p¨0`¥èïÛËS(_³þb À¬Bü>wWE8•v ŒBë†j%Іƒ"‚>¬í/‡µÝZæç¸›zl0t#L¬>×q-JäûßdMñˆÜ†'­wüáS:Ê5¼èÿydl„O%ñ·îÉⵆ–Å$Cðß~ü¿Ï<è#Ó;ñÆú„þ Ý9‚ýÎaü3Þé-4Ó¨³^U‰g¿{÷:ÌÓ©çéƒôÝa@Ì$e‚T¬¿“g®®=ÿªànª`T²¾³®"G|²>òãïywDÞ.Îþݯžx|Qp_õ-ÑÖèµAj7ÈŒÉN—œ:Uˆ¿ýêŽÛ¯ÎÜþ­Œ¡ä¢0àßþá*fzÝ+O³‚L@ŠY¼R¨øx¥f³FÞ}­Û<ôôæaÌÄïsp/õ¦³I)ÙºZö Ì«WpÃëoûsÑÇ›SànêïÃÀAér[ û²D¾/üÔûÆN³ÆO´Hä|2Z‹Ž\Þ¤÷GÚ)Vq¦xŸïnM®ñ›œÂ_öDEŽìdeÛHW±^¿¶Oë…Ûߤ :0 ¦!>*rzÕI‡WyÓô’£‹Þzò½ÅĊׅÇ3éÕrO*$f†5ÌX¯¤ûbºªþÂ+'ì¹.U•Ëñå+±,;Îþ-=\ØßjÏõ.ÎÙ §nªßˆ™Ò®Ê{®é¸çšÌ=×fEAèZ¦Š ón½–‰þé%§«X–a'ËX¸w·Òm?]3ãgä‹‘X²:«5K¿ÀPóÚ‰voλÖ8DGu5¢1°¹w^]`? L`ômÜ—/uoʸV¥"oQ(Z}Y#C÷MHõÛùÆN­­ÞÈ0»ÿòY‰r5Ïÿ‚çì¢ô3¯!œß8A«‰bVn–ôÿí?$Ê¥ûœçé¾ ›-°ñ€óÂûÐ`­OÕŸ›ô•_óðWÎ:ìÞæü†€áѨÔG‡m¥Š&Ñ¢ÅüS)··oZUß©û²´8y÷y2 Ѭ8Ûì_ŸC`7b_ÞtñåõSþBcÊ@ü¹Ÿ1=”ÿÈ3yÙî.ø×§kâÁÜ«vn^4ÚÓ:f3?¶ó7û(²q"Ä?úíŽG¿“yô;ÖCK½Ã@©ÐÀzîËʃ€o¿÷&œpÜ;CwùP*¶Ó¨„—f–ÎhŸâ;¿b&P~y[5Ѿ&ï® 5±:Lä.HmkãàðX_÷®¦º Éî ÜÕö{Ìmp7õä¸2…•ªÈ‹ô²Œ'ï“#=oê2£ª>)‘¼ý¿$Ê¥Žþ?’äéfOˆÏ*@ì±ñ‹œ¬;åT#_—|èYÿýò2×2õ±No{w+u5´¦_QHÞÛǶÖõnÊf5€žô¢íé…ÛªJî'˜½Úð½ŽõßÏÜsé?«CEUUʱeet ”t+5ázÅ¢ûýŽ;ôMRÊÛiÜCâH6›g¤^½¤ã‹3¹QŽ÷¾xzËðú-ÃØ ˆß¿àn+_¢•díÛNvÑ{†³ÝX§ë”?¦±.€è¾=cǴݲO¢òz:JXNq«fbOnr¤e^Äo~}—Ä<Ý*ïZG:Š’;@ìx4O˜â­]r <€«ïÿúGŽ.'n¶ÌKYÖVæ] ¸‹øæÛÄÌC|i׌ٲô8#Èá©G €ã!&¾´ùòKfÜÏ<âìK}ŸS…xgx·™¿8ãšä1 öt·mÍéžš¶žʉ›RmùAÇæë3›¯Ï˜¨ ˜! N§Uİnå”ÜÉ‚uÞõ¦ƒj´uõÐS71ÑQ½Ö~0¯îÿË9{.ÿÔ–Éæ(ÊR.˹ÓY_¾wïtÚeð}ý–r¬pb͸W Uííì•Þµ/Xà€ù¿´j,`¾î£†¶ìaÉêfseÿà8Mõ)"ôïɤ¸oëëöòÅì›ZNC"²uEÁÝW˼ˆvôæ©öË› ”Y-d)OxÌ'ˆˆ½Àd§¨ÀpOvö dÖ@+"æë‚©MÙy‘ó«$_d)ËÊ{ïϨ€§#ˆèˆôcÌL+Ŷ} |)ÞžZm¼–!ïX›aEÉ¡ :<ý¸ 1iÙv*]´°ˆ|zuÆÑç\ñ@‘ì0ƒïÞ&_¦Îþ~&«9qVSVsVóN¿Îß:o`£y›¤RjhÁ²‰1³W;®ï&B &› B0ÙîsŒ9Þ]Cv·‹›iv·ý[ˆ]þ‘Hký÷-·šƒí1D w¯_<íé<¼¦µ#§)Š„´P¤)Ú?üÝ—gñ3"ÇO©<¸WïÕ¦ì˜h‰ÇLæÈš¶:Ùô÷}{Æ QS]ÐX@¤oϸí»BÛvóqm7ïõiÍzmZƒ( îvì°p~Õ¢Uñ}_Ûé Ek‰ø†èœ>øl£¼ûtø80Ò›&ݸ“9ŒtÅ púóŠ„,\PE„‚O ReCœÿÝ/_5åa}Fˆ|)îþÁµ¥  ¹„ö#KÞ9ƒà~úqçwT6ã àc?èÈ‹šÑ±!²©oý¼þ ær®uåðü} î}]»»2»˜Q.9Àªç!;ݶLƒ€’,>  äаï@)ç™ ë„qSr¦xËÖŸØü""Æêö«€”b°R¬+æ@±b ­¿óÓ8£×-êxuK&›£\Žr9ŽrœËq.GQŽöÖܺ÷µ~˰÷í5•ùWfFªÌ…º¡Ý5Ãý=-+¦pçä÷yÓúó¼]ÌT ™ëêÚꆶìéùã̓õ#©€’:úí.@ÜMeŽoûõ=[ß0#ç5{5Ð0oÁð}e-k;zÇίк ½ÍõÖ½¦sÕï2L«w¬½G|${ЙUO^&ÄB´ ÒƒŒíHÌBV"\y]y÷ù{Þš îÎ3äñTœ/.ÿ] çwzø2_W­êªÕÞ°xÙx?š@ÞO_Ûa¾µ<œ[Ť¨~:°äÌô?­E¼†{SWÞ[ŽHç ¸×Õ¥ˆ|"‡ÀþüøC×5î~š\vÏî†e µ7*<{5tc‡Ù}®Ì:.#·!¦Ïâߌñ5ö}(€0žÙ[݆$ ¸¿>¦äþa×mû³­\xª…/\¿ënwwûDpÇOõ¼^ÓœÉf“´#AäøûþÇwä³øò?«²û`í¼ÁÚywÔ l[xà¤wáæ²s½ì\OÍ+Dìˆ"4ôÀÏêr#5UõÀäÖþ$›5(j^ðÏfGÐñm7ݳõ³q‚3RF–y*W·ý‹s}ÙRÚ¾ÖÓ; uAº­9½u§KƒzuçÁ¿Ï€´Es‚Ö92ÀÚ‚>´±h€4„¡!Ð0Žs­™åý㟺&õµRO]PàÞÖœà_’¯2l*q,Ám…}ÂÁ‘¨®ZÕÕ¨ÁáiB¼ñÌL|—’þPö&9ªæi*ó‚˜ù‚Ö"^ÃÙ®$‹/ ñiï7uüGÃîuæÌúšVØ7à>þ£ƒ¤Ö Äç8`€žØÞêþ?^ï)@°ÕesbaCÝâG°;X'{Ù"¾¹ucïËç?oEóñÝ}÷MÐs¢­÷ºí˜Š²Û^7¿#Êå,”k¡H(Òˆ„-ÜkÌ |7eˆ|ˆßg=Õº–ºMÛj‡j†úŸ8ðù“ÞKŒ‡ÏhyNÃ3p1òàÏÌUÓ‚¡áº=ýÕ5u{ªB® A@6+ŒÞõc1_¾Ÿ÷M„»¶¾yvÏsïj7.Ö}Ú³»ÊûضímkN·7§·8<-šÀ¤Á¬…‚G.Õ‡œ1¶rMõÓW ±ywY‚/€ÀVC´XÉd uæ‹?rÙß.ÐÞœ°e¸—)Û_Õšˆò–7M¨Á‘¨®fš¯7ÞO ïg=œ!ÆCã+™äÈÚ A¨˜).X<…õ~³Q†ÈO„øs¿“wž÷'VÙ$蟷Ò\˜mp—Ÿ$Øz¸)ÉÖcˆ³„ò®ñÜœ¡ „Šƒã9ñ ±(ï¾ÀA|Á­FVhSÿ½‹]ÚtÌ–ÁíƒØOûÐóп-á2"p¤ikÎá;¦Iòs héÛX3Ü_;OW‰0Öó„R)%0oŽŠ 3y°^ôÁã[iaõa"ÑŽ±'òÇ>BÓêBV÷ÆúŽH(§ ;5ÆÒöœ6×À8g4h]ð-`.é3¾¦ÚqÝ—µ£iš`Ñîul¹¿f¨¿vpàŽ%‡Ñd‚ü—öëš3R“ i"Ô5ôï™?<ØTWßK\„TÀ*…(§Iˆ$Bæ/P@„Nè¾óöÅd¶ ®#ØNŒŸ<…jÓ®ònÈ­UÍ­c;Žnï)»AɰæÅ‹ÒK¥7m}øŸºŽücFC3Øž³ÎZâ.+™ýÚ,$ ÍN‘w¢MŵdQÀÛyÿ%=eçZKHðdòÅz^'­ÁáÈ@üÞÈñç>šÓÚ‘%DrLÖ˾*ð&Nµ>þ’Ó¾ù×ë¦}wÃÜ{o$Ü×v´ °zÝYM·åû~jfâ~Ùa‘7 €Œµ1‰³V„)ÄY‹¼â=}ŽåòXŸaH>…O…lú®*`e5Ü#ä ÷˜ðU _„Ñ®ñǤV·T­êÖ%$ÿyõ†šL6K UçHS$i»õH!ÞBû\äï¾ Ê›•s¼­öÞ'г.=:tpxgÍž±ÛVl'ËÁãÙ”b²9áÄMCƒ-Ô×í4פ!—/Ñ0Áu_‰€»n_ì(¼íÊ:oÿj * †Þý•?ÇòàîkáèvˆT2¢²dQš@ÝÛGóÇŒR°^±€FVœBDó¶Þ ,…wQˆ%LñW×Vj^º¨€yÒrUÂ~~Ö#™G²+ˆè˜†Mß\ñ½ Ÿ%´šOŸüî¯^ÿÿÏhÃýÔò¼'ýø#¤híðb0ýöèoÔ×ö̸Ÿûò÷ÿç½’òÓÛ5˜J©ãïúá!2¶÷ë>œ÷‹š‘S˜ÿ¿^còü*yð]âÅ» †­OåÝ":¤[× GÕCÑÇÛ–c*`‚ärÚ¯û1ÿ©ëëz ¶4Öm!Šœ À„(§aH¢íæ /:¡› wß¾ùàNÎ/m.4]ÿ)ÀîS*µ—ÌHõ¤-ÙÖ2¼uGM[ù#7m]º¨ziku÷¶‘û_ÙyÜŸ2Â&ƒŒk7|xéÉ»[;lï2€0¬µF$fÓ5Œ£¦²ZÚZ  »‚¨"gU-,¶ß«ÀSùÒGäëkÔžÄ'Á=ñ´y¥=0ÔÆ,7ýÍÙ÷½jÍù˜>¸Wm{Ø‘Œ,:ô¬ouä ²`™ip_tSÆÂ±Ù\š€B?v4¿'Y|[O˜g¨Ë.f°‘R ¢H„\¤B®I³ƒøÄ¬Sü˜Ež1ùAÒ§yÅ@Ýâ-’½¹ª#ÊfÉèéÎ$ë9am‘=AÞAÝuñ¾Ãg ǦðäoŸ_ÐÒ“nì'AûÀhõXT=ªk†£š]=¨ÿøÒfkÀØ}aþK ¶AÐX·Õ4WƒÀˉßúÆâŽpÏ‹“„ÝqyI}è@"ÛÞSn«C©ª¼ûjÞ*";kÛ'=rikµiDoØ:üü¿dü\+3 .é ¢æ?2Wúæª^5¼ž\ßuÒLóemÕ6&þ~>òš3.º¥x.X)ú9f]JL·=OÍú`O1¡&·á>Áò<åýÓÏÓW«ÿ @Ýêã1;àþÑWŸ~á?´R[†ÅCk=Ì\yîU™lDê;ïD¢ã:mp_rsÆw/«êÈa \qr'séè¡Î“ €Þòq*äÈ øÎ·ÃùÇP*ÏÖø3#•R ×N¸Ï·å 8¬\S/KµÖ=n  eêw8rq«S`XkxÚî„k‹Œ^xkSœV=wñ=i¤1ýUÌIˆ§û>EŒW5-ØQݸ۠­¡êí#õYIF©Á¨z(J꿼|¡…lCE—ÐX·Ù^O AJˆtNÇR1ÐþÁpÏí4™Vcåÿžï ºß;¹²1Up7Õ<´Å$Æìª_RþÈemÕÆh´~Ëð ouϤõµŸÈ„…½?U¶KŠóUwÍ•uÅ'-M-o¯°a*œ¢ÿ±G3.&æßýõÊ*Yõ®ãšDù¢àþ™¿wœwp—‰®;x¶À}ÚnydÑk¼íðäM»"“8«ùŸî4׈Ÿ*¾¯ümÆø Š o’•¾ãâ€HYO:Ê‰Ž´Î=öó÷'ÁtÂCåûÓQÈÖ“÷J…±Ú>AºA*õ·Â’8û†‹»µ%ü—€j³mfËAKúz1©©D/l<‚kvXë_Ö¤A[óWÌi}Æ—õåmÕËÛªçÄk? !ÂØÀ¼tÃ.Ê;çW÷Q¤÷¤Ç‚êÑè˜Ç{«‡¢ª!ý×—µßXß½gpéÀÐâyuÝV²'ÊiI)Rt$”Ï÷½!ç/Útßíå]ì0(IóãƒøÞGÉ5i!xò´ÂýÓw;kÛ n0 »·ai™# §6sw¼´ó„¿f¦›JÐ9!‚1´™{"ׄÐnªtM/ਨDc¥ÚRí× jÏpd >O«)f¤?ïà.tß/(!¹+Øü û“*DvŸ¼2“¡+Ïþ>ÜT½³ÖTî«—±LÔIb|lý…Ä)& ?0?ÿøÝLW¿ã;D8ä­Wi‰Î=yË9Eû¨ =×$û±aŠˉR*aʼnÏERU¥jÐXVÇ/ØKÿy§P(ã˜[GT€¶±ÞhìUõ1‘Ðßwl_ÑÔf<§Y}T+» ™› ~Ÿs”¿—qÁKþíýU©GÎMÒajª‡”nØi 8TÄ@‰Gm,®Óé±(=,éá¨jPÿíe-ì\N@SÝr]Y÷…¦ICä™pßmð‚LÌÍcRïÙz¾1'Áå)q"æàÍç\i{²æt dwòI\Ñ^CD­7¼Y) ˜8`¥¸wÑ[™¨µïWÞ(©$T—5Vt9Ѥàþ±7­¹àWE&û‹úÐ?ñhæÁ‘¶[¾¯Ô¼ÊËk5¹ ÷–Oq×ã=Ù}ª¥6¯ub ríG=æã—g²š/úðõÉ++9…ÃÿIç<¿¹‡õ¯ü2QV¤‘2ÄÁÆ_(_^÷ýU:à —‹ÖåD"­#Ñц¿~v"¬Çæô¼a%ûnü’Õa=ææUU)sÌè¸.„õ Òü–ïÓrüÖ ó­E5‹@ƒh¯[iÓGåAFKÜwÝÙZh ›‹ü½ÌÛrÃÖ‘emÕËZ«÷ÓN[5 ª,›/¬D@ È}O•¡‰hkcÚpð%ý#Uc*=ýxoÕ°¾÷X=0x@ÿЊùuëˆCkRŠÂèHH„íùNÒñ/ÜLÀwµ‘ÄhNùDmrK#h"è#†þ@ѪKOÏåÄsïû+Ý•‘¬Þ†¥ó66õoèk\^þÈõ[†W.®Ý~Òÿ¶w¾YX H7÷übWË[¶5¾¡}à×£o²±K‚È(5q‰uåWv( TÙ^‘IËùúµ»Äæ/«an€;ozÀ‰1È-.Žì>}yG6*ܪZW­D„ˆŠ¢ü1t"Œ*a4„Ú}žXíÅPtÃÓ{þp®‡W¥¸¸ù´þ–›ËË^þuƒ®Ë_üE‘hÓÝ_D˜¦B¡B XvÇ Ùp OB”ˉÒ)USÍ£ã:6Dæ?ðÇ; VÈoÌ»6"FªúÁ±‘#rÇ5²&B`h»QÞ5íV!-¬‹sÏ9þ^Éj³sû|í*€ºuqÓ^CŸklhc ~Þv´‡ìˆm‘JþX20šªFuÕˆÞ¨ë^R» î ¸{*bû€v`ŠìƒH,ÝpÿýÇÌg€DˆÀB"D[Œvë&òPžÞÕšÿ­"ïKFyRïO-TD‚\.aÈ1Ü`šËZ3¹£©o½@ú›VNzäKjAXõã·å´6A= ^ODK‡n±]Öd»Õ`½"f¾´1þû>pi-€§*³Õ~ü§}ó§×\9qšôÓÏ|­æ³@ɨS­ìÆû«S\}À±Ht\4Ô¸¿Áºp”ôdÈnêg¾qvgòš$¾¿ø/†}ƒ)ÒA'Áއf(E¤Àl0}쾯HB´ÉO ÙzaË4O„Éû`XxÔgD´èœ-:ízêÊ„Dó©P a<+mõ17w2NÁ3V§”0 ht,*øð¯Ô7Û>*¬£A^“Ñ ±¡Õ"Ô=%‚Yâ”ÒB’žHh¡=+ЬØCø>V˜Aù}ñ ¶ž•T0 ½( ‚ºîÿÏÝ{ÇÙuUgÿÏÚçÜ6½ª$ËêÍ*–%Û`:~Ä'#'€q!ÆÓ¼8 )o šC Ž$ Øä5c0¶q‘F½Ù–¦¨ŽÊô{ϽçìõûcŸ²Ï¹e×Çù–}Ï­óÝÏyöÚkeçÕuÇ|¸{ŠëI¸@÷Ô=¹^ƒs~xec&·˜;â–L¤e|DîZÛ$‚a^O8¡’&ý§Ð.ì}v–÷˜4¬G}ö"^)<¸{oVWî:֣޷ ðà­…Mú¾#Ì<и¨œüÒ¶ê¥÷]kKV}pN6¾–ˆŒ<ªKx!È0…׎N|½Áí4´¸­À8÷L8;ZZËÛ?SõÀĬyæº:Äæ¯Ó“j¦Ü»;|ŸË^iøä×¶æú⇢â}ãÃ×)Þ±G7aÙÌe–ÜL$Í! "Ã$rï—óëÌ„ o\roÑGB;-Ð8 %À—ÝÎR²t˜fÙ×u¯?UÄãÔbitz!¬Ã?IS]H+!¯Æ€,’J†‡u’ &—+.ÜL$™2C—2s¬ö3™¹6©d»—<ãÉyZT`ý ;ßÌŸ•âòö­Œ-ZϼO#딌ø!HõÄ ²Ú˜9UÛ-!{ ÙϤkyuûù‘5Dh­Ú=w0ÏÊDFÆ22>"÷®i4  )eˆéwÓ<»*~ÿs3JËv ñÚë)w޼»§ú Ò_}\ÿsÛ¿ûfÝù™y°iôÂË·U/¿ÿZ›Y ;Qÿj",ÌüF)wÃÂð*Ž$ÝÕt€%ó«î‚JKtVª4rbÞñk« ëÅíé,hM•»;Ü/Q±¯Âs”O|eëç?ˆ÷×?Õ"‡¬»Ü4Ðv½0 `r½e˜‡ïÖð@\"O­Â_K° ›'‘ñ§Ï…ºù·0 ·Îý+ÇÑõ^O­GÒcBk­p_¹+ä¥-¯žÑ"ûçÒéÕïLú 2C‹$ËXm´)ÛÝWñJÈ/.š<]ø>6¸«h›•R$}ëy%1»ÿ6M™2i|,ènsò˜ ¤ûPUwT­‚† ’º:E IDATwÏ¢ ¹+^.Ä/Ⱦ§˜ÂuêËA€Â„)À8“ëV(Ï /PUó¼ÚÄ]ø’ð\xá¥û‰ðpfdf¦žqÅÁ”0g$Ëq̒ƈ<º°N­ÁB¦Zz}¡£5bÎh8Œx—ïNˆï‡&¹Bì.äõõ ‡®c–+^Ü厑|÷ Ì7=ÕžJ™ ñ=5/a±ý”¡ 6 !j«ÍŸ®úÎÁ‰€» »«€©oBøžwu®Ð´êrìæ\®d0Qá(ÍF¡v"eÆu϶‡p u×¾Š!\µN‚H@­ç~ÑÚ>¦… Ø:d5ˆPÙ‘#äió"XnÑ:&1‘¿OUý*cµoP-”Y%Òg=ïiÏÿÀS÷‹­ÿ• ÕL‘™éKuŠdÎ(”€æáHPf`63§›‘‘LÊäÉ­.Utäó}á®bþÌCÅ_Ê·„X& :ËQ¯FñýäH¢jvx‰R5‡¨Ù]øú+®þ-6jÎf¶03õ”›+É¡ @0 ™é´iÉX†ÍŒìZXGìgì öË×éhE˜³úQpw.Âní4v£° š9ઔè¼cßüËHÊU»(zL€qçíÁ6™7ÿ¾½*e¦°mÙYu–ʧ}² CÔV›Â Ï×Þ5¶ï½`ä[4÷=U¾«giY½ @ß`EÊLT¨µ5`loäúçÚe8ƒ¥3u{('!$±0ç <¤ÓÒ³¤uOƒa ¶ÔïÉ7«u…x1&ªîƒS©è˜÷*̄悙|53Ké0TŸw)ùY×oY’ùCK|$Öª¨€«Ó‰µÎôß]u¯ŽÀ`Pf u¨¯…㠲׎Òüà¥Æw”_6ËŒ›”$÷ÁÁVÀ>Ý `$L#—ãÓéãªh{nx9R5ý®ñ=_Ô‡M—ËŠõg2/0+ñ;!\=.Þò¬2Í™yf:ËIÓb3#{Úêôå\uØÎ]-ÚëÞiLަÜ%_t¾kW]¿0rf•üˆ$åÌ»æ])/ëÞú̽iU]PO}à*T¥ ö-&7Ñ2~VBT[m CXY)ÝÙ:ÆjYù1|/&Þg¬Ùàœ¶[¸¶ÊàÊ;@•§«ƒÙå± ÚEdÔx׎v©­%>ÛdÀÍƼôoàŠúÕze¡üt¹ÍQ‰lGê’Üã¦o­ðg ‘%Æ»s€ÆîP&Œª÷kå¤Ç}ÿä Ob àÊyf†±+8¸þt -N?·‡ib†-}|{i‘äqEQTË{G8st…d–ñ¬\?J©» É÷É€» ׎gî.Ý©°"ucÄYÖ5¦÷‹`n>‰T׎\NsÏà ŸÔ¹‘UTUí%AÓ]]Pa7ÆG¿ÚE§­W0'ñkÿªÔ6`ÙUý­VÚ̲iI#Ã'çÕêSzv5ëúÚD Rî1X"Ò»DjM½9ûñ?:•È/AŒsÖ>±þÒ3–úM½íÙ­ ñ¦Aûx ZŽ5Õ†0D6'U.ü3îÓO¦@DR\PdßSE‘íÚ >­þ³/»À™¾ldpM•vóÇǪ„1ª…P2nÞ`A‡ÄZ×L'¡r`@b‘ý4 ‡YǺv¹ŽAm˜ê´¶ƒ¢z_#x8ß<4‘è×n!$í½2™¬ã‰ýàG¨ro<Ëu V 3%z'`iúÉš £T9“ n"’„œÜëªõ —†‚Jw¹ÕMßSÃN½¸ŒßŒÖ¢ÙÆõçÍ\ö‚Õ'˜<¸è>•Vˆo›‘,øµuï*I" ëˆB*fb™ËIÓ1˜½´ZO0@¬pHÄp—aLC½qÞ¾»%¬Ô¡<+ñèéì«Of_9'ñ+rǸO@¹ƒIW2ŸK$( 4[™Ö‘aÃ’F†{çÖ*p/X{æøž&õL"bv6 ÏŒ »+¡·ìëqoÌ×ßðÉú¾R§$OíÏÌ\QzØ}—o»î¹­U)’Ó`GJ&ÉÒI– â *P44ú â‘íÞB¼-÷9ë6è=oåqjRFMÊ3âsþO˨ä´ãÖ]^k$ƒÈåÞ©J~-A¼éžb“7_­Ë”ÝÛõŸ˜)H”S[‚´é¡¨iÁºjcí×Õአ-—Žìq±¾'w ÃéB‚… ËŒ}ð «t=¤”»‹¦Í‘§ßCXG¾˜¦D¶ãÙÁø°wUSø,Á @ȸ×}8Ï2PîAußÃýˆÞâ> réÅÌÜl`Hf9#þ¨ä ¥=b°xn Ôy¸l®º—äû9šÞ÷ì/«R‚Äü7óÉ-ÎÙnÃÎÕ'jc9+fYÛ×üAä ½|Ÿ2¸«h›‘d 'ŒøM³þT·tñIò£à§Èؘ!°sRŸöôœRð5‚`gÖLípYì'JDÖWu¦{ãã8Tó‡öÈ"5G‰Ü™€ÈË£'7ù²*i BÆ’j icˆhÙ`GÜÊÄ­ì®ù¯HÅÇFÞ?Mà®"yr?ƒýN%âú[…AuÕ±ýríðÊØÞ Ÿ_žýí‰zIćïV÷n‰¶5r];Ì_¿À‰³£[ˆ ñ —ø\×N°+cå½Ôìkg¢]éy briÎ Å÷UÉ$à³8Bj‚ žÉ0ŠZj“"®:»%½Èv$£È4uÒ}ù5U7?ÐòÐo‰ÇUQ_F`ûDø2/”$sÅàÿ8 Nd_'•;/%ƒ[’O2Üõ´Hê²È9ÌP;Â(­UM 3,a³™c#Çñ†õƒƒ›b–õø¢ ¢ÖK ~³SíÏL1ÜtŸÎÌ›á6tpåüë#ì¾í¼üîÎ ƒ€˜P‹ý¡5F©2&՝ТsXnq‹ŠÝä yrMPY’êvÒ®zøVW‰T=Eõób©I$Xñ’! Œ¤sUI#'Ër\¨3X‘žùPÍeT V o·– ?üÙêmÅ>·©Ì¬‰“û'÷1s~mq=~°~ÛÍûÿÔ±å s÷~^u˜W.“û°º}MJŒËž ¬^bÙ½]å^2ªSF1Ä«•[ÿ9behöOhïš {0$ˆ”4X[Õ©¸©/…ö‚ºòVªvÃ$Èßv°˜ôUM"Œ@†ƒ °Û/UqlÃQÖòNB&Œ¦Ö¥{—zjÕ«:PÙêé¤+xÜC !"xà¿ÍØñÞ‚zíZ9øS  ˜{D‚NXרO¸7½…™›SÏgoàHHV›õ(g«»@L š1”1l6DŽÍY6²lX0,ùû­7`îþè=½G "~:Öžðè9™73ùê׃açBÆ Á³ýÒyÕc+£ZiL½x×cÞ̤N=e$Õ‹bp0räoÝlO’×:sdâ'Ƚ‘‚• ˆwŠŒñzÍ„ âÜJ€b‰}®ý"üos‘R “‚(“uüã!•0`åT3ù㩟«sýUVÿ=5Ÿó‡3;±ŒÜœÂˆ_½¸Àúÿ~ƒ0Hâ0¯ˆÇÄúšç­œTûž¾8÷Þ yÊH‰…sÕ+MA`õ옷Z±øÒM›1Ö]ÕIC g¤Nv?'§XÈ®µïrA‘«P™wÝ I#/‚TEâ ©ö©†Õ·k…RóÊ#ëÁÁC·„³'A¦)˜³»\Fx'Ø!ëÚšџÐ"Y{ÆÀS ŽãIAïWöÿ4°\|û…4·Ý»‹Óé,™!™Y²s}õA š56„Í"ËF¦íÒœÒld䣯œéWó-É_ú‡¾¡ kðÕ·ÆgŽªÏY4/ð¿J—ïk¥ìòãÂÂÝy3’ âKÀÀà í‹wÅî:n#`Øè°[7^B(>â½"ïÞqÈ'8‘ °½†Fl·p©:Ž!•0ÔC¬¬£nLÅ A°r’ò¦¸À[Å—ªrýUÙo¥FÙ:1Ù;¾‡Áù­‚Ö,®°ûùïîØ* †8(—ÅcbCí‹Ùœ†øâ¼{'ä5Ü‹T)ß#p_²y €ÎcÿC¨NŠtçn,ËAI²;0/ÙŠ0ö:>ý AÊ,ÖñÕæaC¯¬»ùYÛ!({ê¦i€µ£ÛŽô3€Åº%8ŽúDÌ`‚å5Z ýŠ`]såùû5¸ IPgéŽ ­ˆJ·0¤»j:cØêɬ%G’íG’Í‹¬}ÂR‰øÝ•-dB=*ç°7UøóI¦ø²kƒIÑ{§ýoWߎ¤ÞÎÛcÿZ•ë¯Î|3þ¥±Öã óønp¨gw7íÚjC°—Æãâòº#VŽ¿8ÿ?&ê5èˆÏψ/'¬ž½óVYÝ»g5'ê¯Ï‚²zNÆEjÁš‘LÔ²;;\O‚ÕšË÷ñ¤íë‘Ji+ú¼ïïû€®¾uo]K_‰¸í…õ✘(fƒ²621xê[Ÿ4Ä»ãܽ.z>Ä:&G±ÐÊs÷ù::¬ßƒTw?O¦u8C "ʲȱ‘…°XXüËe[Xu gfpMu·ž$#L!Y'8>{é4‰åב÷:ö¿Uÿ"äÙ.€Ds åÏLâ§ÜU(Ä£ÊÊ÷sþR¾E#  Q\B„AõÈN÷®ñ>îólM¼S01¸¸ßH ÏšÝ3a"óD*n°lwï+®ûOá)á?¬¿¯?J~®:×_•í¿Ë(Ð f²Ã<¶›gîkרý|dÌ-»¶° A[šº„A¦)þoË=òôIë>–«g€Ä¼•ŠËË·lpäØXþĬž½ð”•_•`ŒXÒφ„»bàò} ûTGT\HgË]ç¸5ýQŽdÝö “7´.¦Û>@Ì4Ô-Y‡Ã*;¢¾óÜüÀrq§µU5““°®O œ¬>÷?m1œãy2ŒÖt†l¦(ʱ‘e² ²,,~nC³â¾7CF2ú†1K 0KKFuÍIeÈ(!ŸµUG÷é+¯ æ*oeˆ‰Àt¤ïZý3—g»™`4µEó#'Ü«™†pWQâG…;€3{ïð-ߨi6ÐÏ/¸²ZÀ'{å!²û“šñûDdW ~Ú§¿ïªòàá BU eÔ¨Pâ›ûGþþ¡DÞàÖêªÊ ü{ö³cüÄÇƱÝ`^ùª—Ø}8 w·îÞ* ÚŸ[$]ÙÒcôÀ5÷ï8Ð7þgó=dÇ—Šï€°rËfŒéËêÙçêHfEvïµíLÄ1[¶ôù^ééEEQ)ÜKÇMö§Âë®ðeiPöq‹¹«¾VÎò¡ñS6ÜõSP".Xe»kÖ ‡ýS „Õg~âï&u\5 ´¤3dÃez–Ed±ÈòîUMʺQ³z”?=½æýCsý½°`®ª=+™ ÓŒ¬ $CBT­¾ÖŸØtý¢Ͻ9òÙ:çº*ÿ~é¼j^œ ÄO[¾«˜;# ƱÞRà.îNíú”¯Ä=Ä£%±”@}ö!?µÆÓàa”†{ŠÚ8DÓ¼Šˆà<á¯Ùú«µ á"^Ù2‚ˆ‘e9Ú”à›3ÞaoÀýC+øNÿ¤öï«ìo§G©}1Ñfqw|i‰1·íÙj´7{‰XWÛýè0~ÄG¿[ÇöP¢{Õ•W ò®RÖ±ýžfçļUð6Iy4gI“’—¬ãBžÉįĽt¼“þº cÃD1S舞 ø«µÖýÊxÜpß‚†õ„O k{,=¼Á¶cr@9tP–)ˇ7„u}P`À+#ã_†ã–zwÉ.µ wÉ404Ì’¥zýµ ƒêÌÀ²¹jÍ[‚yÚ›ò.¿xîM‘ÏÐ9×SxÓ¢yÕ˜Ä_2· À‘cÓî*æ¶&8^ñeÂÀñŸDBèì&A˜‘ZAÀ¹ìÐ*«¿êú9Ê:[ó~¦ÐÓá=G^b‰kÀþ­'ÌCbßóÜ)k;H% A$ˆ2–ã@7âGÞ[ì½oøÏK¼åw4þmµÝÿoƒ¥*POH¬]R~ï3ÇN§ÈâMãÞ¿o«aÐÞÌAxxí?¯_Þ@Àöñ!>´é´BýnÛŸ;}$ÖºpõÕW Â®#Öñ.ÙY©@wžpE-ÚÊjUB¨?õÉC|2.d¦î¥ãúø?š^_„}• ÏÝôÇD@Û§ÊùXÏ3Ü×öþ¨Î¶à0å€ÈfdA_±àܾ`'Éþn&ᢠö úp÷D½ª&†¡Áf3C2  NÖŽÄV]‹àÄBEˆõ/œ}CþgUtÿêøQÀ]ÅœÖ$ !¾|¸èyîãþú§ðÙM˜]µšgÒ{]²{è ‹}mJðØ­ù9¡ÄÊ`J B<õjëÑPò¥gã¤BÙ»ž;¡*i„´åèM8GºC„Ÿ ҈㆖OWÙýßè+Ürœ±vi=€]‡úEÏNÌE;\`O»êÞ·;Ó&ˆZû¹ Ë€°}ÿ)ŸíÞ Þ¶…ª?–ëørþìTâ5妷Nsöôñ•áƒÞÝ[0Š­|Ž?¦Üý¨«q÷î íP®«ú|xõ>¸qƒL–5#ž8ŒõK‡w¶mäœýû) dAY>ÖVç)1Ûîx먒É!°WZ@ÝîíD…d¡ïA•j“* MÑ«8^¾w ™F†ê$ƒM«¯"hL•”ðxáL%õ y+®c üEw ñÐ(_ÜtþþcB.¹/ÒçÖ®%Âé¡]Þ½JãC‰}*dÔhë®”Ÿ:¨{/ÿ=Qó:Núáð,’ C¨„™ð@uÂB!>jô®}€xàÇéO–ó!Ü8ëŽ*{àëg¾VñP$|¸û·P÷N (âoßÛ. Ú“ž'­ûW–7€@„çöñ•ò={úH¼uáe/»À#ƒ£>dp÷/5ƒq¼e° œ™r"•à ¶P¦!ÜUD|Á¸¶ö+‰„çÌxÉ0 ³»…c‹œcä‘sŒ¬#,Û©ë“ÊÊ.¦eôÞ½š–÷½u?uÝ·n´¼I¹Ók+´¾;Ïúú­¨Ùð–ÁcƒµU& ØLT=»¾èÄ¡÷µú¥þÌØÑÁ]Eñ•òý…'?j(ó:¢Ê¶Æ œìÐQîÛôäkvþa”L“=˜uo$‚=ô o••’q!Y[j 6>µ©:eA錣»1ÞZk°ÊŠpÏÊ£<€›ç~²Ú¸óÔ7*þ´¸li=€‡¢kªÔ½“Áh+ìƒxßVC`wzÎ#­7Éy®ƒ±qeÏŽ ñ~é˜HußR9~ Û{4ÞºpÝÕ›ì îÖÉÃÙÞNåÃÄ›çH̽`rÁ˜p—|Úò]…¢|iÄ×ׯ\™þzÌÉÄl+ž³LÛŠg­{šþùMßðá¸îÄ•îv¼ìu&¸æ¤“ÃAÿ Ð¡ì¨ Úî' É·ñP®[7$t ÏL¢vã[êkLõ÷  Ð`Ú ªÙ ꕃÝ3•ç{_£¿ñ²ê‹UêÕ\¤|W¡(¯œÏòáàðã&‚‚µëÕ,hÚD„ã}Ïꫬzr¤A¹Òì܇2èC¸'BuãÈöÿ?u5D”ËI-±20ÜÕÕ)Ã$@#OÈkL×ZC6Îg>UÑçùžþ­Î±l•*w7º;CüG÷o5 ìžýPëM¾_ùÊ€žÝw¾ÒW2¾îú%€-o»ÀÞFWîŠïµk^;êÈr"Wvì쫇¤Lõ\#[;û~º¤¾KDÿmÝ1†Oõ½‹>òÍ+Ø*µnY=€ŽƒEø »ƒ¹h7ÔØ* ú|òhfÎå+Axvoeˆ6 j^|©ñ'eÏt®\·²~áŠ=/ ”ªf60Óó§_á¿» ê—ƒø—ÜuýŽJ$üžG>¨*“P%ÝI/yºzŸRW •Ux~‹·%*äÛø~NPÙ†Â3AÑ(µ3ÛÈþŸBÎæ 'ÏÏÑUymÊBŒdl 'ðP¨ŒepU=õ6ë/Æðñ¾{Å_VÉþ»޲Ujt¸À( ¯?ypëç«߃—¯j$€@Oï=Wækö7 –É÷Á=¸ê­×¡Ðn¬©ŒdœÀcTß>œ£E IDATÓî×5>æd]šç¬ï§ÜU_È ÷›žre»Ãj«)I諸½w9€¾ôxíÞ‹`X·éD^¦¼ë­;Ê”—A?&u!±éízÒ=üM³Lõµ1€ÎÙîì¦ú4ÔÆÕøóƒ¹‘S}jU™艎9n:|eõßK#þ%wÝGÙˆßõàû”f7ˆH‘K缜GN>®Ð©ÚqD6»*ÆŸ‚Í®ÚÎ&5øÓ…Õ½ ªŸû' Ÿø¾¶x«›9º<p_“ŠÃi;DvÏò'ƒÀ±ˆh[î/ÇüQß´úSUÜÿµ½:b— wÜÕÁÅ{¢ÞqhëgRw!'gÓªFøé=å y…xbo+ZØ€uò0€M/¿À®"»±¦2Æf°LO¸_?ëb¶s¬˜m}sðËù"+®%ø~ËíÈìl×™î“óÄ5¬»(w ‚u<ŸÉmÚ|,”Šmº.ؾëU ÒÓðëkbÎÙˆÒßUñjeõü`–A`JŸéc&&tÝ"£~VÅý=Š­¸¾àŽB‰’寲nÿùm†'~ ×U'ò›åm¯"à…ãEWa§E#;²ç­ÇŠüJdÌ„ÔÌw>ñ½pýß¾']¤«Ú8ÔU™jÔHÚö€Ùxš”ößfzœŸù-ë?Q-û¿²Ó틽~Y€+X•]jÝ©hçë® ZTëŠÕü¾<Ä+¾{Š¥ø`ÝÒzÅV¦<’17‹®|^O¾ßÐöiÓ±ÔŠèçË]«¯«1•”E!¸ÿÙï:#9ò³ÓÛ*‚» Ùµ Eªhtr6¯nÑïwnÔxe£e+Ža~š‚P¼FyȾPp¿aé§cvÆt²qÛºóظ2¯jcúòà~Ëo¶JÀ—í¶Òæ.Ç]ç]*î3I†í²[D ÉPìv<=® ïkóý^–¤hÿ‡ò_ÿ̦€S笵³-¸¡Éï2 ±.ÂÙþ\.ÉxþÔf§ÿÔØûóé^ÍK€ïÅ஢Ä?uÿ{„î·P`¾+°®Yüzù¥Ð–^5ʆðœmŒâ©QÀ|ü–dÜ„œ-ÕCšÝ ÂÀÑ»õ£QY³ ©òºSÃi'˜˜þÿó}çoÆù¬_Þ`ǾۮüP|á©{+=‚ÓµŒ‚EµŠ9õ›×4ã©=£ ¾L¾ûo¡¢—=eQ¸&°ì©€û-«>s¬˜“½óð¸h åÒD$ü­¿nwØ]•žlwU¼Ëbø®‹¿”ê2p\v“ã;0LŽ·M)Ðïš„W#ùúªôõÏlN`´®¼VÌ~B9ò jªwíø³ýYxþä¦qõ_UˆWMm^ÂpW1»ÅmR¬aæã?¾E)nC‡¯ôøºeo"ÂÞÃ?÷}È«ñWE`Âa±_(ßF’1‚\¾ кäD8ÿÂ]!;(bÁkû§tÜ×U™BÐpZ­¸“¼_·’sˆþSþ혿…|8¾ÿš|ý7•m•r:w…/»:Pȩ߲ºI™MOî:[ì°z%™|ß°¢û^Ù2Ã赇Ι5Mf]k¥MÄ\ßq“d' î·møP̱îÜùÍI:>¹’ÊðÇ?}»ŸãùìPÞsÏ#ë¨!C&ß„qà›ïBÏqT~½r×±!Z™pÏ}½pñ¬ç ,Ëqö×^¶°ÀÁ£Ö˜nQÜUÌnö>ÁBˆlÛMFkW¼«<Ã5a°aŵDØ}ð s&bÔ@ýfÑh§Q² B"n~ÂŒkèƒf,ÿ0çÅ7èÝÚ8á>S¾o£çPÖU›BÐðˆž "k¶Wãßõ=þ¿cøT!bµbnõmwþª\‰çtî`PÁ.²k UBß²¦‰ˆüÔ®¢BÞ:¶_íGMÌ+Üüò• 몊BñÌ›µÍJv˜õ3Ê|xÂ$.ñÉ€ûíW¾7æX_xúÞ  ¥ËhuÔ…>ë$¼ï§;!ãÒ\AøOŠæ×—3šNŸ«˜ï*"NÄéw\œ~nk¥~d$åóÿ«ÿ¼‘È5Xtå MkÞF@Ǿûƒ1a=® yo}UÏ·ñoµ1‰˜D¶- ¦ÞÏ\ýçœÝÿ…°QSر²¡¶:¦^Ïаvê5±µ2LúíßCt©t”†»þƒ÷|ùÁo:¬D£%§«€Qh%vËÚ&ž,‚x¯ÞÀ‚ÄÜ|¿|U#PqfýxÂèµÏ€H³¶UÉ5¿°¤Y?³ÄcÊ¡Ñ|Bà~û«ß“Ö»w<Oè|¿í¡vÇã8 áHÊ:ð.\«ÝO›/ÞkÅ£yøéÞH üé„R'ÜUèB~"ýw\„ˆßQñ¿üÎ >µ•W5žT¿rý°}çóÈ^÷¢Ð-ß&7àHöÄ>4ÇeîœuwèÝýÏú,¢åÕ„ª#èÉûä©xƒhh$ççY yÖezȾ÷ËX~—ÊZP›§ñÑ7ßôÅþ½Ä»³³ÇÎF!ú_¹¶ Eo?=}4Þº 1wEä®M«<3…p÷Ãî?­˜nž`Ö¶¸wh­À͆YùŒ ~l|ÿØ›nüÂÿÞ[ù«ž”ÐáþÞµÄv‡‰I¦¬?Òwf¤ô ¯ sýßÀ“Ñ ™sï˜àúØw>â'†ï*.ºUÖ±5êS1»9©þNz”ÿß{Þ©²×ÝÅUá翇ü–«7]àÙÛ Y4|yCh ‘á)!BmK÷éòÈîgîÆ¿átÇ?*©nè¼°5Z€­«‰Ààˆ]hyÖϦ×ê‘y÷~GŒ²¸4~Ãúão»á_îûNÁ»ì®p_ºê•k›ˆ„':Bv¼uü`¶÷h¼uA¤2ÌæÕ(;Ïr’Âî?å^b`6̲ûN†‡è¸Ÿí_ö)ɴݧ |¸¿ïçí¶·‚êgµÛLˆ„PY1–ãÊó„úì^*$„~µ÷“Ò—xáîÇì–¤Û# ntRd÷µü4ñÀšxŸå™ò¶-›m‡m›m›s¶téØÒv¤ãH;ç8¶t©lÛaÇaÛf5ÆqÔŽw™‡¥7F:Ò‘Rzc¼ îc¥dé°c»¤#¥ãxX:, a€ iKéH–j˜#¥#ÕvXŠönqÇôd)kSBª1’¥dvè¨ ,݃³T­hXJ¾Á¾ãFY´˜ÁÆ 4îÕHîŸlGä.%Þsªojpû‚u Ø](¾x¿ú²æÐjîEÆ“:¯¹ aÖÏTÿ©«vßI€Í†Yê¿È`ûüqÇûÏgzA¸ê]×OêËž¨Pp×¶·ßö³­¶C¶9[ØRØŽ°Ê9nSk+Ç!%‡l)l‡)rް¥°¥C»—GØR8ŽFÇßñÅ“ïüÂEwŒÙŸy ìb'Üõ˜Õœãä¹ÌOîºÞpÍñ@³ WzCY篺ú~÷ûï"="Þ={Ý!óÝÛ0ƒ‚ I¾£¥Þ“_³Ì[³]ð²"àØ“±øI V-’eO@}iPF®ýýµÖ|gßO˹×,ÐÕoãÊ`ìe{‹Å§Þ¹õ³ßÛæ_uáNѾKvçfñ¥W_Ö Âï<o8”í=oY˜³Ì³eM€§ÊÈ ŸÊP|\O^ììóÇýé(nÀ 3ˆdíì豦}(¸¿ãû×é)16 ·œ@ Ì¡ŒÉDB8@Ö† ¡­”zÛVIøW»o˜ôÞ5“wcÜ¿:jåþM϶|w³š’îüÛ·øÕ\ «Tá.Šz ^ûÊ[øÍã÷h˰A¾ç¤‡¦ÏÊwyšˆ‘A°¥÷t©ÃFMÏ#€K^ñyõüÖí3I«”6Ó‹SúWë«cjrW\µ|á­ú† žÝel\Ñà¹IN%ôCu Žô&-mÔ¸z]3{ˆÜý€Ú5¯SW¯º¬ À;§Üý°ûN(¾ £qNd@2N¹sdz93"0çwÖhœ{!^ueQWcÞøýë²þRj°5ÉO…„¿UxÙ2DBHe³·ÅÔ«÷Ëôü»'¥MM±¸øø¸mW1-?á|‡‡ø“ç2~rg»o»Œö©m¼áµïáÑÇîŽlv æøÜ'ñ åqSARÊüRA÷( Ð.Ápék¾ B×c1Bæ{dÛT¡\LoðOC#¶?CÀýׇ@deå5uO¸'þy¨:ŽÀJõŽ'r Ò»ë)i_º!õËÖ5ƒðøŽ³w? @Uñ½ê²fOì,š2?•a<œ˜µ¤Ø½Nßqß~÷)_•FÂÍžÄÀq9k–,¦%îoÿéV‡)+á¥Ä.ÛC먞6÷/“i¨¤uˆÃtðÆ;§þõO*Ü1±õÅòc6מ ¸«Pˆgð©ð·õ³¯µÿ¿?x??|—&ÏÉ-.&ahI5®ºÄ;9’õ5ÑÄítÁ÷R A´øõ_¡óáê¡Èµz–êäçêkã‚0<œ 4{82¼rëY= Úµþ[˜r¸«Èuíäp¦rZf¿l}3<üó'$f-¹z]3X7´ŽLä¶üžZÖèGí“pàÁÉl/Rêåé^0±óæ`_tcm SŽø)€;&£¿G~L¯f²áî‡_¨Ä˜Å)x> O}«Ý#»¯ñ5͸.”0‰±dw#•gãäO žØWQx‘VÐÊ·Ü àùnŠxîäŸC„×lÃÛ\É/“YWm*ëiÈÛæI¬¤¼«Mu1êÎ ÂÝ©I_¼*ùm˜ÊQñ^¾¾Åcš úiÖñƒÙ3Gã- .ÑÞ¯®ÊpþX—ë|Êǽ ŒÅŠÕðùcßÝô!vùß5ú'0nß¶ÕܳŽçºxm7¤&ä¥çÕ¨Guù¹[ ԣƔ#¾µ1 ÷üä“ԟ/?.8â§ î*ÊAü’*àðHôyÇ=íÞò,„–A A`é[7óšf”±ôÅu€àÕô_©¦E™¾#‡î{GdJˆdã”H«äo€ÂàˆcøW F«Y¯Ä{œC¶Òpwõ´@|ù³ßöª9î{ôød¾º±‡» kÆBéàZWmvwðk& úr IöùÎ;exÿ#®Ÿ°„œýÀݲ!|[ÆIÕNÏ—í•ð¿{OYånâ‰èÜ@v¢^sÁ˜2¸cÊø®âBy5S w£"~iµàаSú8ûÿ£]Šd’,ó5{ öý¼Lÿ?ß‚×Öl½\ö§?’ŽÍÒ»yò=¿¸UŸ(<%äËsß©o¨‰ AƒÃ¹¨£ù9µ1"Ê…2mðÍš½&;ò?zoÕk6´@Û: %<ëØ~€A­†¦í¶­¯1ôçõ)õ)ï–—Õ(?–ZÁ}Ç}¾ë‡ Ç„û|¿Ýñê°+¸«Jn·<¿ÆoØaÿõ{þ­Ò'R?Q“‡ø©„;ÊáûDÁ]ÅAüá;<Äs—mym ÀòN [âÎôe|·=HAeá7ÉK¼ñq¯; fVµÿW€xv uµ÷±éÛ\Ãâ=¨Zc+®¦JªÉO‚ÐP„Áᜲw"ù6DôÍÚ)MJS‘íÚ"mk0â¯ÙØà7ÏgÇ3XåÕL·°Ží3Hí^dU0§ ‚59ïS¾¢Úñ‘ þ¹öD`Å}§®@Õ„üøð÷¶úí“&Â-3X1nñ^uù‘?½±pW1ň¿PpW1³)¡þ& "~E]ÀþÑ~I>Üóï:ñýv½bAUYÏÄ' j'x~N4ßfÑþKR2;’¥ H–ÎГEù):^©zÃK¥¯¯‰ ÂЈA|CmL¨ò}2è}?箺Qú²Nxd»w«^—ª+S±«¯ØØ à×Ïõê7¾l}3xz­²úá÷ŒuYÌ^¹À¹Ñõ„Oy"ôªì#O̽Á=ìøš~ˆþ“>ßUZŽ¿õ×®DûèwÚ„2!ÈaÊ:d[–ÜÄöo/Öõ˜$ÄO1ÜQšï“wžÏG§'üàz\X¸ûQl­|e}À¾þR/¯Üóãܶv?mÆ·PŒH¥Þ^ÊMTÍ JÏyý—XÚ`é*z¸ÄÏu|6`··Aן6êkbªqëðˆí*÷š ÛÞš-g?߯ÝÕ0Õˆ`uï!1o5«{7¶÷;j|­i€œ+ð›·zö”˜·ÊêÞƒp“ÕW^Þ à±g{ó`ºåÂë¡Þ˼5ôìÙQfÿØhô°¡ÿAf4ÖÄþUË»&ü¥šƒ§ßßþËòº I$!šÎ w#H2î{ß½þòC!Aù w”àû¤ÂÝ…sR˜Ä·ÍHèžpW¡0å×4ÕØ}®@ƒ”qÂ=?äOÚCˆ×iõ‚9~ñKog“ë’ 2€ª+þÒKf,!%Ø;ÉïÖÆÖט¾Å?œvü³„b ^¼{ãך §²9ÜUA¼çiôì\9_î*®^× O›í¬z¤ú€9Ý讲ZÝ{&p¼­ÔJr~Pßñõý¿dÀÄ@mÊÈÚüĬ‰ýǿݮRbŽÕÏ‘ ñ`â±7~Œ™Tó蚸Pˆ'àì8¡àŽb|Ÿ¸«˜ ÄOC¸«pÏ8í}Ùk[êì:3?xÂùžÉÿi÷5{¨rŽÎ\=Ï2ÜxÄ\ûa׬g¥è°$–µ=ß„ƺ¸AÊyK…QîåP’ß¾Š<÷æk-Ù–sT¾C!ž(1w¥býÞöJ>S î*®¾¬YÙÓ ñê÷Ö½óYbö×­îÝP)âlèúf‚˜ÀŒdœ$ƒ¿Ÿ=^ÐìîhÅv•±~ÿÇÿËìM[*ÑÊ/ò]t²qœÏ[N4×Å1VÄ_@¸£ ß/W à…)»Š…sRÌè<1aˆŸ¶|W1£1ÁÞW~Yk€½Ñ2[S÷ühøY€{=ƒ^¯Yæ'Ëk­©@gÙ­€^šK°œÛ¿AJÛÍnhd÷šŠ„ê_ª _m­8Ë­`(¸gàÏgl/áÒÀmµJ‰¹+VÕ°w ‚Ý:W]ÖÌÌÅ:@MqÌjJ8yÎB¡Ô l÷.Ut tJh~¼âÜ1óÀ°# fÛ¡ª3À f<3§bÐü›[ýÒþî¤{>ð=µU€Á‘ ‡8™>ïr 'ê+}êòc̈omHèí›|Ÿz¸«X0;¥.ŒŸòÓî*ôY}ýÌF;NõX.ÜócæÏÛµ,û`»¬žV_°þAîÒŽsЃíœ=«ïýT·ö™WÆ@øKak^¥`~¥õîq¾Åô‘ÃøDó(|`ÛùÊF?·¯/¿[Sé(ÑjŠ#Òe,ÛµK­xùÛ¸ô16Ä’1’‘1ÃvÀ@U\*9ÏÀö¹£ƒþ“wµ; Ge¿¾ùÁÿÔÔT†FŠî©Êôù|÷þqÿ4k*zS£Fs]\ezží/÷óÂÂÓ‡ï˜ Ä_pWá#~ìÛOº¹Óîù±ð—킽Ê6†–g©î­­6 ÈXŽºÚ?ç:°Œ›Ë\Î\]?øߤ‹z½3”_,ð•YcO€S@:â^ý?õ£#~Mý)"üî×;â-ò•…xæ Y>ÒgY…Ú¦K`½ƒâ>V'לÿ>3˜Ù4¨„Á°%À TÜaÉ °ÄŽyE)ÿ‰¯²ý«ú~þ€Qá‰êì4¾ðÍœcbš5×»+®å þ‚þ_@¸û¡(?6Ä_DpW¡imCÎæçŽŸÆ4†{~,{DK½A¾MMÊ4Ò–ã9p§„ºjóXýs9‡X1’ Iò%¹ß‰â=/Þ5èÉ­Yö¥Yß®ôuú(ÿÌë¿ßQ³%si^}E+€Ÿßÿ[€ öä5®\Û¤¶¹^Ä—î*œ_ ?ÛµSá°2Ä÷)ă†0΃f0mf0X Ø¥þÿÜÙ®* |éC?(vðJážµöPȬ×ÀßGÉ1@s}œ€3%?àïÓî*Ɔøy3“zN]4pW¡¿¸®Ù¶å3ÇN^D|ÄÚGÛ ¢š*ƒ+ë„×c]=®ŠÕ`$cŸ¨ ˜ ˜‰%ƒb^ÊÏjg zŸ)÷_šSª­v~øÿÇŒË÷¿L–âûk®˜à‘§O««ÖñƒÙ3‘¶MeÆéîTNZpÁÂ÷ªŽ&Âê¾t\Ó÷K†AÌÈæä°e €˜9ad™™’ÁÀÞ¶Ñ}›ñÃ=uÎHH؉€s«ô€-õqGü4;|¾O¸«ƒWs‘ò]ÅÕ—Ìpè|/.N¸«Ð»vlþM»žäîÛ8µÕ¦ÚR›Î8~ýœ®ÔÕ¤|¥èYÌ+ŒÝzŸû_šwo™/I‡øßç\¾:¶=ÿ^¸«Üý@ª­G¥±eM¿Ÿ*Ä—ÿWëì ¼ÞU¹®ç7º*×ôÿÀõÜ™MS(”[9Ng éÕ³a¦˜H«1JÎKÆ…A?ápÏXÐø"ö¬SnKêbˆŸ>pÇ´å;<Ä3£ëäèˆo›™Ð}qÂÀæób&>ºtÉÉé¥ûí]óx»_©XÔU™‚(±ÝUV¯â˜ ‰o"H0¤` 0‘$æÕ‰ƒšD|¡í?F}U>ÁÿŽ7è·ÿ ð¯½b€‡ÃpW1¸ça€jW¿¦¢ÏDÅæ5MÌ ÆÓ{'·¥ÉüY) ¬¿ÅÚæ:;@ .Õ¹P—lóÙm˜†ddsR¡<‹±jÈ£4³Òû,`‰Ã ÐOÜó£IØAŽû¯êŒÓÙR›u[<;^§ü´ãû4„»åüd/v¸hmL,ož àÀÙD“^õÂc ²¯Ûù'‚(“qôêÂO„'„çÅ0X©{‚$0A^VuTåÛ|aþwJ<…Žï¿!¾ÿŒJø×nžàá߀»ŠÒ=ðJÇ«Uõܧ÷Lâ›íngͶŠUñ]¤zs—çÕ¼|ð‡ Ãlš®Wãô;Á 1ƒAzBÞ]‰eÆ —¼«&eJO)ßõh1¥Îwø@§2Ѻ›ÈCü´‚;ÆÐuê£4â_p°¤q6€ÃçO¨Õ ‹ñV4Ø^y§ìWlhˆ{¶÷­Ï´û5Ë¢ÅëA8(—’˜`‚`ÜP{ìó ¾—ðˆ6ÿ«xˆïÿÝyß’”äûøãŠÕÌxfrT|òØodæ¾¼ÒÚ6¬/pWW0›…îÄËÿÛ³_˜Ó f¶r féeÇ[N&R}A„Pr^=*f f>0…É‹ ¡‹zWæèøpÀœ–ú¸Ÿ’?}à \³é†"»¢|$^2pï=o‘0H½ç-Eö™^1ƒéêOw ñëíg^ý¾×xåå­÷oúáO.ßö£ Û~¸~ÛÖo“¶ªHÏŽ-¥”Ò‘K±\a^{,™Y•HØÑ?ûu»>!zv‰ž%žK¡ÿ ƤÂpÅû¦•¿åò’9U53ôžËX'UúX%ÞÎîòÄ{Á{#ñxí“ DB‘-A‚âqOäæH܉Ѱº*zkTJ$ ”upÉÑï.<òÝ/~·Ò72qÚ’§-çTÆÉïsªSskRsk«æÕVŸéÏhzÁ…~WáóÝòèW‰Ó IDAT/¸#¼ÑéåK—Ô$’ƒ™4³4e6ç0˧:{.ôË,ë—7Øq bñþ±;·š„Ï}pTÂcÅËÜØÑ®Üƒ4•D{†ç±?ÜònxÍ7¾zÙM/œ~ª6¤ß?;ÜõºÍ3ÞzxJ¦ÃO`lZÙÈ„g'NÅ/š[ `ÿ3Û³g:Gí¿Z,œÎ ó ët§s@ .6À«‡þ›eÁÃ3j”w ¸æ;3lÔ1€i äÒgd°ëëzô\:ñåÌÆsªSžyC5U¦»Oƒ¢}ǧKŠ‹†ïÈCüK€ï‘Ú¯X¾¼*S|g昻ˆg–2øÙK©þÔ0ï_f–`ÞÛ?u bÝòÂýC_½Þ$6 Ÿ{ÿ6ÿÆWnj¥ò*½xïn¯.f¸ÔåîÁ™¶Þ ïï︟Z{sþA>×·Àë6Ï0•|pùªF•^l9ºüÐKE îú%€Úµ¯Û¡œ®ˆ¢ˆïq‰~\=ü#?OFÕ-ˆ™ +Ë^:¼/K†ˆ52Èv€A2Ó+ôž­‹§#è[êã-ÉÔpFT[e*âeèèÙ Éú‹‰ï*å™/-¸Œ™M -nhÍfí2ùÎÚ]ˆ\³ô6°dF'}_ßúeõvì/sü{¿úƒØ„4›ÄŸ»í‡‘¯ÚÔJ ÿêé²(ïÇGöµ“W‡ó·ðŽ{”iÄÀ'7àû¿œÙþº-3<ôÔi”Üî4qùÊFf&³•,JGbñ¼jÏ{™ÖñÙÞÎxëÂ1¤ê«];P’à²k‡Ú4$J®»^=ü#UÀ@™ñ Ä ’€••¾Aïzî1!–å0Àf3ƒ(—9L®–@zÉ4}~¢äª9- N;ÞJ­8Ò[ÙOz¢ââã;€Es«¼xì´êž(¿ªÜ¬¦¤ZÔñ ‰”Ž•uñòù®þp ÖÀÜhÀ›0ÿ6> uS[·¬@Gyp¿å«ï4‰M’†`lî½Q¸«xõ¦ &Ð#…’Ë …éž'fYð›µ!äƒ}?š»~Ë/Ÿ<yì”!Àå+˜‹&•–Ž%mÕwioÖ±ýÙÞ£ñÖKs+Ûm«‡ìÚA *Žo©]†‹»jäÇ.ÞÃ˧ XY%F÷Fd²ŽËq€b­>å”>®d~îÑ; (?jü%-Í U/žžÜžü¸øø®ëw\œ¾X;§1»9 Š–ÌÇP™|wÿ•Ì@s} à3糞8ó†xjm°ùRuü¿öNƒ`’4‰ b“ø ·n+õ‚¸›Œø‘ U¼Š‚ŒþÈ\—ï™,’n>:ûãûÝŸ!6ÝÔóÀÆ ÌØ^ùÆ’Õwê|ßçñ½²‚9‘à®PR¡K5¦4âÓ?ÑÔ:3#f’²à³Y)ñ±D&'5mÌd öè÷Ê0èÏ-½0 /ë%--Žœ™ê†ìßõ\Iå¿ãbCü஢`Ũ)‹Ö‘åóàUÏýKGx›•žÜðG®gâMÏÌœ½*ÿé^sE«JK«(§¥  o_ˆ÷¾a4Tw}ñ7÷ø|Dÿ¶.º0;±ay®d™zÙ‚;Cma¬cû(ÿdœˆGWh%ùÝ Š¼*}Ÿ/ÕýÅUÓ$ÀÝÅ86¡¥×@ªUs¡´|z SÌÒϼtAß¿lê@_î*Zjoµ¨0õ^¼9ÅÏ7žˆ$Â+¬·ÍL¶ÍL^\ˆ[œ8“™Ý’œÝ’áD‘ÒQ“½U³Õ…µKëì:ÔºÂ#¯¿ëÝKÖ‘`•«.ÀD,€'×ýqÁ<Êø‰=êfwf`xì>^º À ª” ÜVA,z6dÚAvUß×£J²¨ð ç‚(¿×ö„Çö} ñë—5ì88:â—-¬pðh^Ï/õÙiMÇóס»ã Ï}ðKµÃ¥ùë ìšîŽ‚ˆ"õ¶«2÷ƒA &9qSœËI"¨­­ÄP÷©SÌ bØé Īæ¡ªa!C>ÿ"1 Ô到vI=€]‡ Ûî|×ÊŠQnŒ!Ø„4ˆcÄÿzSaÃEøîþŸ±la ûBãýO¿Hw­»Àm;¾­Ô8jWÎû–ú½ó4Ìžú®C~üê$àÏ:î"…Lµy=1¯À©ÆÆúe —^²^¾°À£ƒïUm§âsWŽóõP÷Îןþöšª“ÿ²¢è×çFw‡úŒ NÀWZ÷ëº;n’ç‚VÖѼÿ¿@›û&O¼füš6$Ï>ïß+áí† çVެ˜ЗÉw?Z3ªj‡Òvç¹ñfLU~/±‹µûT¦mfrÞÌ$MoÊîðü™9­É9­É)Füš%õ ì.÷·ãFlÙÕj*± ÷w—¢Cvv©vÏûm¼nËŒ¹—»Y.èî(18vgÀR3*Ò2$ç³];/ò\èÝìüU÷¾‰1@ ÅŽƒ}ë—Õ¯[V_lázÅ%µö) wÀŸ'BÂ+X÷bçÈìíkÿÂÊ’ˆo[‡îDÏN9/Šø'oÝbݯ6&Mb†•“`ŽÅ‰„‘ÉJ0¼I›A“PŽÖP—zªþ"Ô¶,ÄÀ™ƒ`¨yXÀ}ˆ 7{§êÀwYBÖÊ }¥pÐ{ÞjMÕT'ÿîÞ;LŽêL¿SÕyrÔŒÒ(#$FB"cÁ^ÛØ`ƒ10`l0x×îõ½ûìÝg½^ã¶Å˜•#»öâ¸àŒ‘$4Ê!¡IMŽ=ë|¿?Î9UÕ==yFÈ¿yDÕ©SÕÕÝÕo½õ~iœÌºy³¿ þ>•*4 ßqªBüœ€»ßê«ÃDh?YÅîׯ,°ïõü>±W>xƒElM„ lH[‘wâ¯]7¹7u*vé–€ˆØîâ·‚Ê»Â÷O½ûFwdï1œÑàÛëWæC!áá pÓîÿ€éöénKw¿ADêe€®L¢yýŒRŠÆ³kJÁhz-âO_^ àÀÑñÁ]·Ï6ü}Žž6.{å³’i}ôøW×Nþmš,b’‹ ô„:7õDÀ`Nf|¬Ü&f$ÓÒ/sboÀžÚîIóÑòU*Ì$@è;qÀ/Í{tzA22³FyÕâcê-œ\[\VRµ{qÀk¿<ßöPŸ`ŠUñÚ:“Z‘¯™Uñþù°9w}faMxÑü¿ß‚àþ®?qåC7Ø‚-Á¶%mKZl!m‹-[ÚÖœ;Lˆ:€ËΫtò=~¿Z°—n¼åÊÝš/EB⥣^•‚O½÷†à’FEÕÿöó_T{Ý´û1ÿ¾áº±GÖ, T/ˆÒÝÍú¯§yxïïR'ÏÅLÔé†Õ%ÊÕáM‰–3À¡EëNµí›“óÙXÖA{‹î "‘;™¨«mGgëKj}ÑÊ –¬ºH¸T¯)„ß1+!¹{ÛhÓ¶9÷\þ®ÌÕgæ âOE|Ÿ+pWæBüÉDùÚŠ0˜N&¸+kë4ÅñgñoûÁí‹ÿßÇ’éŒeéð³À–%sWƒ!- ÷𤂻kOmïÜÚp•»ºyÁ×]Œ¾|km^O‚üݯÅÇã±Í ¾~N;Õqxºñ6­£ªƒæÚåSx.nûrqgo†3¯‹¾¢ƒ¼–×ïžâý²»›èà‡øö»!Êaßâ‰"a+¶| Þ O·Éa÷z²y* -/v4?OªÛ¯p‡Í³ëÍFµZ ô½üXË3 ~#cñ@W_ª³/‰ùøSßçÜ•¹íäOÄ«ïI}g'ßZ;­ãw¼šÔÎþέoûÑí–ÉTFQuËf-Ëi)dZ– XÒ²ùþ¿zÀý³—ߤªž|À/Nk¿bkíèhÔÕX(—§çÏÿΟ ówÿîód©ŽCÃ{“îižtæ†Õ%ÞóÚ ® YÕª[³ÿÈ0L–Óx6S§ÕävßâÇȰևѫ´î°uÆuã@¼Ü•¥ëÖ¥œ |â`¸ó Üa½[!¯Že×ËBA¼ByÕЬ9w#¬ø€Þ{#ÈQÇÛÛ>»håù‹Vž/DUWòŽð½»I?3 ïÞ6Ô´mð•™ý‰Ñ˜D‚c7ÍÄ +ž*¥óîÊÚ:“ åγÿæ‚»kâë¦ñg}çSg?z«-Ø")ò§6lKZ6Û~ o³%øþ÷ ÁݵÏ\¢aý«OýûØ­ñxLñ÷+¶.¨ yKáÇîŽyàSÇ_UõÙ«–N³qu)T Àºÿ4ààÑa§MñóhC Z: "Û>ÚtõG^Éñ¸Žw×Ĉxÿ.ò Y3kJf8™†x° ûö¤Ù…Õ2CF·dbq !< žˆ,rz!ˆ„(ŠÚ$h4í a ó ܵ­×¶¾3ú œ"â+ç èö~ÅÅ÷#íñ‰gÏ·5ÔE›|wÍÅ÷yÊü\PpbjµÚçÛ–ÔETfóñ‰>Ò Þf b›¸8"d:í¸5l¯Þ,Án…™o]yòhûÞüõg*¸É ž©Ný ÀæKnQÀáš…þi÷ÿ:g¯;Þ™³úÄ:ñU¤;t%æ¨|ªí¥{ššJó kJì>4ˆñKBž¶¬X¥fæ•„)”0¥Ó”>3¶ÃÌì®ã7HÉY éH)Y:\qVïŸ_¼ˆAËÓ»¤Ã?:ëqLî~‹tT‚Ëh͛寽*’ðzƒ0#$0FSþö ^ñR7Í5¿¥·U5:Ð Ÿ ½ù%ÎïâMŽ„-fIȱem$3«ØJ½ŠêÍS­¬ ÛÆvsõ&T†ãÄ\pD¬¿¹Dþ$€;|°>Dþ”w-æÃl¨/ ;œþèë¿{džïÞf[¬{qL!ÓGç£ Oб,Ø^däI÷Oo¹E-Œî~Sì{ù¢ØŠEEOnït,øÿR”ó—·5ï srò©¶ýºÚî>¬n˜ ¸o\SJ.¸·ísÁr7¯š4«–å"_ á9 †¿¯þQ‚”«‚zvV®ûÖl~ýhp3}è•©y\‰Zý¦¢Ý‡bݯíïô«êºû"Ri† hXøB*óhº!*‘?¼‚à[Õì‹Ï~YF1w!(‘’Ê›£Ø˜Ðá­¢{ç¶®S¢ó%x¿uôX0k­¦®*¬õ÷#íq…òoÄŸpWÖÞ•T(?·ª»²æŽ„"ï~ˆ_óÝOŸþØB°eIÛf!Ø\!!d:“UÈn ¶mUi@ƒ»e±mKËâo¿g~Áý޳nU ßxáÁ‰gú)¶‚æ#­#GÚF®Øº`¼à™IChfïéÖ})7ðœ¤hûiSi›— ÎAó¡c#м¯Z’sÂ%Á9åŽçÖ´­@ÓÒ¸É;“8¯Œ â'%ïÊ5§)ò Ösx'½Ã_‹À'ÄP2Í ŠFD$lƒ<ÿj~ô¤ ׫aدØA‚ºŽï˺Åg©É®ä#ü“ÍMBßÈ Êû°^ä ¾ ®Û:wnëxy" ŸJÐjgoR‘÷Ù@¼j‘ã_}³ þd‚»ks ñ§&¸»vìø(€¥õѕ߽sÍcŸ¶ ¡Ã…`Û’EQ²,Ng²¶Å–š³ i ¥¹K·ÂÌ·ß5_à~{ãmjáþßžî¾ñC»•wôÉíOnïòƒdRÈù/„F$~h7¦/Á§[ö¤[ö¤M½Iy¯)›Ü¸¦ŒA»4yß]#a"”?Ü<`åâœß¬ÿ&\²€LËî)¾‹Iík  ²Læ‘Â6UÜôZÓšcÑ­u›‡.îÓ:æhõšxõjE½‡÷­$ƒÐ†Î "¢dZ{\£ËGÕ ~pÏGy?w÷ætuìxÁÂ3 X{!7š¼§¥ð€Þ|Mçý°žë%!бcÛñ—·µ½TèsCà'°Ù@¼Û (?~FCü¢“ñ õQdpW¦ ¾¾&\?;”?ÅÁ]ýó'K¾zKQLXJÒRX°…,Š K1w!-ËxS-¶5…ס¶Åß~Ç܃ûmkoW ìù挢x·K½ye}CÏÓgò¶^~ݦ˯Û4ö8S´tËž´@ƒ‹×ƒH‰3¡…k§îgžVF„&·¿ŒTÛð$•Ü·è߬÷³¿ux ó ’ƒ,Eu±¯@`û«ï$A·;W¼çÅÉs\ý¯Ò>çÃ}«k‡§Õh¨N¦9™–D Û¾->ªnÜÇë}ñ—Þ @•ÆPu"Tµ!‘’Þ°ðˆ<ùÒ ¹AôÂødV…@ûËÛZ_ÚÖü¢ôÝifFU`J_ ñÓBy£·ÂõÝ åHÛüz\•h Øå›e.¸Ÿ‘ÇõÔ÷%ÿq—Ð%ÀdQX‚-d:•UõÙ-È¢ˆ°ˆ3é¬åoÍAn±0]aæ[—Íe¨Ì§VÝ‘°‹¿{ðŸgsäûÜ.þûسÇÕHÃõ6¾õ}¨lP{¼(}ë¯ÿ*oG½¯ôawuã6¡w)€'þñÿqÜãw™–Ýn…”à]:1ÕºoºzUÿ&· «*:Z|†¾ILá8ê7+o´Ç³Í»`Ú›À÷À’i·¾šÀîèü¤”,%KG:’¥ÃÌp˜³y$°…A‹†Ÿq$×5<|jº/î;BÀ¡íñXiþÏ_ €9´Mf}WwŽëP5®W_åa_®¬YÏ@׉½j5¶˜1’Èæ¸d¥)8ìk!"Í+J5®æø·rþªòÍ.Ûò±ªôd¦úihp§)L^ÏÂõÅŽ´ÅÕå²|Qìè¼Aü©î0°®XüÌ þ”µ…Ûî²À”u„T6[d,B™´´„Œ……Îd²–×CÚ›´Õ&~èÒ9£í·6Ü™?vøŸ¾ux¢ÆoSµÞû_bÖ]^  ~å]]­ ˆ6hÚÞyÅùå±R98’÷{òTý»6u%€Ö ÔïjÀþ§×]^ªŽêü—Õê¶)ïk¢e÷iÕ^ÏwäKðS2EÅ–-Œ5ÔG´äÐÃÀ’ ™–¦LKS`–ÙÓ2å¦$¨ªŽ,;s–!hYæ…£Á­m%×ý©}wÙ;äÕýW}ä…Ž«&=¦kÃ+JúŽìKŸ¿žžÇª o¡^ë%Ò2шO8 ]‰ŒˆME2"©ˆ· ¥W’ ¢žîý•Õ§W×®ïîÜ‹X’O:¤*’)?)X0¤PeÐô 0á:B˜9ê LÞÖÜÉ`Hæ7^ÜæÔ60Ã^vá? Íâ+Ãu•Ꭰ!~l‹æq󛎴ÅÕ³ü$j5o¢i”¯žžPsÊ’÷ß»{á÷–b,K ÁJOOg²é¬cY\¥¢Ù‚3Ù¬’_lKe0Á¶¥’el17à~Ë¢Ï\·ìï|ûØ×;üO³? ¶Þô6ä‰*]m ]m ÇõJ×(ì·núÝ i+)u"¥ŽH©¿¬í¥w¢kÇ_¿¯«­Á-çh½ âe[šü®ËÀ’ >Ún”÷Åu¤šÔÒ-{À\|†O‚Ÿª½ÑP]Ìyç2ˆ¾Qý _*©. Ý‘ éí j/{ !Êžø!€sü|êǪX1X¾|_úüØhoQ¢Ç":œFáw"Í ŠE¬hÄ«¯„Ž / ½áEJ©4n ¼/”Þ•m¼4(×ï\ucz´ãÙ'Ç{žX5€ˆ_aÛáç§A¯@¦nšÑñ“Ô>Ú_¾(6,þ!ï~;Þ•T}ï0µî¦cï–§‚Õ|ïsÂ4ɳL“<5bC ÕžÉr8,,L*cYPÕ}…à€KáÛ„‡.™¸ß\{W"X´­õlûÚ\½Áëm@NM±t_÷Ï€†Šê+t% <‰†l1$RYljˆ÷mÙ„*^x@WkþÑüàî´41³^KsˆpzÈ~ÖÚ2;úÛrrÞÿ§eÇŽ6¬Ù̯îèin²Ìy–nÌ47eš›òÎ|VFÒ$)AêÉCË%ÉgZ¢wT\ZÛ÷[byÙ WÿfËœSóË—ºÞ=ÅW,kØ?€õáíEÉÞ‘H% šq«Wa) ¢k4)sxº634†Å 3úz_-¯XS_¿v°ÿµxB‘IÕê› •å«&¯K ‚I)9f+ f É À¢\"OĬ2„ É8üÂ65á´­“GÐwô&ëÆgñáhJýù…'ž³¨SÜ]SøÎ@Ç„ ‚{å÷ï±ÀB%"¹à•¸$-bAÜ-Á±ù{¥f³ŽjÐáw~èâ î7WÞ5(ùÞ‰ÿ3·o0Ï.;¯öÊ/ü@CÇÅjä]·ô|e7€µ›4§>ظ ÀK{µÂ Z’) àãxÒ°vþ¨Ý¾¬x¬îOnÝ ýðïŠÚ®¥[ö(Ð.>cºoá¬ÓËì<à»?IU÷ Îèi |蘋Von5%‰²ÍMì9Äwà¶ž[8’³Ç‘RJ‡•ï0KG¶Ä.DuïSjðw[4]8»úW/w¿kê/t~êG`€0®ôå=±«­‡Ã˜ã ÕÂR‰0c´JÕÝ[%CÕÖ¬aæŽÎCysÜU7¿)oUúÔ|ŸÈžÛPªd(ø÷­¯\ÈŒ–î¶1™SXwþ$@ïRx?ÊGSêï¡X<€ c³‡øSÜa˜{]u¸®:<ÄŸjà^öý{jkp‡4øÜ-°¥á›ca²!3‡Cdmr2RQx îMÜo*ý\"Xüýî/>Ô{ß6$§ IDAT|¼Í–Ëߟþù1 á-ïõÚÚ) Ÿ èo*ÃØ‚¢”ÉJ¤ñôªMHà-‡=ŠÓÕ–s@Ã߰ƈ×né® ;sç殂p”àãºXgpd½éÊÒ€Eµa]i"j,[8’Y2,Af‚$€‹FþØVüö®êwVuÿš—<µ#ùéó¹û]”ÿgRíè}ÇT^è¹Ð‡ÏOý˜€âdÿp¸¦9ôãdJFB"±âIª©ŽŽ3'Å—µ¯„¡2›t§PQDŒ ±C å«ú^7áéj3i¦H·ßCLŠª3ÔAH’Çñ=^/À Hõ Išg !ÖIñþíšÑ7^Pè‹àù àhzýµUhül þw¿ÕU‡QˆÅŸRà^òƒ{` ¶ }²ŒwôÙ¡û_ÇÂ$œtÖ°uÄB°‰3Y'@üÈEÓÓdnŒÞ›ý ÿÏÓ{,hªë•×»#±XöÁP¹6cbl8²ÔB<éä„ÜøbisWƒÑ°ÍÌ# ‡™KK–3Ð?pÄ-càß+wÕ "'ZF3w/œ¦£W¡8 Êêhï놼 ¹Q¯¸a”PW&ãtަW?r– PAàƒìŠÈ¿ÙçRÀb?üë’Þ+,KZB ‹-Á–ÊN*UZª¤;hÁ¶d,JB°“ÍÚ–´liÛ°-™uË’±0MÜo Þû‘âðÈ连lpßRæ¼}ñx¬«­ÁîˆÐ•@WN(íþ¥Ét yåk »¹àÞÚ„–Ü´Ï1þUeM´gQ×%?FÝîÀì=¢úøŠ¼ëÀ_rÍœ€ûhÒ 7…õ2,¯ŒAÝàSDÔ·à*·€ûùÏê2Ï üÕξ+¶–ü÷™e¿™ôEw;o‰Tªl¦ÒÌ€)ìº^E"%U {,2&:Þ„Ã{‘ðºî˜þ×çh-ÐQDø+›yE |ÎW7ˉ|9±¹ÙO”˜Ànµ47OÊ_ùÀ$ÄDÏ\½I5m®9=þ®lÆ,þ/ ß•¹à®áþ ï‘þER ÃÐ…‘Ñ5…×RŒâ!-Ó"5"Aìd[HUPLwOÒ&üùÿápóDßì'Ä_†ŠÁ⟠~椽e¿]º¥Ào_èšÊä` à¾âóÜ5üœLg"*+õ‰Õíÿö….jÝ ÍíÀG˜inÒ.ÖY„“o^W`Çþœ[QyŸuQ0ni‚ïÔW‡ÁhÙù<|qñ³1±[úo—’¥”RÂqX²twI½ÃŽ#;+ßX¥Çâ8*jÛ/È!ç?‘EMƒoŸø¥í?€¹$= œäýuÏòø5 Fu$»7îm ÌPä]Í)Ž-c`¸Ù£ð¾ e¹â»÷p ýÌ=WO÷kô~"_S\ÃŒŽÁN7”^²Çú¥È¯ÛríxŸƒâÍ-`ô×V,Œ)ptÊ(ÿ—î®ÕU‡IÀ› îÁ}A)*lÔ):6f¬>£bØM|,HB°“ÉÚ‚}à®3˜Ùò8€UKcÄÂk¹… oà¿ –$‚E?ßù&½{`šà Ð×Û}ŸkøYwY•8=ð˜¾1 0/žÄ÷˜iiD³÷³O/ðò\pŸë¢yø ¾*,#‡w g¬ÙA|^yÈ[ïP¹NŽÃ’¬+G+¤”ßÙ‘Ü]u%È*i}Ü‘ì8œ•üâùÏ‹[Š~–E»‡Þ6Á 4ÚdpIJ©[Ôo—(<Ø„-híÚ×êº^ѰÅÐ[]|/Š.c0Þâ·ÏËš'ÂøPÞ·*s'ç¤;)/+ÀÌUEÕÌ81Ô•ë\eß‹jÇìÚs®)ø (p÷ëï(M3ÄwËJ3ˆ_VðÆ_&¸+S­MÝI'ÙìÁcë`aôtA°Õ ð±uò\©ºð¯±° ³YÇ—¡*Ý<ÕGÎõ¼©«—©p²CÍ#Ï|!* –ü"yÇ›òÆóìÒsküöÅ©â{(¨/Î;š?µ|×ÿó‰§/3Ø,ÃÚ\Þ52’ã_Ö–§²ÍM €0Ë¡³×•xy¾ ŸÜ27E}•Öä;á•á‘Ã;˜¯œy„þØÚ¿Ÿþ´#Yµ]c½dé(F/=/¹§úýVQ˲Žt–¿taÏüÜè/VÑžá·Žw?*4,K*®Ùgû\Á@$,ÃÐõæhØb`8‘…Ϋ…Xt!Cñ6·Ô041÷y7UÕ#ì0É®œ7Ù]•f_ßU±Jft÷è¥ÉŸ:móG ¾}?¾ë‘ÇGN` å'†øÿ€»ViŒz2)<ýøoµÉ.=‡ç8ðغefª4TKhk,D±“ÍZþÚî[à×u}âïBÕ©HÉcÝŸ¶rã?yú2b )‰å†Øq0Gìg?ô[0~WèΡÀ—Ç„½ÏÀ âûp÷¢hfe-Mïè|ðÎþ–Ìin*)²£+ÏêîŸIóÈñ »ß<|§”R:p$³â잣*VÒmúê>X‘cß×îVɎû ãžùeÒ*Ú;rqÁ“9#ð'…åe™!¾(zEL3h€Y‹0\ª®ÞÂÈhÖ“SSב™Ô1cxô¸»Õq÷6PPÀ.éCü|F_­d k¤— 9W ßçÕgÀ÷±à®ÇÇ@ü”â#'°£íñå cËÆÆƒøS Ü™,IX“O5æ¤QŸ`]Uø$@ø4Üû;P)ãê.ß0§¹"òê eðð¨c²Ÿ|!>m4XËŒ‘Tg¾øîN6T]ú÷uÙwΪÇúý9Se¡Rfô$ú9_Á÷ˆü²Æüœ€»7Ç'ÇÏ–¿+sY<¦ãq}SŒˆ ,ÎfÈÒ^¦FÁPQÒŠ–ú猂{Ÿœ+ÛÔzƒ ç1éÔSãMuqYá>™e=5ˆ“´€€ ÎJÄŠx¨À¬W|ðÌæ?d"ÁL$Ø´à-jR²Av…¤=}ÉÊ’åÅvÿ ‚Wý´iîú?+W£ž„b ˜ý¸/}«ÌúnaÔSCº™5sç¾”ŸÔ»?Ù½'³z sKI,Õf]ðub–)õæ›ë ¿©ëmçÔ¬ Ý÷úæ.Û³ sà4ï"ÀïíœÛZ^ÐëHW–”ú‡uñµ{¯øè¿>ù½‚ó'm¹G‚Te`©ðLA`AÄPÖÇYc­?Yò±Ìš›¬C“ÿþWï{kaˆß™ºb+ý4ˆ½’¾Ü?¾/ý–õÁ§¡ 1zE¬RŽ2¨©.è\t£) Û ŒŒf‰Ì'Cê”Æ~pŠž-làî¦.T7 $³WA!;L‚!Ù”B@B°)oÀ€$ÉR—=È?©©€; :¨« Ï W–çq=É»kœÍ0KEÞóðÝ÷€Ä‹–/Ä8… V/x±²4@@ßPÊà&Ô÷¯H±ù¾åq¶2§_ú‰0ŽSöÚ…oµ @ÜÅzß&"€Eî° ˆYJ"¨A"®îé¬X“‰Ÿ©Þµ>#°äQxÅ +JÄÜ—Ãâ¿ØbàÞpÅ.pìgr~®Vãçø•%0÷¤Ôîìƒoï™@æý«Ùu`IÈ‚å:ë€ÿf ñPãD©¹NKÓú%f–ÏÁ¥œ»¾À‹cÀ€Ó² €µd^ðýò]Ÿ‘’ψœøÊéùïW¶4K6(/00` þîË>þ•ß|7oþû©Þ¿Ków©|ªŽtØd?)ò®‚jÍñG—Ýqð!Ç‘Žä¬dÇáƒã@¼²óðD2Û•¹Ô?¸>øgóÖÿTqR!t§ 2#± FF³®'Ö\hžª®¨BÄ®d žîvŸ r™»f+yiP.ÍÛ6/<¦4PÌŒþÔP^’”¦ÿ’%cɺºopŠàî·ºªðÜðwe Ö‘W4ìÔwd8“ S£"”Ó›Ô‰ë”ÂÚÊPf »³7©à~iÕÞpe©—ûS¥Šàþ¡43@:…YÝ£}ˆÙšRÈ.˜"&H"&0ˆI'A0‘T[a6A«[¼šÌxÔ&¡tdMψŒPGåú#KƒÇ§P\ÂPuýÀjðW«%lp¿·?UQb+¯~…ÍôÓp°†qxB_®nîþ&òEyöN@f%˜'÷æa~7y«¯ñF°;jü4±ÀYÇÛë{g?šH–ŒwUÈ–]ŠWÌ,Û9Õux¹{ö—Û8/×D ‘/þŒ§»ÎàeHåáOtBýÙ²â@i‘=8’ð•ß|÷ηòë¿óŠãOÜWpL? È‘$ô‹’–æ ]Æ üÆ£Éå7Éu·Ñþo‚X@2cÍ®–’¿½ðmøy\… ¶ðÏ’v¬ÉÑÁòûÒ­ =£ôqEž»©F „Z+; 3ctÔaF,bEm…òj_ÖÄ‹Ìgà $œÞ°U VfzáGçÓyPÜÜ=;6ã¤ë¨É^Å0³Ã¥vqy¨¤?=$AÄš¹ƒY]ží•ÐÑ“œKþîÚš¥E@Îî§ ÉÔ(˜)ÂÅþq•ï×z´Í£óॕ»a0(RS¦øxE‰ €€~Cä¸J<^ï­*äJ¼üS‘§±¢á:ZFA ñF7÷È;i:Ϲ[5…à õƒ2B2L¤g\›ÇÊủ_%Ï—Ñsœ¥å%AbîLÔöíâó GÊnäíëi>ÞHyqÌýCé<ÎîÝÌñÐf–X‚™¤³J6Y ëÆÞüèÜï¨ov4™£¿yWBKÀðî%çT«ç³€ø-gTxao_¡WÔ%Ü‘¼(øYÚ½G®Ù5¸€™V~9 ±Ññ¥E63†âòn¿äSüá[˜¸nLÜ-–’¥"i½t\^P:’¥ƒÔª[@–ÜûuGGÊë2G.¤Ò¹òÉ@l·s‰Z]zÆ\nõ¨Ë"€ZSB}¡±°PS¼ø÷Bt>d•3#‘íóvÿüûæ'CA‘qÿ w©}±c` =’ÃÜ=.Ïõkt_šwesïJ¥asó9e)¼k2$KOyWÆöúXd? 0X~ ­®!pÿPº€Ãù@?úò+'ã8$É/¹@ ‚O[7èïJíZÉ‘ÐÚ‹Ú¤À½¡cHFÉ ‘ Ò` DÀÞê÷{ò‹_÷!»Ä>Þs‡`®(¶ÕêÀPʯÏÀ•wr9»Ç²áý&ü8žÃÙÝ[‹wô¦óboüóÁ|ÌÞÂì”ÌrEæe?©WÁ‘ÿµå;þ/q,¾+°c£WäÙ[Ï®ã;fñÓwÌ5¾ßsäÚ]C XòÎúÊØ­Ü² ùN $f+XIh4Ÿ.¸¸1yd ëÒ ”ôwzrõ©“žåq]}„•Ù}Ÿ†x:‰7¦Ð~àlç—É@Ñ^y1€ÓCÏÂ}¨dØeÙdÖ@Ç3溢a‹˜‡ŒÌâ=ÚgÚ •œpònÒ,y@ëkU‡’ÞÕš#‰(€L<ç~ÀîÝ‚ëV½³wÖ9o¿e»g®‹u`8S^P^p¥½SÓÈ’âLŠÓIÎ$D0R³ .ãÚ\YS݈†íX$OÀÃ5ìáDR$S#}ñ@Qh¬ì® sdÇg;jAÆüKž³ÔÈëÂ]Õ¤žŒwTs3ðí¥¶ âe­ƒÅ™Lg2¥VW4šZIË&`oÕû}|Ã\k¯ì\­Æ¥"p/}fæD* fD"™eöˆKf“¤Á¾ÊIÒM»Ve“$$»Ë^#5vuJ‰²¢KîLë–`éYâXèÂ~±¸Ÿª ©ʲíp˜Ý—üÄy\t%™R,ê/“Í­&d ÎwÇŽ.«5ÔǦ›z½¥±TÜY+3ù1‘ð‚Ê` ¤r‚F}+kŸÕØGÒã'ú‰¹8j+Ébd4kd=/,xxÇ1‡ËÄ@KÍ»]YÆ£XyÎ|(‡ªe޳ªyЉ’&¢X„f)ÀÊ­º·òýð©1Ý¡.§vù;´bžÅèÎQDžƒCixRIŽ8Ã~FïÓÏÙË4W{AY‘MÌúÈžž£(|[ô­ì0KH¹tô÷€lâ&ÿsóã±Hfi)3>§B*ÙEù³_2ƒ]C½Ü¶¦^:ð SÒ¼Ž´qØ‘l5Þ²IˆÁçþ¯ãpÖhñí—M£lõ¦ôSVi)Gh$žõ=€êGéú€¦Ý­ ÉàXÄVmPÞ{ÊUãc …ŸüâmåqÄ÷¼C¹[ý“£10,Sâ ú3W6\9ûöŸs?3AðûÇG—ÕG—ÕGOqˆ_XÐŒ ÎP—URSpæë]oYYóG¸±1CÇw÷§¨,ZVd—‚GÒÇÛ‡&'â%êñK(-î‚Hgíö'©‰¿~ÀU· ©Ceˆ>íð€¥l„†kí΢H£àHIà½ï3±ê+àÁ´ÖÁ¡øµ‡³îí[dŸR/†]Ëî}©²"»$j §}<~Øue÷±T3wb–YÉÌNÖ»t•¿GÃ:K–\ßÿ;0;¾#¨þ윊:^à7µíÖ·ÝÉý¯¯üÈ¿üì‡îêŸvtxËYÕmªúó+±ø‰À}‚3cgÃLL1˜ÉpŒéœŠwLåŸ9ÿ£¶>û}séBª3˜®`É îÕ“È#Õ…®.OÉ|¤¾–À2»çËŽÃáÍ_záÿíæ fÉŒúß\í8ÜùŽ)µ9ZõnËNüzI´¹Å^ €ÝlSF{ ChqÌnÍÆN,bP fô'CðtäËx…¡eAJDTk%Aé”/äéVnF+±jÒM`•õª‹ÞÌIoçùÍ_õ›bñ8U‰¼÷vŸ,ã u)´J è¡+«ÃÙ=­†½Õ²"ÀàH:Þô„’Jdr¤T툄©1‚(+j1ñ3Ä‚¥ìÕ`ý¡~'FÙeÃt¢$¢´ò\²°-…Ô’€eWiue¬㹎P¸Ðìrmxœ½ `71g|l:?bÒ¥Øî ¾ùп¬È†ÄàHZMë­z³)!%K§¶÷WŠÿäÅÒüêÜ¿ù¢hx}­UߪÏ<Àü¢MUƃø‰ÁZw_Þõð†X׿œ–sÎs^¥à³-×ɬ”ï]Ê’Ÿ9óŸÇ; çÓðcº ô7¯ÿøWw<2Å—ÞòÜ÷œ-¡/Ö®4 V¹šFäÐMòà±W íˆ ,½”„ˆùŸ½EžÇÕáîwNñe%Cï’¤á–ÀZE· ?ƒ‡-q,žfæâˆÍà¡xÖø \Û‰0¡¸»oU7”É+.V€¹›×õm s€™ãȰ}5³$f×­¼j–àŽ“‰ï0ϧd•à±øÀìT—¨UV7v—Õ¿ÕÀ76’pi‘ pÇöÿ22‹§˜+et¤–|M ¢¢è5£¿ó†}™˜ÈFȉP&(z"!7 ÒåÛb¨D¹¥W¢”çSx¾½KÞ7Ç좮ܼԤõ†¹´È,‡3>…Ç»øƒgŒð➃~¹Ò¨ æ¡‘43Ö~ˆ¥é€eʼnŸº§ç—k˜ù©-øüׯþÒùïÅÑÞœ¯òØk'Ó1¹hR‡.ÚTÅŒgvåCüÖÆJÛ÷ôÚ ®xåN–üÔæoä;Í»›È:»»íãŽdÎ:Žä½£Ë¤äg7þSÁ™Ô¶/*Œïþ‘×ÝôNý®Ï~!¶ý …Ú€‡Úzæ™ÑKÍ7nxßäpÃbøõ_²öš³š0ºöc@xaeöF~pWveò¾T0œ„[í5з`Æâˆ­!~$ÉŒâ¨ÅÀP<ëb·+ìØÙ032V‡ïÞ ,ÁÝÕ±@ïÿ0‡¥-£”q§IæÒ¢3¸´€W|º6ø>Ýv§`!ø‚àîš3С‰|ù¼M+ªžôsvL¤Éü¥ ‚Ì8R…?’/7•ô[àá‘¥$Èí.³¤#á„„´œ µ8²³+ÄdÊè¸xï8˜¥äXÌHG½µÒX€þ¡tlAz™3°YùWW,Š8Ò6í‚bÇŽ6ÔGO”_T†éQ9Yåõœþ6§¯•Q±ØÛ¦[¨³Üá~¹BÈŒ°‘Ízè¯Ð‹€ žéJ‹tõ53‘ !Sü†ê¯T &â¾Ä&åÐ! ;¹Uw¼„  ƒ¿±,0xoì½CJ?òrU‡Y5Ü€»ÌÍ2esÿ€TIÞ°öŽæ®ö9%1;±À<<œq…Éœ] Ы‘ÌŠ›{C:Ì>ú]uªn]_£¶ì5eÎ1±à€À²ùˆr€Û «žÑ2‘)}æ‚•ço¬T‡np¿çèµ»†ê˜ùŒHÛï ëÜÄDLT°VUþËOêJ'XÄúÞ¡ûo9óîwýë”΃ÈYKÄÏYìHHÉÑ-LÊñ íÒ¸훕æáPC¯ÿ¼hÕUŧ}°ÿàÝ]Ø÷˜ddÖ_o˜»åH$RŽŽ1gÎÃ÷ö¾†ºòæ¶¢%VžxniðÅŒm÷YUúúå¡jÞA„Ît@IQ`0®áTJ:Øu´*¾ã–d0¦.=æÿ6à;¯ôˆR$ƒvDÒ–ªc+XUj››9Ÿ1¸»Ö`<®oÄOÜý¦ðÌ¢r‰;¸¬üçp# =þΩWnb™ÀÁ€`Çq”,sñÓ]ébʉtLd"” ‹Ž’°Ñpr©½'Ù£'¹U·ìYB"Ñ\M @äO}ª:üz ?4Ébä0n}(¿¿Ô•n|»8j©+x8žæè=»žöiÅël’’:ðmòouO@ÌÛ/,ìRûôý ùo·gz^Tï\,]áÅÇ xuíž·}ìË¿ß6õëAÙ»/ªã—Ït8`ËÇØáÝCu,ùŒpëW× ÑcsYïzÿ'îûéwÆÎœŠ}öÄMÒqtSGîO­aÉ«xÿcãñ÷ö=A[ˆ¥¦ ‹X®Yóù‡› ;lóì£òôg±ú E_i—†¾k †Ý»r€dÉŒâ5E$züØs$˜%$8³þã D<ònò=%§¾¢Ù}z­iÍÛî³*ÝÇÞÊHˆM:ÒP&Í@2ã¸Ï¿Çì˘٠z½&DîÅ›§Ø¸Ì½3ÓÄà¤Í¥±€d Œ¤%#R] ò l.ë‹M׬+"ò!~àÀªX @ö¶ÈÞ0‹ª¥ÞèoCÙìKýg·6¤›‰Íf6]òÇÎd1¥c¢gY0¡tXt”…U¬º0Xè—zt $4¨?ëb½-Б¾¼'°Ì¥§)^ôŠ+§@eùV È/ð°ÕËlòMæ\Ξ{îa=3†3`.ŽØÑ°52’ç(õÖúÏ)yKPøõGâW«É g~ñ¢‰‚%Tr@ž;öSl-]1]Ú>p?Ceÿ`š­ÛsÃf>×r”Ì’%“’Æî^°PI9ÝÓpMÇÛ‚Y! œ€À‡WœÉÍ»SGvaáäíã À})ÑË b)@ ’ "! Ø 0lZèHD_ic@’¹¡³Ô•ü!%HJf îõ'ek?T¾þÚž=ßw4Óƒ%íy,pÎRr"%õC»€îF+ê‘ÖÞ†úŠf€˜¸-¶„ õÃmUÁÎŒèµ*AèM¨Œ†( „‡3©PÐN¤³ê)wIæ·)§ŠSZÝæ'ª~`x#x1Œ/nP¤~i‚ÇÿÃøÀ¤ªx §‰æ¾ÏßgOÞ]sµš“ ñ<óOQT.‘½-dÏ1QÕàØÀU %?‘¹Õà–½&&ðÖ»SÅ¢gE(é0µ–†ÝÃ2$“öÖ2@æö¯‚˜ý±öl 8S£¯ë±ôZ÷i‘ ’Ï"² ˆ dæÏ®üŸüâ§ùdxFNE0iˆ‹OZñßÜö?0À<0œ.ŽX‘‰gÕÌÐÆ¿ÑµbΞûŠÂ˜G³`_½ûηLwÛ×@˜™€Ð‰êý5j¼vׇ¾攂Ç'50^±F&ƒømøÇkÅ?T°TXO$Yõ•rÇÏ\$%;RFwg°ÔQó,YøDHFïþÇË×_S±áº®¦ÿPºTjqê¥G²gÜx<ÝñÄ9#m½ +›Ýì‹¶¢ÅLX8ÔVèά^»’Á]£)U±HQ L‚$'i¾É¨:÷.cf,MýÉýØá’tÍÖÅúñGÇ«UpqÄv gHóH=76|ŸCpWvò!~QM@[×Ì›e+}FöãžcSÕ2iŠÈ«ï7}øçªØ…/t¥‹D:&:—†2aj¯ˆlÀЦ‘nsUø)¼AS¸ÚŠz+¿2Ï~ÍlïWSÔã« sÜ+MÞdþÒƒ¿ðë->R_è}Œ>Wi12:Íîßà°,ŠX±s¾èd–Yvf¤vþ ˜ca+›•ñѬïh:Àb÷[§Cª,Ï—oÑ“C'¨…tݺ¦Ú€ÛµÏ]xý¿=óØt÷òÿ·ïéSe Î]_¾õÉ+%›‚B€²ŽTí¤dl¸Ñ“Ýsý«€j¢à,¬l15#,lOíL Ðg—«ÁÚâ bB">š–©L€@ ßÝË¯Õøá 8þõ #-F†" PpØ$þ¡¼bµ:ÉW3ü§no¦þžgŠÅ˜W"¿¸& uŽÀU5àî7Ð}´µûìEK_<û飩ƒ©ˆè\J†DoqL #FOç¬ÃÁÂ"V]ñ ¦ªo_*9ž5—gïª@[ù{!¥W߯VäèàÞˆ„ÿ½_:V\¥ÎÀÑÚ ‚È"G¾i|Œ(ï þ‚!þy4ß½7T_ôe–;½Ïü],$À…ÂôlV2s6+Ý›ÄÁ·MO: K·Žt½ª~E.¸ÈáÕS°Û.¸ã›Ïæ÷)ØÎk¬à1yªŸ=q#;rPr4(ÂAkd4Ë,Ø<§²¹{ÜÑs‹[»Ñ¨o(X¥`LÀÌLÈã×ëSË>µ­ó[EQkˆW1<R1“„PÌYØm÷DD€Z#k8’£;Ù-¿bЉ¦ÇjÎüDýÙ·¶¿ü`V²I&fÉ Ùôˆdˆ3oòâgüZ È“ã[z–.¬jQºꆭ~l­%‹¼h°½*˜Íì>»¼c(^[RL‚Ê"Å’G˜RÙ²t¦Ü¶û½ß'š7|ÍüæÙ ;™Ÿˆþ¹-µ¡ËÀ‚JJW9R?q­¶·åù‡ÝÕ×F'‡þéñ÷ù#ï~›¿¸Éŵ­sî9Ö}tÍÑS‘HíÂC‰þzŠƒÊ Jð¥2ÁT÷e=°-`)¥9c».?<^þ0≌o`|’®º»ÞMÁƒ~ã›Õ’E3ŒÞö= «{Ï?nÖQÙëß~?KÉ2ËÒÌvþá¿t U y4‘U'0^›ž ì|4hÉ€û—®þÌ$jNË›P^R ”¥¨@2€»¶Üpß Në –‡üLçM¬;I3;2²v /K¥µâÐÃË{a¢"Ž3³;zn‘Ž4ü]/¼Æ¥äeÉ—‡ÿkóã( 8¾@¦~ý ^ô憻~Ðõu†Ç‡øˆ/zÁ3Ìlø±”’Ý~ܺ%·£ Ç›AÇ”™,:tBºe ˜ë6òøŽ‡³Šþ›WÉ,%3ÃÚt‰‰ü¢ê¥ú–&ò Ç2á@:H[v_°Œ¥ÅBµö÷g2¥ ¶ƒð˜{Žãv£©§uß ‡£I餢’˜‚qu„¢âUæÜòC<ýãT`\ÿ½>¨¡üýä€;æMŽŸ?p?{×/EÅ£±’¡ÕgwdE³l,ç7ÂèsY9„Fõ4÷¬# º«ht[ÃÄ ªŸ>zgL&•Û'¡ÀÓF\(G. —ù@¯°~ ù»æ9Á\uÚú ™«ÿŽ„EÂ"²ˆ¬ögï1GóŽ`vÄÒ+Ö°ãøo?ë²~苚ãNqØóÈHÌG/›¡»,käXpŸ®Í ¸ßÙu“4ù9ê“~q`yÀ¦èéX…4×àMÛÉÜðÝ‹O7ê› ¤ S¿>ر/ر/]7mˆèØ}7.½ûñî¯G­q!^Ť IIUoIH†€Ôt^KóAÌdiÞüá5 TÑà¢ÃÝä8\³é¦¶—– ©úµkÊL’9ûÊ#ö¦›4É÷¸*"¯[º–*ˆ‡n°©¸­YÆÀ²ÎcV8P“íIÙãƒT_ZAá JDú)—L|¯—mÀZb'@ów·Ç&GB3â)š§‘òO0ਠ#ÒŽ“BZ“*5Ïf5×Ç@XV¼MLßO¸+{Ó‚j¦ië^{&+nm8ýD¹×‹™ŸÿTqj¸"\Òc@ZGÑh”÷©.Dœ•`Ë‚ H Áú!O=ÉIfÅ÷ß XŽ#GÙ<÷$ysIzÞj…÷­sÿWÝ£Õoúýœñ‹¾"È"a‘°üúF»W¼÷1v²*ÞñدnÍaô9%±9©´sÅgžãèÁ|æ×?ðÑC«68~ÙßP^r|ì`ÿP}Á£Ýzîß~ñëSõ-ë½×>ÝýI©k£°4­H$(•aGðóWþúLÆ®CcÞì<„øø`<¡F¨¶¦šhŒŸÊ4óz¤ù_?¾äó?éùRqÔÑØÞ8?tþ÷‡­ÿóÿq÷åá‘•eöçýî­Ô’µÓ餗ô¾³v2€ë€«¢HP}·Q££ã¸Ìè¸à** ¨Íè(‚2"n㎀¬M7t7½¤÷Ng©Tª’{¿ïýýñ-÷ÞJ%©J§¡{Þ§ž~ªnÝTWÝJÎ=÷¼ç=+Á°á‰¤‘"DLÂÆ/²1y )ÌùÀà› V$‹+ÚC…VÉ c¡a J±²Â”âá‡nMÿN3ëÄ£:®°ÐÏØ²g^çŒm0Ã~DzXFcÙÔaQ>-½`_ad`]óÜÖi­õظ; šS©¤ê¢uþª #– Ù´Ï`ýÄ8 aRô"Öÿã€;q–™EPäÌ+UïÏ3¸ëŠf\›w(Êwvdèž"ò~Äú?‡²O-{éèg)]llnÌ—ÚQ¦i—ùÞÙ:›e¸aº)†d†Å+f(cÄÿt€su"Tj¨…Ä*à ìN`=bBнîTnÿQ¢yüg™™±íÁoYxêµ ðâ3¿-„'„/UÈ2d`ÃÝ—¸#Q^E[ru^ò†W|À €ãW¶<\#Ä7ölxféjÔ‘[«ê·•ö©Þ†ð®ã.ýÂ_«EÔ5Z/¿vïåJ:d3+ÅO+¼œž,ò#ëúV/oYµ¬ùÑgúËÞiMÿõ„uMßÕ*Bv7ŽEáß6ú'nΞÜ9ø§qæTоqÛÖO½mî‡~ÒóIõþhˆ † ¡Ån‡²ÀZ'áA‘H ôÆcCÄ â:ŸBÉ2”ò÷,9ë[³_vî¶ÏŸÅ`†`½x¦"C-ÀŇ¾‘>þrmC,ÿ|ì>3Øê ž­¢¢Œ\®é7t,cÁî­~FúA˜I£ÖX_ΕFýTÁšÜ€ú'IŸFœŸMû •Bóñ•û­2:¥*j24 ÊÝU'qßâªô÷ßui|â§P™YÐýøP®iOë‚qöñ{¯^w©>;$ˆˆ(Ó¸C+ïfU»Äìš«)ìð=í­UVÇ&ÿµ`ä2Ì–¹GÐìFŠÊY9ÆÄî¸8Ãñ‡ˆc<ÄmŒýÌ`¬8ç;äyBøJ†O¯¹ÑÏj®Áº[cxÎ÷xd]èǯlaNl¿{6ùÈ=ê¨Í§<•ü…‹+èÚ­ÍøûþþÊü½¦ú‡£¦øë“fåk÷_É’ÍêDöŽ^Qôéðf^*ÿ®]µ¬Y[ˆÌ{àuußÕ*Tf¥ÓP¯Ž¤d¨”T›ü“˜yîà—ÿ‘?uݘ ehi²S][½¹ó_¿ßý‘±V¸<ßÿ¸>ÿ)³Î¢Rã$x²6y’q›¼Hù’e¨„°KJ2 Œ/?jµ÷Ì»¬^ÞÂhà‘uC|sßs°Ï¼ëãd‰˜b“«‰¢‰ñý†£/ûâßop7W/:r3þö”÷«û®ÖÝT¥ž.ùPG¥˜&¾sUC¦ÿè%ÍG-ibæ§»JjÏT Öu‹•ÀA0K}ö†5Þû¥.!ø?’ÑlS2bÓVì/„M£!^¶H’P¬b2‚ˆé3D Ü„‰Rú'‰ˆ“`«ÅCÿΑ b1½sZ¥äæûÍ9ƒ™þz‹†xÀ嚆yŒ-{æÍmß7}âôÉŠ™L¨ýÆŽ/ص­”*6åÂbkºP’ÛÙPöÈ B.ã3¸P’F¢5ç ôÄ‘Xbô­°>Å!žË žˆx¯¼ :Þã|O‡¸ëÒÈ>Vnþ¬ÜóðßMßß+öØ×:¯6áþ®<ÁÂSž§<=Áa~.CÕ5n«kÜÂàÒà¢ÒàbãúÖW¨Ì¬‡Ù ³b?ï–R¥ëD:%¤TƒCŽ‹ÒÓæ2V*]Û*%U¨”TfMKý\(9Tl~Î’„Ré›^G/m,ͳQšû‰‡a¨ÂeÈ2Tæ&9Ô/šýCÉ¡äÞ3Öhp_µ¼™ÁÁ]—FöãV´¬^ÞRöTKÿ––¾Í¦-Ê·.‚ÐÁÈLøÜ…“Ÿ¶ÿâµ€ûÓ÷«ú®QŠCµFFP,™ŸÁj†Xü½bÄ€&ï}O=4³-3þJ#W½ìÚ>€HyOòw݃#O'9¸3Êg¯[0½ç«]ï»å|÷ ¥Ž•ô¼Ø¤ë+û¿zå´ëhdoª÷› \3ü!½b™ @“•§Có„ŽÁDÂâé}êê<òH*¶Û ¡—÷2»{:yO!„ðDßìÖÞYÓz:¦ÁœRÐÿ×[“47¸›RP ¥H1)¦-»çÙƒI̤˜³NÅÌB)¡Xï 6uÌÛ?½^ôëw:ƒ½‘Mt†ùÈfRŠi°¤Ø¸¦ ,ô«™N,LRA2¤r7R•o"ñP’R´»öª âßÅxø~¨•#ïUBüÜ™YPÍä=[ÊèiíÊ6×ú³ëoÙõ¿> OQ±S³âº†çô™|¸°ŒuNžeîã‚ l WK¥ ÅPCòàP`ÜÌDô}·º| ˆÕ(\VR©$;g´…u+Ayü¡=°Tè-Ž›Ÿ • UrrþÌ5ù³ ò®ZÖ Æ£ëûÇ?n޼¯^óiý[õþ–…λùb#/zú¯uÌW#3J–¸ÅwxçQï®ékÃâ•}×jp×°.•1\k­æÇ …ÁÃJñ÷«,€<¹a`ç¾a+6ŽóŸŠ#Æ®Ì_ÇtÝŒdËê`®w—6Ÿ¿vM (ôî[ºÞûuƒò=€º¥÷¦Ë¦½â8ˆ×o¾I„0‘×pÛÍ„ŽÁuu JhX'"òt›V²’Í!‘ä =ÂAž !„G½3[{Ú[zfLc¦¾¿Þ¢A\±0w”A|ݰy×\{iê >ÚA –f# fÚ3³e{zšìõöt1à&0eÓ)fQ(I˜A/?,×gƒ×LîŽTú†Q7rè¿ß;¿8ô1æ_É!EÞ]mÙ9´Åùñ÷œ;3 `Û®jÁ=SÊë;ÅÌxuã—eîÊóàyJå öèþórÁË> AB—!„> CêiF/Do{˾éͽ¾9‚xËÙ#”Wú·ìž§X(%,Ä †P,Œ0©˜™¬W‡t„ÇæÆ¶\o1×_\Xè™^ècö˜E6“bP¡(™…¹ ¶ôƒA”–ÌP%ß¼¥$Ä›·4ŠÅ÷§ßÔW÷¦¾Ô›Æú*ãû¡ î®&„øy3³T ¸(¬ëªßtƒ“e<¡„ǞϞ¯¹¼jÂl»Ä0§rOk}ºX<†AüFz•Cj îƒCA¡JÉu>Õ¥H) ÄI½¥Lð£¶RÆ–_¢ÝTB« ã^Æ)¼ %ËGÎY3òºrµÄ5€«¯¿¯ï/nÜ0cZ@os”°î-ëK^*¿ôŒi[ JÄnûú¢—úÒú=ሀZÛ{Åà J±¾×ššR¹>JñïD&1¿ô Æú±^ÓÛþ8jÎ1ëžËX6ÄS’Е™ 1 8 îRwÖÉÓ‹Wý9Aô¾rí÷%%E#’n¼µë”GXkÜXÅúVß¿¿½åÞ¡ IDATÿéûqˆw°«Ã˜é'ódÔ"Aé´Ð̰õjfdñZ‹tc×z£añö| „0ƒzBAûÛšŸ}æŽuko·,~ʈ[vÏu´Ýê3ÔÑØ¨;†˜5¬Èu!ƒi[¼¶uvn±µ42³wok¡ŸuÔ ”3ˆYšÈÛW€}HE²”' ž)Ÿ;/Ÿ=o¯ ¾/™[:tÁ]×8?ofÀÖZÀ}JÊZ™aÏgá+Ïc_!Þ÷XøF‰Ñ"L*·ÖÏ>ÁÌÃÅUzÞz£¦íÒªêV<‘R  ©)Ÿ aáuBo)ãì*ÄÃJP¨Õ•°LX7ü=¢ðå¤þœ5òõtð£—43ãñg«"ïºf íhܾ·o8»xéÜŽZµ,¦YX7W´‚¿ðæÉˆï—-oõ;kûæCkû./¼K;íXA²¥ê#ï&óX0 Ŭ~tBUooý–AKçÖ~j2üÝ6,#ˆPÙ±i!@¨7ɺåÚï…âň" ñýèÝß©p½_k}§ï6ÿ‹¾ï þ~ïÿ‘¦íd´xÙ0ëÖ˜§Èè3L;ÙÝj/d$x¨ú ‹×äÝyÏI=$ÊÀHéø¥ UÍ*J©BÑÈî¡äB)ÔD>å'µ¥¤âPŠbw†ˆ©ícë!‡a èãÂz‚øëÍxýœ[ÅŽ^Ò°1VWíC&1¦§¡ÓéõÇ.mpö×.1²Ög¼ñ&qÆ߯]tå­ë?[åû9nE 3~ºïC7Ú~ÛÈ߈¼Kf½æÜ¦Ô) šWü£>³ŽóÊd§Îu=³eÀâÎúEs(_SüåC7þnew!ˆ ¡õ‹¤Íî]_¿öÎP‰P‰ÅÊ-O(Ì^}Ãwºn¼ý@QþŽþ]Ðüoú¾†xuu 2F‘‚ˆ˜b€NTWç‰ d·Ñ@¿'ˆ £KVR«ùB!gîä òHá9OÂ3Ÿ»³ ÀÈ곆WŸ5¼ê,åz­Ñ6ïžËÊÀ±ÒšŒ‚‘ÑY˜Ö+ˆ!DZ)ˆ°˜¨PT–,Ú6{®¿;ßÐ_œÙ»ÚP^·^9:7³`Fi¤Ó¼Ëåþ^œvn±åÜbó¹5üé¿:º¶ÔØq=x5mûµžÇÂÊ2¾§„cîžò}Ót×>І‡cë-Ò±þÈctüØ–ôÖdÎOy8rQ3žÜX-¸w ïÕwöä*„;>öìÀ²wÕ×ûD,„Ú“¡%—,zõ;3pꦫ.-¾;R`[þnÜvRŠyKæe€˜“ÿÞíý7uýÚ•ý)» ›¶0Y¦¢íÕ ÒùFv·—†¿ ÁFŸÁËþ0&^ëê;Ež‚0¢¤@Òuwv½ë“GùƒÝohŠ.žC"J×yVD¶ã*ˆÒš¹›žj¤¼ÛÓ@„ï{Ÿú XÍ:öý‹Ôvòˆ<Òç ë&Uá ²mXm°yöŽÄW&WŸ)W)W9j”m·2I³n”îµê3¨ƒcý™´Ç,‡ä`Q2 Å¢ûˆ%[fÎnÜ_l(uôío)œpÏÚ+ÉFr¶¿.œñºIsÄÿPGpwµeçïQÊ/¸C5Y}ÆÊóáûJxlÀÝW¾¯<ž¯|Os>ޞ橸[}Vœž?žI1ä¡¡P†Êð,sr·„7&z{ªGSø²jdŒ1n™ú7®©Ùm%Ì*ãÔ¬ gæð>{2{²3ÇÚ­P ‰8—óŒÀàAŒ3Ù4†>suç5ßÚôÕ¼+«—·œºéêžþ@šët=§ÖnTë†tóô[²¯`³~£·ßûw#œï>ÔÓÚÚúUSÅY¹ðˆA¡1uÀ™j`¦ƒs³¾sõ‹7<*!žœ‚m%¦E×ý ë†»&ƒò_U_ž1ØýºÆ/º-?è{7¥ë<+Ä X’žN{HŽ ßz%5Waíî:LZs×Xžùg´£Æ ¼µÉë=×ßÞµö¶òÆ«ÎàUgð±g_üž¹ ¡”týR%˜=ƒ¼`‘&Å42dÉB1dF.“Z¿`þæŽY ½Å†¡ö¾Þæ¡A¶þÊbR¥PÍ>GÍ:GÍ:gǹ¬ ¾Öà`þ¬\(9Õ¼™Ùyµÿy`µï»2wŸ}ë™ñ…ÆÞñ”ï³ç«ãtlNÕºâ:Ÿú±u÷ðÅÜâ-™–Z‘q .iŒ.”B ľGã¢ó(½Åé9ae¤¾HX}¦éMkšÎ¯ª[¸ra£b<µ)?áž³“¿¸;=cœÝ^ýõËH`h8$â\Vä2zN7½qì÷SIŸùj÷WªyÿV-oþÇÍWïܬ™¡ê6U\;Só]Ùåi!ÍðÙë×¼ÿ+(|ÝÎ'‰&ÈX×£zñ÷êK3t"O0(Œ­§­GB™¨mß½bÿ¬s«9ß~Õí B‘¶NŠP‰PŠ@ŠI¢kïêºþ‡5£ü×ÔÚ Ýg5FßÅp&‘N{uOK. J§="ÙHíFš‡ÁzMÃÙÆ’t–Ì’HÛÞÚNºà-Iw]YaõwJjñ£'GA¼.Zu:{ºR$óK˜içP ”¶¾˜íX1S.›Je©PRaɺÚ!‡ƒr™ƒžY0K‡æòCí}½MÅ‚é¯v¼í/¯õÀŽS‡·þ®K_ÒnÙ9äÈûó ñžßSžïwö=åù0(÷ ¯ÉÌûˆé|޲»ì~Fkómé#œ]%GF•äbI•d*O œæ+Æ$øÑŒ¾¼e*9”H8R²”ÜzþšÖ®jm*+4‚ñôs€{§ì›öØ™š¾+Ý6þÎd~Å©É{6ëÑiêcÕ¦Uëv?vYó+¶\×3H¶ »•ÚÙaºÞnQ~[똼ŽÞ_j­æ¾“Ìá+½šá!íëÝž™ÝVÊ_:ü> x‚„Ò¦Äßü¸Að®Öw¶‚ñMF(ÏÑ–k~ÔuÝkCùoªÏ´ v»‡?)¼{$4$=öbZ|\‡1÷aš®†›ÃŽAiþάȃ -ÈĽ4–ÅÛWKäMÖL½zVš÷èñÛÆ”×¼U¯ö¢3†\ ²j3»ñ³ Å^}6Å üb'Ѓ˜…å‡@õÙ”ÞøÌÂy[;fíj™ÞØ_ì¨[ÐR¿²¦ƒYM þä=^[w5Ê?o?«ÿ ßSZŸñýD7ÕpyÍÜu»ÕçŸ Ô/ü¨óAÖù$¥* jw =½sè)ÅÜ^LGãêÐðw}@\X/–B)YrP}8&£cÚ‹y$:¨3ºÖ̸ âŠ <½y"pWƳ#Õ:ákžöËH˜ *‡eq8$âlfLR2sú&—Æ¡ooÎ}âæ U-¼wìÒæWl½~ÿˆ‰‘Q6€06¡ê4ɬ¤ÔäܲDîÕãý7—cÑxΟxuï.ÙO41Ä[ÛŒ5¿Ç¶èÑÙu0˜Ç°Ð”U¾uñºÕgnXz¢sÇk””d¤Ë_ýß]×Þ]Ê߯Ÿºˆ>=&x$` ñé´O$Fv#¬ì bT8}FrúŒ±Ìæn›«ú!Ȉíz­Û†µ,^ÄH½ððØØ¯-vκÔ_zŠ·ôfXk<Ë¥D¾  ‘ È"T4Ö$òEÅ,ê3uõ™@ÞìS¼Ù§ì\}îÞésšúû§÷w7Ë—ó=žÝ6tø‚»#ïñÏ'Äk‡ŒóÌXÁÝÊ2ž“eøþÒšŸ ©¸Î§PªÁbèZ¦R)òöþÇ5pÌn>A†J$û¥†w•Â0T‚–é-•€¾Ìãi5öÕf^°fVÖòåó™±n\pŸ‡Á¹œÐíµl÷«Ëó!@@h[¤^ðA Hµ¸iŒ)Wf~PüP5ÿÕ»ê?úòîzF8J©µhÎP1ò.]UñΖә¼ûãþ“¢ã&A2IÕÓ»Ö4<óȪ>;°cOiǾ€öÖôø{j@š¼ëô+¢ŠX!`-4'þïÄ LDϬ>ã®+¾c!Þ`zÀ˜)•˜EWÝÝuÍ=Õ¢üwùão¡éûwÞ¨ý‘#¡ÁôဵPc­ñQ(}#òDb`ë­ Ùºø2²†»Ò$QÄÙMt°¾I£ÉDŒÞùå…øû·+C¼þ °-¿ Ù‹SKOI-=E±¨ÏÖ1ÓàähŽ)ž2l|îù!É úE/é8â¦÷"¢µoìiîlØß¾Kãо*æøuxë3Á]×óñ·nwãyÊ÷àùÊvVùþB…4’iËÿ-åS(Ua(”aRCWJ*µµ÷‘-=1cnÛIógœ<–ÞR–¡d! bZƒò¸sÆe Ä;¨AÈAÈo^Óù–š‡†–Ïkóú-ãû|2þ¿nQm¤Ï+¾ñNíy7n™ÈÿÎÿyúT„ø²¶ê¹ _¨&+ø"õÿ“¨Ž¶'Ùº]ØÓ(ïR±¾²p#Nñ×Ô.‹÷}í€l…ï8ê»zJfL«g7„ 8ñiiÒÍ=Eü½Ê˜{Åñ£+n ™¦‡1.J-ÍS èÊ{º®ùYUù{ü/]ôIó@h}Æ×ØLÚ3Z ¹&j"Æ€bÓO†æ“`V`é6F NÖ4éYáÞê3‹v‘u\5ô?\ âµAFßïί°Ž.ª[vJÃ/1ÁÖÛN™Â¢ÒR RuóOi{Ñþ"jkÎúb“ aúŽŽ¥»ë[úö¶õnm:P.ãû8à®ËAüÁCùù×iã£0mUÛYM±ç+«¹ãþ|娩™Ó3Kþá³…bh:¨åÓÿ,%¤äM»ÿ¢¯ Î|YddŒ·LCU,…¬‰àLʃQÝWÓAµ†÷o]³`²YŒJ/®3F-ôРĀ­hØFµ$@Ä¢L4–ÐÉbŒÿòÈEG.ó5ÏO}ú'ƒ7Løÿ¼ÿ¹¹!¥÷ŒÄ“àÙÚfdL¢Ù3ýl†hÝ}·Í~}Jâ~ùºï+Œé †lÖRzÄiÏþaÓ›+Cü;ÂxžžSeXÛ»Ñdb³¬\lDPÉ©¿e!t„ç/¿ÍH4ì$šʳ¶ÙЈ¢+ÞuõÿLŒòkøCçÑИNDá`ü‘ißÁº‹1°ec DL£7C6{Òs(ob Œ(oº¬ä6FÖx£Ï$lòûv×_n>˲z¯ÏG‰ÇÛóË·ç—4½9M@8çEé%'§—œ¬ XP6Å,Â"gœœ^przþÉúwD=ý# ´5gi¶Í^¹oڼ澽Ó{»ë‹½U|W•ëpÅ÷³r4.¸ër×IxΪ)­°[“»²˜nÝîžò|¾`Ì…4‰šyÌ¿W2)"¾å™X×ý{f^Úyêò¯ U$°„V«)Ëâ°”’‰Jܳ1oLjcŒÙ²ø­k¿mò)»KçÕƒùÙ-ƒŸ]äë;[¸Âüý8õ’[¯ Hc1s—†ÎÛ?g±C<91‡x ÛfÚ³c×…øpS½/•êͨX€»µÇ8YÆ4TÙzÞ%3Èyô+¼¸Ià½ß86qW§ïV_d_woï€iM©æÆòŒd¨:é‹•žþ€mÓÒm--Ûg/ï™ÖYÈNkíÛ‘+Õ0 îê°Ä÷³s jWìs׃ñVs‡AvϘ#}_y)ö|¾¿wLôœ5=`gO Àœã>ëŽ"9b‘ô§žûµŽ=9rÑé‘’3A†¡Ò¦Ç ¾óƸ`™eo[³ì¢É#;€%së™ñìÖ Q‹ëÂE©ÏÉìfUóÀŽÉ!p«y}&¹fÓS›òÚ‹yDñâÎNéùKÆ7ã¿nÊù¬¸w ˆ2 Ùî»)ïå{ÚßÈ$š¶ÿ0TŠ÷â §pɤ¬ ?¹ÐÝxMOÿˆ¾ã‚va‚vC&ÇV㒮ъú­w‰á%£ºëèw~ïåߊËîå(Ïî¡™E—ßßu坯Cù¶¾î7e¾B‚†6ò:iD”I{1AÆÚäÞFÊ įïBÄÉ»É DQŒAd—Ô1÷cWúí]LŽ—öÏÔF¯ÇÚv ~™x‡‘eü¸Rù)ö=þUïšû÷ îmw]eÈ72Êh ?þìý­ÿ¹b>fùÙÇqn\Ï ¤ÖKÃ2 @ œàròÊ‹ÖñöBv‹;ë™Q1§hIÚ˜7…4+ÖÉß¼2.¸ Kámì{9^ˆe©Ÿ‡ÏÜ3<Þâo} >ÒTŸRÌûFœònï þ®¬†mÚLOûy ÑÔýCÛƒ­ü¿hpWŒÓþçã *ÖHÞ1*‚¦w èχr‘EÔØÛñwØd`$ßát‹•ŽùÍÄ*Joó|"jØê¶Üwù7CÒ˜â)Œ£¼î¾¯¥y $½ó1í(ÿ5çKÓ{· õ€I€Œ¯/†C6O±€x*_Äê3Ћù•Ó|ƒõñ¡'L@ }Æ=™Hag¬6Æà÷ßèâQß·÷Ý=%;òË¢'”ƒ)s¤óï­DcöíéöõÆwÄö6Ï*f[úwg†+_1.7ßtØ@¼ÎèГ~5•øŽ¬^´ïÀKv;ž ƒòû?°¼Å-gµeìÜWŠo\tòçc8ž_B7tjÚ¡ê¡'ïÕ`tü±ç¡ ’>¹8,ÃP»á0à£ß¾æ˜w(²cìõÖ—e±4Ë6Žø›‚ñZ‚ã”XL[U[h„5ÂõŒ ïí¦<€• ÎÉa ×.«·xS.âM±]¦õÙ’!õ}Ž%°ÑdÌ iùþ_Rù»–0ü ›«¹„¬pêÐ]õY/—ñ„RËîÑRM.p&Aá-Ði \ßuâuqù­¢ÐÊî¡¢Pź¯qjow¸äW]—ý¦å[SnÉ~º­¿{Iø¨KŽÔsL *…Fϸ°ÒÎ?c¤ùRßO˜UýŒss·§k˜±úL"©&Òâ©&Y¥É{BvoøCE}ÆÕŽü²èš‡\›üÆ&…ù‚…ûH¢!«Û»Ô´Ìø{}Í¥tCóÀžº‘‰1Pû# –έ?ôQ~Òà®Ë%Â8įL_ìy쇌Y°É÷•ïóûjw]Ë^úÅ(ͱ̢žhºr(Õ_ýñŸù/V8ùø·œrâ…¡ÔC§ß/ Ë 4ü«/^³ú’)@v çÔ+®îËë WØ0\ãR¬Nüæ•æ×^;#µ8ãÂÅÆ~áµÏåÛZÖø‚†¯ý²x±C¶í{–Åw{³÷1-°4Öûй7?"Ùj/.Ò…0Ûø¬‘F¡oÎ[@^ý–ïëäýqB¿yÍÚ%©ˆN~ìG“8cEÀID8oèN“ALŠ'«qr»i±ŽŸ_ñm$/u=pù­÷_vk4÷äL5,´¯5ö¾áò—üºëÒß”×àÞ—|§ñ“={f/ ž€N³$á΋§tÚGÌ+é¬/À Ìv$Šˆô´*ì€S¤ÏÄÝ1d·þ™xŒgœ6¯}ÕÌò›_~Ë/ídCœ¼»Ú™_ªGšã$-ÄËÁAó,‘=°û‹Gر¯ä7µ¶·¦¹0AOµ¿©}¤.טߗ ÆKd1“.ÁîP†øw]ÛvõŠ}ñÚø(…O)ßW¾öLÜuù_N¤9ÊòÔ¸ÍQ†ü¿ù¾Ö^vÊ%‰j ÂKÃòU7üøÕïú‰^1cJªâª+<Ïñlñ€Ö…Ѐ.̰w|³ìê8µiûÐq[>þ`Ó»ή¬ø_àý«fë9_)ôD£LÎ-£Y9Kg~BÄô¥9‘yvÞ˜ÿòÒñ¾nÉôäüU qÌöÇ&q4*òwõYs¢ó<Òæ™X+ªL¨‰IÂfêZ§ªêiìÑôÁí£ŸúÕe·jò¹h¤%ï¿"¸ëÚ™_b–ÜÓNñ.†n°’=oº‡ögíÐÃì¶åÛ‚T€WÜ!jW=»­àˆü„¯ûü×”€»«‡x“CàG£L¾Ïìï¯½Ê C«¤ËHo‰ ì1é&U(Õ¯ÿpû¯þ÷ÛŠùÔ—_þÊÓ®6ýXÉÿpéš“.»koïðÞÞaSñ fçÞ¼#"ïG4Õ­lLX_¨m=ŠÑuü·®r v> ôö6Nq®í¹€ÎÎÆÍ3¤>Ñå\ctcÎWÌ}ù+¶GRŒÓmœ’e¤ÂK…Áù1‰ôÆï¸¨ñKL‚…Œw·f/ü—º+,5¥Á½P”…¢Ôª‹ç ”;g Ð#ÉßDNŸÁŠq›Ÿ®*Rx]¿¹ì“h¤â´“®ÊèòÆX)E è¼_¼ñ’?&›ÿ¨û~® à$ØÞ·Wß®ÝêìðÖ" ÅP íÅÞ1ç; X_¼¨°sÜ+)Œ>ã,˜`¥¡¿®Î»óS¯¯îºvå[ˆÊýÆ&& yî±Z“<¶»÷°®âu…~€eÛËÿbeˆŸÚ:ˆ?¶åm"ž¯sf”ï³ïó;&@&$ﺎ;ýÛ€¾,(Æ .ioL¨‚PýÏ/¿®áéô×ÜðâËÖ¼ø‰73%?VŽ9qŠ=²Ù¼àºÁÚ“«\v…&7ÙÁ:1ÄŒó u˯÷xËÎÌÊê᦮ºOÚUU e™>m˜áhÕ¡¼´kï) ôRAj:ψy"Y*þÛË&8K½ø2„Öâ§à%K»`ž¡äqÛ;]V;æºîfOó®„ur¢Ú×0›3 ;*>û¿—}=4„BE!‹P )…TŸŠr(Ïä…J”F(TtñŸºÞñ—åŸjé=6“( ÓV¥á€‰(›õ2?é«DB÷R »¹¦XŒˆ¡9*0÷ØÚ­žfñ:¸Æ =1$³‚ÒiOøBï»yœ hìÊ/v.š 7ëY5è/¢Ý½#;ö–P Ä~ €‘=¿#:!~jÉ»+ñ5¢¼g‘]ûg~»cÍÝSîºN<ûënÊ4 m”c,½];a‚È"©É÷Ü÷Õ|çKg,>eiŽ–Õ—kÕñóff™Ù ÝZTKÀÚ‘§óå¬auì·®v+4Á‹­æ!l8áæWy/M½ï>ïr';oÞ9ðüYÙ˧ÆM-pω ÕøÀ*'Ü6«Z¥).¼”‰¼õ·ZqfâõÌâã˜hùæ¿+ÐTœ#òàBúˆžZÒ-VÏ3‰ï±óa$Ô˜$ÞrÊ?a?Ÿõ‡Ë¾–Àq+ÑŒdz&H*Q”M(éíîºøÁ.÷õ^¹¶ýÅ+{´Âœ…xk,…ÚF)²Ú:9jHÿ†¡Ò¹Ó´C22ÂGZ¼Åw"÷J&¢Ç’1±Ó˜ÖÔÞ3J=~öÕñ!~‘>x®›ê74:æ^é˜F? ˆ <vx¢òï!ñ ÜuiˆPÄûv6Õ÷ù·Ýk25»®¿áÖ 4#¦Æ4¤s=†6Å7ùÔËלzÅ]Ö´Ã IDAT6ØÿdEcªì5Ä׊òYÅѰØ1ÓÍ’ÐOõWVý&Qå8gdüïbñ½7Sž ¿e×Ðq{¿®˜›ê}eW_ŠÝLP”J‚x2D Î63¼ø Ï{ú뎼ÿýåïkO8$$“dRL7Ü9LXqpw¥1zD‚A†Å'ž%+Í;#ãéó‚aôdµxMÒ_’LžµÆß3.ÄïÎ/Š]$ˆü†f ñÆlŒb•kr€…À8óM‡ˆ©æ ‚»®m»‹Ý»‹¨âOœý“*ãá·[«÷Pmà®KZήû¥Îf58 ùUW®yõU bûì?SV6¥lI|( ñ¨•È[ØX5£åض&Oì/<Ù7eK©ýík]‚M¤‡pꫵЌQoIì¿óï) Ÿyåðm…@Ï.5Öû 9O)îÏѸÌRO0Y@OŒ2•‘'BF'‰j?š"¡lܘ¬MM]0ïúNCÖ£$¸[{ŒÁè@i/<ŸEšŒ³BF€®Ú¦ÚøûžìLµ—v³Ï_/½å/ï¸EK4®›ª%š@iY††Œ9%E hÆ/µz×Õ÷äWô=âu²sO6”†D)`Èd|·ä Á`°B‚›#&¯;þ«ÅGM(k± ñÏœ¸ê<@=ôÄ„€ÒËÁzQŒÁÝ_éúÑ—ÆìdìÉ/t”ÜN»%Üñ=…¥ñýE®€²a«{KæÌÈÌi¯åÇëX9SÍ’âŸpwU=Ä»nêo·TîÀν5ƒ;€ÓÞz›v=šuP2Ê9ýª5§_=¦d±ÎÊ&GMK|}µv\ç´gܽ§¸ºÃ„ú>ÞSíxEµåV tÎH PÚIáé+õÊXt ½ÿ{ÃÿßrZxû©#ßuq`}ùýÕ7hÝѪLÌåMCÕD‰Å–ZU.¿–HðS_väýñWLüÕ7íßHÀÂgvFxºþûµQxm‘lÈy Vbîñ­Å3à{Ü#’îö'òIøÎ=¹`¢èÍŒÝe׃ï¸%pŽxë¢ e"ž,Œw_ey÷U1M½ÝO.ïÔ@|l´Õ @•´ŸñÉúåÅàH“qj»1´›i'B”y%…ˆá…}81KA,I³É{ž1Øx‚~xÓoÉ ù …1G›“WK;,€Ô ñä8SÍóñÏ'¸ëªâO^pﳟâßlš;ùøõÚ·ß.m5´®Ç0ä³®ZsÖ5¿§†Ÿê+8zzã13ÙUBüìfÌô›9À£{ûÛWíÚªUÖ·]뚨:ŠžûÖá â¯ÿcåÏ«•™y3ŸÒO oWv9ì|ašRÜóõI±1ë7d½ˆ°³Ñ^8Jxg©Ÿ\Õ‰</zª:ò®F‚ÌŠZ¥©ñàˆ1þ³·x+³½[@‚Ï‹ówKç5dèn¤&õÚNG}¢jö|ø7Këˆ×˜áŠŠœ/I5 ô••ØòÄ f±gFcçöÇ_¾ãÎH‹¬ñ"âE6ãe3>i‰œUÜil¢%Ÿ¢LJÓqµ^I;±[¦ÕÚäò£Í3Ö&O‘‡Rk5$<ºkì¥w÷ä8ÎΩ†F€ÂBµTiûÞÒö=%ÔñUåÏlx! ¾J²0µå ~n{e”÷}þý¦5¿ÙP¸Ïž‘¡É’wWg]v§Ë™ $¿îš5¯¿¶¶SË“½æ:lU{"x}BˆŸÕ–aÆì:sbøûî©\y *—ýk=ÁvæÃÄFj _±.ðÿíÇÅwÃN½ž*ï`Ö‹_™[c½ÏÌýù  ÐèœËzîYŽ¡¼d¸¼÷«÷ÇQï†Á£Ÿ‹ÆYUuªˆ Éðw ’L×­©Â %užiråÔ•ØSd’ $´o’G 5 <«‰NNÖ®ô "š5RU4ù#ïøªŽ1Š ¼P‰R@eÙÂí¾†±^«Öåõz§{Úý>õ²wÓóßZŸÑ1š<ëŽÖâIh"ÿ0 „8âbN¤Å;ΙjȪí0²ŒK&p 4¦J*Ö Xë6€ÌÆÔ1îlßÞüüÄGþ÷ê¿‚Z!¾Ú|±çâήðÜŽ ñ&»ºmǵ"Äÿ~CµwÝÙq`à®ë Wý ù ×®9ïºI^4<Ñ“|o?€ãf¶?»Ým§ã:szfqc[g¦ÀÃ;ö>²«g’ï~ÜZ~ÛudÕv“0ã\4.…FT¦˜‡üAøÏö¿BÞéP;±zªµ?öýƒ+ä2žñ¸‹ÆµXc«5™wJ÷u§Mü-´ôm&PËÂ]y›Av—HSËñ©ŒQàŽJ9b±Ä€Öj¢¨ØþC}‚H4òQÔÚbÅÄ]Öx=vñW4s—ŠJ´bÄØº]¹[ñ‡R’6>:C1I»ÚSýª9>zßÕóc1þn})àÒ‰¬™üb@fO‘MÞæ‹™Az<Ê uÜTãR 2iPRJ³Å2w}ßs3®‚λf|ˆC¼_ß9T åÆ>Îâ;Û3U | ù‘ÏÄ¿€à®«{wqÛž"Æ€øjj Á]W× S =ºÇ =¿¨3r›Tì¸v´f–4·„ ÀCÛÇk©h‰ˆ¼›>ªñD0#ßÍÕÞ\ô)^.¿g Ž‘«ÝŒ2 ް+F!Pгi¡u`dëÖ¯Å÷ÔªyÅ¿}zrä]ße½$C»h$èºVEáržj4¸›—µsLqìFŒ§kπ狤>„Wk‹µ_Uo»þk!‹âˆ «‰’jŒ.¯(”v‘$è+3”¢­~¿l †8ªçÇG÷þØ ñ¸µ‡²("¡‡½œSž5æÌ6F±1¦ÃèIDÌÝê3™¬¿lî©RÊ'Ÿ{€„14÷ Ç71zÐlÂÚ›ŸgŽ¥ËâU"Ó@‘©!À„_[>°ƒø% å_ppw5iˆŸrpŸÂzd×¾‡wìpâ¼9'-˜«7–u\OY¸`YkÀnÛñ·î]ïÍ,ùÎõÉù>ÍÆL”˜³À‹Jq)]øäÝ#7x}Ã^Wÿ¹ˆ±ÛÊe„RÜ—7£LÃøB¨˜³užJúdìØªyX·úC èÁOJЯðì+«:ÑÆ¯º5sW (¬Ÿ°s2+>릥9±9&½1”* µ·¿Æ0Ç÷gV±‡®©V"Ò+¤OX:aæ± o 9t*Ó娋&4«AÐW,$ RŒÓëUs0¢u›#{|dÿO\Ç•tœ$‰âƒ”çÁÑüH‹Vp×þ™Xš¼Hðw#Üëvl&ã "©ƒ#›¼[ÆÏsbŽBœyéUÆ}ùyúÄ«¯H+ö´Æ“nº÷”º5‘ïâkÎw‘°âΩà®KC|g{¶s²DþÐ,‡Ú§,šï6jˆù²Å¾Oþ°iÛAšã–E8û`gþÆK¨úöÒ‡ÖàCÎiü¢æì‘ëQ±RœM{й0p¸¯É»´§€|!”ŒtZdR"ZœÏ¦ŒYž.@$c[duÇÖ­DÔÛlŽ­î¬J¦…Çuÿ]¤VŒ ¹ î(YJ*ìŠ"‹$¬V#¹ÓÈ‹§T9Åjªj‰ÆáÓº‹¿´ö¢/‡–¿“T$%d<žLÚeClääúGÚ™I1)íhn¨TfiSÌtDßOŽì¿Û.Ójð Cp6ãç2)ç|Oê36´Àº¡öÉΪ‰1D餉x(nb&«$ï®öåçê#Ô'W‚h,ª>~u[­f¬&³¾Ç†m… ÝL5Ä/œSà¹í‡ ¸ëÒ JˆŸÝž¢ä=^nÝþ—ÍÛ¼xÉ¢—,[ à¥Ë–.ŸÞ `]ÏŽßoØr°ßÀ‚ï¾Ëºm¢êûnq;Ý2L´2sNãM‘Îΰ®Îf<Å<8ªØSÖ i’dcp(Ô¸Ÿ©†¶»¥V׿è£$¼þ?L1B»ý¹WW§’%0)¾[G͸ÚXï’¢R„¤…u}ß}b{bOP aM5œb O U¥‹fZc @ol¶yýÛ¿¬‡žì”I)Éá~¨`:®’¥É;±"MÛS=²½H±Ð UëvDßÝGÜí"…†Rë3¹Lʼqë³,m„r9“¶ÑšÍx$DB lާ“‹1ˆ1úÓÞz[-Gˆ •©zuçÑîÝ%sÇ€øÉ¯ßt0 þЬî=ÅnKäÇßsN{†€{upwõ§MÇ_¾bßk÷v3OfƵÖ2ë19ÏŒeëäYR» L¯5V營ú©ºþ즛¬ãÈ;#›öXƒ»B¬¹Lµ½Tœ/†ù¡P*¤SÂì -y'„ïp_Cµ-)RÿìŠo)rà­ûqíO*SøÆz@¾ P%þñôd—qR3¼ër¦!ˆ„á¹ýk¢ïÝ¢™ˆæòx6ÙÑà®ëÙ‹¾dTøøjÝôebñîPÒÚ‡:“4š?·°¹®Guô)}ÂB±Ylveþî#ò÷‰TîYò‹K#L‚rY?æŸö_8t×£Lñî«îLÆ'Ad“LÀõT­e>Îèk8‚±êÉw™ª4ɱÍâç¶gÊP~nGö€Öç›Bˆ_4§ž=ò¯ !^›–¶>à®ë6ýá™g¬Û»õ©=[)Šœ¬Xs¿{cÌÉ2pQÀ®Æ'€ØdÓE…ßåð¬¦/±‚#ïõ{bôÜ`zaš&ïz¢ÉyXžîVeB¡JÅu)rùÍ'Bìûßjی޾µ:ò>}p;ˆz;ã8ÝP™ÂÇÁ€ù{ô/ËòE¢|è-I%3:~EøÎ–Ä@ëÏj‰·—ZŽîº6^tSåUAI®`°Q ÅBëZÌâ¹Íé^Ù>P4žmcC+º²p/ÀÌ $JÃÊA<œ±2Z bÐ/bZ|&“"A#!HÐÜé/bðæ}’Vµ¾xãŸ!!^òÆ[k8zÕÒ¬“Û,ì8ˆ×ɉºþê”@ü¢9õ6Âà®kˆ?LÁÝÕïÖ­‹?œÚTáÑå`Ý6QµaÂ…DÎHþÖIX/ß{ãôiÛÏlú2ÇÄŽU&-ó`1tª [4wÞv¶Jk{g¥¸P’ŠQ—©IÅ>È׫­:çLµ­’ÃÄùß# Ϥ€kîI jS½G1ppóð—Ë^êM™O³ y7š bˆ¯Œæ^)Ÿ€ˆA¡â@ò¾ß½‡„˜óÚ›Üöê§œÆúŒºÆw]›/ú¢±Óp%”W$c›'ÿ6Si7Z áíþÕ{œmF)lMµþÔŠÔÖùgâYš“ÖzôF‘t¨Ø:«¨„飭“¡‚¡ðI}•µ Db>•CO5àn^á¢/Hí¥á¤D#ïf%ôåŽQc1ccgsºW¶çKVˆ ×ŸW1xyáÞÅ{IÐÐk%&—I‘|ô²D^$ ßvYIžH“vèÉŽ°¾èÌ 1ý“(‘®H ÇjmÓO¶í)yùö”3ø ñ°+sV_‡¸ëê®±ãzèWEp×u0 Þä¹[L'¡f¬Á/2ÕÀÄ>0ÔUÌ5ĪvElf6fG,†Ê®…íö‰BÁ8ZlOJëŠqÒ<óPI•$È‹‡M†Uƒµí$¢½õ³Ë¶‹¤6Ï0I†zºÆ #Mõ€B¹ óú–/”m±ä=1”Ðdbg˜ÑBÞ³LQy’p•U}VÁXµí¢Ïo½ð Ä'‡žâŸøÛ,¥„ x‚A;›3}²½PR0Gðí7O XV¼wùðÏŠ#\aÈæR#ñÎ é´ølÖBŒlWw2øN"¹ÚŸ0+¬zÍÍrÊ«œÔŒïs;²z•¸þ¯Ý…–ÈOÕk²u\;²‡;yÜuM:U¸bͼýÝQ²·psÚf5CØøn%xàŸ{Þ0ÔØ°1ut¹’Y1"æn[®µu8K8f˜a†bÚ4«)Ó§f •˜É 9´X¯¿mm1šD,þùÜü½$¨Þhñq¯¤Ð})àhè‰D[z9 ö”Ö%Ü5V‹_ùŠ/MâóNP1L¯iZØ»Û25ú{YU ñ‹;ë ØÔ}¸‚;€Îö¬^ycNûd¢™¯š’ŽëŒÛß‹Ï1EQdôw!xÎÑûw¼ò•ÇlûíP}ƒí©F¶fVŒ†œgzªŠÙ´OíTj|½ÃßÊ» Ö+ði*«óß‹#R*A™‹ï®òÓu”öhO¦£â³Îù®l–d¼ã:NÅ#$ßû,'IºU]ÊEù1leRÆ´þ*H ¶£ýÕ—“hÜuíºðsš¹KE2’k(ïÜÌ‚9z¸qfS¶_¶‡-ÁÊÆ=ÚÏëN{Ä Îü½s‡~–Ëú¹¬ïÒäë³)4<☻™aÄŒX®äAI?© 8@©4ˆ8™ü«MÝ‹jBˆ×Om<ÌÁ@÷ž¢#ï‡ÄÏhMØ»¿†5˜â |#8¹hX¯¨¬vÓÃÄǯûÅPcýÆôÑe“¨šƒë¼ßB1ŒD'€X¾X´¤j<Ø=¾²öÜÓ¿AÂÛøÓˤI FiD*Ũú›_’–LlewÓne(&!™Æ"ï¨Àß+‡‹9G £Îúp'ößøÓKHx+οӼ=É 4ZókœÚÔÑŠœÄ»®½oýÏ s×(/YÈÊ?ò×Ùʹh¬ £Q>³Wf¤A@11ÌÑU‹uRêC$: ?ë,þ,—3Ùñd¢g\ËU+0潑erÆO,:éóø‘ǪIÀúhò®ë à;b?åÿ€{Ymß3™hæ¶Ú[ÓT#¸ëš´ßzÇû,sLåQ6ö}þ1{ÌÀPsú¦ãÊÈ»Íësždí0=U+ÈHæ¾|kBy—IòžÌ! ©˜„=Ð$!í”ÓžWÿ@Ïήâ›ßU Ù]" RL¡Ä÷™8×ß|#ÁÐc]Óøv[O&D15ž É8m´´¥1ÕR5ÊëÄ *wž°zÞúÙ0j·š¥ºc XÝ´ ˆgbÐG¶f÷Éù{ Š…™nUú—Å0úıbÁÐÊuý|)~‘Ëú$¨8¢âÉ‘&4ŠÁNv§OÕ§ž ª˜b Üqðð1Cüÿ pwä=¾ñ0‚øöÖ4€=µƒ»®IÊñ®_jÀ=ÒߣÌbž¿jŸn¶î/ Õ7Œn¨*æ†œÇ ƒdt¤YXÕuYÙ®ÊTFÞmCUo_|Îw!Äú^$mWVIv£Eú› ÌYAˆvÖµµÃ.ûš‘Ý­‹†„ @aü¥ûâüjCY×4Éë)Ê–)gu{’ð D|{¿¥áUB|] ANÞ]õ¾õ3RÃ:›5@BŽŒ’ÿuN‘¼ žXÙšÝ+çï4VV¬”qÇ›¶„€fñö!›ÃNsJ÷Í*Þ—ôJQÞNPÛÀ1k“ï<î3Sõ‘+ùu Á2œ°Ç:¸ã â;þšjæ¶gi¸ë:, þÁ]W­r|Ëï³}T&;žêz­v5a᪽`f }G>l  ¹#Ùèê¶¡ÊF–é/NˆOž¬P_UÕŻ˘K’yÑÙ·ë&)Éln:½à¬(Ë[gNÌâ«i…1H21H‚È# ‘°¾ºü—•)¼‹;·ás¨<¶Š$|'dw”oº¯Âó…çÇ/ÄOÎïË}ù@KÓ?­1µ“ꉥÙZÝ7ãUÿ[?Ó÷æÏ„ÖT#UL±QB*+²³õÒXçû+Zs{Õ¼ž!Å4=ûƒ÷ùâ­>ŽþF $!h ~¹÷;¯¤–æ0ŒÛÈõWgóïSøyÇ*ò|¬Æî0¸CãûY÷eªß[¢ ÄÏ­ÿ?@Þu\ð¶Jà®ËAü!ŽòSR5@|<ø7ô‘ú¥ÇíY´zE`pÐ(~=çm£ªn½Ž·I]ÝiîÚ!“TÞ,#Y*Ìõ×ñmVœw—ðü'Ð¥•w;Uþ tfܬ¶ÌÌé>o5~p™glÍK÷¹þªóÿ•M$¹ke+ä(Gj–¿áN%ƒG¿w^B©·Á@üØ,ÞõTÒ:koþLÒ\>ÊŽ7úŒ‰+0m %¬'–OËö„s÷º¥b–›(§”˜ „`"ÉÊ|v!ÄBþåBº_ãx}8àAo‡^¢Ïù#§üÃŽYn1ÂJ5!¸Ãñ÷³ñå³q¼>¶6vR¾H¥Äa îY¦ñÀ]סÜqmŸž(yW5ßø½÷Ç›¨:Š âï"^qÜn6Ó˜Ì@û¾Á 'ØþÆG™ê³ž2îPʬžÁ‘aF/Ÿ¸ £¬,¥¸ó_œÚW`ÆÓ?ºðÉ5ozì{çE9bŠ¥â¡³+,Äã–Z,ƒø9²DÛýi£$^ʪðÂ’)£?ôöwþº…'«ÅÔ•„=&IÕu‹uLÛ{$ß+©¤t`‡Q/Õ7ÀâxYÅ 3öÆÿà“«Á þÃ,ÓÊVˆWâo#™ŒF vDFyjÉ´Üþ°sÿCÃz>@ÄNÄ "ÁúÐ2Ìl”Ò¶"bÐúåñ€>NvÊÄ´®øÄÁø°Ëxœ´½wR%Üûêkôƒsþç¦){kÉZÜY*Ô¤c ^ðÒënw{¶tuhv\;¦§ ØÓ3eà®kÂŽ«É!@ÔV¥èÆD¼ü„ÝLZs`³·õ4ˆ^/cÈ;\’ ê³3 É„·=rÄÃu_•][5fçY'}zÎKÿSÿÝ?{ï¥ëî¾XÙ¥Vãä}œ¿¦ûJ»z†õÁŒ}Àª†9càŽ@F¡4fœ•©bÈM‚¿3âÊ{\uQgYõqZ²fû’3¾©TøÔ߆،Öy5‹ŸÖ˜Š£|™r}AÑŠ†ql ½ùßC'Ä[ë$;þžtÑ(Íî!žZ4-×+[ %V¦Å ÛŒUFϱ'<­Ï°QÏraÔJëZÅ:…ÅO>'ròEQÂWCÞáøû=¯¹öž×\‡ƒñN–©Øqý?\‡Äk<Ú=Õà®kœŽkî{ÿ;²…¼ Ë& +OØ €Á ¬‘h¤ÉëÉf-V[7$s}V(Åý…0’hlXXB„‰©1lí1íÇ}tÖ‰fYŒM¿¼îÙ_\©·X—Æc*–Š‡Ï™`M ñíâ«Ä÷¿]r³'H1F¤3JÆÆY5…ÿm9…qþÀ„ˆ%È{B{Iv_cIç—« yw15æ4ü(1åäp¼"‘7ÿàH4ºJ|Zq4ô$ýåÏ®G-!$‹(€ ÐjÌÚ…-uÙ\6SQîÈ`Ö0-œ,ãL5I¥;ÏÚÑ¥˜y¾x`ÿk ñ ‹>v>æXeŒ›Éß®*ÁeýÕŸ¾öz¯»ï‹Súu˜v\çvd©jò¯CDŽ?¨à®k¬Žk|1Á øˆv1 ­ú6kw>È <ÖöƸç½>ëI…þBh’•ESc\,³‹™~äf¬ú0flþýû6ýúFû”“Ú ÊKƒòU}^}0Û[ÓKë ÚFMþHs£/A#¡Y¢O9e‰³Žú)’êÕéoÓtSåÆiÙ=á†DèÁ …§}I)¹îçW$žJÌ'ª7h”omª«8ʤ …UµIÕðŸÒkm›Ž+3IŒ¸WR(½®“–¼ŽÞù÷º‚”ûë‹ë™B… ly½ŠM?ú*J÷X™™AÄÀ|ï×óýß¼ÏX}UîíŸÑÿúŸ—GMº*öT/ˆ¯é€Ž®CYŽŸò*ƒøô÷?`Õö(aFcý1/Úy”wwã¹Ïõ4Šžº ޼kpWŒüP9JqSýnNÒvÉÜ?õƒñ IDATت·´,WëïÓ£.Ûþú‘­ú [pÕÁz4àj·„‘wWZé"P¶®ZuB%ÖbÄweQ^1Þùûƒ¡"ù'#@¥·Ç¶GËõaŒí¬$+‰HÀ)?=T¬Þ| £í†\;€ÄÍqA˜à‚Oi!^£üŸÿ2WówÖs­Vu‘ú> fñ×ÛÒýaz(ì¶fyp—ÎQc³îȈ1H) Í@ºá -‘0èØy¯MwñÿÉ{ïøÈ®òþÿóœ;4êmUVÒö]7¼n»6Ƹ6Å€1,½ ¡DúýÂ7$¡’P–`ÁÔÐÁ@h àÐŒ{Á}›·¨®vµêÒHsÏy~œ{ν3¦käß祗=ºS43Z½ï3Ÿ§Ue3(¿ÐÊ–E)ì¤]÷N/úÙÇ^üÓæùlÒ̬Äç w­ÕµãK¼D|`¶Œï¶“ÀÓv(‚2/›6\¬'ª«uAd ðÑ&“ÔâdŒ¿2RWCÖmzkÖ?g3FøÐà½ïS^Wª­´1”÷ "ázˆÏîÅŽž^Xˆ$ZÃéoY_aQš,+OÆìýð]šÄ¡ÁŸoø˜*€é¥Æ ±ÙÁ´Ô¨I4å©ç²(åúõ»–²Ù㡵AÌÑ…€–†eßP"`rÊõrªfëPâ8xïÇ›nO áY‡Û X×G,Ö èƒ©W[:0jtªÁ–Ì—JýôÔâé©Åà+‚ "7Óh©—}H*á2ÝqÇz]ÿ®í¥Hš¡4ß ÁŒ#=Ñš“nÍ„+Á ¸ oØdB­$ÀL°ÊHçôY*ˆÚÒÒéJ÷¾(îÒsj–m9.-û+ùásÿâÖçý%€~òï9<î–î(ÈŸ ¿œÊ=ãšõøåTbÄ·7GŒŒ­Âx˓㠕•ŽÝÚAÄ;w ù­•^]3°ñÉÉ…Z1ZYÅìÙñÚK©Ž8ŠyÚ[–m‹ßýñ2º•%sýŸ‰Æwh<Ÿ:ø™“‚}'¶kÉxô~ÙŒ«ÌÒ>熌GtŸ_ÐÜ" ÙP01åxè ŸöÂL˜i·zuŸÉ²JÚa[Å=Kw;ME%·§R0xgs`PÇ®÷+%üáÝH> $Ü~æ» g wý­¥|ñLÌ‘ sšJòWü²’ÊK«jç]²lx zfß½³¥vTnšQJ$ÔJ²éneϸ·N= X­IƆúõD‘ãJáº".E\‰¸K×D÷<»¶D”Ï-Ö\á”kÿo9?­U¶מö*úG ¼[•IƵØr¾õžW-¸Šˆ+䃤»†™À ¯A”ƒcub¸®šÁŠ €‡^ÌÌÕ¹ÛSíT3ÜÈkeõTÝÍšM #;ü9eæ¼K;í=ÐÂ*½:(o=“_9“õë´#êS‹šë+šê}Êá®eüw/›ê“I’ßëôÆ;}È’Ùím7{Ø«7xÉUš[@\R\ "Ǖڨ1±¼¢«k÷<«¡¸”ÏÙHXù÷ñƒçÿ€—ü×Gnü¯ÍðA·tGêÏ¢•©ÜßÓ^àx¡á®UšŒë*ï€ÉZ/ºŠ»/>o÷ƒúŒ!¹w`j1ê(XÌÕU3ÏÌÅ9@ç ùîD¯] ¿Ê{f÷ÔgF÷JÅfoª™lë Í ‚ÄV& _ø%Ùï›*‰èˆëçÇ&L5áòÍýʸ1qcÄ~ýLªÕ}нø=¡¿)032)ËjŒšÀq¬ÛùfÙ÷ûRUÚ$ûÁ³\Rð”‡øúps}%±àƒ ½ì¶Õ+¤™&Æ:H‡bf`wŸÕX{ÊÝ0:g÷µšX^(`b¦ØÌ&€Ã5O2Sch½+i~s1¸’@!W’+IJ!¥ðXïÒU {®i* åóq‰3:ßþàù­/Üøã•ŸܵÊÇŽ/*ܵŠq]e¸ûÿéjwí¹ŸsA?ÇpH{/šíúóñÖý“ µÎHuˆ•>X%Á“1”§Ê«œêçè\+f¿¬¦¾èŽÞ7Ùy)Ó&$¶2ù}¶¯2%ÑÆ&ã§'ãë*–ï¼c‰S‚mý»²«û7Ýí…ð¤ÔR%U*5ÉÃñPÁ² EåŒ$iÃ÷4p÷^¬¹ª¹>ü§SSD´³µ>Û·1g…_ú?ÞÑ­GxvÌ0ÖP¬‹±èB±¸ïŒ¦ÚSnï©y6íN`ïô¦Ðglþ‚HJá*—•4·W‘wE\Q\’ë W ©„ë WÒU{®i-$åóLfúyêû/ø›ï½ào¼ôÇéfëä w­r°ãKw«"!¾£9B«wxÕ2ú/åÂÝLp3`'DÚ„Ñίb b°¾š=£F——Û}>n·/y‘»s)U\®ËhbS?Åü×lå;{á'ŠxÕ5þ”Á»¹P}cvÁ;Ò&ǧâDDIp‡ º½IöCxôÕ}~/e˜s$pIÁ8Ígþ%+9ô§±T¼ëæ$¿zÒ<ÚŠp×›\›ôùRZ4Z•/}¿sѶ"Þ=æ/"ä8 r%›.'ñÀަZ; ºdÞog55ú¢2ÞW^´®×ŒÌ-ÀUB‡Èñr­’â6W"îÒ•-{®n+åó¯ïÈî—ñ½ëÿÀKôÏ)¯ÝÚ¥<வºv|)á®UpÄw4G ¯"ܽñ'€ÀE»½ý à¸Tq¥Ћot|´ùØd¬ÆÑL÷2«ÌGÖ½D)LÏÅ'ïbñtíê,Îý:6ý‹újSüøïätsp°¿„O‚wö %í³}[#’-¦®|o¬«Ð`c]E’W³ÿõŸ”âwé³[Fý^§›î}ÙuŸ#]0ÙµR–6¦v]ôëOU:ÈÍÚmG˜øîdw+øŠ §Ä|pïðó*.z‰4-¬’³J8Ž£ âRŸú…ÞF«˜ê†âucn×é9ã [K£¼ËÚó#WyàvYç}.}\ãÝÀM¤¼+⊮hÛsUGî”/Hñ^Ö¿Œï^ÿ¼ìGÎç§®¨ÕB|‰=D­g\ ]ó“­Ôwþ»/Øuñ{µÞLVq©&€OŒÇêœáh:~W)æ™ù¸e·ëž§øz¹ð»ÅùÛü¢F »mU*H†·);ÐåWC*?×jkÞuð^ûÒ¬ƒ÷`f5IèãSññ©Ôv| ³)¡^[—H*»z›µ5O0ùU­åŒ°žð|¶¼•<ñø'’â}èBÀ¤adÇ ÊîZc“‹‡g¦**œÝÝmYݱ"¾èF3q̬Ñ€tÿ¼Ý­Ìô›Ë×Õ¸u§eÇD v‚)„GàUs=Ik`]IR’«Ä\LÛñÂDñ«Žw)®#}]fãÒå{®ìZµJÊ\N¶ß}áÿ°ç‡ÚóÃÙƒ[{¢ Ì/xªôˆïí¨plx®d?ѪP׎–€áS«ºï›X“~¤×—¤ v™™ æëù*Gù!$‘ÏÌé 3pÏ”ñ§Ì )ïrã·›9blW5¥;ÉλÁ½z ¯f&ךwïU.3tÅÂ]k)D¼ l»lüvV3qÌZ7’qÞÈwHoÁæYPߨ!ÿ 9®qxÆ`*g¼¹c NàÁ ¹¼#ÀØä¢~SÒ´G¢ðE/ÑÍ«ŠE($‹…8ªjN+æé©í°ëüömuÃñúÓnÛä‚7FéÑcÂ̈gfveŠà]S^;6Ö«qÙŠ}­«³¯èé™]{®èÉ‚ò…ê¼ÉñÃÔw^øn}áå·þ4ÜƒÇ <û×"~SWÑ)¿Šp×Ê?ãZpw¿ûÞ‹/ [`fë8ÈTø1p•Úrxr¡.t*ZÅ€"V (°d0s|~«ÛáQÒýŠï¶ª~‹“Ac£u™¸R5Ø¡j¬vßy—p¥çÈ7¼,ëà}{T2þëKB¼2[>l—Sp׶ݾmFƒ¤B {5H8n®5ø®ëy£Rî©CŸ[R6ck½ ð+stðžC¶YëÎþQWÑÖº¦R"þ¾áku+BåE7HP(ä( ‹¬? ‰*P:É,~ÿô¶ºný¸Û6½à8Íc“íÌpÝJ©÷ŠX‹F’渱ÚÉ•4ƒ”BˆP\c]F²¯®çá\Ö½çò +S¾€m•¹›eß~Ñ{¾ý¢÷xË/ÿE€»–͸ñ«w«œßÙ¡Õ†;€Ý— °õu ztI»¾ÍŒ3Ÿ«CuU̬+tf•þøu‹³›½òçaú1¸-ªñÇÿB²ø¿8Òå“ÆËØ9ìµ8¥ÙŒ·¼–óñ’‚÷ ‚ˆ÷+g`8®»œãàõµgV\Þ0ÿX‚ëâ½·ÉN:’OdæÞ›Û“©Q „êæÈ \É)»X3s\”6Šg=¤´ýÊ=ŠÅBœÙÄòÞò/] §CAüawkÉxÄÛ<³Èþ€xb¨ÙÉV×”?jÊÆP^ù±üì»R8"¤)—s€Î¾zÕ5úé²Þ=—oZ–ò…í™Ï7rß;?àÂÿßB<™Ô:<0ûäà,Іøò»Vv|gKÀÐjÃ}gÿôàídêñ2lÂcÅð|ƒ3Wí¸Š]Ål&JÄ®î_xö©ð6fˆÊýTñhRqdp>Çt•PõèmaU¬$‘»¼Û>UÛâÔüòì÷eÌ™¦º ZîZúª¦ºŠ©?ÿ´?™ ØÎjÈnJ$=£¦iö˜žjèo¨ðÏ¢© ÃúšÎW²RãǾl°(…´ENæ "\é%Z¾‘ãˆ]E£»½Z­%¡üÃÏfˆúÚ0 B;_¤XèI¿Þºm[ª¤üÒIÅtû…mu'܆Iל ½u}R±›ÀtáÇò*°þÛC¼˜‹±«HJáˆösâ’â® ü]ïÛ¸ô@—ôŒM{ž¹%™ò……;€PþqÛ?páÇÿï+¾ÿ~ã%ÿÿ¦Ô“ƒ³›º¢›º¢šõ…Ò†ŽjGËîZƒ£1 ÷®¶ˆµæËYvü¬âý€.|dŸ'Ö( Ö”qÆ¡‰™u¡UbÂàÜ¥B8z +NÕa6õŽ^åŒ6¦Ì=VW-‰Ð—ðÙâH‹õ`q¤÷mN&Ä™uÐCjª«pzy¸kOÅõ-C!¡ fpiˆYOÈ— Á$aÚš\Ýâ¤ßÛ$ ³m[E°äQß@)f†) L/0ÍU¡wUÜåÀé!Çþîþ¡Ý=ëw÷¬¿ëø€†{kCøäDvÙÚÔPfæÉ™8E/ºYMß{+ˆ•W˜eÞ%¶'9øŽó[v=8¶™fvÔèã§Ú1+¡ÀP V`Á¬˜õ¬kÅúwÇÂ+湓àH„'—2xcÁ,À5ýó/8Ü‘'ß­í~ð†÷¾âûïðªï½ï?o\cˆ/O£øôˆïhp`u\éuAÇ/˜9~Ï÷…™æWXÈ”B*@çtzÍ>ÙÕÿê$@JÆ:x~ÇÇ2&Ú)Az «¨Üù¾µ·ÀÁc3úÛo¾äï¼ê{ï{Õ÷þñ?oüÇB<·²ˆ×—ó|´ ÕŽ•Wð”äÓ ¾£5‚U…ûù¿0‹.™Í\?Yå9 P`Ìà¹Fg®Ú?HAÖE~¯˜¼½•agn>î !`Sxã]€bf¥¸>:â•Ê0³™91ݬX%`]ƒ^²’ƺÑÞNö£€­’œ÷¬àn‹sU%9!G±+™È/¡aEþÐ1ÉD„¡®ÚÆ…À¿R/ ç@Ø æZ¥‹SlØNIýM^g“.Iês|ô{üîäÄ¢]ßXTÊ 0C—5¾˜™ypŠêŽjNô¤˜ùþÍ|Þí÷35ý‘ö` к)ç“Ûù›E .nE#xGÎþ{Ü­tðþêï+„GëyÚñåw­4×ÎÖ­*ÜÏëøo˜9vÏL,SüÚ3P„³N‚þ`?{Ú?ןZ7~$ÿgå—;¯[ñŃ;rãû¶ÞJ¼[}ý¥ïÓ^[4ÄÃò9 ~mÁ]«¬–üíìøÕÎŽ_2³Þçî¹5ðYÞ‹/MÀÈ ŸÝ|znÛbµX ‹¦ê‡›¢z-¬ WA%ÌÄÅÌ<“й2,‚½±‘ðº™Ìø0;nL¾’[™ü?a'Ÿ«¸û•99ï‰Sp›ë+Œew-]‹sÌerB³¿¨M¯€6åYBgŸúA0HÄì 3#+¢Ïf%g'~¦qŸ@y{b …{ÅÖ‘§¤ÇÏYËÍ¢),â—À _EfÛ/yËú&¨÷. ˜­³³{<¾ù⺉“Ý'+ÈÓÓ¶ë—G|QáŽø¾­·Àåá®õõ—¾ÿk/û€×~ûïr{f™È"¾ P«®`Ô*ïçvüZcÝÚîðÃxÏüj÷ñ™ÞñÙ t̵GqšhãD pÙ¿£$ù©s1W“.6 ç„Li`Ù0I–‰e3zÚŒ›°Ÿ/××ÞRGDMh®”3Ü4Æ …8CxF £FO/€—ÁéA%é8@ºp4p-Y|Û_PEˆ@—lÀ‡„G[2ä [ýáà¡ÚHÕeÛ¶-½ÊC|SekS^”_ w-=;ÌLðÆHtb\X¬[Ä“^¦­Et`Óî–ÑÁαƒù<7«ÁË"¾ØpG¶|ÏîV_}Ù¼îÛÿ/Û§•¹²µã×bðÔàhÌrhUàþ´ŽÛ&˜yöž[=gFO…d kžšéšœíÖˆ¸æ‰»Ú«;ÚUº¢FÀRê\©-Ї­†Ô)ÐÙ˜œ‹¹Jq¸BG†©àx™@aL²K£¼y™X#/oxUÎÁ»×Ö¤'žë¡Z9ËßÉR@Ì…9Ž!GÛÎe¯v†å±þ¬Ó?°ÉÏvÃ;âT^ÁJÅf~å[íÆuñß+*ã*0¥Àwc8ñ´‘›ž±uË\<þûR^kíø¶\¿Ü<<|…Þ¡ ãÏD;˜hvx 6¢®{›ºŸ:€Z6>pÖ³ZNĨ0`Ÿ&/’²à{¶p×úêžxÝ·Þ“Õ½²RævüZ‡;€®¶ˆ†Té7@Óñ¿l°îPµ9U˜™05»nz¦C3PWs´®æðl³3[Ò½N$ˆ¡ÛšÌÌHf–àý•Ïó¬å¯Ïž¹ŠQY!le¤fýåùeÉÖ¢’¬‡¸ Mîï€ö w›)4 q=É"äA1±„é„2Õ¶¶¢Fÿ>³ Þ½ M Á~E…`‚˥šo º¶8ðØWrI±^ºy#€ß>ñDšÛœ<½pòôò@|ZÜvÀ Øíºw l'°+õ½Ì§–‡v\Ùrr e²¿ ÏF#@¾Á;Š‘_]ª¯ìù'¯ÿÖ»_ÿÍw溺ˆø§Ü0jPB;þìŽßøA64ÚyæÞ[è8ov¦qf¶ez¦ê¢u5Çpî£ã3u¡ÁÚÂÀ]yæ2+=öÝ«}„×€êíÞóÊ"çb®d®¨ Š ³žÉÇ¥½_3ã[1¶ÆFO‹Üòêƒ÷ó×5‰FÇ 5¡sìÕÿœ,¦íø—u OŽžš¢Ï€«'ã3οËïz:³Z\øm¸aR¬Á†U?B' œ ü«˜ry©—lìð‡ƒ¢Œæ„ø4Á»Ö#× K0‰îüwmÐx‘¾WÒG–G·]Örj af$«ç¶œNÄ4å»×U•îÈœïÛ{k(ûàÝê+/÷&M¾á›Eœdñ;«é©wým±7@YÝñ; ÷Ú½E˜™©ŸiPÀ ÔÔŒÔÔ éz˜]÷žšmpŽ6W{w0KmKC˜€±uc†ˆ%,úXˆ³d‚Ò[xBPhpØ Þšãƒ¼Ï8Ár/ÅêÅïÀ¢ËKç*p’Ër2Ô®îŽÝ½ëÜ~8 gÃ">Cʯw-ë¼›jHý¡Døõ3zdMBPòùì‰Í—´žÈüµ¬¨1Ç!Ç¡Àò}{o €ý¹Â]ëË/ÿð—_ña7}ãÿäó8é•2㺱³À‘µ ÷åTlÄŸÕñ{¯}&PA§Ãø©û~47Ÿ®ÑG¢µcÕµ§¢5'L +3x¦)4]Ò¡: R©”2Lç€ùnvÛšÄf¤Í¸ÎŤR\l&Ïëß•÷«Võ¬›í¯É1x@D!³G» 2ùÕ@oõIÉ y“Rt3¤w œ9ñÃ@„îá˜èB†rÝ;}œù? W8L´B =hp‚óîÎPvzcßï8r<Û÷A#@[ó ˆÏîà¥X ëÉùõ3GN?ß¿Gª+7\¸éøƒ¿”Ô½®JOµ³Q|Qµ2ß w«/½âŸÜô¿-È£¥T¡ ÊJiÚኇø3;þÀ&Úfƒuí¼OLÏMWi:TÕNV×ÃL&°©¿ó9=Sç ÖEL1àJ¿{|ñû±wÐïlûN£€õåù)“ ³CRAIcȘ}6«ƒw{ÈYv­«9‡¦' w˜ØÜN¶·ÏºpD‚âl>7°ë•Üìe\mÝ÷ľóðXô â^5$(Ù¨1ÇÄ Ã™èüöw÷ßu,Çhwôô‚¦üºåŸ ÜñØð¥ì57 U·7bîĤo»'‰ScÇͽKêÉž½ƒdý’–ÈÚ26x/6âWà{aá®uË+ÿÀÿóoÞøõ¿)àÃ̸>‚÷õmf¢Å2*ð(À·ÃݳVôÅé©éÁ)h­ª©ªbòÉnWñÅwši i*@8ÄÌ®TöD¡ WiÃüBשĠ›±ƒ©±© ABÔ}ªl¶8É )éÏœñâ÷œßp…CBîŒd?ÝêmtÒƒâÊ[HK¦Ì„ù8sê‡ ‰Sù4f©pŸícBÜá°Ã X\!ù*bkL§8+@÷ß²BŠõ¼uîÍÿ [ñYÁ݈`j%Í 4=A»6ñÆË~Z9Öuv×Èþl~ô ²”/*âK½,Që–Wþ«¾ð¦¯ÿuñ~Ê“ƒ³! Wˆµ ÷uVdQ،뎎Ûí”c˜czhbfpRí¬‰ÔÎrе1µð Ì„éæÐtmˆÁŽŽÜ•W é‘ÝwÞÍõl¿OÕŸ7À«]S{!®4µ… »ÇÃì.áO,0ßõB§½!—QÀZÂDäaVÔô+ÿÕïÞò¬ö¸„+ÄÖÎòn tƒLãWBP¯ËØ+à q/´TÔ$DúÅ3‰5i^ÂÓZëÜ?rªPïÉ ƒø4|†l{› IDATz|øé¦W@˜ØÉ‡O=+«GlßÞ>z8ç'“2§Zlħãûö 5 ïV_|ÕG¾øêxÓ×þª`SW4îòbœ7vF7v®I¯FWSÙâªô*HÆuGÇ;:þ†©s3Ï Ï ëю¢;¯·#WaG\˜pÁCcÓõ¡úˆptª¼ì+[gÆc:“®æ³L_²ÇÛ$ØÝ¡X*^ˆ«E—¥‚ñj+j$¤ïË羄@kCxGÓ:WÒÝýC9¿«iÄþäwHÏ|× ‰Ç. H÷…iî+`Çôt¿’»x³bç[Qcû•ë˜Ù\kBE /qc¬Qì‰M©sšké÷{hÖŸ{ш_êîí–$”Wô"ˆ¬«cèl~mZ¾#ª¬tÄâ6´7ÓáÙTý-ˆ|ÃÇ?=Ðr|?»±ÀC''‹ñ¶œ[ДooŽäw<1|‰I´šI½jÉm3*mîmÌê9¬X éÛñ…F|êµ¥»Ö^óïÞüÕ¿(àc& >2äQ~Í!>7åfÇok¿Ë ž=qzvxšìí ÕíõìËC¹÷ç §‰ëöÔé–ÐtmÌÂ",J¯–ÆÚ8ÊäTõÞ>>ä\œ#)λ ñâ‹q¥/8Ù9‰”Ïq €ÖÆ0W®àQä#ß‚7ÛšLúT§R!A •g"oé³Ù²íïeemÝ@_¥@zĘ«)‘¶Oÿ——°ÈŽ©tŠ-Êàñ€ÿžœM]:]R—N.E³£6 à‘ñ¢§µÚ›#:sÞÞioÎÑuÜ?¼¾ OLâÐè3“n#LJr»2|Ìɺ¶šÙÓ¹=Ÿ4*8â€'ŽL"¾ôúük½ )oùÊ;ó|¨4Û?,âËœòÝmýùmæËÜŽßÒ~Às§ÆçOŽÏŸô*«Ö5T­k´e‘€I°VÉfVø·Ÿœj MECŽŽ› 3öζZ&aì ü!À ÑúROF±R\‘ ®©ŒTJar¦)¶¨\ÅäP°¯UæZ6ÓÚX ÐÉñÅ¥sn «…W|8)„ב;Û½Ûž(¿ÆÆUP 4´ølf%ªž4±¶‡i/r_PÆŽ÷Iš·ƒ–+{G`>AÐðµZl¯ xl²èã4ÐGÆb#&xÏñ^,]œ dèÍ$h&ÚT5?•þ69ô©jÄ÷´Wõ‚òÞ?\‹øÒïVŸíÇ>÷ºø³/÷åü ›»¢”vµSùÛñÝëª@”'Ü­Ò#~sû}[ÚïM£ºµ¡ª­Á–EºVl7n;LbŠ©–ÐTmèt]„ qeÚ—¼’Ho-‹ô€èáÀÞ„w?„‡’OÆN û¶ÉÎŒ\XôJ'IW\ü¦¬ƒ÷ডåæÜP ²sÞ•w™´ŦéI‚ M¹H\åíšk^|¶Ö #÷ùÅ„Zxø)VJ¢¶ö%ˆ’ãw}]û·Åü#zbºèËT“d)ŸâŒì2?ı±óRÝ$—I óUuáÅeÙóxù#Þÿ‡ûÄ‘éÊ QUé¬ Ü­>÷ºOxë—ÿ<‡ûn^áðJ{ûÊÙŽ/Æ`Š4vü=ûôx{s}UKCUk½òÂEÏ“±ÙT;v&°áŒ+¶=_Ý}ÿ©©†ÐpK5ƒãnÐp÷’«Þ´ãÉøô¬ãð-xeËf`ÊÛÍqæLbœ.YJÄ=;žzOSæoÔ®ž®Ý½Ý—mÙpFëúmÍë/Û¶õºsÎ!ŽRÉY*PEsþ®a6Å3zަé]Ò#‚mh Æ®d–í‘_E*à,š„¬©®„ šìÖ¨Y²ñ#E}¤~Š„M• ÀÁ9µÆ-껡eƒ÷àAýmGŽQ<±]ô‘âÊLÍ÷$-†S#8Ï?äã#óÇu ß‘âýý«;6Ö.Ä3¯b¯õÙ×ï}ë—ÿü­_zŸyç2¼×æõQ‡2]ÊzdhV56¢_uoêÞã À®r•Ócöw`ñ9p¶´ý¦­ÉkŽ4á¸3ÓÙ4Õ\1ÓPÁÄ®T^Ÿ1ƒ˜™ ÌðнG3ô8‚gÛøÝKTà•E2K»juÉfþû¢d)•HÐ¥7}{kµ "! DäwH ï:á,}ßÒL¸- [O„%ÃÑoH÷á(=KŸ’ƒˆô/¤mwV@ÏÄÖ=v^1‰8Jòü3ˆMÝ*¼àÁƒ\«7Š@§X`½>7<çYo=tûL÷eÅ~+´RÂ]kd,ÖÑéh‰ ŸÊîî¡‘ ·´ß{ìÔÎe®/dþ¼PÈÇGæ{:ªz:ªŽçøPßµù®]Í÷íjVñÿñúO¾õKïð¶[Þþ™›>½âí³…»–E<¾ÍSU6Šo«š³+[ºÖ¤›õÿ†7·þÊŽóÖ'VOÚ™cç>~z¬=|2ZI¬ë>ÖŸx@ÐÀò´ž‘`À£µ’ì;ïð«h´'c)Ï,™{Ú_ÀÑ3ãnàA€Ý€.„Ó]ï,Ä3 áï>>X¢ЉR bÖ@¼CÇÖL L¬/Kj™_8Y]©ß»ÑØ3Àª-ò{©ˆY1Q$RR*¥æä’Ü)Ãwit]¬ôO˜|õoð“ŸêŠ7—‚ïià®5<ëh‰t´DdEùC#:õS£rjÔ©k³Çåä(@N}[šû®–Ž{ˆ'àXö”-=¤±¾}CÍ*#þ Ÿð¶[Þþö[Þöé›>S¤Ÿ¢`ÕùŒ •s“ÇLt´„Û[*OÆVö¦Ÿ¼Æ^ÞÔô¿z’¢øÝ÷uU ·V8.•ÓlØŽ`th¯f 0í¹†{b=q…mß<Þ· ‡c€0q)3ˆ¼ÿ131¼È•Z6^`2RÀ«4Òë„NfpËŠ_þ!þλ“Ð]©äÿ”)™ˆ.H±Ò¡»—üðÆÍ0hýÄûë^¢¹6…æÞð"t½ ØßD0çïtù¥¯ðó_~6—$uöZîZçbšï9ò)Bõ‚¿üY#@oGU¶ˆ!1x·ÚtfÕà37}úí·¼íí_|ë§ßøËÝ&·àÝ*XT³Zˆ/6ÜUlÆ~R¯îj‹tÕ¥h³TOÆìõÖ}Ÿ}3;vÍ82#®”dù˜õç{ÌO÷’çD ÿ2 *ѳžóÇÞf?Ïá±ËC™Ùë»Ñ¯ãðÀ¤^ßá²”<1Õ¨¤’R)ÉÒUºæýÊ·< ²­) ­©2=âW îZ DÀ…»õ\e"f"`0)€ˆ€ô*8Û ¨Öª; ã~aŠ)#•ŽR4;çÑØÂv¤C JJ¨zE&&a~éîW2ðëßÞÂDŒ¢>C¸ki¬ë@>;Ä/¥yáø^¤?díÏôvTe‹ø–ݪLÿé›>óö/¾õ_ü³O½ñ³K¯ÍîV«hÇîjqÎ’Ý©®×´Ÿf&ez›z >u¤µò7=~øDWãðB›Wz]‚Wˆ`VÙ‘‰%É÷õM IL-  yT1+©”ÒÿUJ²TJIï[)¹¶zDºÞ%æZõIHÅW½Å/¾âõäñLbü"I¬˜ˆˆšì&¿K½¥ è9‘Ê|8ÒíN]ÿµ?|]e…(R)fçmý yùûK°}L^ÖDîö4@tñù/ð›Û¿b—¡t ßÙÊñ)h^¾;J;6<Ÿ-âéí¾?ý-´U͸xÇÿL—òæÏÙƒ…‚»•­¨)åu/ƒ­ˆ* T|Á­°¨LáÆØrš âåÄÿ1AdãÁè1Ÿ9}`¤ë¬±{?ßÜv²\ƒ]˜o+kL¤<óÝæiÁÇÔÕJ)o8ƺÒ2k¸+¥”´”WµÕ'¤dé²’,%»&x—RI©XB_p]~ÖŸ%z_â«wê;'„ºä’~,HX"b%<Þªs?=ÑhˆŒÍïd µú^˜™n$€Tê@ø:ýÖ…+HIžë5Òœ)¥ËJ*W))YºR*–®r¥þV¹’]©ÎÙú< 8¿¿çÛ®«\—]©\WÅãêÚ·å:æa%e¼'©³%¢ÿ]g°”XΜàÔ4¥9’ƒJ¶’©·£ „cCý • {-Ö-èWE6xïÛ÷f}¡àpGÉ«ã‹w–.»q}Y„«EeTÅfTlFÍO˹I9;!gÇåÌéã‡Xœm­˜”“'ä䈜Îü§PËÆ«bÇœÍgŸ®ì¦K?01]?=S;7_½¸ ׎TÖ UÔVÔöWÔ÷J*Mo½K¯ÞÖ¶'LlW±3vÒ¯ÝÊd»¦JÒ|Á5s€—ÂÙï *¥l㒞̰s€ÉÔ’¹€bŤX•튂 ¹Šزø3ý€ó1©½¤'.l}dB‰ä9[¯Óçè;ø®oÓ› ¾H/?¸°Á{GëÊ¥“NMˆä¬7MÎŽƒ(O¸—R:xïí¬ÚйrédFûÎØ¤kžO.}òMŸÕÁ{ßço.ܵluü†"#¾§½ŠŠw¥w)W›%jq^-¤ë#9•0qÛièt;Æ.§q½Ó´^4u‹æÑÜ#š{EK¯hÙ@Þׯ§ßsëTkçXóïˆIèbÌOô,Nö¨Ùnš_ÏlÆÁfdáDÂ^Ž@Yd Åɧ¼ß*€]3sF*hÛ].?È×ßd_Á;€ÐË>`ç‹I6CLjL]< ý™±¹³ÁÜXý'=ƒS'¢]]ï'¼’yݼªoÜòF¦›Ã¤+jÎÜô,<þ_w?|«t"úLP,å w­á“1¼g‚øÄn¦\:›’T²à]Ëï+">‹Æ¼r@<€½7ÀzMQŠE|±)_ ‘"§"xDDjD¤FTÕ:ÕõN´Á‰6:5MNm³SÛâÔµžŒ×W6µ÷lÝгmc?esÏÐúsmèä-¶ Á †`“@¥\EW½ÓÙRÙ¡V Í6m¯×ÉŒ³Á»ôxÔ6cżÊwo ‹_ }kº%|=A‘2»Ö®‹íd1öZœ`0ìèc¥GK’#˜á2›yËÄŒÍñŸ[Ï/HøHeˆ‘»M¥šJ0°­ûJþtðÞUÉ]NYìêË\»•F|犈v3åÚÙdUb¸kš?:ä%]ÓÜ,»Æë`éd>O.Oýìýßð¼|eßo.ÞO9j¼šb ^w/‚í.‡„CNˆBaQQ)ÂU¢²zÅ{e;ìÊþßM5u“ZòÜóg›m ðX¯Çn£³¦sCCûææö3Ú×)ðQ÷êÀ4`ø¡:³2žL`³Gà+0]ÒõlšX&=½ Ž 2;|@fÎx ‡ÞÈJ.޹g3«Æêǘ ‚˜Wæ^ÆáQðÝö*#àF"»7w^àÑ#¿ Œ„)³ñÄÿèS+ìrÊVž• âƒ3'òœ?±*p·ZñY¿°U·ã·¬ø—×;þ–â"þh¼šâÁ=e>¬£52ÙÖñ0:“ŽÛõô^;¦^Oï˜Oÿ:´÷b|ììYWÙýÞªÞ°c‚5Ü“6eÛy¿Þ,IéÏ’Ô^ÍóÞ–Ñíuͯ3ãÅW]ÖŸa;%X³Þ.ý÷YCq}t¿ÞØÂê]°«?6¹?Ý4¿ æÌÐ1So¦Ú.ðÄÀoü½N~! öI»’ÆzÒÀp‚w«!ƒøÎåþ%ç³—ŽÙÒÉT^M.'®U´ã5Ü ÌØû¦}{߸@ß—Šˆx˜@¾Pˆïé¨Q¹ÁÝjEÄw´F6ì¿ãþÊ­K¯2..ëh]¯Uð@ï“`2ãj7¿¯~ë·°ùŒy)V3ØP>°)[·ÚMŠÕ Þe¦´í¾;U¼ï26ÑjBx³Ž ¥4Ç…l¦H*‹xïfP€­A… Òç@•‘¡w·\ÐÁ‘? †Œg¾ZÜJE‚»Ö)¤I‰x©‘Z˜òbýêïV¶\r©Ÿû“Ò#~KwäÁÝjïMûô}¹DˆÏ“ò=ÕŽ—õ>Ø4ˆïlpH4§¼c쌙=—L{á<™Y*½=¸TíçüsçÎ]þGz/úw;XªÀÀwöËl¤„’Г#u–õú·g¼e)¿ºˆ¿°ãçá]7Ø…´Ö¥Qf üé™M̪W=®Ç3Á•¾ç®O þ(Æ&ù “GõãñùÅ@eØ©¬tôUçxrôN¿¿‰üljRëÓÒ‘9«¨p×:aÄë‚ja$D$ˆ• ÜµŽ Ïë¤kâó|êÛñ½E§ü–î(€Cý) fö¾a€¾¯”ñÈ#ïí¨¦²‡»Vš‘“ížlèYîŽ^ÅÖ5îÿ ½+°bó%ÛöŒOì¸ü“g]õ)©ØeÔDGõÌwíÌh¦OÌ4ºÒ3s2QÊeo«ŽxmŒ(61»?%ß=X13I!˜)î²²fŽ7 ÞØ5†õ0áv0›[Pº¿©£öìöšs:6v¯) Lè{JXùTP3£p·Z.XÑa{®Á{YÁÝêèÄç;ØÚ·ã‹‰ø­ÝQZîZ{_¿@ßWoîûÚÚ³ãËSK3®­‘ÞýwÝ_¹9ͽ¼ð\˜ ŸðÖ^âw/û³Ù,:ev>ç?.¸î³7ýÅ+ßôׯrÙz2¦¼R±+ù†w¬¼§Ù乺ˆ7ñ²7ÿ]Çàì …Çät7ƒkkŽ£uf>®XO öÊ(9Ñ¥wpƒúxd7?‚À@lQE± €ãÐÀÄ u5ÒI{{“NñŽçy¥XK w­Ôˆ'áe©ò„»–E¼¦|ì?:³ÿØ ŠŒøµ÷uûô…¾¯—£ßÛQ àØZÞ­‚W]pP¬Ôb—™rÂ[Xok(sûw§Ç¢¼ãݯ~ç{_óWï{M°>MÍ»U{s„Ò®i¶ˆ_Ê›äª áíR'ÝC¬@’XyÙW 3Ú׿W ñ“»–kù4†¶œ~ôdìÑʰ“b…“YÈ·tˆòír*xÅLò?®ó~³P©ŸM‘u4P_°Å4ÅCüÖî(€ƒËïV{_»oïköèûÏò²ã×"Ü­GcŽ Câ¡O5t§¿ñô†OÚ<* =á*PW#à§[³—Y>á}½çïýû¼î…oùÆ oûÖŠ{<Ú›"à•ë¬O`\.²~·ÙìáÒLÍt28r„€»¾!îÅïæ2%…ð~‘»ùª™pjq?Í/J*Ãú—AË´¹’-³pÃÛ¾µ§/ë$‡UC} %ÿó¨F|®ñ{9ïVGMu|!ñ™ÃÝjï«÷èûF¹Øñ½Õ µ w]míð¹;3©Ž×X÷’¨z»`[. ëÞT¯xW:·§½9ÂÈ錰b3®%Cüy¿d&U]ôbÐÙ«¥afV"$˜ \feMyÛ¬Qã1[Ÿ6ðÿø5ìDµÜà´{ض³Æâ ¢p…‡EB÷DBÎsoúúõoþF>/sá®58<°~]„œnš&'ÅŒôå´&ànuth¾À‹%-â Bù­=QPvp×Úûª}ú¾ysß·VÙŽßÐYMÀ±¡5 wƒ£±ùO¤“îzKs µU&Áÿ=•Û˜*NõUå³í3[âe ´°3¦¦[T}ÃI0-ÄA.ÃŒ"Ðd׋[™íY!P‡ã¿M5² À¸: êÄ•8Sv’]¾çKW½òËÏyíWó|«w+‹xˆÄSîZ…_¬¼ù­=Qç8afï+ÿíU³ã7tV8ºfánÕ9ràtCg† P§Û?l[õ\xsDÓ?7%ù3”YÁ^ÎðñŦ¼é*ü]¬zŠÊPÀ¢Ë ‚)3´À0:Á¥Û>)RÀú%ƒªÝ¦¨?!B7[T±¸b lìx€v_¿ïÒ¾PX>p×8ÐÓQ­ë•3ÑZ„;ŠÁwûå›qÍîZ{_±oïË÷èûNyÙñkE6xoè°3A¼Áº)¡ñ­N&b¤XtZ,å9Þd¤„v¼Wc®Hzó61;Ó¤˜£u§-ñI *é!ƒYcˆï·³*Fd¡À´3h>O%–·ë#D±8‡ÅÎçüÇ…ÏK±k!7•ܵ4â¡ù§®ŠÂw­r(ª°wÏ>}ß-µ¿Öƒw ÷ÆÉáùHm𪕟TBœB“kü~ùß\zð·÷½"Í]:š#”÷쪑±XQ½šs;n ßzW<Ã,b/xpaü8׳Êd]š îc±´¶â,-³“/ðËØvÙ'6]ú‰Ë_¼¯Ä¹åÕÒÀ‰Ø€õjÒjï(*ß‘«_à=¨½/Û ï{EG| _Ûp_ß!ÃññúŽ¥7HÓ`¤ñ³ÁQ6~÷ŠjVêlJ©lÍ™ÂÖYñ6Õ©ÌÖ\øbm×ÌM×;¢u“ qözY=žMð.™ìÉ€mŠÕ»@±¹3csOà ßo ¯e)°¥…A½»?ºù’%½ÒŽB¼Øò ÞƒÒˆï^ñkî(6ß‘½_p¸kí}©‡ø¾ò!ª‰5 ÷uFc" Ë.íZaä¤E¬•4ó þçT±vUEB<ƒt ëõ á3âì7©2y#‚ý9ï0PH1ÌÏîÐçTý‰ =âWH˜!ãúìÜù¯Ý|$å+…9Gæ¬ò‡»Vįi¸£|G6vüÖÞ(¨ðp×Ú{£É¸ÞZDÄoèŒÆ]Ž»ü°ãë'Gc•é~eé2®d*#½‰ðöHŽOF77%}-wã"5I6¶ *±#‰æ§k\U7mëdôÔß>³•Ì,6•3Ö«±íQó3[„ªsªš6ðfF¢aû[Îüкs>œæ•êw/çWºVà®Õo¤üZ‡;JÃw­¿­7JÀÁcE\|º÷Æ}{o؇¢!^Ý5H[:YžÒ^¤Žh&ëÛ2¹KJÄ÷WíóZR¾£Ð P9ËCü ‰x¼®!Äá`1¼òöÞ –Úñv”9ºr†!˜œbu$èl‰ °m¹¨*⃟Aô…úš0€Ùy@õ9×WýBå—ÓÀ³à™½6„gRÀüt5O²wÎ fêmèØÒÒqFG»b<·*—Ñ`ö“P†ˆ_‹Á{Pý'bŽC¡­u¸£Äñ»ÖÒŒk)ƒw«½/ôß÷“P>%ܵÖâ»Û"Ä>ÜsS²¿dÅ~9”kr•R}TJ¸k&Šg/Å P}mÅDÿÄ̼í¬µ&TäÌú¼‘¤eºY뚟îb ¢æ¸7ÖRÙÙ^Ó©˜¤K R¹Îù>Ó¼^ñkîºÛ«\É®äîöìBœò‘†û“³«Àw$f\WîZ{¯7×Ÿæ…øQZîZOÕ¨”²ˆ?Ä·$O˵³éê]_]jÎÜvwÂŽõ"+X^ñ¹Ù¦šÅ»¬§’/-ü¬Üq}ÅöëuN.ˆ™c ˜ŸjgP¸¶Ÿ‹ë«Ôú:±^2i¸3à]f<7šûtßÿÔ€;€þ‘ùþ‘yýíÚ¥äø]QŽ,w­rθv¯«QÿhÁþ,-âý)4Â,ý(ŽV‘,ÙÚVgvÜnrÏK^j½„±©†É±¶é¹è|¬Ú›)Æfn°-© O~¯òáx·ŸJE5KánµVŸw¬nünuÀP~•±ãÿ'#Äg¼U&v| *ÀGcq©H 9w6¥QgkTÒœjziðu¶D:3ŸFvS]À驸Yvê¯ÓcؾW€¤?Ù¦*¼iͽ Ò+„d”ØË²âÚ†¼Bx2®­‘§@ðžîZÇ5â;ÊñKáŽÕâ» ÞƒËñ×îÛ{í>}¿\ñ9Ã]«œíøK`Q*ç6¦>£W,/¬XÌ¥GüŽŽ;Á¾Ù472 º½¬ÛõÖFÄ ¹WôŸz<ýƒêüX|[7ÆÎŸ‘6¢çîçïÂ_¬ÞíõÔ†»–E|R>%ܱ*|O w­r@<€½Ï^ñ›º¢”ܵV׎/YûÆc_'B\)Ög°þé) ¡S±!È/w]9Ó\WàôÔ¢—f+jñ;tlêÆã3/˜ÝçŽ3Z7éÇ‘çþ›&8›rIëfâX¢3c÷ÿæ•v¶z»½:[½ ½kNÂ]ëø°w³ÞrBürpGéù¾£·†–»–Eü*òÏÚ ïW)¿©+ àÉüànµ*ˆïn¯•®7&bÉ Àú¶H”_j¾×E”_ðTzÄ3SS]%ƒÆ&ãs'&T¯k _«Í²82ñ¢c“/öîuò€ºH]ð¡Üsþ“ãw’ é ˜Lʯzÿ}VÓž<_ ýüd kñYéøð¼¦|Y!~9•”ïÛ7Ô0á‰åá®U.WøÛnî»-‘ò…^_bÄg°F$𿇿3`J0³Bü³/¾%éH]4tçCo.g¸k¥A|s}€±©EÀKs’CxòôóŒ_o𩣨e£*éÑÏüèüö£–ò¬cvߨñ¸¯/ò•ʯ-Äçü·pÌC|¦ëŸŠ§4Á;Ê$¿ºTå’q½Æd\ÿ×C|úw3g•IƵHº«ÿ›vgÓÀhLS> Ä'–EÖÕ8 ,{¸k ¥Ê¸v¶D:5g¦ù“ãDTÝÖSNsèÔs]ûäØsííÕØ1¥áÞº©d¾kÍmý¸¡›<ªµà¥±nØÜ@1]Ó’{Ÿ2ù¡¿íZ#ˆÏ3Ð)įˆ£Òñ}û†û®¼Uˆ¿jßÞ«öèûíÍE‚»Vi2®=íUTâà’ÇŠe…ø -Su<=/Æ“,’†–d\{šï ŒñÏ]‡N^}øä5IwWcÇôjÙ /)›§Rþ¬ÙÍŸ˜ÚðIߎ7^ [g¾)Ÿ³ Ÿ&³í!¾ì-ù{lx@oGuoç*P>•ˆï9À]«\ìø+÷UU:/ýÞkŠw­bg\uïñ’Öt6YÄÿÿ!éêg\[#~ðš?5ÁDU­ ‡F/?4zÅÒ;ª±ãDs¯0ppÿ9Ïœ:„ךèþT°ÕXð¾ o«h®iË:„_±liÐ ¾l)_@‹R#fgÉ”a¬Y ¾ç w­r°ã7¯~ïÆ¯Í/ȾßÝÜ÷‡5Ùµšp~w EgS¦v|¢335_nò{™kèTÌä8tjrQ96vÞñ±ó¸,åíÕX?ÑÜ»ô*¡dúŸ5Þù©“ë>ãµ³J$í·&œÏNÖ¤®°ÛkµUØ Ú±á¹cCs(!â37ŠÎ÷j(¸k­®¿y}ÀáÙ½—;~­!~uážF™Øñú¯±¾&D„éY·ÐéíÒÉV67TÙµ/ˆœ†ëmÈÓDsOÊk—³à“4ÚòvÒ¤d“nMlg½º#Ó¾«5B×,¥Ûíµª*ÒßÂÑR!>+—¸Ló«)µZˆÎ ÙûÌ}{Ÿ±@ßí%B|a(O”ûfâkEÄ×Õ„L͸¥{N…V°š°ì¢ÎÖȺæªåÖÊñA¢¹{¹G[1~·iü¬·ÏO—ƧhzÊþÅd¬ÁlséEVQ’!>s—ï;6Ö‚è‰ü‚÷ Jø”gKñw”ñÈ;ïí¨&àøpYïûNƒøú/§l^-ù,¤†NÆÇGêÞœ"<—ãCœ¦õiai‰d ×®?ºO²·¸U €ui®êZ9„ïj‹€CÙ’‡øu‘õ«½÷®Ÿb‹ølK<ŠÈ÷k·âì¢ðýŒµT4¸k86sðØ Š†x›SM3x%C|æ”_spײ×îuUšS³OÛ=áh ¢¯j MÉÉ–K·&)+&IùKÁq¦t2uz¦ p·Òˆï.=âKž”?:4W@¯&çΛµ”_]ª"!>C¸kí½xßÞ]ûôݳ6ìø5¡Q=˜P$93«´†/G¥,8‘³ã rjšì‘þ'¬kªìÙº!ÃG>Ò{n>Ol¿ûåGc_ñ¼S)å†|‡g"ïä]BÄëqv4X)eŸ×l>m•…çûk<^Ìà=¨¢Fñ™ËCü½åbÇ÷vVƒÖ^ðnÕÝV%%KÉõÑP}4´ý™å’ Á¤œpÒ­«ljGi« ™ýj°Pri_ØàÝªß ^1-ªVîZA|Î*0ßK w-‹ø‚P~sw”iðÔÞ‹öè»ïæ¾ûWÙŽßÐYMÀ±¡5 wý£óý£ÞŸe]uŪ>£‚IÎM‚ȉ6xßΜàÔ¶<¹ ãM}Íîh•Œ+7ù!üú¶îZvÙoQ¿êp×Ò°;£³§|ž3Q É÷U»ÖÁe\·tG 8ܟ㻹÷BcÇ?°jˆ×~ßÑâÀý¼;Ÿ6XŒG^NÓsñé¹8×GC´ >jŽZ.ò%djÞå짦9xƒÌ_›*ÈS}ðô×ïýO³ÌÏ{wuæc 8p×êé ÕEB|™À]Ëš«Y!>ÿWôöߟ󓴊|·Ò|?¸Òâ”ÚÒp(W¸Õ÷ÀÍ ìݹ/ÿ‡J#Ë÷€5_x¸_¼ë¨RÄLJ‘bbEÌð§‡W¨áËM6x×ß>ÿ™ŸÖêªC¦æ\?ùÝÛ‹ñ£ ¥åà®b3D¤:g¶|†Ttn|¶º± OXkw÷+*ªàC“?F‘áT÷º*ŽtAYÁ=¨QPF« 2Ͱ`ñû™kiµáŽ<ìøÂÀÞóöèûÓÚθ^zÉ‘Kv yàÁõ>Ôõ§‡»˜éigü'.w²×U‡Ö(Ü¿lFŦ,w¢ø4Õ„"§É4º«ÿ›8úmá”î0KÄJàÅ—ƒŽh¯¦k…?ØBª- ßÏÜX à±Õ†»Vaíøœ¥ƒ÷¾‡J—q-`ð~ñ®£–ìwÞ½áî{{﹯ç¾üvù‡édÆ9gñ=mU€;–¬mÒFMw[•> ”¡ÒÀ]-΃„¨Œê(Þ©®OÿPƒ+ _Ë¡Å)8ý£­/,Æ#§Ñqƒx=Ù1OõvTQYïZ:xß´<â 8‡¼|/+¸kekÇ6x·Ú{î>}ßÜ÷HÑ)_¢Š(Üw]è ¿ýŽwܵq¹›é¡ŽiçŠË]yùÁ«¯<Éí^Wń㣉“‰›=¼á‘úöe‰øté"å.¨Åy"R›É£ ¦¾–[‹Sz­o«ð«G¿]ðG^Qv[džˆ×kóŽ•+ܵ,â—R¾°K&òõßËîAebÇoé‰8t¼XƒÝû¾YÿÝï=«Xv|П±¦Mnºè‚ãD|÷½)fÒ.ÕÙgxä±Ô-9W\vˆ Á‚Xïë§¿8sé-—[ö}ýå{—ÞøÇ¿íÃ'§”&'©â ,ãä„À,ÂYók}[DË;ÔßûÊùIGn|]Ö×î«ý–v·WQ®½¦kîVîÁeÎåÅ÷36ÕxüÉ2å;VBüÖž(€ƒEƒ»Uߣ7ƒ°÷ÌÂ#^Ã=©¢&ÊŸ·s@ƒøžûü¡WŸÑÏJݵYÜŸuÆ€GoO:þÌgð»?lð¬«ö[¾Û¯[|޽q|G™!^OVÑý™K¥çÁJTæž&Y¿® €Fü÷¿zsFæLæ”/¸kå6N`mÁÝJS^#¾àâòâû™›jÀ9Wßß÷ØÍ ì=£Àˆò¹"~çÓI° šìvïèAº®tåG¶¤¼ï™;F<ö„øË.= à÷·o^zã<÷QÍw)I*úéÏÏZ"ßÓg/w·UiC$åÝK¦à_+ΨªÝ‹ÄI "çïë6‡CÎýÏý €õëª>õÑó”’Ì*‰ï}j»€ ‚m]ô7ò׮Hù²:Sjõ—&sʯQ¾ØÔ%"£Ðë?sçûZ»–5â}k¾T‘»æ»Ö¾Î <¯f{a(Ÿ÷§Ô¹ç áÁ‡’ë/ÚzT¡Ö ô+)ã®ëJW>:˜l°œ±cÀã†ïÏxú“þðÇMéèõÏ{D*"¥ÄÏþ{{ÊÛ¼ðŠO,=ø£ß¼3ø­­»X-ħ‡;åº$‰r]  ÁJ€ vœóÞ‘‡CxGhÓb\2“tú‹A4watH1ÔGÅ£ö!—£|Â]++į]¸kíØP à‰£Æ©pÖæºlï¶¶àŽÂ5@-UßV—?ò}á‘Ûv…ιÚûÓºyè¾ÙñûfÇïë;P€ŒëÆÎ(-ñlGN.…;fèRw'!rÄlm{0x³ÇŸhÓÛO¸ô’#Ì´"Üüø§g?|ÿ…¤äk®ÚÕå3|ªIê?1¿Šv+€…ÒÃý{_9ßÂý­jÓ[ÔÆ7Ëž·‰Í}á-ø‡‘GôµÝþá-¬À =¬A°.ºô¿Z:prÀÄÀ;Õ}j»ý)ù¿ÞRêøÈ¼&».fO£µ÷Íë£qWÅ]µy}T¿*”èí¾ßòýÑÃuÄ­9¸åñyï–ì$Âg_yç%÷¿¸2,~sö÷\ö§y1®¾1? œÈYWÜžt—ׄ/îÊÝÓpGÒFèFñº’ý¡GÆÒž¿ñ°‚„#’RJ×u]©Ãxו®{tb—½ýŽm'º×OÌΆÿ¸|ÉÍs/xL8Bž)~þåm=ëª` 㮹j¿’â¶ßn ÞøEW||é#üð7ïZîÁÓø<ÅSžÁo¹®¶)0CÓ[Q8$*BN8ä|bnÿ{ψ»j1®]¹èª¸«t´®v¼·¶ :5¥ ™”‚RP õqXÿ ` _¶Á{P=U´<¾Ÿp‡™fhážÃ|””`=‡@~Íéà±™p…‡BÁ]ÇæOüú‚xœã^ñX‚k~P×Ǻ|Ýú^×ýÃç>oºóÓ]×O¯× ¼ñžóþõùÿúü-?º!«Ÿ¾±+ ZîÈxªpÊ‘Œ^ðÎ H"$H¡Ýa!z›î¶·olœg`l<Å4ÔkÎ~ðÙç>|íù’ pW@B-á¯nÛ~Ûo·^qÙÿ×Ý›‡Çq^g¾ï9_U5ÐØ¹ ‚û"‰µS»lG²dÙ²[öXyìøzîkE‰bgä$“ØÉ8^bÅÑä&˓ĎǎI–Ë–ɲö•%ФH‚;¸“ 4€îªïœù㫪.,$A$A§²ÐغPÝ¿zë=ç;góÕWl.ÛâÈãÕ>ý*~¼àþ{²äwe1ãM‰”EZ¬FV‡¯v­wàwg‹2ñ9Ó{M4>cî¾+½Sà32ç?kkö÷žpGRÉ>{8¶Ã}P´ï*8²·“Šoßnïr”?!â—Ì­­âÀüÙÕ¥PJ¡ŒeTš2=çºÕÍÞðÁâdßïÙFzùšÛ®^s›Xˆ¬¤j‘¾ÝbuÍúuG+€oÖÀª·9Ðä·»Åo#Y⌮qøóZ6:N¨ªªBUf±!ÃÌLÌd˜˜ÓÁ?_¸M;w6 ÓƒðÊy¯\½ðµë–¼NÄní&Àá|DVÞ»| †øª)ŒF)âOåÇŽŸ“óî–s¬sü"I@o¾ûžŸ&§ði -à MšFªäøŽÌYMa&xà;çŸ-pw±=A|–òï¸ÛŠ|–KxÙÞnïZ:·véÜÚc5KçÖx»ý,†;ö0˜?»ú¤šÕ¤¾©§Q¹QUC½‰fþÄÛûƒîÑoÕÍ‹ëë`Ð áô’(¥‹Ó)&è™m·ÏÛý½jôûïêðó‘ÜŒ=²*~X¯F‡4†u¨UU§c5èzc33 11ÏmZ=yædUr%ó æï¿î‚WúZ6ŒäëANô³OÞ&„""24i6hµªÏûÎ}}î-Ó+OW3v8:=µûw3T ·¯8²Ê¤¦ß(8y!Å›‚@åž‘ðnG¡ßÌÿ›âfþúë‹1ª2ù3Û÷ô9 ÏžYyV3=ã̙ز«zñcñj§}Ž£âÏv¸ŠM'?*…ﺛã5ó (GÂ7Ic,¯œ`‘B’ˆw 3¥™mxJ›L‰*¾W—û@w˰¿.mMU4bñžãd\É÷e3×Çâ=ð0{L'"fŽÁÍÌD\[]Lá ¡w#”òS}€ˆ˜âˆwTÉŠˆˆníå`ýNÃm#ˆSqm™^ å{°Sg€À„²lÏ~¨J¡Uσo ’èwÅà‘… †©±x—a%<à¬5wþôÕçâlKºnßÓçÈÞÚ˜?ÛÅû Ù~j,B~˜´~Šø,åßp¶ ~äˆw´ýP÷ì›»g eR†²}iêûµéf¯Y,þ½¿ƒÙ‰9ƒ¬9ãög¶B„Dðo?Ü5C4￱»éýÝïï™áÄû Ó&»bqLÄÇתôÃÔ›q„PUuîwlÎ$þL}}I]G㻽Kç½b"¥üçt'j#ÄñÝÊeóŸË>…Ë/Û¢JÙ’ùAÍg(¶#S„øqÉâ:ªÞm—¹kèäñá®ñ&V}O=_À=ñdÒÍ}JR ¯©-Kx&f¢}Rü­ªV&|ë`ûgeÎ6Äؾ§Ï3ä{ü.†»‹ÔŽ5â‡/Ûz÷e\Ì®¦c¬b âdoénCÊô”? (‰E" 5 U¬:çݹðƒd;) 4cVRÙ ñûð‡;¦¸½å€kÓ#ןsÝj"(öÐÆwÃf\³ÂyñÔµ‰å‹÷DÊ+2µ 캺‘é:Z©Šeçm ¡2ÅÖ'²ÝíØV‚’iW'Þ‡‘ð§`îÞ„oL¨€ÞR<“¦Qš\%eì’ƃç%ÉU);3ifµniœ\¥4¡šÚ2îïjˆ èÎÈTÎõ‹eÐïÊ" ,МøÑÚ˜wi‰ÖÆü8°ž°1ij,7͸.›[{¶‹÷³«l<¶Ï~ü–“î·vµ5Ïà YKx0”ÜÃP*v°Ò á Ç÷D¶¸§‰Åûî­©Ù옦ÀÇfRÀ0ÏÿÅM¬äaœàîbhÆu݆š,SBR6£q6Ï}›P ‘å±·®€@•Vó²ZWf’/‰¿Ö©HGö4²Ïj˜ne'Y?3l¤ˆŸ5 ÇQ¼ße'§=f0)&M% %{Ù¥ñ^%ÓpqŸöÿ¾,û_æ´EÚ$5m¥wÖ!~®ºûâyÍUóN’ò'XS÷v{WEÎTä¼w1Ü] »*µGnénU(m Óc¬À½+‘d%‰…ZT9ãv¦6ÅpOÅ{úï÷·O…âã³ß>ûðð>U3ýªµ·Žïà¢aæ¸&MF°'Ó¬ª0ñ˜Z4dˆ lT¨¡¶Ë›îsjÎ$¶»{ĶfR²[kåÜ™?OŸÒ óýÖë¾>Ôœyè‰?ÅÁ¦8#âÇ ’¶*´s(6V´a ²àc¾û‹#3•3µ IÙLê¿ÈÜöûݹh1U±ÑjŸ‘‡‚{µÿsöÜÏËyg—ŠG†ògâG2M¿å¤¾/[ÛW´ýE{–5#„»‹A×Ôøþ`÷,uÌ@0sJxGF†²Xz>ØÇJïKÄû Ù¥)3ËÎLœ +Û PÅ÷¶M€oÌP2†w]ÿ8wc dÆB"ÃÊq9±*î™Å¼«†”z¸âw""2L F€Ó®ä,šØÅ!0«³°²‹èP ?öâÈãÄÎ}ÉJÈÑ"~|O%::PÂ;_?e¸+“’Iê#”\ÍŠwInrB;ƒ>š¸ªÉä‰èGØÂ¤7Z!Ôˆª™Z¥¤½Sz S  =ã:섃³ñc¬‡Ù¼«°Ù ù–‘"þx|wLÏ–ÆŸ¥”?©pˆO+Ü?ÐÝœfw·¢Ìœ ^F&Ë %2ÊF]ZU$1g„Hˆ$egF”Dˆ÷tû·S´o:€¯Ïà}WáÚu·ûÁ–Ÿð`ýî%D”²'¶h\¦Îñ#‘ð ãÜõ†I=*¤•K«(ð‰¸dÛ¢ƒÍ;˜ï”-Ó̰8q1¦5âÇ îÿmˈ÷øJzäH•’WC]CêÂ=?Öï* E6³Z]—ª’øÏ˜Ø2À‘Š>fj”2ì!¶…ê&Ñ¢I•³‚ê΃ÚÑLµ(¹ÃËGVÕÆn»‚’KÔSÕ£ 1©¯ÒB˜À¯j·0áê]mÕõñê)wÅ9zXXØŒ©3±b„ƒ'·íîu_ÙÚ˜ßócñïi8ÄÏo©šßRµé؃ç†×ïCážÆÛœÙ5Ü]8¸÷ÝwEêd–%<4^C˜ùÉ3(›hõ\)[’YÅ š™LåŒîàϼ>y¿Ë¢º*Š:òUõ“»¢}Qÿ>[<¯~Ǻ'V®}ò¢ã?ù4®XÕ~ùe[V]ºeÕ%[/8&À+¯ÍzuuËk¯·¬~£@±$uõ‡›gmÓXÅ9C9ƒŒ9ƒÔMHªh¤D<µ¹ŸAD AÉ5J$1Ûé1-x 5g¼¼Ÿ>™‹.Ø®Š—^™>r*ü™4Ê O¤âgͨ¤ñ€;/YXä.Km°SöVänU«kÉó4îO-ŽÌˆ÷ìÎ>)(ÐП¯íË;ˆ»¯7<£ywIH¶£en:*z«´`"Iø“š*<13®ã÷4ÙcÔ £ßw'lcpfcŒp'òÖ>yÑM}“CW|'žœœ?°“›œ„…ü¤ithoÌ2ÏWfçÏ(ÅõË f2DIe`j6ïëgïŽB „‹û¦µLòEU\‚ú t|¾Ãôk-ro‹ÞµkàƸúŠÍ±Nøø /µë__Ó<Þ™3Žvõ†Í³¶ªÖIÅþ®Èג©KUB<9+†âEªrÌ IDATFmœvåî ÖiÄqŽLZö."~•T›ô 'ÞO¹tBþ8*~tS„†¥ü§/÷n"à®`~ TÅf J²¬ñÕ=þ['*þ²5翲rõKW¬_ñä¤!UTÇg9öÿ”è®îÕÔõåUE ¢`C^˜Ùn€kÌË´} ®N(\Uj\3?ƒ*öà”OD8Õá„üQñ§î.6í,Ä^< 3dt°~?!Ü]LèŒk\<ÊXvíˬö'¹ýž‰ù›]«ÙD¼#éR?\¢5—ƒñÒeMqñLM=IÚª5“buZmó‚]î—|P›gV{¥¢”Jé¦7†3o gÜXšñ«ö™“5øÖyçð@žynî³Ï·=÷BÛs/Î9NÏ^›6OíØ]×Ó-D‚*S39¬®-‰xwÍ ÓRå\]C`'ޡĉ9a‰/°ÙŠòÉË{/¯¾â8Ïç”ê÷4Ž£âÇî+¿Ã7‰xgö û̆i x‡B»:5•íN§Wχïgj±² Vª(Ôzª $ZÛS Ѹ: êZÓx/©´)¨«qùk<6$βÌ@å57ü&†„?)ñž ÷-sósΨ?Õpw‘Úñn”t6ó}älœ˜×…­5ÞÔÏÐ x†ŒqšIÅ$Z&»K‹)õS%ÏSÏlΤÙÔÔ™q)VUl;g€÷t6¿¯Ð”Áº”ŠZ,iÌúäßï­oÈ|óþ+qŒî4˜Õ}z¸ÙxÇö-SUÐÓí?ûÒÔ’æƒ*Ã9®ªê¯ªêOŒånÄ=#ƒ›TDºº=‘íõµ Æ Õýö#¥»U¯Ê÷ªüì¯T<ó‘÷ýåЧ÷o?¿çdh$1lÆu<áþÓ;¾¸ÿmϰK•ú†|fϰÇÌL™ÃJf÷ï—VÎä*‘­yï­ë¢ùî|Rå¤*ê2ÝoÍÝây¸r÷<+86gþ>”ÿƒª¢þ±?û=F wé7žYÄŸžØ¼³àÄûüˆàÏ,›W `íæ‘º.-ã:v¸_öú-‘¡ÛsÍ?,vü4ØwƒÌˆ¬Š8ö¸›f"è¡=T?M㇉ EÝ$:@bË}"óu4›šY÷Ú¾dk±^Dïl.AOuûñ#é]:þuýä[¦â›­ã wÌÇÔ¹K§ýD&0ìŒ þë›3ž¿êÈÁ]4¯F‚ŠÞ°¨UUE×›ª·¯H®mTõ™BP0m ä»L²ˆH”ˆw¿Êó«¼ zÀ oPåûè:GŽ:N6ã:òxø;+1¡¿$ êSûwºñÿ5 PI5RQÑäeD€öÕªÄn˜ ¤¤PߊgòÅtvÙ¾IUÊÎ ` ¢šZ¹Æì¾7cÄPáhR($æzœûE#*gx•{£ã ¥:Õ1F¸»Øšªø¦üÖŽÓíÕœñžÍ; ógUÍŸU•·(ë÷“…{$ã:v¸_·þ6ϰÛä$¼gˆ({­e-«xÍþ»ø¥ãë®ë:œrÏWÔì.Ūرr á¼·›…®¥¢¦j= 5®ÀQØ|Š~z·{Âw<äï}׽ì¿üÕ‚A¯hþåòæ_z^àû©Ñ¢[:Æ[gXê KGÂÞž(ê³r¨8¥¹¿zF/U˜U6ʧò2Ÿ/VWÓBxñ¬ALN¶kì즙U+°Wå{yß«òWœ·s劜¿óüå»TiÀ\ïÑvŽu¤×Ù3óã%Þüî…Û¿—_X íïæÖÀýþÙO^i劚äU–•í©Š÷|xž:ÑT í6Sœ\€ w¨Rér4®“™7Ÿ³Ùó°ró|×#J=GÓßQÆWχñÕøÈUÀó8 4Òâéqq{1å›N«Š?ýpw±)QñNÈǧpÔpwqÆ?v¸xbñOŸû ç‘gÈ"ß°ç±g(Õ:iÍ‘}1Ó•Óˆ…¨(”*óY²—ëgö­j'ÁÜg›¼Ü”`]Ã’ØHqKÖt£ø‰µMwmA¡óµAˆgæÁeò·=íûç~øAï¾xÆ'˶OãQÊúBglØQRƒª)=UÓ œcVz‚žžÀ±¦ª²ø6>ê ×® ©·©MŽ»4˶»’zU~m]®ºÂ;Ü>É=·†úÞÉ“{Œ‘‹.Ø~ñ…ÛÇrúÆ;öÆ Çwg^ÿn0÷³Á¼bh‹¡Cû)ÌÿD¸ ªZï%.oåB(´Ð­éj%g™ÄþŒ_n)ŠpZ¡4¥ ¢þ||£§¤î&€‰v­Üì{tÞú;?¿¿ÆSöÍÕø*qj\@ð «5žaw»8Ó‰N¼Ÿ6ÄŸ)¸»HÅûüYUãv Ï`Æu\à~íúxuèÓËô <ßÌ ›=ž!'µR>uäÁÝá^,¼^P ¯iêùàæ#ïÛ¬@ããÍ¥¢†¡FQF§kfˆÏ€=œmë6wµÀÝeÄ»«Aö¸._ø¢çžÃÝ‘=Þß?ðƒ€ÉCHQ–ŽD]‡Â°Ï–vaH UMî!C•9RÕž‚ß{ çä_à[V…€Ù0³KKˆ-›-ФìÝjU©ŸVQ[°Çì1€××´Xᇪ)¿ë.¹hÛ¥o ý‹BÿâQwuÌž7&œ=3?{æXYpë'^qX/†¶?´ý¡K¶ÚGÀ÷&­pæß°ý rÉAæ;¾¯¾¯¤äùP%;½ J*êí¯*—K)9B €<ùå*â–elÐןêÍì¨@™–!ì{ôo¥0WÿÆ·í_3Ö“Œñïi¤ˆw­úÞݱiGÁQÞØÅ{®nÀi¶ãÇP,SŽÿ÷VÞs Ú úDÍ,C¤ ß0%”@]QÕP.”ŒÕ—À È¥¶{l]ØmF?)0åÁYâš,"1ö³q¬•>ÃÜWwÿÝŸ¾³çÓ? ÷kÏY @4@’ÅCf!ÕÐÓÅ-*¤%XhïæþÚ–œªlèAQ‰júCÔW½p¸çÒʊЯ°eV[AÜÇ""v‰V‚èÈ„‹€ ‘Gì³­ñöÀ"çÛ¡Š5o6eç’‹¶¥û%ÿÒÄ—¡ zöħplှ}Ooº?{fÞ}8ºxà;çFZµœMH6ÐöÎûå¬ÍiÝðÁ­‹¡ £‘-×JööhEÞ¥pT•õ<ð}RYPÙ“'Püb¡øÅ¤frÃ^¹Ùý}X»x£µd#D¬ÀFˆ,¬Û6‚û`Yoõf¹~DXøâŸ¿sÉŸŒåO:Š8Epw±µ£×Iø¶¦ªqiÅ:lœYñžM; ƃìiœþŒë¢956l½xë‰ó‰<ãÐ Rà–\sW¿¼ßk~<êxŒ÷¼ßÌ„QˆªU±iQ¼³à§X?@Ê¥î¤]âݳÅö"ÿí9ù´nŃúâbÀ§õeIÜWs¿ãû—*?þ*áÑÇ–¸~åZE IM3(ÏÔh”‹¬ã¢…ÓŽ6‚HE 6?ÝhIÈUôTVµ¨­yÑFË{{¯¦¤l>‹UHì̈ۈȊ³„ E%?0¨ñžx`€sÏÙ­Jo¾Õ8èX^|¹Àí×@É»,}¼ä¯˜rì‘…;2”#â]?÷L’´3½wÇB€ž[´á±ë_¯}c±+Šw‰VÓªâÄhœ#u9UQÚ]-ÉË!ž)ˆ~W "σ²R%×óDPdo RDVÔ5…û÷⟙<Àÿþ¨|tqJáî©ø¶¦ªS„ø‰w§¤Èi[5v¸§q›iLÌþ~·ZexDž!¸éhBQ•¤°”®çLÖ:©ØX¿—kf8~wû×F~f()ƒLRâ0äbêÜ`(Õã Ú‡»æˆ¯ÐÞêÞ†BØÐtæ—ܽaí½‹àt}¼LÛßñA ·F$1Ù鎺”ñ5}Äñ]€†V&7Ì߇þCû×Lž6Ò%u£ŽÓ w1⛫ƫíö„;FÞ?rqJ‹jÆî.ú‹Ò_”bÑö¥Ø/Å¢”ŠR,Š DñÓ ã¹)»=C¾çú‡d ÙæW‰(¨@)Â’;w÷û»\ruÚ=íóg(íÏ HÇ–W¿ó`²Ñ°ÛÎÍ´cîDðÊÕ_pgñÓÖŠZk­5ÅlÓ.ÞV×+@I@¢ÇÚâî$îË’š=®>W2|w-ˆk‰Šýßì/Þ[ï-yTô¨ß£šÜÝâbVÑ"ÐêÞ6æ?íD'ÞÛš«Æ2Àz‚Ç©-:EE5‹çÔÐøÁÝ5ry¼zfbNZuNvM¿úP €’Å‹»_Ÿ·Û3ä'“ªÚÛ¿C"(}en¡Oî/ö•pÙ×7·}a“¬œáõQ=»‡lˆ¿{+:ÚÓúóá}_åýæ¿ð×½õb]ˆXk#Ë[…· ïo—ñeЋ‚=‚" IÕZs¬ª‘}4*>l‹üî~¿T4Å¢’O%C‘ˆo"?°Qgd;mtƇ#{8Š:­tYíS”ÀJ&Îî°1Æ3;7^?ô4=ñÔb}ϵïŒËIwÑ:3OÇ…»‹ô ZO¦¨†Á¤†0˜éY²×ÕsmÕÔ¢¦—¬Ž$o¾e}¼°5¾—~€ @ûåíkµ‹¢ùÿÚBÕ“°Ä’,tR@”ø#ŒAýóÄéëÙea¨ÖJhmd%²Öí„Ö†Ö†îñÈ~ÔoVèSjtÕ“Ó/ÞÓHÅû?1Å;N5ß1QÛ ¿(A|²:woãâ¡E$X»t÷¦ wÃåéå·´šEñæfà£Å'ßÂÆêF}5~µ«xô£›»nÛÞÑî}a‹ýíÍÇ–íY]ƒ~ïìÙ®Y¡vs÷,d¨ ¸/w?€Ã=U—®û²µV¢ñöÖŠÚei·ð5û±>½°¨0l`IK!© Ÿ*ú• JŠŠ A©èi¿éë7½EŸ<²^T´=V Ö:‘Þ­(‚„=ÃÆ3l˜ÓUd^vß°gØ6ÄÆt´ß8tmÓÐêϱ„ƒõ¶‘/Û÷ô:ÊñÇ"{ÃdªŸDõ T×·‚fÀu/ZùâŠs~½ÀÖÛ×uܱVUÃR"ῼ_\®Š !|ã¼w_Û_*R)døÒöÙ 5­Uˆ’µq§ È•‹k$–Ä–‹n±¢¡·EVBg¡‰(!T;sjå©6gÎ Ü]lÙUËkL`¸ã4ð'@ý§-žS ÐúñsfÒp@çTï¸aö´°½¥mÝ,« ¶+ví½¾Ãù4Å^ī”ª¤^)†qè( ½xéU<óÖî@ËtÌšž­‡AÿGÛ»nn?¾~ßßý»tßΘéñh¤5löù‹$E÷÷ßܯÐUÿ2A¼µ‘+6ŠïXo#Á>Kû…(>ƒž5‘ðD¢…Fƒw}**y1å{rQÉxEgõße;{m—jQ5@äú¬˜Ø}!f6&Ù8•îƒT¼ûw϶ «=ŽMkc4R¸§±ídŸeú”i4y*OšJ S@1ÓËC¿â})X±èç+Ü5mÿÿ½‰„'*8_Aòs”« MÉ>`QEyCŒ{kÕŠÞJ€~ÿ»·ŸÒF»gîiŒñî8=|w1.ˆ_<§Àú­§$g›ÝQžÒ»Ú²¢xÖ­ó6µ¹J³7ïìüÈŽ¸l-ÙVþÁŽþr>*lßê*TT`õ›˜1 Óë°4Ÿ[ˆŠ¶žÊsQ ŠCïmßsEûŽ‹¶ Òï÷éÁ½êš kÜ‘,3®‡[4ÿb±(½ï=ÒG¾Xúª^Ñþ•¬Kão­•(A¼-ïhá°Ðá£BG £Eƒ~Ò ‘Šfw]ø¢‰%<´¶—öSÁëíe6ÆD%0Ȱ!cŒç¼6†½A@çôoÐFÆ3ûvÞ’=ÀñÕñ#˜4m&OISgДéî.DÍrd¸v_1Ö³ŸšÿèòYœàÐyˆ ÕE÷l\tφb‘uášUxßµU£V`•v´ì‘½Óð™užÏ çÛ҅‘"¨DEåðdwŽ{ä.¿nìíÕ~Nî›Nâ'Ü]¤ˆ9å'8Üq:ùŽ1#þ”ÂqYpyèeBùdA)xÒ4j˜Šú)`hë[sš^isߨý‰mÝwl…èò»v{çΟµí±»öýãÂIu¨­EM-~ãJTåñËóQQ©¹ ÍUêºh¬þ§95ßmb{gë9[7,Øvä :àðþ²ZП$yD’E‹ÙÈJx_*ý«*®Üöµ+·5ãÒˆ8IY×[›ŒÎ‹ÄÚ(T±–ºûÑ#Ô+^è ñÎuU6;j¾5­ïæ}]M]ÁöΊ7‹Aaîë¬GÈ3W3@Ò¯¾gÙÏw¶Œsc8Ù)[4Tvf8+íÙ°1æàî[ÓB®?ÃØOôX¦vüqÁwhwÌë ÖA Ó]»€aˆÛ5`Õ™ß[ ëî7{?·&,Q¡®¯+ß_ Q™C>K.ÄŠóuù2ߦéú†Îo/é:Œ0„Y%+Îå#ryTVe±NLdˆ]+&6ĆŒq 'CL8e£M'Ü]¤¤ž;âÖb;~â‹wœ~¾cBf\?ÔÝ àÑÚmiŠuÆ,šÑBÓ›¼ýå÷^*»¤Ö«ÿßsrÿÐ `ÓAj’úôÚ¼©ŸŒÊJå^ŸzüŠJTTbÍ7[^»·%YØHAnh©´>ݼðÕfCðÖÍÚõæŒç¬ ïéæŠÒ«(åó¥¡øË¿ûÄô» zÍž¿J-xëü™Èе½ØÐF‘HdU#qªÞZQOeo]d¶W”æµ×üþ††»—ïþFGå¿,ë¸}éî+Äl&ŸAF­axÊwÖ¡Ls&;ìt²3Ø6²mbi&™ÍÒÊ:ÒÆ27›}eöÌMǦ“ó³Gã+O¾¡=ÿ;Ê‘CzôvÖžÎøUÆ:âÅpéë u·¼ò¯—˜öísþlYEOÎëÉ•úZx†)ô4b„ Ýz‹ú‡ÛJ°»ß»®wROoC¡§¾·§®·»¦·»ºïhU_qj_iZW¾x´²ÿH®ß©xŽe;‹EƒÍM‰r;ÂÞ¡G:.”ŸÓ”'špâ=í;O€ø³î8#|ÇÉg\—Ì©°îT:3n§©šæPckùÝU–ZªN¶$>XUk˜ê ³mϽ È‚)ŸÝØø‡'7Ú9^e…æ+4_¡×/ô¨hŽÜ´uÀ÷*ªâ5ÖjÉ’·ÏßÜtÕþ¦ÀÀë“÷ÆXOÊf@Rf°Á q¬N¼_¹ªýŠUíÙÇ_Øq™ìÏ&ß©ª×¸7vcÔ*TYÕ¨@ÊòÄUdj™j©3¦Þó«ÙãfÑ*Å­±Òþä®ó ýìµeî(¬e3¹“ª†<©H§}bȸ`D®Óˆ ×¢€Òr¨Pˆ»?âæ~P^kL€F´}^ Uõ²ë[³¤¥·áþ‹ÓK¿±SÆ}3Sp ½ù®j"x\®ža”Ëh¦V+ÕàÈAJª³PRš•)?ºËäÙwí» iºµ}`¥üYwô_þrø1ͧ-Ž?ñuq[ €Swó¾õ¡š­>|tŽ[Ê?cD 6nŸ*qU’´¡ªMŒwö—8 µò·¶æ*µôÝyþÉæÀ ÂÃ34–X•þï·ûçÅ `LÚÛ£Bñh{öàùäûä¹- ‡Â^£3§Ý®¢Æ-¨Z|ÝkÇ:(Ç÷´`Þµîrq§ù´cëÚkþ?J²ÈîD ¦ÎwJ•9"6D HU° Xf"Ñžâ]–g+JF­oùt «(ÀuAd%‚±E ””»â7ɨѴU"àZ=¸Þf.£Pî…® zòÍé3¿é†·üø§KOêüž:¸—E¢jSð¹U¬Ÿµ 7Køž ÑüHëÉ5`È·t~öM?€ïÃx𾲜€P¡z?¾‰U.È‘„E÷’Ïã7ßÿ6€sŸYA(¯éT–î»”¾&«gï5o#É% {°'‹ø³ îi¤>«èϾŸIýî"UñBù%m5Öb¸Š•i§Å=Û f`õÓ”ø¤2ß킱ü›Û”¾;ÆWD´ú/æ^÷•Í¿¾§“›V„ýÀ0âQk‰¤¥¤YD£HË>FDÌJ„_è÷F¿=ßÂb¼|ìƒzöù6—]²Õ}xñ…ÛTéåWg¸ÏÞ§÷éêêâ%¯ÞûåÃßq_pÃoo ·£’JI+sDLDŸ €ï“(1!«TÔ*ù«>|žÔzþ7‰aB©é±^U‘k½¾CFày\â°Ï†ž)7èÌbH'cA]¿•ò@9sÿ¾Ëìñˆb”¢ÿ$âXÚVH eµŽ!ú½üHªñ“¶¿Dä¾w×oû>|njmåWÎÓÀI~õ,"S ü¬sE°*˶ô•kÞ~õâ×/xy…$&MÜgÆíC‘ô¤™4Dth_¹ç8{¬[–w_¤XŸ×Rå^°g Ü1øŽc4Ž?mpÐþÁá‰óán¨c ÃìvŠIDAT¤­sÝ;NtîÜ–«ª6þ2)½{”¾7·¢ÌÊ 0HñÔ=óˆ…Üø«K@Ì÷¤„‚¨º=GÝ/&VD­j«¬ý™ênpkílÇzfÉ€ç_Œý™‹/Üࢠv¨"Šøiý3®Úðg_˜ô‰7®øC[Òh§j(*”ˆÉÚÀó}ˆ@JýêÚþVÕüP| ‹ª‘* ‘‚$TÛkÁT9Uº;4‚æËÆKP’ …{ÒË'¹Šfà¾pñOÎ(ø~:1”‚/ýuAü¢¤ÇXLí!Q<Ï]è×ݸÎ÷áùH‡‘7|{AµÂ!Zm7w) ÝÏq_åÒdEXôâ§—=séÚçW¼qÙš g?Ưp8•ïOž ":°ûIw¤F8Úô,ïi´ï*œ/a|cBðfTwº(”ʒϼLùäm¤ tw)Ú¯ÜåêôÀ‚’—4ãc!%R%%f!Yb…kïÛÛ·Ô=Ö6PÂ;/Þ½åHDÃ(þ”¬§°ééœì¡½ôJ+€‹.ØÑPßë*çÊ?½ðOo9üù‹^ýïÏ-ú¯R’£"ò<ßó=ÏgÔÖµÙC‰•zÜ p9¢€(,_iâ[„dŽ %¯h¦Ìpÿcj°÷ÄG `NÓ‰§WŸíp0¯¥* ÅíØ¼óìðkÄbJùÓ)Þ³¡‰.ä– Åº5ûB*4hÍ‘l¸p—“:¹ŸÌ‚0Á(Xu˜†R¬a ` KKiÖ‘ÀSíäòÞ}NDÃ(nr“Ì´ªÿ»°3ŒäŠ7n½öíÛFq€ÖÒÁCUGò±_üÔù÷XÑ‹×~ùè¡¢”4mÀ ®[XÚû\€µžˆÊQI}—&%òI(GÉ"M-X)X[°õs‚ Þ+‘O5“˜|‹UÙ0,…aX*•Ja©†™K¥0 ‡…;†[Àuœ˜Ó˜§3a *ªñ ûûû¾ ||öýò~àß7¾ÏOž»áé•럽dûÆe¿^±ð+æþxA“ q¶&,Á½ ‰–( “×R¹ÔÈY4jU¯Y½4ŠðÒÊ5q˲ +ÉŠèìνr`ìß#ûO¤ßÓ8áhÓwÜlÞYH±>ï,Yuæó«Ccåâý%{š…¼«"¿©«1ë¹g£' £äA¬[¶×¯¤¦7šK%ça2)ÕT¹š6çÆ¸¯T"eYô÷©’þÐ.f<ÛæÞ´†ã7p÷A(‹”/*ª¤d4M·>L»Aô[µÍ.õú«%žÔ1®\±óµ×ËC®ùÀš°Ç†=Ñ{¶~ bzbú=žA õ÷¹±€Àt‰ŠD‘@-<˜z¯d*M©Ÿòýx0J—ˆU±ñ*X®%¯Öp s ÷í }×i¾§'+‰[@«V$µVÅj¤*rÑ¥åi|ÿüè_¦ûW]¾¹¶¶¿PÈ=ùëù'<Ò9y[Ïœ;ìøþ¯,eµº+˜—Y;š^ȉ€Ÿ¹Þs>ŒçæhëekΧ¤#³õH“F4ÐR/’~3â³é#k,øå¯€ÕÞ‚ Å7†|Ÿ}ÿcþ:Q¬zk 5¡OÄ{ö‘{y-Ž‘\6ŽñwÜOøàŒ‰¥ß,[Û_²ý%‹3´Jž4TÌÊùx¬‘2ãÍûˆ0g]³%qÕ~ ­­u‹›bñNz~k±_Áª„¨„(¤L|,áë¦ ïl"D‘†‘†‘Z@4Þ?Y¸»}Áù;ÓKÝa؆ÝáOjî$"€Þsð«žg0‰“Ùƒ®”Q¡""**"ˆr„^I 2<Ô’I–X¥ÇFÝ‘í±ÒcóM>ê}“c“3u“r¹?hðƒ† bR›ä¿¹íÚ·v¾÷‚•¿¾ðâ§³pwqùe[®XÕîŠ>»»+Î ¸#Yãz÷}Û‘ó¾R»SñǾgÜþ¿Ô¯ÿþÔõH’ö®>çü—Ï[õÖùn€z¬2½L™´Øw[)fZÿ¡µQ ó¼œÒ³Kø˜ØVÕŠZÑnYj#üzѺš†¨x².ú„‘ªø¬ÀýXáÈ>ñUüDñß] ª¢9#v|ZB£.ÍE`P²Æ&Æ.ƒ^š¾×#,î˜U„ˆ(ˆØ½çQc·”ò"e®iº›íØs//T'LžA‡öÇåðšØñ¢ZU¦›yÆÃfÏ¿v§2òXqáÊ^ym–ûpÕÕ/‡Ý6ê±¶_<ÏÿÅ”?ð|ÿºƒ_»rÏ—ž™òÅîÅ‘í*Jy¬ð`“:n&bvÇGLd­JÁR¼îO<²Àoü§ ¦+ÂÑÆ=;ç"™±0lp5@«.Ý’>òëgG4œo"À=m»{ïžõù¿»å+Gß¹·wã=u‹ ôÍÒzχï)+TqUûÂøþ `ë‘§]Ü`| Õ¾’6R6ì2«qñMæ÷&©V«Â gÄ JªéïøÞ€“ï.ÒéÕ#̸žq¾yga^KÕü–*›&ªŸ@ú}h‰ä)rüøYõ¾_Ôì÷ ι™G^˜¼™V¥ª"A‹û §‚]³c{ˆÑÛãÄ»£í¹æR›–ls"-þ7ñâÓ;æìEEnÈŽ¨ZÍ…™²æ,Pê Ãî0êÒÖ/lÌsÍŸ®Øð§ˆÓºJª®ñ˜Š¨ G”#Ê‘Ç%Uµ¹˜È€ Àºø`@LLª-X'áßûá·Ï]¶{ïššŸ<º¬»7rÏ¡­©*óô”H{qEWpUúø³Ï·=óÜܧŸ›;šÓ9bÛî^ßãÿ6y _íÚðW½ë]; QܰgÞ{w,H2«é¹FÆmO‡i_Aµ,·• F!Œ*÷ä§çÚ•bYÕÛ÷/±~Ö²qxñN"°;ê#ÝÚѰn®zˆ÷š0é§æOT!?ø>lœþf5îMñxžŸUîûy徟Wîýyå¾_Tî rä8ðS•ûŽ˜’*DE¬ªM´½sYÊE w0˜¥ï()«²Äúß *!ìÔë (Ó”&JÞñå7>TFºô=«UôçÌÉÄ€Êæ°' {ÂrÓFc<ãcž]ò§.ßôg—·‘‰ˆuhWÀR ÐÙ^T·à±[Èd!r2@œâ¿ŠôŠí±¶'²=18Î;·Ds®ž»h5Ä_¸rGA//èH¤k =YCO<÷BÛIᜦM?q(¿¨µD¿/m«ðö–ãùì§Í« rü±ÊæU¶|¬²ùöÊæV´|¤¢™‘h)"Kja!p¼NÃ%Wµ»3îè°:7# aCW‘)‡÷iVÅ+Ñ´YI7(*kyýÏ®m>;}ÎÉW:¨ÚE©+Qî\.‰ÔFNªíñbýŸ<7ùzy÷_8ç]ED9b?^+àºPÆÉv b²§Áä,š«V­°æÍ¦7Ö4ýâÉ…GŽTN™\èéìØ:FÕóÕüô¨O\[Sž&$ÜD6îíŃO™‚÷ôâ]7ÙRH—YíéRu­1(naâfN" tf T×({ð9ñî\+…–óM.®Îdf€ôx+Æ&Þзì*¸ g!âGW³iGâÕLÄ;¸oØÖ}æù>¸»8=^Íë—>ê|ç¶[~п³_dUqÚ=S«­ïH, Íj&"à¿'•ï®^R B±oC¬6B!)ŸÔC{T3*”•íp¿ä'Õ{üNÝìÈžtŬù¾tÆO‰4]wò—¦3ûUTUä™Ú{TõªÂWT>Q'Wó`ƒžîH¬µQDšÔ¾Ãµ'ãŒ~w—'ÛEÝvÐS:r4ÿÚë-O=׸¥£hu¥¡dØÅª#ж¦<€-îÞ¸ñ»Ÿ«Y¨À7‹›"kUµ,¸)%3Å£ Ó¾ð¤=Gb[&ö¢ ef2†J!R¾B|êÂqA¯µ‰Ks—¿8²t¿n <“ó9çó·°õ[´ ã÷ô‘ñg åÇRø¸iGÁQþÌ">…;θ~9Ü]œ¯&°SñÏ^ð`Høaa‡…¾'œa#UQ¥øV×Uy§åký7 ÌzôPR OªPg¾# …Å3´•ôànÝßQN·ÎhM-"¢×t@/]ð£gÏ{褎貋·ô…—Z݇2ÌÌÌ™ ÚĈɢñ±‰ŠÈSùÏzMéëäƒrD9pyd`·µ)»42½¬ß¯°{ùòlËœr>àSú\¡/´ªÒ¤Ÿþ_}$G7Ááîbㇿï$¼Í\¹ËÛ¤)±`O1uuÆuºÉª:H<]ÉÐ#óߎ"\üú¹=G‡C|ÊøôYýnUÿ°z‰ð?ú7Þ׿é¾âf÷¢_¸»H™øˆ—ªö3‹ø,Üqfù~²pOãT{5¿>çÁ§ÎyðWç>pù·ÙÈÉt¬FVˆ`|8¾ÇþÒäjÊ÷lÙ;Ò ‹/çÏ,ê˜f#¼9}o2EZ’};ãYœñÒÐħqÏ-äÜ(ŽhPåÌp³’Êp§=i±L¼ó„7€kzÿêªý_åE½ÊØÏru åÓ›eñ@ ÏD¤%I³¬CŸ€BŸPUi²”ׄ«f^0iMlb¿1ʶÌÑÃñ€ÆØsÓª±rgFäÄ;ÁÅïe¾Ì{Qµ*ñ&òçoK¦ð-w¼vë'^9E~öz5£‹ñ§™òƒàŽ3®ßG§Á«¹jímÎ*uÍx­÷Ψ¨ZQÍfÇÈMdb¥x>˜õðW çºÜÔ'í=Ÿl‰ ±„wF€dïvݳ`jœ»4Ôìpë'7ªôšé1¢˜«LÓ©ét[4v&ÈiøA¹V¢‹ŸIŸIF—;sú"çÕœpæÙ÷Œµý÷¼9‘ð1â'O+—½w,WËÄKUÉÁ&™¤÷/“ÞC\³eQ* Žvòß“úÈòD¿­ðŽ@ÿhÒ¢W®ÿçW¯ÿçÕïÿÎXŽk$pw1a½šSÑo µãÌ®Ç;l wœÁõ«c!û€Ÿ“ô÷xö¼‡V<³›7a#!¢¶9ýýb“ž¶åHq‹HÁ;í¬8БÂ:n˜˜ò ? KKÓJ¡‰{ *ÜR׸|»ÚZ|¸{ΦÅ­nýäÆÛ{Nê@hH•¡aÎ:4vHÕìÈ ÷Ä¢QQ @9zê©çÛñXõ—¯Úÿgaž¹HäHÔF–˜)²Ll l\m 1¹Õ¬N¾+)±’driJàÛ?úÆÐg>·ù‡v¶= ‡ø¾s¾sÝ8éêÒªP> J"qSMIf©«’Ãî†ñÉîs¶uö§ãÅ(F0ÈîJÓ·qhìÒè]ó|ßúÀ÷Æå FBöA_<Ñø~ê:ÉlÚQ8 |Jvÿ¯8VÿÆŸVGIEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/Transcend_esrb_checklist.doc0000640000175000017500000026000010305076760030227 0ustar pabspabsÐÏࡱá>þÿ «­þÿÿÿ©ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á3 ø¿Š]bjbjC‡C‡ "Ð!í!í!6%ÿÿÿÿÿÿl.......BÞ*Þ*Þ*8+4J+lBI2Â,°r/"”/”/”/s0{24¯3œÊFÌFÌFÌFÌFÌFÌF$EJ eL”ðFÝ.K4o0s0K4K4ðF±:..”/”/ÛÍH±:±:±:K4|.”/.”/ÊF±:K4ÊF±:б:;;">..n>”/¶, PicÙ¬ÅBœ)Þ*Ç4 :>n>\ãH0IB>,ùLg5JùLn>±:BB....ÙESRB Qualitative Information Instructions on Completing Sections 4-11: In answering the following qualitative content questions, please include a full description of all pertinent content. Remember that pertinent content can be found in any material included with the product (including bonus material, hidden software, easter eggs, cheat codes, advertisements and product placements, etc.) In all cases, the most extreme instance of any pertinent content must be specifically detailed. All pertinent content described in this Submission Form must also be incorporated in the Videotape Submission. When asked to describe specific instances of pertinent content, please describe: (1) the sights and sounds of specific content, including but not limited to sound effects, changes in soundtrack, camera work, graphical embellishments, slow motion, or cut scenes; and (2) the context in which the specific content occurs, meaning what else is going on in the game at that time. Please give clear, thorough, and specific descriptions of all requested content. Do not provide promotional or marketing language; this language is not relevant for rating purposes. For example: Correct: "There are 11 levels to play as the hero Omega battles and defeats the Evil Forces controlling him. Based on the famous comic character, the hero uses hand-to-hand combat as well as an arsenal of weaponry and superpowers to complete each mission in the game. This game features several different styles of game play, including: Action- revolves around the hero fighting enemies in fierce battles. Puzzle Solving- requires the player to figure out mission puzzles, such as unlocking hidden doors and getting into a security system allowing the hero safe passage. Stealth- allows the player to avoid involving the hero in outright confrontation, and instead use a variety of stealth moves to knock out and evade enemy soldiers. Some missions will be impossible to complete without using the stealth skills."Incorrect: "Product is a thrilling first-person shooter experience that has to be SEEN to be believed. Nowhere else will you find such AWESOME gameplay and tension!"  Please note that examples given below are only some of the potential sources of pertinent content. If the product has content that seems to match the question being asked, or that is similar to the examples given, be sure to include it even if it is not listed as an example. 4. OVERALL GAME CONTEXT Please provide a description of the theme, setting, plot, and objective(s) of the game. Provide any information that will be helpful in describing the overall context:  FORMTEXT       See attached document 5. VIOLENCE Violence Depicted: Includes depictions involving injury, damage, death, or destruction of the following: Please place an “X” in the correct space. FrequentlyOccasionallyNeverHumansXAnimalsXCreatures/RobotsXVehiclesXObjectsX Describe specific instances of the above forms of violence, and the context in which they occur:  FORMTEXT       See attached document To what extent can the player control the depictions of violence?  FORMTEXT       See attached document Describe how violent acts are rewarded in the game, i.e., is the player rewarded for successfully completing, avoiding, or preventing acts of violence, aggression, injury, or death? Include specific instances of such rewards, and the context in which they occur:  FORMTEXT       See attached document Violent Sound Effects: Includes all sounds associated with violence, injury, pain, or death. Please place an “X” in the correct space. FrequentlyOccasionallyNeverViolent sound effects heard or depictedX Describe specific instances of these sound effects, and the context in which they occur:  FORMTEXT       See attached document Non-adversaries: Includes characters who are non-threatening or non-aggressive to the player, such as bystanders, pedestrians, and teammates. Please place an  X in the correct space. FrequentlyOccasionallyNeverPlayer can injure, damage, kill, or destroy non-adversariesX Describe specific instances of violence against non-adversaries, and the context in which they occur:  FORMTEXT       See attached document What consequences, penalties, or rewards exist for committing violence against non-adversaries?  FORMTEXT       See attached document Blood Effects: Includes all blood-like fluids, regardless of color or how they are depicted, such as puffs, splatters, pooling, or bloodstains. Please place an  X in the correct space. FrequentlyOccasionallyNeverBlood effects depictedX Describe specific instances of blood effects, the context in which they occur, and how long they stay visible:  FORMTEXT       Gore: Includes the mutilation of any organism (e.g., humans, animals, aliens, or other creatures) by dismemberment, decapitation, burning, or other means, and the depiction of burnt body parts, non-identifiable body parts or "gibs", guts, or severed limbs. Please place an “X” in the correct space. FrequentlyOccasionallyNeverGore depictedX Describe specific instances of gore, the context in which they occur, and how long they stay visible:  FORMTEXT       Post-Mortem Damage: Includes any damage that can affect the body of a dead organism (including humans, animals, aliens, or other creatures) by shooting, burning, or other means, utilizing blood effects, gore effects, rag doll physics, etc. Please place an  X in the correct space. FrequentlyOccasionallyNeverPost-mortem damage depictedX Describe specific instances of post-mortem damage, and the context in which they occur:  FORMTEXT       Other: Please indicate whether the following themes, information, or actions are present. Please place an “X” in the correct space. FrequentlyOccasionallyNeverCrude or vulgar actsXActs of discrimination or racismXSexual violence, rape, or attempted rapeXSuicide XTortureX Describe specific instances of all other pertinent content indicated above, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of the other pertinent content indicated above?  FORMTEXT       Please provide any other information that will be helpful in evaluating the overall level of violence in the product:  FORMTEXT       6. SEXUALITY Suggestive Material: Includes anything that could be considered sexually suggestive, including attire, dialogue, sounds, behavior, references, or exaggerated body parts. Please place an  X in the correct space. FrequentlyOccasionallyNeverSexually suggestive material depictedX Describe specific instances of sexually suggestive material, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of sexually suggestive behavior?  FORMTEXT       Sexual Activity: Includes depiction of or reference to any sexual act, including all forms of sexual intercourse, whether explicit or implicit. Please place an  X in the correct space. FrequentlyOccasionallyNeverSexual activity depictedX Describe specific instances of sexual activity (indicating precisely what is seen, heard, or said) and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of sexual activity?  FORMTEXT       Nudity: Includes any full or partially obscured view of male or female genitalia, buttocks, breasts and nipples. Please place an  X in the correct space. FrequentlyOccasionallyNeverNudity depictedX Describe specific instances of nudity, and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree, frequency, and duration of nudity?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of sexuality in the product.  FORMTEXT       7. CONTROLLED SUBSTANCES Tobacco: Includes any use of or reference to any tobacco product, including smoking and chewing tobacco. Please place an “X” in the correct space. FrequentlyOccasionallyNeverTobacco depictedX Describe specific instances of tobacco, and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to tobacco?  FORMTEXT       Alcohol: Includes any use of or reference to any alcoholic product, or other liquid substance with similar properties. Please place an  X in the correct space. FrequentlyOccasionallyNeverAlcohol depictedX Describe specific instances of alcohol, and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to alcohol?  FORMTEXT       Real or Fictional Drugs: Includes use of or reference to any real-world or fictional substance with perception-altering qualities, whether prescribed, over-the-counter, fake, or illegal. Any form of drug paraphernalia should also be listed below. Please place an  X in the correct space. FrequentlyOccasionallyNeverDrugs depictedX Describe specific instances of drugs (indicating which specific drugs are depicted) and the context in which they occur:  FORMTEXT       To what extent can the player control the use of or reference to drugs?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of controlled substances in the product.  FORMTEXT       8. LANGUAGE Dialogue: Includes all language or other form of communication that is potentially offensive, rough, impolite, profane, or otherwise objectionable, including but not limited to swearing, discriminatory remarks, blasphemy, and racial slurs (including relatively mild terms such as "damn" and "hell"). This includes language that can be heard, read, or seen in the background and have double or vernacular meanings. NOTE: ESRB recommends including scripts or dialogue sheets if the product contains pertinent language. Pertinent language must be highlighted in these materials. Please place an “X” in the correct space. FrequentlyOccasionallyNeverPertinent dialogue depictedX Describe specific instances of pertinent language (indicating the specific words or phrases used and their frequency) and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree and frequency of pertinent language? Indicate any specific words or phrases that can be directly or indirectly triggered by the player:  FORMTEXT       Soundtracks: Includes all language in the soundtrack that is potentially offensive, rough, impolite, profane, or otherwise objectionable, including references to sexuality, references to controlled substances, swearing, discriminatory remarks, blasphemy, and racial slurs. This includes words that are edited out of the recording (i.e., "bleeped" out). NOTE: If the soundtrack included any pertinent language, please include a copy of the actual soundtrack and a complete lyric sheet. The lyric sheet must correspond to the order of songs on the soundtrack, and all pertinent content must be highlighted for review. Please place an “X” in the correct space. FrequentlyOccasionallyNeverPertinent languageX Describe specific instances of pertinent language (indicating the specific words or phrases used and their frequency) and the context in which they occur:  FORMTEXT       Edited language (e.g., bleeped, scrambled, etc.): Describe specific instances of edited language (indicating the specific words or phrases edited and their frequency) and the context in which they occur: (*Note that ESRB needs to review all edits.)  FORMTEXT       List any songs on the soundtrack that have previously been labeled with a parental advisory or other similar warning:  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of pertinent language in the product.  FORMTEXT       9. HUMOR Pertinent Humor: Includes gross, scatological, sexual, or racial references and slapstick used for comedic effect. Please place an  X in the correct space. FrequentlyOccasionallyNeverSlapstickXBodily functions depicted, overheard, described, or mentionedXSexual humorXRacial humorXOther pertinent humorX Describe specific instances of pertinent humor (indicating the specific words or phrases used and their frequency), and the context in which they occur:  FORMTEXT       To what extent, if any, can the player control the degree and frequency of pertinent humor?  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of pertinent humor in the product:  FORMTEXT       10. GAMBLING Gambling: Includes any of the following features related to real or simulated gambling: Please place an  X in the correct space. FrequentlyOccasionallyNeverGambling advice or instructionXPlayer can participate in gambling, card, or casino gamesXPlayer can spend or wager real moneyXPlayer can win or redeem real money or prizesX Describe specific instances of gambling games, information, or references and the context in which they occur:  FORMTEXT       Other: Please provide any other information that will be helpful in evaluating the overall level of gambling in the product.  FORMTEXT           PAGE  PAGE 1 ™¢Ï Ú ’ ª ²´ÈÊÌÖØ &Lø'õö  ÂÄØÚÜæè*,@BDNP‚°!Kôõÿ  >``´JL`bdnpjl€‚„ŽýýýýøðøåøýàÞøÖøåøøÎøåøøÆøåøàÞø¾øåøàÞø¶øåøø®øåj¸UjDUjÐUj\UjèUjtU6 B*phÿjUmHnHujU jU5DHIZ[¬­ghÖ™¢ì 3 Ú Ï Ú u v ýýýýýýýýýýýý÷÷äää÷÷÷ & F ÆÐLÿ„$If^„$If>]ˆ]ýýv w x Ž ‘ ’ ª ² &Lø‡……………………………………w$$If–lÖÖ0”ÿ4$ \ tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÖÿÿÖÿÿÖÿÿ4Ö4Ö laöøú'(3@FGNOýý÷îîîO0÷îŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$$If OPRS[\]_ööU4Oööö$If¡$$If–l4Ö”ªÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$_`qstuv€`XZQQQ`8ZQQ $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ƒ„ŒŽ‘”õöW4QöööWOO$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$ õ:>Â*~‚° !KLWdjýýýýýýýýýýýý÷îîî $$Ifa$$Ifjk“•–—˜›ô:`´ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö :>`^`´¶Ìæòýýýýý÷îîî $$Ifa$$If òôlnptv|H¦`ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ¦ªjÈÌÐîðòFH^x„ýýýýýýýýý÷îîî $$Ifa$$If ŽÐîòF¢¤¸º¼ÆÈÞêñ !¸!¹!Ã!Ä!" " ""<"ö#J$†%ˆ%œ%ž% %ª%¬%¶%Ä%8&b&}'~'ˆ'‰'( ( ())0)2)4)>)@)4*6*J*L*N*X*Z*b*|*¦*Ô+(,Š-Œ- -¢-úõóúëúàúõóúØúàúõóúÐúàúõóúÈúàúúÀúàúú¸úàú¶õóú®jìU5jvUjUjŠUjUj UjUmHnHuj,U6 B*phÿ jUD„†´¶¸¼¾Ä¢Ö`pZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ÖÚÞêð ñ !!'!4!:!ýýýýýý÷îîî $$Ifa$$If :!;!I!J!K!M!N!R!¸!"`LZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ""<"ô#ö#J$L$b$|$ˆ$ýýýýý÷îîî $$Ifa$$If ˆ$Š$Â$Ä$Æ$Ê$Ì$Î$Ö$†%`„ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö †%°%´%¶%Ä%7&8&b&c&n&{&&ýýýýýýý÷îîî $$Ifa$$If &‚&—&˜&™&›&œ&½&¾&¿&`hZQQQ`˜ZQQ $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ¿&Á&Â&ë&ì&í&ï&ð&ù&ú&öW¸QöööW8Qö$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$ ú&û&ý&þ&''' ' ' 'ööW4QöööWO$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$ '}'(()D)H)4*^*b*|*¦*Ò+Ô+(,*,@,Z,f,ýýýýýýýýýýýýýý÷îîî $$Ifa$$Iff,h,´,¶,¸,¼,¾,Æ,Š-´-`¬ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ¢-¤-®-°- .¢.¶.¸.º.Ä.Æ.Î.ð.ò/F0Ú1Ü1ð1ò1ô1þ12Ö2Ø2ì2î2ð2ú2ü233è3<4H5J5^5`5b5l5n52646H6J6L6V6X6`6n6^7`7t7v7x7‚7„7Œ7¾7Ð7I8s8÷8ø899: : :¨:úïúúçúïúâàúØúïúúÐúïúâàúÈúïúúÀúïúâú¸úïú¶âàú®úïúj& U5j°Uj:UjÄUjNUjØU6 B*phÿjbUjUmHnHu jUD´-¸- .Ê.Î.ð.ð/ò/F0H0^0x0„0ýýýýýýýý÷îîî $$Ifa$$If „0†0¸0º0¼0À0Â0Ê0Ú12`xZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö 22Ö2333æ3è3<4>4T4n4z4ýýýýýýýý÷îîî $$Ifa$$If z4|4œ4ž4 4¤4¦4¨4°4H5`TZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö H5r5v526\6`6n6p6^7ˆ7Œ7¾7Ð7H8I8s8t88Œ8’8ýýýýýýýýýýýýýýý÷îîî $$Ifa$$If’8“8¤8¥8¦8¨8©8ª8÷8:`XZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ::¨:Ò:Ö:è:Ä;Æ;È;<<4<N<Z<ýýýýýýýýý÷îîî $$Ifa$$If ¨:ª:¾:À:Â:Ì:Î:Ö:è:Æ;<*=,=@=B=D=N=P=ì=î=>>>>>>L>@b@ÈAÊAÞAàAâAìAîA†BˆBœBžB BªB¬B´BÂBÊCÌCàCâCäCîCðCøCDDKFuFUGVG`GaGH H H˜IšI®IúòúçúâàúØúçúúÐúçúâàúÈúçúúÀúçúâú¸úçú¶âàú®úçúúj` U5jê Ujt Ujþ Ujˆ Uj U6 B*phÿjUmHnHujœ U jUBZ<\<~<€<‚<†<ˆ<<*=T=`XZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö T=X=ì=>>L> @@b@d@z@”@ @ýýýýýýýý÷îîî $$Ifa$$If  @¢@À@Â@Ä@È@Ê@Ì@Ô@ÈA`PZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö ÈAòAöA†B°B´BÂBÄBÊCôCøCDD¦E§EJFKFuFvFFŽF”Fýýýýýýýýýýýýýýýýý÷îîî $$Ifa$$If”F•F±F²F³FµF¶FºFUGH`„ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö HH˜IÂIÆIàIDKEKLLMLwLxLƒLL–Lýýýýýýýýýý÷îîî $$Ifa$$If®I°I²I¼I¾IÆIàIMLwLOMPMZM[MN N NNxNPP(P*P,P6P8PQRQTQVQ`QbQjQxQzR|RR’R”RžR R¨RºRÜR¢SöS‹TŒTIUJUTUUUV V VÌVÎVâVäVæVðVòVúVWXXX÷òçòâàòØòçòâòÐòçòòÈòçòâòÀòçò¾âà¾ò¶òçòò®òçòâòjšUj$U5j®Uj8Uj UjL U6 B*phÿjUmHnHu jUjÖ UB–L—LªL«L¬L®L¯L³LOMN``ZQQQ`OOO $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö NNxN€N¶O¸OPFPJPNP]@]D]F]J]L]P]R]V]X]d]f]h]l]n]z]|]~]€]‚]Š]÷òçòåàÞòÖòçòàòÎòçòòòòòÇÄÇĹ²¹¦¹²0JCJaJmHnHu 0JCJaJj0JCJUaJ0J j0JUjüUj†U6 B*phÿ5jUmHnHu jUjU.’Y”YÒYÔYÖYÚYÜY(Z)Z*Z`ZQQQ`üZQQ $$Ifa$$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö *Z,Z-ZRZSZTZVZWZ…Z†ZöW¨QöööWÌQö$IfŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$ †Z‡Z‰ZŠZ‹Z˜ZœZ [\\ööWUUUUUUŸ$$If–l4ÖÖ\”ÿ“Pð|ƒ½  tàÖ0ÿÿÿÿÿÿö6ööÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿÖÿÿÿÿ4Ö4Ö laö $$Ifa$ \"\$\]:]>]B]D]H]J]N]P]T]V]h]j]l]‚]„]†]ˆ]Š]ýýýýýýýýýýýýýôîýôîýýý„h]„h„øÿ„&`#$ 1h°Ð/ °à=!°"°#$%°tDÿÿÿÿText5tDÿÿÿÿText4tDÿÿÿÿText1tDÿÿÿÿText2tDÿÿÿÿText3tDÿÿÿÿText6tDÿÿÿÿText7tDÿÿÿÿText8tDÿÿÿÿText9vDÿÿÿÿText10vDÿÿÿÿText11vDÿÿÿÿText12vDÿÿÿÿText13vDÿÿÿÿText14vDÿÿÿÿText15vDÿÿÿÿText16vDÿÿÿÿText17vDÿÿÿÿText18vDÿÿÿÿText19vDÿÿÿÿText20vDÿÿÿÿText21vDÿÿÿÿText22vDÿÿÿÿText23vDÿÿÿÿText24vDÿÿÿÿText25vDÿÿÿÿText26vDÿÿÿÿText27vDÿÿÿÿText28vDÿÿÿÿText29vDÿÿÿÿText30vDÿÿÿÿText31vDÿÿÿÿText32vDÿÿÿÿText33vDÿÿÿÿText34vDÿÿÿÿText35vDÿÿÿÿText36vDÿÿÿÿText37vDÿÿÿÿText38 i8@ñÿ8 NormalCJ_HaJmH sH tH <A@òÿ¡< Default Paragraph Font, @, Footer  ÆàÀ!&)@¢&  Page Number,", Header  ÆàÀ!G6Ðÿÿÿÿ’ ª Y ƒ † “ ¦ ü ý ' ( 3 @ F G N O P R S [ \ ] _ ` q s t u v  € ƒ „ Œ Ž ‘ ” õ  ! c — Á Ã Ú "#MNYflm•—˜™šö "3²³ÝÞéöüý9:;=>A§ÖØ8gikzûü&'2?EF]^_abeÔîðòøóô*7=>LMNPQU»ÐÒæÂÃíîù  )*+-./3‹ ¢£ªÿ*+6CIJ_`acd…†‡‰Š³´µ·¸ÁÂÃÅÆÎÏÐÒÓÔEZ\ßôölƒ¥;<fgr…†¬­®°±µ,.¢·¹ÊJKuvŽ”•®¯°²³·?TV½ÒÔÜEFpq|‰ ¡¢¤¥¦ªö  k€‚‰Š   1 : š › Å Æ Ñ Þ ä å ö ÷ ø ú û ü I!^!`!ª!¿!Á!Ê!8"9":"d"e"p"}"ƒ"„"•"–"—"™"š"ž"ë"##L#a#c#|#\$]$‡$ˆ$“$ $¦$§$¶$·$¸$º$»$¼$À$:%O%Q%™%®%°%·%¸%;&P&R&^&h&ü'ý' (¡(Ë(Ì(×(ä(ê(ë()) ) ) ))«)À)Â)„*™*›*¨*ü+ý+--/-0-;-H-N-O-b-c-d-f-g-k-...P.T.ï.ð./7/9/;/²/Ç/É/Ð/Ñ/Q0f0h0q0‚0ä0å0111(1.1/191:1;1=1>1|1}1~1€11Ž111’1“1 1¡1¢1¤1¥1»1¼1½1¿1À1Ä1]2r2t2Ð2å2ç2î2ï2l33ƒ33š3é3ê344 4-43444S4T4U4W4X4’4“4”4–4—4¼4½4¾4À4Á4ï4ð4ñ4ó4ô4õ455u5Š5Œ5“5”5 66!686C6D6H6œ0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€«0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€«0€€©0€€©0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€©0€€©0€€©0€€©0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€©0€€©0€€©0€€«0€€™0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€˜0€€š@0€€š@0€€˜@0€€ 0 $$$'Ž¢-¨:®IXŠ]4AMU\cv øO_õj:ò¦„Ö:!"ˆ$†%&¿&ú& 'f,´-„02z4H5’8:Z<T= @ÈA”FH–LNTjTT°T’Y*Z†Z\Š]5789:;<=>?@BCDEFGHIJKLNOPQRSTVWXYZ[]^_`abdefgˆ]6Y e k õ   c o u — £ © ö¨´º8DJÔàæ»ÇÍ‹—EQWßëñlx~#)¢®´?KQ½ÉÏökw}  I!U![!ª!¶!¼!ë"÷"ý"L#X#^#:%F%L%™%¥%«%;&G&M&«)·)½)„**–*.../)///²/¾/Ä/Q0]0c0]2i2o2Ð2Ü2â2l3x3~3u55‡5 666G6Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€Fÿ•€  '!•!ÿ•€ð8ð@ñÿÿÿ€€€÷ð’ðð0ð( ð ððB ðS ð¿Ëÿ ?ðG6ÿÿ&Text5Text4Text1Text2Text3Text6Text7Text8Text9Text10Text11Text12Text13Text14Text15Text16Text17Text18Text19Text20Text21Text22Text23Text24Text25Text26Text27Text28Text29Text30Text31Text32Text33Text34Text35Text36Text37Text38Y õ c — ö©9Õ»‹Eßl¢?½ök I!ª!ë"L#:%™%;&«)„*./²/Q0]2Ð2l3u5 6H6  !"#$%l  v ª »KçΞXò*µRÐ ~ \!½!þ"_#M%¬%N&¾)—*.0/Å/d0p2ã23ˆ56H6CIÔØñ4ò4!6!6#6#6$6$6&6'6)6*6,6-6E6H6±´loñ û GJÑ3Þ3ñ4ó4!6!6#6#6$6$6&6'6)6*6,6-6E6H633333I™¢ÏÚ’ ª Y l m ‚ ( N P Q S ] ^ q r ‚ Ž ‘ õ   c v w Œ — ª « À Nm•–ö  Þý;<§¨»¿ÔØ8KOdk'_`bÔçòNOQ»Îî+,/‹ž£ª+abd‡ˆµ¶¸ÃÄÐÑÔEXßòlg®¯±*¢µv°±³?R½Ðq¢£¦ö k~  Æ ø ù ü I!\!ª!½!e"—"˜"š"ë"þ"L#_#ˆ$¸$¹$¼$:%M%™%¬%;&N&Ì( ) ) )«)¾)„*—*0-d-e-g-../0/;/²/Å/Q0d01;1<1>1~1111‘1¢1£1½1¾1À1]2p2Ð2ã2l334U4V4X4”4•4¾4¿4ñ4ò4u5ˆ5 66 6!6!6#6#6$6$6&6'6)6*6,6-6E6H6ÿÿAll=C:\Documents and Settings\All\Desktop\Transcend_esrb_4-11.docª;¬etD€pÿÿÿÿÿÿÿÿÿh„Є˜þÆÐ^„Ð`„˜þCJOJQJaJo(‡hˆH·ðh„ „˜þÆ ^„ `„˜þOJQJ^Jo(‡hˆHoh„p„˜þÆp^„p`„˜þOJQJo(‡hˆH§ðh„@ „˜þÆ@ ^„@ `„˜þOJQJo(‡hˆH·ðh„„˜þÆ^„`„˜þOJQJ^Jo(‡hˆHoh„à„˜þÆà^„à`„˜þOJQJo(‡hˆH§ðh„°„˜þư^„°`„˜þOJQJo(‡hˆH·ðh„€„˜þÆ€^„€`„˜þOJQJ^Jo(‡hˆHoh„P„˜þÆP^„P`„˜þOJQJo(‡hˆH§ðª;¬eÿÿÿÿÿÿÿÿÆhÄÅ        ™Ïvw' ( 3 @ F G N O P R S [ \ ] _ ` q s t u v  € ƒ „ Œ Ž ‘ MNYflm•—˜™šÝÞéöüý9:;=>&'2?EF]^_ab*7=>LMNPQíîù  )*+-.*+6CIJ_`acd…†‡‰Š³´µ·¸ÁÂÃÅÆÎÏÐÒÓfgr…†¬­®°±uvŽ”•®¯°²³pq|‰ ¡¢¤¥Å Æ Ñ Þ ä å ö ÷ ø ú û d"e"p"}"ƒ"„"•"–"—"™"š"‡$ˆ$“$ $¦$§$¶$·$¸$º$»$Ë(Ì(×(ä(ê(ë()) ) ) )/-0-;-H-N-O-b-c-d-f-g-111(1.1/191:1;1=1>1|1}1~1€11Ž111’1“1 1¡1¢1¤1¥1»1¼1½1¿1À144 4-43444S4T4U4W4X4’4“4”4–4—4¼4½4¾4À4Á4ï4ð4ñ4ó4ô4H6–žžžžž–ž–ž–ž–ž–ž–žžžžž–ž–ž–ž–ž–ž–ž–ž–ž–žžžžž–žžžž–ÿ@€ò4ò4D‘tò4ñ4Ø   ÈÈÈRRRRRRRRR V!V"V#V$V%V&¸)¸*./01j2j3j4‚5G6@@@@ @@@@,@@@@@@@@@"@$@L@@(@*@,@.@0@2@4@6@p@@:@<@>@@@B@ˆ@@H@”@@N@P@R@¨@@V@X@´@@\ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿG‡z €ÿTimes New Roman5€Symbol3& ‡z €ÿArial?5 ‡z €ÿCourier New;€Wingdings"1ˆðÐhø˜Fâë˜& #Ô¢,_ùu-`Á"ð´´0dÏ6n5 2ƒQðÜHXÿÿ)Instructions on Completing Sections 4-11: AlienwareAllþÿà…ŸòùOh«‘+'³Ù0˜ÌØìø $0 L X dpx€ˆä*Instructions on Completing Sections 4-11:rdnst Alienwarenslielie Normal.dotsAll11Microsoft Word 9.0p@’²ã@ƒ—Å@ìüجÅÔ¢,þÿÕÍÕœ.“—+,ù®0 hp|„Œ” œ¤¬´ ¼ òäu_Ï6 *Instructions on Completing Sections 4-11: Title  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghþÿÿÿjklmnopqþÿÿÿstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜þÿÿÿš›œžŸ þÿÿÿ¢£¤¥¦§¨þÿÿÿýÿÿÿýÿÿÿ¬þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀFp©Ù¬Å®€Data ÿÿÿÿÿÿÿÿÿÿÿÿir1TableÿÿÿÿÿÿÿÿrùLWordDocumentÿÿÿÿ"ÐSummaryInformation(ÿÿÿÿÿÿÿÿÿÿÿÿ™DocumentSummaryInformation8ÿÿÿÿÿÿÿÿ¡CompObjÿÿÿÿjObjectPoolÿÿÿÿÿÿÿÿÿÿÿÿp©Ù¬Åp©Ù¬Åþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ ÿÿÿÿ ÀFMicrosoft Word Document MSWordDocWord.Document.8ô9²qtranscend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/esrb_4-11.tex0000640000175000017500000000724210304632471024675 0ustar pabspabs\documentclass[12pt]{article} \usepackage{fullpage,times} \begin{document} \title{ESRB Qualitative Information} \maketitle {\bf Game:} Transcend {\bf Author:} Jason Rohrer \setcounter{section}{3} \section{OVERALL GAME CONTEXT} Transcend is an abstract shooting game with geometric, morphing graphics. There are three levels. In each level, the player guides a ship-like glyph around a grid. Minor anti-glyphs (enemy objects) attack the player's glyph by shooting at it. When the player's glyph is hit by an enemy projectile, it is knocked toward the center of the grid. The player can increase the power of her projectiles by gathering elements from around the grid and bringing them to the center. The elements form a visual collage, and each element also represents a section of music. Thus, while powering up her projectiles, the player is also arranging a piece of music that plays in the background. Anti-glyphs also attack the player's collage by shooting at it, and these attacks push elements away from the center. The player can defend her glyph and collage against enemy attacks by firing projectiles at the minor enemies. These weak enemies can be destroyed with a single shot. The player's goal in each level is to destroy the major anti-glyph, a large enemy object that circles around the outskirts of the grid. When the player approaches the major anti-glyph, the anti-glyph attacks the player's glyph with powerful projectiles. The player must shoot the major anti-glyph many times in succession to destroy it. After the major anti-glyph has been destroyed, a portal opens that allows the player to move on to the next level. \section{VIOLENCE} \subsection{Violence Depicted} {\bf Describe specific instances of the above forms of violence, and the context in which they occur:} \\ \\ The player shoots projectiles at abstract enemy entities. These entities are controlled by the computer and behave autonomously, so they are best described as ``robots'' and not as ``objects.'' However, they do not represent robots specifically, but instead represent an abstract enemy force. When enemy entities are destroyed, they morph into geometrical explosion displays and then fade away. \\ \\ {\bf To what extent can the player control the depictions of violence?} \\ \\ The player hits the SPACE bar to fire each projectile and aims projectiles using the ARROW keys. Thus, the player completely controls each act of violence. However, the depiction of the destruction (in other words, the explosion graphics that are displayed in each case) are controlled by the computer. \\ \\ {\bf Describe how violent acts are rewarded in the game, i.e., is the player rewarded for successfully completing, avoiding, or preventing acts of violence, aggression, injury, or death? Include specific instances of such rewards, and the context in which they occur:} \\ \\ The player is rewarded indirectly for destroying minor anti-glyphs because the player gets a break from enemy attacks until the next wave of anti-glyphs appears. In other words, it is difficult to build an element collage while anti-glyphs are attacking, so the player will benefit from destroying these minor enemies. The player is rewarded directly for destroying the major anti-glyph by gaining access to the next level. \subsection{Violent Sound Effects} {\bf Describe specific instances of these sound effects, and the context in which they occur:} \\ \\ Each projectile fired is accompanied by a musical note. Each enemy explosion is also accompanied by a musical sound. These sound effects are abstract and do not directly represent the violent acts that they accompany. \end{document} transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen1.png0000640000175000017500000023277610304371203024631 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕÙ\|U IDATxœìÝw|ÅÙðç™™mWÔ‹%Y’%7\pÃÓL -$$$@h¡%oBBz)ï B é”Сw.ØàÞe[]Vו½ÛÝ™çýãdã 2ÈòÉžïGût·w7·å§Ùgçvñª_,‚½od‰›Ý!x¯+)°`sWj_7dG9&tõyûº!Ûɉè‹ûº!ÛÉ èKdW«r#z³l^åšÐÑ›]ëÕˆBZ;³k3Yê@c[vEVmEêšœžíÍÆhš¦iûŒÎwMÓ´ý“ÎwMÓ´ý“ÎwMÓ´ý“ÎwMÓ´ý“Ø× дýîëhè|ÏNÙ™ÙÙ*MÓvç€Î÷ì ¬ìl™nÕÀeg«´Ìïšv@Á¬ü«“•ÊÒVí)ïš¶оn€¦ Y¾ï 5MÓ†Ý×4mŸÊÎÞ_V¶jO+l:ßµá-+7CÀ¬l—.h†(ß)×öl͆lmWvf–¦i»£¿¿ªiš¶Òõm˜ËΊìl•v€9 ó={·Áìm™¦iÃÆiÃÈž!×õwM;P "fßwX[:RPVdïë†l§¡ÕE€ªξnÈvêP;2<Àéu¾kÃfáÀžî˜Qc_7d;íÝi(ηöuC´!¢ë3Ù(  ôAMn†¨ÿÞØæ"@eivíìd.Ùž¹|»¦iÚ~F×g4MÓöOº>£i{‡®giûšÎ÷¬”Ñ­ÒöÙ¹jee«ö¨Qz¾gåÔ4Mz¾kÃ]Öþ…ÎÎAPÚEç»6P:°4mxÂ|ÏÎpÈÎViÚÞ‘}__ÈÖ­0;[µGôøHMÓ´ý“ÎwMÓ´ý“®¿kÚÞ±ìÞkÃÜÐå»^Ûµ½B¯Xš¶ºÿž•²3³²²UYÙ(€,n˜vàÐùž²3²³Uš¦íŽÎwMÓ´½«¦"´ã×Gp‡ÿ·C»ïN™À˜Ê/ñ±Ëéu¾kšöQ”ÚÚ§ûÀ Ëà  ¢dgêÞݳ¶½ŸvLÎ=HÒøÚžà£ÊC»yüCZµ·vxû¿Â0 Kõé|×´l÷W\ÚÓï+1D(-Øí5p»ÿvóè`ë¿j íéåE÷.Ü“$ý`š’ÿE22=÷u ‰L¬ó]ÓÞW”kÖKeÒ¡8oO.†·÷q b¶t¦áU`KϽió ½à ÈôÜ76Z4=ïÚ>P8x1º—^ðãÄ$Í\7uPdzîm]Ù•¤Ú^¢ó}Дڻݖ·y€ÐGË$CIÁ‡týh·¿ íòæ‡<'Û’tp÷ì;z½Áy¡-=÷öžA‹fMÛSÃ2ß+KÚM"íîÞÝmþ ÊŠöä¬{¿Hˆ˜}åH@ŒVu^ŒfþÞ â jÚ~CÔV„w·ÅÒ.nm±s'”vøG™CäU#vq‰mÚÍ3·½÷NÖ â!”A,G–Ú°¹+»º~Eyè$Õ´ácÏO0xIÚ°Ùýø/’Q^l@K‡.Gjš¶¿uMgóñU—… ¾uТYÓ4MûúüÀš¦iû§ay|UÓ´&;O"”­Úè|׆7 š¶;:ßµÒIºg²s~éVHt¾kÜŽí@²Gë»Î÷l”¥‘•¥ÍÒ†¿ìú&ßþã@ÏwYš¦í¯ô|ׯiÚþJ__[ÞôzµGôìöödðýwMÛ;t’œžW{‰þþª¦iÚþiûïúo´v@Ñ+¼¶¯éúŒ6ÌíéH5m`öƒKç{6ÊÒ+K›¥í‰ì\ˆÙÙªáï@Ïw½^iš¶¿ÒÇWµBýçð£!_Û×MÐDzÿ=KéþÛŸÏu¥a3ÓæÀ½7“¸òæ7öuÓ´amÒA÷ß5mð¾iÆ^”Šz#'˜3-€0€±¯¦íÄ,c«"™Xç»¶ÿ+H ]—ùªù‹ßøŠXé)Vߢ¤5,vçG‡ôþònecñoCSj*Âûº!^ß´ý_—}L)¼é8|£{Ä^z‹Óf-v]™JJ7I¢@‘T´¹IÄ;‹GŽŒ›v: èÕgJÔ†ÎwmxÈtnüußán·“ÏWrÃZ×61¯45X 8ÿôå®Ûë¾_Q  PÔÐ{"zó§Îèíj/mm- èþ»6Äôú–²nO0ûj‘°¹+]R`•X›»Ò»›Æ°X?ÊI-&4®l­05 £ A¦A–¥G†C2 Ö4›ëK¾°ÚuU*ÙèýÿøŠmœ‘NL×kXŒqã¦ï\P ôö¦ ­}}ìÌÒæŽTy‘]^d7w Z7óãkëL•Ú¥…v[gµj “å{s6¸Ç—†¨ìt¾¹¥|bmƒiòµ›Fl;å̃’y¹A~¾WPèßõ÷²Ý½à7®®s“ÒMÂ}öÌ¿«š8Z=c«7&7-6fÂæ@0ò=W÷,6Üg×P®oÙ8¯6»•%Ne‰Ó°ÙÝ×mÑöÃb ÊMc !PTQÓê„x(Ä'ÍhsBì_OŒïícÐgÎÌ ¶>ëû×m€Ÿÿ®zçÜ!Ѓm"~U{,m–ŽˆT˜f½i³ÌO¦ø.íëú{6n…ÙÚªáoèÆÏllNÀ¨òa|0Z¦  ‹…iIGj¼/·„²$_’¯(páyk¿vQ¼³*ô⼜‡Ÿ-ºëïeñ‹'v½;õÙ3÷¬Ž×ù¶*0§€a3ÓBÃb—œ~u ` €;oðÛ+>àkN¦Å„‰†Å “‘D^Á:hl:tK¸“/û#;s¸uëÝswÊ÷¯}cCfLäÒiW(Š0&–(ßê[qÀØÉï˜Ëü\pè×¶vÞC[Â}ßæ{Vý…Ö>$Àùt¾g)½Äi—üŠÿyÛå»›F˜Ì0™a¢a¡é3¢}™û7ÔЖ.¼$_‚/éÙ{µõ‰©4K¥wÜ@¶0£‰@JÓO;˜©«ï¢óÞ?xfëñU:ùˆ¾ÁŸÚÞ0Ì·CïÃ} è’&@àK÷\³ó£Ÿ=u•a¡a2a1Ãdãó&˜Ñsôí™Gû{î’|Õ?Prö…ßÞúÜY£~0kÔvxÁmÌ(‚ž Ž|#ÝpàøCeÎiXì샿ºmñ=î’(,ÜíWm5mé|׆·S/ù½ (xýž«Ÿºçêm&3,4 &,fšÌÎï´ó:3nX=]Jòøòý”¯±¯Ê<6Ö‡õ;¼Ý6ÃÞ‰”4¼T€ðMÿ³ý‡U3Ã"qKçßÏ÷ÌÏ쩟¼ú+M{}ÖhÛpïüé|׆½$P(±°à¹m"Þ0Ù¶oXÌÎëpòÛ«kf&X½dj Hf±* Á–ñ‹ÿ|ëátšýÏ™gn}µ/}sÃ6c"A()d`!L8ò½þÊÌN#gB[*3[K4ÿû犭/ûêß¿?³)#;+;[5üé|×ö@vn†Ÿ¿þ.@’¨pKÄ¿¼%âû«Z(L4,&{ã¯kßøË:ÃzͶŒ¢‘’|IÈ(ÜvxK{˜öÞÿÄ[+3™ã±ÝñN/‚¾ƒ·V5mö©ê/m­¼o©Ï¼îb›£ÁÏ>pWj¨f•v`Ñù®í\€@:ñˆEoÜsõï½Ú0Yfpdÿ(/üì­¦oÿç©o}îê…SúÒ($YÒdŒÀó±©s’ç±³f|/3e@ÛE¼—t”@9³õ°êv÷-U±ýÏV)Tز³ß°Ú|׋qàô¼ÚI€$  €ŠŠŠ ‰®;ï&±%â «˜éÖŠåçn}úò·&gÆÏ FþšŠÚDnë¾i{>ú~ÿfâ+:zôÅ™ˆ_ºj’‚§j2#gÊǵÎpE¦2sʈ/fjî¡í¯ò†À?wïµ6)Pr§ô>Út Î(½ZPtÿ]¯òÃÂEßøcÄp€BÀ"„"@·Ñþîy7]{ÁÍ™B°˜a`€ |3$­…Oýaëkø™3Ž)ò çÊóÙ«+¼©}J¾³â¤ƒ~eˆ“¿ê³'«sS9I€™â;7˜0·;[dx›úL¦þž9¿˜±%ß¿ë ‡TˆJÿ±»¯Ü퇫~yoÏ?m¥¿¿š¥²t^ei³Üþr€BÀB„"D·Ñ.fxÃ6/IF`¬¥ ß}ò¶7ÿ5,u’¯úÇÂsbŒ‘¹é'A€J¢ç£°³¿¹!PÄ,3ÉYL±Ûïþ5I@XvP;çØÖR½æÝbÃbïWÞñý*Í6õ÷þSЄH9JÙD $î¦ o4fç];Ðèþ{6G–6 ™%xÖ%ÿ›‰ø$Q 'ñE©F«ˆÁ/¸)ñ_ýîç%PبK3YœóèÍï‹ÕVŘgä˜ö¸ç1?`™1‘h˜`½Šz%ÌZ»„M¾¦uyGóƨ$ãÝçN¿5ÖCgÿýÉöáŽ$žø¿‹¢È•$¹‹|tÃi<Ч3Ð>:½öh{ Ëÿ&’€¡LÄ# Ð×äW¸7]pSÑÿ|cR„ÊÁok#º#ßzøg?˜öÉUBQÁyWv>ùÆUQËO¼2«±{gÄ—ÝÔ| S¦1 O ææAÏ 4_˜z²’4fòFÛf; ›É•)»¿ßs‰’±EDa%ÃŒI¥P»ì¿“å¥Ê_ò¹¨íc+úXð–I>à ¦É`|ut‡ûw~†Îwmÿqæ%ÿ÷ò=_wSý…,4L@(ñ†‚Ÿåæâªr (–GK¼¾>ÍL–¼ýà^ø‡ã£_ñçJ1Z°ñÆ©U¿Ê /ödM …Å… E~,›(tÂ{¯=7a,ý첟Vl9pæ_/ üý3¿Ó‘¿Grcí/UˆTˆXˆ(®ÊvBSÐrb[ýå—Ž€ïüåÕ!œÃUMÅvÚm¦â> †@=r·W%Â|½ÔÚrUÑ?Îwm¿â%’€!)´%â @¨·É)ªp…¢T@ *e2˜ˆˆˆw^ùûkî½Þ”ã¨*;~”‡ŒQc|IÆÊ˜ešFÚU†'QX¢`ŒNZ¹qÉ‘‡íPs?êô=ðV¿ßs—„ä!¢0©Pˆ(\”Tžïòë—­# ·ß3Êê>ã;ŒËþÍóGçŸ$ƒ PvE‰½ÃçÝÓá  ²Äùàɶíh~@Š ¨?:€iGUúðIwùíizé&é‡ZÛøø/’‘鹯ÞûÐ)u¾kû•3.½ý•{®r’€I€P°0S¨Q]˜=u9NqzÖy7<ù‡ÛçãϼÀDƒÛª Mƒ–¤™žûúÆA{Á!¦ó]Û7Ê‹vìŠ~d™p(+²ય®<â& ”0¯¿^Ì‚>‹ÅbÝØ´®-wCa'”„¥¼0±´Hùá%]ãžG:/¿3FÄ”¿ÚõÇ H§…ÿ¾1ò‰_ 0eŠS:àäs.ÈSò ½óÄ¥‡úµ›šWŸÙÝî;ü߃ȟ X~÷»#zuSàûPòœ€;@„RùRRù2éJ@’Â+K#ú[:ïçÌ8Éó0åGš6§>þ¼Êôܳíjò™žûÆæä¾nÈþFçû`ª,Ýýnï6]“\1Š!ÀÎûã Rw-ÓªAŒæAÑßûÛò!_˜ÿ?—žþ³d‡p] `!xŠª7b¥kì_æÔ<áhƒ£-ƒü¤SÉdàqœÈX¿`ãøi¥Ø¸®Ø“ $’d$f˜¾’ÌB`’á$’‚#$AIÂDn²q‡=³äåcûB£$A_òg$áh¹ü›“d€ï-¥ƒh£r‰È””nR‘$’{gTמºdöY‚õö$rï|驘ßÏùÔÏûžØûsZf†k¾*hoŽvø8¨ñ!IÚí/{Å~¿ÝÜ1½ÑŒLϽ¥#uÅ[ššìhŽçåå§L =Gä „ò(êQg“yQEÁM'³ü­×'z‘HWNÈÈ催Û%ÉS݆¨â *&ÇRËêüÚ±ÖØSSKŸ^<µeäâB[8©@ž #žT`$QIn2a¢°pö›ä_d /$I B’BH!TÉ̵_¥d}¦ñ?×sÅA±‘ŸúYµ\”J ›cÚßõ•`3¾ùŒ§œë£_þmìÁš¥Úþá#æû.(oÝèw×=ÍE©­ØíÁhÚá…vóúƒk3«¡mÐv{G–:0(»äƒ¨¼Ø†AæAtöI]m­Ö£/ÀÁŸ¼wÍ“—Ø€ùyž­03]°b†}‚>I>«ôÒkÇMØØ²ªÚ¦„Ÿ« ®”í‚#ÄNƒJùÐ½Ž›% ¢Í©ºâ†ßS~”pcœ2Ä4àʰPX(Lô_º_XàŠ©] ÍÜqîê[/f€}qda©TŠb ü@*’AIWºOþ,â’“ä®+kÃ? =ÉTÎ}oí¶o~cèl¬€¬!œÙÚð ÆV†²ó>(Ù:ˆIº±yÐŽxT—… ¾5»*’ÚžÚÜanûk]›Y„hçù6@~Ä'?ž €#¥¨ P´9÷K²î月;bt}¼®Òò$æp–<éûŒérB”†‘. •·­\Ûß70fdØ ‘EJ‚X/2Ä”àÀMä&2“¡L§}F…*öÎõÅŸ^n@à0×Dd P“’PÂŧ^~ÛÿX(™5ûGIW¥ f0$›TüöòöY;äï:ŸõÀöÉ ÀôѺ(üíû¿ªù­ ±ÿþÑ(gzýuƒwt[Ó2F䯻:Í ïGü©—ÞñúŸ®lè1\;ކ*„ñ"ˆ&ûŽãæëOñ;åA¸²Á¯¯T"`JL_»ÖIÁê†Êª¸". ÃG¥V¯ï>dц%U£6VsSq¤º…L1ô D¸Ü`ÂÀe‹óJœ†n/U[‘|lá=‹ï¸¤‡ˆQU-yÈyÉ”‚¿<·|…X¦ÄïÁº¾Sjnï ¤ J&U=gCêèm?òw¬óûµ,%qT¦¦í¤{{ºŽ÷ Ý-ßhm î@¹Læ@¬ãؼY¼ySH¨„Ëœò¯y¤/±È„ÊœXKO8MIîG@Ä C"J‚R¤|ë¥ñ,Œå£[9ˆq4yÊC#`蛌©¿s}É’I»'ù×3Þ±ÁM@K9bðd(!iôsmŸ$zêWüg<¸4QRâìŠ;R)¶)ÿyûÇ‚ |Dg‰| $m6€Ìóû{îhúdù``ú`=äÞ8Ôs_Ëbúü3ÚðA–H¬«/[w͕믹ºîÚë6d:åÒ;\ê?ãXšaB”Byï¾5|èi9Æ÷)ð¡eMi*…±â&OJ/ù<iÏçä½±™2 Ÿ!)‚¸ëNX°:ÂçBzú„¤i{õ-#%qdŒ—÷-& ä&>þÇ£Bv耊<˜‰ŒQÒò(J! ‘â2I¸’v-,J¾M~huÍS¾ŸçË\_†]Ïé–»hêÊö“6óÙ™pÿ¶ñù «%‘Dä ‘ã¥—¯ŽÈqÙs®Ûà¸@.’˰ó»—óÖ W®—ÏÚø>>ûNÑÓ;FoÙ¼jVµo.mÌm++.§Ç³™k»LI4„̪.±¬oS‡ßÃ8Oåæ´¬½|ÚÆMµ§.hAdÈY 猛( V¿+‰¤3û ÚMǾhù}Ì(=ë0J*JJÈÀþZLzðÎð¢ Ú!™J(ÅÔ‚¯‘JFI êb×2îmPÔÖœk%”“÷Èöyn@¦––¦Ö¿½4¿²üBYf¸Ï-Ý׆±t·5¾fó¸I ‰„L$d"¡I™HÊDR%’2‘¿ü÷÷r*’vqïì¿^6þ¾K :—cCýçÝ=î³sNÿAàSÙØV¨/öR°>¿5íQªR+êŠÚ$›1@T¤IRgÄȧžUðÉÓ•3Æ4 T¬±­Œ¹}áë¥dR±pˆ‡Ã,â¡àÌ.¯pÏãlí°{Ðc á®`t¾g©ì‘vó•» ‚ç¥3#—+ÎÄ /§Z)^äý^*ž3æá0ïìa»vtïiñû=²<²<²·ÞxÔÿÖ¾^ÚÐðv¨ë3Ù*»¾Ø´EÖ¤{™Ñìy0fbk"È8 ÏÜ@d€§ÿñ¿$¸âþY‡Á½ Æ—\p0#fDúêþLÍ{OÁc«®½?ó”‡ô»qÑoðæýöë¦Ãš¿÷Ã¥å›iγȾÈ[ÒÐ=¡Š8¡i!‚οæRHc9"̰˜uù±ËŽ% f'*â©H(â=ŒA8Ìqô)‡ÔÆ\¥<È{•f|§"iw1Ï@â D{a7P^Þø«ƒ¿€pˆ“d抆1°_xhgj2™£©>Xߨ׋b¿–5+üG£ó}¸/ÁÎôŠyÀ¡¡¡&’ 9œø»ûŠÌ -ü %„2 ¡ B)AJ) €äè¾·úsÓÌhÈ‘©ØÊ{~\ýÅÇ3¾»®îyÈ{xÈO§­šÛ¾_÷òmö.úo ÷á§ –5vŒ«ª-.ÜÔÖ¼`ZÕvâ'ÿ}htóê…ãË-Œq`>{ÇQ0n܆TJxɈ¿)gÄQŒ1ág\€Ç+ðפoG›¸äŒ„ÞìôǪg^ßµô6 ž?þ:D V-)_ÓTùýÒ/y`m;TÆë yý¾^ZVÓõ™,+:l•­Ú×Ææ¼™HÈùs+‚ ]XÒ:ûæÿ;âæÛcžïÉD~°(ì-‰{~Ü÷c^÷ý¸çÇ|?îùÝqò0ðÀ¯|paMÎIÒwê9’$š]ýõú„*ýÊçü´H<ÿ„””J3/áJû•\*i”á 2±dEcÔìA"–H”¨Ç½ü¼ nN92†L0abYEã¨Úz. ¡±¤¯=ܱ¡`Þµ¿íj ­4î‹„r^xTQ, á0ù©ûU¨‡1$qBùãÂaFĉx8ÄÃaþäÓ‡­iyCÑE>Ø88ý7ÈyBép×>„Îwmx(S¯•Á뉄lnvåu÷©¥+¢_ò5 ¶!=I‚¯To÷VÌ“q?hj-hj)hj*hh,¨ßT’èÌõYá?0N Ra_82€eøÓ¼ÆÏ½ÜÜ{Æ5À¯¡|ûßRÒØOœâI±âÍ×_üòi<0™ #ëc@HŠ‘2zûF<«/YÛÈ82Žï>TžiêòçIÅ•'X+ÖQÃᮑësc=&óˆ¥UñUá>N\È×½AÀ=óºp˜pB³G›n[êºèW}²=²=²}°=²=°Ÿ¤k÷íâІïZöº ôãóœ›Î¶o€6»Žm–ÇÆCÓ‘µí©ÉɃ毘úí£~~Ëiß±xéÚø)€yŒr^NrYRaR¡+1%1¥põŠiP`0âÙy•¹G6üé  RXºí;ÆZÊ”D? @ÒÄÓ°½p~ IDATVÊ[»à¡LŽ\IJ3vŽ‘RŠÍßà’2ÿÊÎãнÉA¤ñçyëª$qIœ@Ió´oþå‘ á°¼Ò¼ÌGÛÛ§?åÁù]³†6 é|ײÔ%áë-p-p-Hžkýâ3æo ÄµD½V¯\ñê´Qo刵c#ÈúÛ>y–ênòfoLnp'"Òãì×!ó]â)à+—–+¥ Ê„‚—TMWfŽ¢Àoº¡½§­½««½3¶V\Úº®4˜ó8¢ ¨&æL™[ú|kBxŒ îQÏ•âJ™¤ÌšÄ×_m‹µ`:)^ýmí ××IÅ¥âD‚Hdúï1ß Mw2Ñ×ÊùõœÇR*(&J†’ÏëìJÆE]ѳwÜ9z]]κºœ;ï[U9‘ Õ?ÒÞRŸ±Ÿ‚¯ï<¯þpËñ¿üùÑ;ß¿3Ä]Piˆébä^¢ó=Keç?d­º6òU \’&¸[Î1udèÝÍBwrØäqÉ)µÁä‘&ÇÖ{Î=<’×’S\×cÆD^ÔtÇF^~±¬¬5Tú–(˜§r®XÓ®Biéøí3#Õ0ésÒÈ…‹Ã½wdÞ7ê4„›n‹*@åI1ú¸S9å/¯r™ÁÑàhU¤Õ%e(e–<ðV²ÛÂd’µ·3ðSjìñ]A²^¡06=JŠu½å´«Þ¬šÜªæKÖ=ºÍ­’܃ÀP²yqé³™¡¼)3ŸÆ¿HÞíübb¥——œÚyƒ¶ýÅ™ÇÙõOãU;Ï«[ÿpjbÜ2v~HÛ/ |3Ôù®e£L²[˜IöTGž\“Üjîoa(ÄC9Ì ñç,š\8æ¾Ïð’2#o³[a£ 1Ï7’\1!âPŽ#£Ñ ©®Ï´Èp(gþâ©Ó+8)0Œ¼â‚‚âÂh(T™¨úŽç‡Zî]˜L…}ŸI‚Ù&€o¾º1 ÌÀ&bIÅòWõú–ÁÉS¾Öמ¿xýæÞX1øi¬jz‚@DË»ŠFõžp霆Ÿî¬oo…•«šÓe>™À|@ÉÒ)é¥(å"è&¤ål~ô´xA¡wøºd¨z`{è<Á¯Ûy.ýþÏçÞüÇO÷(.,—™æÎhSvv³ŽvÒeCöf9&t÷ùCöŽ‘5 '–]­Ê‰ЗöuC¶“€{½U?Î9ƒB FÄ@1 … *H6‹»r&&3Mf˜8©,ƃޚ½húR# qŸÜ’Bä„•S:./šŸ——ë~úÄð¬b~P90°¦‘\ö^®¿Ò®©Ÿùå²Q‡wšÕ]Ë##М\ÝÛlÎé™ÄãëÓñúxjlVFŒº±¢KMOJ{¦•~Êí¢8r£;tHÒõ6<ûnñ¸Ö“Ê£Õ­Þš¥ÐmljjêÙ.Ï]ïØÞøC]„S"òZ“+%›aþ«ìâøÊ§1³pâÑ=>Óñç®dÈGÛGËGû)±‹‚û-¿0æK NÀ^!B7\ýâ@æmnÄ€Þxv­Wù9`ÖmƒP˜k@W–EVQž=Þ‡N©Ç¿kYçÇ}OgnÜ:‡AÀ!``2¼ÿ' ``u÷æ*]^q.ðþ3Ïd¾å4)}ÅÒ/À'þór‚Û±½8 ç®·9çØ Cˆ#¤ Ìp7qFNÈJ¡ÕÞæO=1åsg?{K À·Ãõ÷ÂÛ+¬ÓgJ.úü¬3î›{JµóòæÔËÔ1e¹é4òŠ_ìf ‹ #Ç^óæò§Ö¥Ó”ò¡ó¡½”€¢(5ÄÚ¢%§¿Ý²’§¸Êôñ¥6$Ÿ]Pø\Jð}$…ï-ª¸.tïJeX9­Œ÷ÁzZìX“ùíÓW&“^¯Bκ9çðCÂWÛ'e©K当=°ŸöJv˜'¿~þº^ÓÔG¸9…éͪ¦+˜©/Þ¤í‚î¿kÙ37¾).d H&‡€CÕÚ¶lg↉õSÎÍœ?2'?ÙÝiÙ!¯ñê9é„uæ×6>u¬«7ÝÑT0åжןª9ýÓÿ¼­`ô!ç½—ö Vµ’¡Öº?¨½4óvžË£¥®Ûâòùe(+‡e¤ø COÎ¥u݉ñëH–÷XkÛ)·5QQ·jâÚEµ±†&OÅ G›!0 Q4n<Ÿp²yËÒ;ã½”¯¾4ÃP DŸh nÏ.® Òf¼ëS^ã-ïAîÅ}~Ä0kèì)z°ù f—…û¯ÐðÛy?J&Ó‰ áC_@I‰é&lŽ!¹ fë*±¶·èþ{¶îGvö‚ßý}pŸµC/ž\C%»Ú¢&z£—?T»òQ'Äb½v¬ÇN$‹ "Vœz‰Ÿ’]“ßyÉ[òVÁ#ß›ËÚÿ šf˜,òÅ«œ‚ø$\-L·iÝß—Òq˜pé˜Hn·÷úË›^­xgLñ?§Lï0£›‚² Ë=ÊZÝáÏoIK#^TÙIÒÿ¤ÕÐu×Ï—Äc²Üq{”b~íÁå.?bZ„äÄ|Ÿ=^'ÑÇ“F”ƒâ Š×¦*£Œ0›È0Ÿ’Ÿ1L³lT¸ÿjÑM½>$ƒ>W%]J%6úã™!˜)˜e2K\Ý;²s<é€ i¾ëȸìœWH€ûúÄg·É¿üAÝ{ݱet¼kk»i½eRÚTé¼W,™÷da‰©Õä>üÏé=fôЫGŸñÅœÜÄæ†Î¾N³·ËèYýlG‹ñòí3š—¥¶’h³ê¼\7¯|ÙaŸx»¨4¶ƒ‘5 !’¡ ›S¹¹?\vµ0ÔäC6V­ñríty´«ÐŠæ¶ ¡B¦Ïâ›ròñ“¾W÷)ˆL›¡ˆÕjz¡û ‹úŒ}†>£!@P¤xjÌ­~Çu†S|m>†2M2͹eÇ–™H½ÍÕ¸—HúÝI™ˆ+hökåŸhf˜Ì4¾{öŸ8÷³/°³q}ötÿ]®þî¾n7É5”k¨¤If¼3€”_”ã5üþ™ ¿yáŽßÕÞq[ ¼öÊ„ùsÇ@áá—9!—üuqû|à±g¨÷eÆ!˜ý #’®JÎáùñ—ÃI¥gÆl«À+p_™~ñÞëã„™ÎÉïÜ7„ÔÄð¬ a!H`UŒî<{–îÆæ@4—¾±Œ •âEª(‰„L¡‰øÈê€ö¹R˲{íPwù„ÿ"ãÕ±Mdšd™`Y Gß´~b_lFym罺÷’AÜ•ñ±VUG#‚ þ:ï›Z’°åšò»sçÇÜ}ã1÷ÞxÌŸo<ö£.œ=“¹&smExhÞîÀ¡ëïÚðv;ûãÈBNk¾L’Lö ÖÚ"LpÉÓÓw`>o]’™xÉ{£@•_Ko/†Åen´¨V/¬›re^Û»ÔÛk_pcÁs]úî«/žðéîË>_ÄUž­õް’ÕS7vŸ6rä;k ÃÂ&OTn†ØÌ–µà˜SŽŽù4¢sµYÒóËnÅ“æÁhø»:.Xv·*¸BR(=@E¨ˆ)Æ'Îæ½ZÕ½éäÉ£.€KG"èéž$¸€‚¶ï›"‹º>Yé“èReii$à @˜õQziwÝx$à€`›È*’WÜüúà-(mÐù®í‰ìÛµ€ÆÍîÈç™ò»z{º¾øNÁÈî".Š \ ƒ¿÷ÇgìË·NßÚVèŠ3Ëü»ÁìˆÅ7T{r¢ií»ÿ2ÃOȃŠót±ïÎW6rŽ5}JðÜÒž—Î "â,¾êˆ*¯wÝ“¯œd:ÌŒ †ˆÀ =±pêqÓÍ[涯ÌW˽¬Ì³]þ9 1RýkYÏ­¶˜Ë9çï|ús•÷ßMœ“m!«ÆÍvÖ¼3Þ4eaúGf“´°ÎfAÌœÕI*M22Œ˜2!3ˆ™ÈL¾ËÙrçwã€"“Ú¸c”Wnÿ«ä   àË7¿1TKOÛcßu¾Cvþê[ݪ¡ª¡úÖä¾nËûÚÜÊR§²Ôihs÷Þ»üòKÇÉ ð|ˆ%£é4÷|ö§×žÈ“ ã¿5ß:èÈõÓ_|˜$Ô˜ uW‰ï*.Ö*0XðB)0«iåéùynû¢7׿þJőǥ778‚%ú’¡¨Y>±¬ÕÄú7­œRg$N™J¯-í¾™„± ’ÊÝĘÁ0™\`æZ¯ÌÀThI¨°'¾!Œž:åü9v.Rµ1‚´à 5«%â¼±vàWœsÞóç{ˆ‰Ó¯Y>í$$pˆ «£:ÜóspÀ”ëS¹ÇJîmv®™@·¦ÓÈ<¤°ðƉ â&g6»ÿ»3·í€ ŽX…¸í;tÒ·fzæË½@]—Ý÷}æÜ÷¹ð±;`sS_ÒZ=e›Ä·Ž¾ƒ™¯ü¸.€ rÔsO+.ÞH}aÓÊÎs¾á¼÷zÇè³g¬}zQó¢—Ç>KY§é>ûVýf•ŽHp§dÑËÓ'æS1vÞ™wðí®®ÉL À0A«'M˜P¿žØ÷ì2²Fµ%®/Mœ0;ÿÕŸŠ#1DŒƒBFŒôŽ+.m&dëþ„CýÇ®Ÿþå»[ÈMbáQ>+ç^œ™Iâi`’A1ÆÐ „g–¨Ê\½jW!¾}îgýý;Ó]@í]@Þüæ>[ØYÃÝæûº†Ä˜Êð˜Êðº¤Ún}ç/¯fn\{Úié4OyÜæÝ!³C@0íWm;ýXçê Ã(³R–t›^g’˜TL*&‰­P–va8\“ $pyøÊ¿MW<•<~üa«49szÝK‹V>»;ø¬™)Ÿ™ŒLÛT†gMIÓ|îù,ç¸ÉÍ.ÚøkÔ§<AQÙb¶vÈfzñŒCìùå–“?ù[S7ýïs½Öª £O>iåÊÏ]>æÞ?sbé°QÎù‡äõq,ˆ·uN›Œs›º«Ç„B€à_ß[ìJxxþñ^<ÿâ­Ÿ1‰Fš2cÄ â&ûÍá7@åƒÿÞ]²‹]g`'PP'PA'@Ð¥7¿5tËXº>£e¯ß?óÌÖÛgO=/åqÏãiŸ{÷üþŸµîíc«‘%TÀ”$¥H)’ŠE’H)%ïé±1I¼¬¥NëíR2ö½åV4Ô9²¶pÒØé§³ô¹Äqé–L;ÿ¬ôc¯$›WðÅÃ<_º}ißG_ “ë® Gš™&cY‰†…Óy­·¼Åüpí…§4a]<&^{d¶GÇ•vž?å)‡”Å«ìðºâ‘'æ·ôÚ—gƒ‚Wï{aí»#òÂ^^ɨX|4€—.üâ¶³ÂEÃcè€ÇCäê_ÇþÏÖGÇÝ<ß»ñðm‹3»ëÂ' “檠kKMæ :Ù÷S:ß³—ÞaÝÖ£‹Úzûðïwl«kâqϸreåùÈ@0 0ÈTišæ<­€ѦâÚ´ïU¥ê1'ˆ›àF&ÍA[[gÕDŸ¶®©_ùJj0n[‰¸eð˜0}Ÿ>|ú›¯mú/Ûø¸ˆîhrÊŒ‡^_ÿy.ˆ Æ´,ha£GXçŒã·v+…£jëܾ÷ô&B[§[PÔùØÄŸžµøçô#Í}ÉAi¤*f„|çþ…óæåpV‰©ºÒɇ0òc¡säN?Å,Ž9G Wf÷ŸKr"}Ú`]Œ‰pëv™¾c²gœvôWØ©  “  ¨à’›ßÞë OÛGt¾@¶Fiv¶j¯aõ'p`PcÀ8 'Æ90FÈ‘qBFŒ!öÕõß!æW5>’¤ ‰²õVÙ^œf2DÈME™!Åë›BVÄ…p‰«8^ÖJÀþ¾:1bCý…½à¨I“Rqù̱é§_‘Ï/È¿ñ(f@ÊU¡¡ÔÄQgëßÐôœWxTQkÑœ#3 H¼²&Êð¨³&z©Ía×vS)—·5¼_DZ¿zÒA“>3ó{,oã™þ ˆßšw˜óÅ›ï ¢%““äoŒ¿Ë™I=f!ï<,œàr\üqÆçÄÄû_6«ÜM²÷fŠ0@]Ôÿë7ÏÝë uÀ°•}ˆ u¾ë¥¨íŽªzYøyug “bŒ0ÌT›1ÂLè3b ã„ ˜¨Ï eš’n2b€Hˆ¸0ÙKSI²/\ÔÐÈ€i¤ 3@Ê€pE ss¾0¦Qõ¹(‚»OšÒ×}1O˜fZ)˜ù »}™Ñ×àØ¢OJHiûép8ÌBŒˆ‚Ç÷ÞlË0]VöÅ#ÒeF3D«ÞŠ5™ùt~Zqðƽ{ߢI›þÝU¿¸#<³êû¿?èÎ_q oòQ“`ã3PX÷-dŠ¡D.“ˆ’qÕ3Åç,Í‚8[ÆCöÊŠzö8ŽÌ;ÍDMt´uute>vQÊ_œMá®í%ºÿ®ežÚ§·Þ®ª;‡Ù)´SŒA&å90–éÚ‚”‚ †¤-ÓªUEŒ#cŒ™øn¼K! ‚zl] IL&Á' óJ»Ò .‘úbqä0rTOl —×>ÙSµiþ¤©Ö§gøÏÎmÿã[E_›å 9c^¿Ôq|C÷Ãöæ9½ÑÅ, \@üµ:Óâ•G•ÿgÑȲîvÓñpŽíªÈô„ÿvÈ.ž­‡Àúµ‡~Ö‰Î[ýÎÿÊ?hjí”Ã+ßøå]S™ çE£ú†hS[;A2Ý L2&IÈF~“gH/‘û›gZÄþò:utm)Ètm¹'Ó‘ÿΟ—ÀÆæ,w«íp7Yç»¶‡†v¬¾öß[oOZ³ÒÌI‘è!n„!#BÅ ‘ H ¸ÁˆnG80$Æ_mì18EdOÆç'P!)BÅÎkúä!òt"iö|Ùá}FÑ¥Ášã×ÎçaùŠð ,¥ 'ŽZ³z|g—lk5•2l/Ì tçlË?}Lç=‰Êiù©WyO[dFºtµ¡¢‚#ÁkN¦ñ'uÝ¿xÓúÞƒsí׺&r·²¦Ôk_[÷¯U­íy§¦…PkVå#“¾»¹1÷,ÁãÖC³4ñ- vwM‡ßýü­«¾d¦°¾¥Óî[â¾ᢛæíý…¶æïÃ~!(–þÇ_||ˆÈ!ö­“‰E’FDŒ"ƒ! âŒ#rJ3ƒ!d€ÑÞäÙåÀ y© É­ (’ˆ¤Ýä-lr§UGÎâ…m!{N ˜W»)¯ŽN^5›æ¬š×Ö~ϼÒë®gKßø¦ôm7Ñ_¶Ñò§ÞÎË;e4 Ì)õ¥ï´ÔMÎ)nxyN—½aŽÄ NÅ Ï„[­^g•U¤¥ (:ÖLö¦8‚ §güú•¶wAyñxó¨2Ãt” ì%"" “:í¸@„‰ÍávÀmlŸ#:iŸ@ÈFˆ@@>'<þc1 'Fð^K·B2Zäqô§žórd¢4§7®X²àÐqÉ–æ’5mÕù‹ögÖв±²‘ÍM‡øÝ´°†Õô `ÌÀØêˆT}õmQõ˦ÌñÞksŠF0Z·¼}Lo³d¸Á)évTgÊIõ¡Ÿb©Dß¹'å &{ß[¾íÇÌyúÍ–Ñ}ç%úš¸Ñ¼Æ¥O}A"pÑ~'`B'À—u·ý#î=?ïÙk¸¯[ƒâ¿9Ï )5B˜6©‘ƒ2LB" ­~]E@…Ÿî•\9väÆ 2d„Q(‡ ‡€)øû|b€¨€›ÈQÁ´1âËGYŒÀ4üÿžûíGä€GaÇ=µ>÷£ÿ°rã:ɪª¹ —ž±xñ4?°FŸFo9†èË?yTïê¨l-–™ñ˜œáäqÿ’‚9¤IÉO¤Û_yÂÄCs*Óª,í¾û¢á¹ÒK(få<ø)) ¦søä™~-ÍŒŠæ¯"“Èc’1•ìkâ9@,Õe`H¥ðæÿ-Æ©XãÀD”ñGÿxÙÙ—ßµW—ÔÇ¥W÷½@ç»–¥ù¿¯ØJ•Ó!å0²• 9J†‡@†ˆ‡” (Bf"¢üð]uRÑkÏÿ~ò·Þ¤€0@Ü4Muñ1ŒrÉÉ738„ÂüåÛŽ€ƒ&¾ãNû©Ek«¯lï«W$ý‰§t¿3÷dæy‡µ¾óf —JãgD[Ùµ:—qÌ“•îÁÕ|cé¡$cÍ}8 ŸhXÒpö¡c¦v~Géß"€¬é»ñš4áÜO-}ø‰Þ—„¦6"Àªi ZhÓšKg]ÿæòWó¸ÀQGÄ7½¸yeͪɗQ²—æ?-HI šp(¬Ÿ<-è•Ò£ ­¤GAš‚4>‡¦Ã'Œö¦/mKͯ; À›?÷0?få‚qÉoç&¢D¦åÛ¦ò{37äÛG …Ù‚%ÍQŒ—0A™pg\qAØ;“ì²$€OtÁÕ·dÝø™}}ݘýÒç»Þ Óvï™û¿aqÃ"²™tˆìþ|W)C)SI PyD@Ê'™TʦÀ µ`–G>´Ž>ª@t"<ñ²s«ª[›ZËPà\“n] C¦ì•³uS*1ªÙ«Õ+“®¶pÄŸ›¶0@ðÆ¯¨Ä'¢É}Ó0­’Ëéʉ¯ |%rŠÖTžò‹5¯Ž–}l,HCéQ+Ü^ò=z÷‹¤"Eß9çðdR&êÁwç 3-Lwæ´é¶Í,‹Ùûç ?âKšfá{#£cƒŸŽu3d;æ\ç˜ßîpWwµÑ¸È0øÙiÆ%œРŏb‚8'Æò@RÉQ!‰ìë×ß1TËSÛÇtÿ²õ¯Nv¶jïxñïß·”*ä¦PJrI Hz¤’*J ¥ %M’† ¥ %ýÓÊ'åù |ê{(ywòÂ7# €Â¼¶xÌ|òÉ9z\q·ïSUz“—VÁâvPkiÅÈúºž.|Œ÷ÚöLgýYX”MëI%ê}Å=ß²ŸÖõÒ ü‘ æ!HÓº%U}Máɧ¼¢Úyª®BI¶ª~œïÑtΑUÓç.|ÁÑäpÎÄC_ó)X´rÎ! êîzábZýŒ ¼F÷2ÿŸ½óŒ£8ßÿóÎÌîÝ©K–{/7 .ØÁÓ;B/PCÊ/„`H¡&@: oèB/¡Ú€Á±)6à^$Ù–dYÒÝéÊî̼¿?öN–HF6g{?œÅÞÞîÞÜì޳ϽóÎ à#ÞìaÄAW&¶]K‰E?”R—¬œ']êý\ åÒe¤ q÷I$¤µmV·‘i#´‘h#:¾ÀCð!ÝM¨ï!]dÇÜuŽ8÷ZÏÞs¹´FZ#ÙJ«•1Š­´FY£¬vØ*k[ÇêQg6kžãÃ0[À‚é¨Ù%ù>ù÷ƒœô•gšÖ•FKÒŽ<ëéLZÖ½2É×ìelðH½U­Æqϲ­=æ~—­¨ì[Üsíê·˜ci?ÝÔœˆ”)jYÞT¿cÏ_Ñì)—³~Ÿê5sG÷;pÁÿÞêïyª¨^Øä>5cՄѼ=<,Ù&‡l©{á?å J¢%µzîó÷­®C1U¯DògL%À¯¿´öÃ"C±•°IŸI¡%i)P¹äíIo‘ŽÒ e„´BiR>‰dN?I”´" JÚH$‰RD§ý¿{vÈ™ )`B}ö,£üYÙ¡uuÌ7·/ÿë¶Kd^Ü¥ÕÊš‹¾¹Îóàyð=h/è–Ï«þ%k\¶¾7:»öb¶ÂZi­d+,Ë…¿3Óúúž°¢ç€c¤Öÿ¥š…£IjM÷}¸6âê†(V™rS)iÌÄØry²‚”Y€ÜQÅ$(µ¦.­«ÓɽÇMþ vÞ XñúýÓ„ò¤û}cÜ IDATj8à€·­q×H€18¨ÉQvùÅ”«·¬º÷n”B‹|¥õï³Z*-”Ò*޳m³:ÁH%H$ I¢$!I"Itê÷Ü‘'-¤p õ=¤@iª{¿ (ŽºîíýÜ­ÖRA¹PiXì¢çyÆj*î½7ÛÍPR–ð=o©ž6Ôôµ,«ú40HG|vÑZ[ÖæëÊ”+Š ƒuTÑÔ—¼kð(*ÊœþùÜ"#œ¡%=ß'<ádÇL˜OB®¬©4F’ô'NyR0¥›÷)êi£8»×«Íÿ!Àö(GÏÏ\µ6 `ðÄg“éAˆ#‘ DF¨_-\Wÿ?ÿQç㌣³ŽŸý)Ò‚rÊÞkÜÏ‚ÐGÌð…lâÖ¶1âÖĉDq N”$ÇFeO‚¾ü£î¬3òIŒRº™9¢­6)wÂ@E]`̰ÒOÝ2Ô÷‚㮫¦–AåÌe„5WMø+ÅDJÄH)“N8°`Äz d+è±Ö­ÛdÖÀ!#?мxTÇ•I š—WOYžI ÝVÚ¯<]Ée‰ºtlúz®®³IÞkïìâyÕ’õ™ªjN ø +”NeÅÁkÖ­?!C¢ïOMmj.­ªj™4õßV»ÙÖÉ%FI‘Qd û–»ü)îÌØŒµ‘"b„mœ2ónñÏ ;W¶ZfOffð¥¥Åð,=xÙõgÝ mu‹5qP !N¢•D‚Ð ‘ $@qI¢8!I":çªg°G²× âmÊ$muq›¸Ž°÷àOSÒO·î„:= r¨ï!Ä]3§”å@¡œ¨ T¬ø‘)V]÷(ÛÊŽƒúÄ6[Ó£çêDsQSã  ? ù=£ý¥Þ½ëW½Öš¶V?k?Z´¤Ê/îs›Ó­”Ö€<,³à­Q:£¤å½3@V*]RÕX;¶%*$(+”7hïw`œ%‹&g“ÃG _‘j‰•öI IÆ!ëq¨jðjÞ@Õ÷ `" øÎÒ¿†tïÿ3½øîW<ØÞ0lñ•ñÊøHúqë÷^ôÚƒ­Çø²Ô½FDŒ¨¶²béBÏ·¾Áã¯pBˆVP’(N”ýèöOê­ê(0´ñ'Ÿ Úæ“moÖîÄ6q0b`÷”ª»È߀»!yó㕟ÐpÞ5çþáòO? }ó†yÝõ®a¯AÅ–¬.¬ùù†(°¬¶°J5¤_ oœ¿Á}c‚˜C7r×̩堲veÊA¥ÁS ¨ëÿqíV& ô}uÿ‰4vòK$°òÅùP’¤‚#…’•ÂQ•ÒU""“«Ê¬P™”7xš.‹UD<À[úêD§h…pµn™Ö§w\ ¼ 7"¥¨ö³õ0rá[gÃD‰#BZANQ=rt­¥)ٔɤl6e^x‘€ÒSÆQÞr‰ H\òÎ=–¡½Øßf§ØhÃ0àó§Hð̆Ù0[†Î-#‰™üS h†±l˜ŒÙD›ï#xÄJzY#­Öʶ/ü{˺ <©¯?E³v¶¾»€ïoeëz©ºQ^ç¾hU·Is·Ðy}ý{ÈçÆà¾E®¾p|90¢(§œš—Ê;ˆ{Qûò•?™~ÿ]ó7;Två%ÆD*­kÙ5ì&Sœb§xȪåOHÉBÁ0) KÂbÆ’!aŒ´T2(_])*Z÷ê·ï¾Eõi#1°S]}Ÿ±ïøñÔ—¨wD(€D©ÁjJ·ŽÛ{Ôö‹Y¯\ÝȦ*—°” l©w_jëÕÐãÜñ­¿—zâƒÊ³ök¸‘2¾sßÙ·–¦,,±V›–’Ó±R&ÃLò3ÀL\£Z¥„tX*(ÅÊë²ë@9ì¸pÅ}\×>:ÿ‘Ï~ÒCvQ¶_ß÷T²ÙšN)©CÈGá;ËN˜¢;4«Ûé% èÆ ~p¿Yµv;x÷Ìé¹Ü˜M£.e9ùFhÛTÍ·Xæ•™gs‡ &Ñv6¶UÔ-sôî—Œ¹›ˆ uø›\º R²‰N*®wÂ%³2iûâ?Þò|1ñ‹ûóF©ÉŒ~³5•èóüÀýHdâÆ1ßñáE‹ óš²:¦À™#ÝÛqŒë£Œëv ”‰À”ž~È Çe×ë°ëÀq õ—ßéž«!¤°Q£†”òvxQl§ì ¿ÝÂ’î ÕfûêÐ~Å(¼öÕíæî™–oªì1€ÀhZ¶0éÁC€i ¿åÁƒ{ᾯý¹wSQŠ#=²oÕ :ÞÒ¥VÊ}zíCR}°êÔâjœ|éìgïžåi2†Œ±~Öî³pioÖˆéÒI qW¬ªé»¶v€Š4Žš4‹©{­y …}Uz̈T“)^W×4¼,Æóß6`Õ[ñú~Ö¸µ_‰L¼;[ÝÔ'£ŒQTäˆ÷G|!ÓoŠyó•÷ÞY±_TŒëó²Ó'ëJ“}_ùÙˆõÉ¢ŠÕT g†Í€m@g¶¹:,ø¿#þ 2}¥RVÉ*GGV6ªŒ£L ñ޲®²®ÃŽâ”Wqwö™)¶ß¿/ZÜŽ½F*AáåÏ„ìPîžyP»²« ðÂÈæõZ|š[ßreÿMâë7³ë¦¡×;¾EϾéeAʲ‚Xõ±!aš·px‡°ÿj5ï·Åý/Ü-Œ!«Éa}ÞçÝ×ë—û)ñ¢ÅCŠ«–ô+õ6¬(-.!iÙ—ì;ÖZãîÿa*Óã&ýóýÿ¸ru[ÑëÈ1üÒ»u·gùRTy½ƒ\x£„U¼¡•—‰¾K3'ù¿×„ðû¸Í¶m-Ê¢m‰ØÚ²Ï­YD=C0 ^ÙcšVJ#…‘ÒJi”4±Ö«•Ü8¿ö§òàÜǶ\ù©™‘!» ª³2CB6ãî™3Jó±—$жuáÞ¦¬‹mlY;sÿ©×¿óý¹¿jõLÒGBHXA-ëcn¹uèlÛx¡´ádÔvàª^ÇØðáŸg×e-ÔÊ·Øwõ´“¦g2¶é§7[yØø9ã‡-z·à¦S—#miW§ÓÝWC}é*Ý$߸~ögÙÝýjí±ö¡ i–ÒØ^½Ó%¥É¶V×UÕ+}Ïþn=àÿŽZuÇoj¿Ô«Æ|@"ž0xÎ+O¼º"fô—ûÚZôŒ¨ìë^~úàÉæ ±²%#°îÃ¬Š©*1bÑcýôÐPzRÛ¼Y¶ÊúÑyÃGS­ÃoÎãb5ú+Ãɺ'æ·µé—ð…ú7_YwÕüq¿˜`û7¬{tz6Í­-‰dKky¼D©XÄ©Òni›üãÏõp€g#Úþ&Ù—p¡—äŸÑv]^ëý{‹nü̵^¸è]§@‹ÕYB}éT—üµ¯ÍlÍš@Ü‘†!,Gá¶_Õ"éÄ`x/ç•Ælñ·ÒàHi›;ñ€ˆ"¡ Ôþ’K^lzú‡K7plÝ!x½+ik•¥1IgÍ[yÇ€i°®µÆ'Y{l Y¦OŸÆÖú²íí2K«cÖD«ûßˬe,ꔩƒm²Ä×ÑFD^ÓÆ}祅?}gÌÏ'÷>ev$&þ{Õym‰õÍㇶüt¿ÒukI7Ïú®yÿ^Iº÷>x±øÂà•ºSÁp´ ,Ÿ›¹º]÷ïŒl>éGøüOàÖ(ÌRíâ„ú²ë¡3YßcíÃXa, Ìd‹ýEL)£‘íY¾Ö“±rniZ_¹®êKN,ã¬M Û'!$ …}ì7•öÅ JÆ.®4¦Ø81PEöÑyÍðÝo•üõñæOešgY€*Þ{‘„,9z¬õçX£Šú4[?f­»zA •ŽÎ˜þÀ'w,aúaÂaÞÂï~|ÍÛéƒöý E‡_û÷×o8ùMñ‚†dæ4ú±·aùÓ²¨oÓÂ;zŒýjûîÏ]à¨ô†Œ€6ðtG­¿Ð»B@+òé?«p>¦=ŒN§ †úªu(ÐR@±üLVû0Z+40Ð]èzEJháEÏO(†ÆúòûKFß]¥¨ZÕ,|«'j—‹HqâØú…ŠY{÷Cñ"O–²u¬TŠ™Ÿ»ÞN®E±<ûå97V\D#†-s£fíûR€­{wÞAÖ8“^¬µXûúY¿¢©îä­–³düšì<¶˜ódœ„>ðGo¾vÄxÓŒ»²øÃk{5ü«±çq=†AðS5Ï´|t›#tñÞßnßýùØ%ÁÂÑÙ;| £È×p´&­àkréKÍw¡W¤‡»vÆ ÙEõ½€)%-@nøçe­>CÚˆJµ¤œT,Ù#Y!dÌq¢+Êr“„$?º¨ïA÷‘¤©ÇoŠÞŸ…ÞWÌ}û+iùŽæ¢þŠ¿JªÅõÉÁ |áˆq½ÝlÖ·°:嘪Êa½]I+×üãã U±£& íY“õ<^ùâ„AG|¤ÖÌ1æ©'Ñ6º(Tþí¶7ΫZ4·nÔ~ÄÈ$ÍAWÌ{íú±­ JÈ(ê9@¦áõ’‡gÖ<#¥Ÿ]r«#µö£ŽÇy.’³öÇù–ð Œ$ßÀÉ-Ã7p´þ¾Þ®õ7›ÿÛqg!d—@|ú&Ý ¦jh±B¶‚Îz~Ö/¶«zÐûQ›-…uQ•eÑHÿ˜Û«8Ò£ãÆk_?¿¸BW¨Ïývõÿr RQ27ÍšP=kÝAE{gØèŒ|ðc®õÁŠ2A ÜsìWX%%Þ~áʺåSÀÒê˜ÑEÖ¨l›×ÖbW??Àn©ÿ%ÿNQþñÖËJ8ýïÆ³ý?œ¿×~%ó^\ÿæfüx!ÛLmåñB˜²EWˆö:ÈíuxQ¿Ã#}wüe½T®º>Vû‹-÷´óõ'œog)–EQ–bY*jxT”E‘G±öåïª àœ÷#÷ì+#§ï€ó² °Óý{(£!Ÿ á·ÙdƤ”€”ÅŠŠ´ès"®„ãˆhds˲ä_ç\0ò;M ‡+ö•õ{”^#Å¥²¤<- Ã,Òì0¤yà诚Œ]òÞÑÁˆL¦mhbõé=úÿ+o6Yo<µ2þ“Æ÷÷>è¬Hv%*†/‹õ{§îÉó6{ë WmjÀ_Šk¾Úö÷ÙÙ!“°3®XöÚ ýW—5$ùLÅâ«ZF^ HÏ—ðHøÑÖ"¤µWÇûnEåŸQ_ Ž·’Ð’I¾„‘äKr$|ÉFR‡<ø?Œž˜zAú~ü·[OHHáÆgBv%~}÷)N12€ÈÚ¡¡rJÇß{áï.Û¼³Þ1ÙÛû-ï q9­ØWðþQú‹øŠß하5£ºIÏwÌãk}áðÙäÿ-ðY²ª‹µ—kÃrßý o¿ïhª;ÙKÿ=(tÖCâ9&2B‘P Ê¥¾ÇÜ·öÙ󯉞ªbYËÎ\óB»™ÉÌØ?úêœÁKf-pô¬Ç1ýd}Ðu¯ßX­½z'ZݱÀ¶çi ÈÓÂ/N¿Ô£ñ O ­«¼i«µñ”øf°p"~/¡%ɾ$G’Î =L ïÐhIþ¹Éãù?ÿºë¤ìÆìêv4Ô÷®ñù¶¯ÚlŠ‘iÖ½"®*˜¨”…+NüÃØá[™Õé8ï6ž„¯à)ø÷ÿ @jõ­JbZyHÒ{¤!+£öŒ±’]k(-–ØÃµÇ&›ÓwÂÆi¯“Í«²~E´ÄñšSTYh†/¼oøÌC^w>ó?ÿ&Ò"úˆQš}×JyuÑ)à…`G³âR«ß Ÿ."A™ä†ÅMüT]Å—†y¯ôXþæa¿îXòTÕW8‹f_)£¥=ÛNΊÁç/ê{å~?ÞjµÐz_²#È8õ¥G KÃâþ9Ùoߟ6”ñ})ÊâK•ö7Ö¦ï‚güt4›°ÊOœYá|AÓså÷·ü°MeßXÑóá¶Èf¼;è„ù”jküåì+nšýÓO¨ŸÛï=n˜EQ±,e9–å¢,b¹5(Ê"öPñV¥ Ù•èt{aèßBý–j3n¹ù°CŠJ¾ü’M†5ïXªô| O±¯ØSìß›°Å®½A LŠõ'é¿Ü°.R’9©§°È”PIÙç¥súé´5A|†6ÓÚ°@ÅÞF4š` K3¤m-GY¼)+„ab¢¿ÍÖÙ ¡SÖ÷Ï~O·¡eâ´Êy³-ùç›ÞYê là©ÉÚûW¨ÉÃxN¯šï4 üÝfùø×n‰XÛȽËmᬆ0¿xîû1ØýÛmUÔÌw‚…“[%6 ÄK8]­ö=šÂüvšPßCºÈçwÅû¿Âu~ðÝg¶|)hÉ<Îܦòјö¿wÇr¡j¼V*¯‡ã¿Y-æ“Ë´Q„ˆ¥ß÷<©Œìyû±^šÇÆãâ~  mŸœ_fëèõ΋&zê¼ ‚ÉŽ:]7ßÿ¢Ì É63g "¹Ëý ƒèîÿÚ‹YwꇌIœÿÈ^§pZ.Û°/;//¶êTO[2àœ¶š»Ø¯•‘Þ?ѤWÿ1~›µ~ðàž««¸ÖpÖE’,]÷øeQÖ?8õ“Þÿåç~œùuÐ1êÁÔÕÃ*?Ã9ØqìâJZ˜„ñ™&ÌÚÜ”ÿäõü襭¿F˜Ôð«ÒN»Øø¸;º1®íHãH©«û7ɤØW°¶vVFDZ->`¸ñX{V{V{LÂè0ˆ#gKÇ>%„d?r(IZ’^W2Œ‰E›0>ÆbR_qîxÅw¼fMyVà£öcd“vAñE¤‹Ë?šÙ¾²xàÅ5îaÐuýÖ^`Èìûözõδ)+ÚŒh3”ÔHzœôìºlϵ™>élÞ³×ié¶ ãñéÔµ¸ÿ]ýåý$ܱ$ýrÝ«|xu¦è/JùƒG¿%ˆ“ Ýñ#D|³Æ9˜ìš —0Ã@ø)+R†R†Ú % %}$|®K¬M÷Êø‰Œ×~cÆk¹é¶/v¦¢þ™¾xþBv¡¾‡tÍZ2?w¾ˆßEr8¥8ír:‚´‹ô]ÎÍ›mæ(í( 彫ëŒU‡ Q‘’¤‰°HŠèÊÈ¿uŒ…1A˪öØÆu`öІ½ ÈJ²‚-±¿æý{™ûž¶ØÆWGÇÆÒÒx`†µ0¾Ó¨úŸ#*„¡§7㣕q£‡|º’&¡é"p‡>Eý:Bé ÚîRÆ¥Œâ´Ã©ÛÄŸ·Ü8˜„zHãÑÊåá¢ʃò¡üò…þ¿½ûªŸh<&kÉÍgÛnÞ5ª¯n®d×NœN0ŸàK¡¥ÈpV/¡Øg1-«ûX~yD€L&+M–2•æ²JÛÖ8:½a´Óv9€þCÓc÷ i„Üd¾t›¤²ŠÖOŸ>äG%*ˆÒˆ,‹ÈÒ¨*ª²¨,Êò¨¼îü¿ÿì¢g^úØå_ÿOçk/˜G>˜S¾pX¼* `äà’Ï» »¡¾‡ìJœìÜzººÑEÚ¥t¹¦T)eS/ }pËíGD¾å:º÷⸱NÄ@ùp4ß4—×ñԣλ AbÑcg[b˜÷²Wž%æ Ãóm³Å³$!l*ÁD˜öžìŸÀéðתZ—WøFÛr¯¥äˆÞý2Ñâ'V±N³É ÛC{å™H¯kPq}I…ß'öµÑ“6¨t›ºoíW;–yqÉ¥²£3¹`ú?üqYÌ)‹9e1UVä”97œð»ŸŸtÛONý뎫ç݃PßCºÈç×Àzªû«¼mϸH·[ø?ñí7dþJ„Éc*:n?¦ä®c–fÿì¿Ú‰ØÞ™rv|ëø6Ì]gî{ÛJ§™èøŸà­ï+ØæÌ;[À¶D Ó§fy:!WŒxd²©‡”¶´"3á„”$ÕF}Ïõ–Ê¿õÂÿ\±xÐKÿ:D ­„ÖFj-ƒ"#“²<%b©þµ×Ά\=ãŒÿ›Sºìwå|Ìš ¦÷½BšÁë¿Ü^þòA©ØU˜™SëfN­ ªþÚ-¿+kVHç ý{H×Ù¹êA¾)—2.RJG¾Åÿû¶ uà€ \Çd}YÁë•UP¾u|v|oAöÅ' BA¨¦"vXɦœê7Þ Ø•ÃŽ ŸYÿÑåã‹æÇª¢m‡~1÷î;€"_‘ïãäÛ`9—GGçµý$:ð‡Jš'‹úh-I@2Zi_j­Ö)ùÎ+5€¥TQ¡¦¾á {Y bîôå?=ÀkY‘jYÑñÍׯ³*yÕe¯UU„ÂÒiB}é2;9EÒéÀ¶GDÚ¥”+2.e~å=°y©:Ø÷ˆc*ŠVG\ø°š•o ¥³+Ò?*H*’¤“²…}§HÓülÖñ}+P¥=^=÷ûsßyÏ•ÚøìWÅþ©}é¼ÅŸ<Ðw%µÊÇßó’KL@õû±RæùÒ¾ÜBàͦfí+íI#ô²1C¬i´6"ˆpbÒ©~Á ‘§Ë&CŽƒÞ«6I`üƒx¨TœÄWî»rÊŽªëÝ‹PßþµZ°Û™ü­íW¹€ Òs¿1óð–›µ×Õq{ŸqM<; œ–[¥Yi(/Ó˜úË?¤IBBÈV …ЂrhFÓ…àWêûøÍiÆV<<¾ò#rÕTß-ñúÝío¤„.N¿ì*“pM°æùè% 0¡ýt)i”4ÚH’DÚ—ZK_K[Ú\6˜-G••NT:êé¨ @«%7B#S_ òãŸL¯ TÊNí*O•Dw„¿ó` z|l¡¾u´kìü Wd\JGDê¦ÔC×¥ßF±Â—ö9#âjm)êš•sz@yìølÅuw !•’„"é°tZˆZA-r aH£eŒ79SÕÃÒ a¸Æ#mö}åÏÓ_ý%1•¥õ±ˆ^ò\†¥omÿ›·¤„TB(!H¡Dq­D­D¹ÒÄŠ=ËB`€š'•]Qþ÷ ¯Þæš,¬öaÌ1¯ÞêHí*ã(½éÛS®y”2J™ú#þ!$„€ïK­¥ö%—´Ô³m`ÛÈœ>¦Ü˜Psï/!P†Õ<Æ_nÍ zÞ¼ÓÆ…û®œºc*;d÷!Ô÷.ó¹ø¬kŸ>_è>U‡T”4¶yÕÿ^ðUš•ÏÊõ½¥PR(!å­@ q+ÑÀ³²ÒEß´µÖ.¯xhô¤ £'mè‘üš” §€(ë¨Õ®öÈhÚ?ìíUeÜMû2˜è¢Ln²§dɵŽ2–|{/oˆ”X%›µ/Œ'ti|âÑÔÀ¶¶œ¢RE…S¤Ü˜t^¸§œ@ –NëjææF ÝÂWæí|%PAtûöJ|è”÷B}é:)D(Ž6F]~hîcýWœÌŽo•O­ †= ¥’ÒB‘PB(_È8‰À¿+Ò9(¯Þ8½Foçc¡ì’ØÃ¢ÐQö¢ìÁh2F íönßsG7}ûY4íëe”4ÍG= ¤„Ö9 ¯{qÛk™Á‰@âcÒ‰Içá;{\wsè±\ü–QF¹à{‡* j$ÏÂ#ÔwÜ‚R˜JZˆ…ÂŒÁc#.~ûüë¬ò­ò‘Ž-ô)!•JH‡¤µµ‚Z颲 %UÕ‹‹saýAÞ¹R1;¹YŽbÐQö#Ö—FCò}¡}a4ùþ¯ÞÝØ•”‘ï×ÑAsÔ¯”2m«¿ÖV6N*2¸{ÊöEss£5l׳µ$£RÅ„S¤œ˜pŠ¥ó¾™Å5ààQË\„ö,ÉA›{g~a'ÕoH!ÑÉïàÎïßTˆÒ²«sÅIGG\†(Ðså Vù0bÅ€'‚W…p„p¤pH*'Šƒã@+Èqà(ÈÁ– ·†$$þ<ÁÆØÄ¬5iÚã“ö„ö¡ý% â­&ë*í*½NÜš.“¬nÒZ-Yu7À6€­m´¶‰ •NL8ERŤóè­gÌógׂkÁ«Á«Á5`§Ý¼ÓƆÖÛ¯ %~G²+kVèß>ƒ[°B£´¨Íu1kåÛ¬òÙѵ}žkUJ%¥"étÒ‚‚ÆÕ8Q‚¨¼œ‡>tž ¶-O’Š¡r£/\öö¯ŠHÇHG¡£ìá5Oh í“絿ç»f3è2ã¿nÿ»”2#cßr\£Ò¾ èƒkÙ6‚×îg^ë•NL:EBI@ s ¸]åkÀhoqmØlG”¦0/¬Â,Õ®L8¿vÈvPXßÂÏ?PO¼ÿe«Žµ¿©z“iZ•r„P‚¤2Î6$@­ o~·Åq (vþUÓƒÖ€×0¯½¥üýÌSÁŽÅ‚‰,Ák²L†ÁZ’O°‚<Èþýõµ“Í­`Ê%Òä!@ö¾Ç©9ÛüMô)—ÏØ\ßç5Ýá:¦Gñ2×1FK£¥ö¤Ž¦œóÓçO¸òÉF¶ëÙ4›ØÆ±¼Ä8÷º·k5„æ \|ñª)?‡Î›÷vÿ,\|Ã\?;ôÆaJH—@±gÈóisÿNij‹|åßäõÕ÷DS]ºÊuͲ¡-Œ'½’xûîç\þè‰ß} ‰yóæ QL81é´oðÕëæœyýœœÏ«üZpùÆtúóÌp~í„þ½° XtšOü`[w)!œ6P‚$lz‡"Å­àÀ³çTžÑÚá×û»²ÅÐ%l+„öÉ÷6×÷\d&ÑäÞẮöÑ'âèc÷>ÛøJki|9tùirÊ·ï;ò›wm`ÛÌìÅ„óôß¾ßqƒK®Ÿsúõoåñ̵Ì5ࢠGk£4!»=;;&«Ý‚]ì4#‹Å ·òõ¿ÞuÕ0ƒˆ@ÁÉ{vîèß¿zÃ;rõ)·ÿò¯´cŒø$6µG„Áwʵ`v¨ª§>zàKûžQìÖ'³½W¬ßŠ“ ¤)mÙ²ÀÇív/Üý½b!b[npÑõo¸gæ ÎHåDŒ4ð·™^xý¬í®«ÇØáeùáÛ™oW4*ì³Wù'oæG†ì&|Â7G ™%‰N¼ôO¹;Ì#F –ÊÎ9#¿µãüôì{¯½ûL¶J› ¬÷Ëþ³Ù ä²h@ß§óoµ÷mV¨¾÷ðÙûŸZ]ºîÌɧ>„÷^}ö¢ßO;*;|llYÍ/ð×ox{«‡úÉEÝô—cȸZû›½ôûTqçÌÛ¨§ˆkÜ¢0ê=UU{tuí1ë<»Õ»àfÿ½ÿêÃÏùÅ„Q`Î¥62f3n~d!3_qÆ>  TÊc‡•2ÀÁo"9¸ÿ0ÃqÀÈA%­ÁO¼}n{Ÿ®mq€QŸ ÍÁQ;¦vîøk±“JÚ.ÛªmØçþÁ’ÖOÞ,Ô÷Ý„OøZ©Ž<÷ÚM7Þèß;¶¬ w>·œ7Ïϯ` ÿƒ³ö»ùÁw·YWDíw‰Â!¯¤ÝPªV$>ûAç¾py·IóÎ$Ô÷®Ó}¦é ûTm2ázþ+Þ±õœ{ž‹o£ÃØðéuŽC'÷ðÊÜO—æve'ÀËeCr{ZdgÄý lKòŠÈ™³h+ýiŒ:2sÍGüüáh½•$™Ï¥׿à®™3.%~fûõ}ì°Íåµ3ª4¡|Rëö–ìø a76¡t/Ý‘ü„_ë]eß‘åÞ[ü)m;…L>-½ã°_rÃ[Ÿå°¹!‚ö±$·uaýü¡o<ÿ•ŸäOy«àî±ðílKÜ õzéfÔ¸e¼E[Ò–tb“NÑJúÑÊn¬QCJ,ZUXáHäûâ+XaŠC篬|О(ïÙãùñ꡹‰œ°i#îVøñ}¯lò¼»Å=$dûýûÂíòƒc†•¢[[·C> Sà»ÀÆ1|;&Ï|í³™wäbc¹!‚á8B>wÔ‚¥»dÞOÈçÈ'™Ò]ö&‹,¨•s½™¾Üã§ÿËûþ™Ñkƒ(M òG¯8øù¡a<¤›éä7pn_ ùÜ™ûðÄOxuòóvZIºHN}ó³ô¡[[‚QÆr£ÀsaŽ T€E*ÐBíÚ„ã‹…tã¿ø¨G.ìäø‘Ÿ¬þÝNç…´}³V䆻àú7º«yAB4”Oà/4 ò¶S˜ÓŽdUu’п‡t¶`ËåWÑ7æ±¥$œI§Í ^šzû¹B°¢´]£ÿHüN2ò]ùRnØÈÀ¿w§¨<œ¾ªãÓ« P °H@Ák&Ô÷tTáo_3`ÕìÚÁÓ òóömöýßÝïMºˆ-³…µ|›»H\jo?]@–A bA ’^ÄŒïo˜pkÕüàø;Eâ;+AûA:™¹¸ûÌûö—)$¤Ktî õ½ Ù¡ê°YüdîÓ{ 9¾ßŠYíÁ—o_`蔟}ûNâøG/ÕXxH3¬mAñæ@â‰ÉF0ø;Æ[2¨\¸$¾KuEFÛ±„òùŽÿj©ve½Ÿ_¬\Hw À™¶šúO\l÷QBªÕuOŸv'€ OžÓòÔ¹¶úPbæ™o,Lç'†j¶e=ú’jƒüŸñæQ·U~¼Ãc5ž }ÂÕVà«×ÏÞQå)`ö¨ ~O&ôï{›öCNŒ¬]rxÿ#Ó fpúW©–wÑ4 Vyõ¶áå͋ϞÓôê=À·«E’éüäpßâÎA7žÜÂÆ¨<3còóvxskW²Í)Ÿó¾£ {І|Ž„ú¾§pÈí—Ì­€ïnØ÷¹âU°èõ#ÛКW*Ë8“€½uÅbÕò1Zˆ1íîsë†MgƤòƒæÇߘÓôÚ@ëÜ!?’ óÚFhkØ"ˆ¶o´ð â9}ç`¸qL<ñö3pÙå®3ƒÄçî*¨8žá¨0gH/LvåôÈпïaü·ÇJJÓQmƒ}òrCP2ˆÉ2s0fŠÅH.'â%²5½ü™âaÇ“`f"-ð$ÓÙ©áÚ˜÷öÆU1Ū:ŽSĹ<3ƒ™ @DB‚`ì‘] ”t¤øÏÉí®Ö¥ñgv^§í. Z¼§ÖT·ú÷=‚ *26SÆi±!Äù™Û1EŠKls|„© &ÎÉ3†¶b ã² †-çÄ7š÷\³*SÛúüf† Ayq— zêÔ;?ó'Û”N÷~lëæ7٭ٕ4¡¾ïæâþÿ6Œ â/Ï–¬ ¤WE9ˆ0(§õ¬31,‚A˜-"™8ˆ§y}ˆin´1ïÙ7Zø\PÞæn9ßÏ9#ˆ;ú¾#Üï|Lù’=~Ê‹¿ïÊJZ˜„ú p/¬öaª>ëq‚LEaAf}s*0ÕÒE0Õr£Ù ·ˆ`Á–¥êW Qÿz.Üb™åÌ{»ÄGË‹|»k>öÓnä@ÜÛýû3§w·y/X õÒ Ùõ}w&0ï—ûã¢EBÅX@@NïVn{P…r3Y¸1‘ï›D°äf€˜‰­…ë ’2#YÄÊræí.¾câ s»¸çýûtÕ…òÞyºêvö oÚžF{¦9‚y³D~\-“¥ö¹ŒL~2 f Ä–˜!ˆCN`k™KŽ™‘ù&Ùñ÷|Ü=y‘wA$!Ú*m«ÌÇ?~ÉgøÄ¡>t–°¦vu:yC}ßm!¡câ§·$R€(Èå:›n4Ù ér{ ƒˆÉ[‚, ¢ˆŸ1Ö‚¤ÜŸ‰–nnÞ3-È%Gï„ D¡@B8[–ö¸G.9ö‘‹µ6G>|Áv~ÞÂŒ)‡Zº‹ó)³,~N³3!;]ßë}§pð?ÎûNj/ º»|9‚ˆ@‡x}hV¬>ù|/%€!«Ä¹¨¿3Ñ/^!H ·1 ÁRòsX´çDvHžÉ'GÌ9q'BÅ{ޤMõý¸‡.9æÁ‹µ±ZÛàïö~è‚>œ8Lߥù`i+€}F|Š’&aÿ¦‚f;n…Ç>v‘1V ‹ óZ’hN¤+Šb– `="Œ ÀÁë)¢ü\ê À‚ˆ|3¯µ½¦Ïâõ‹ÕYÙ?RB7Îô”jŸ /îDD-Rrøˆƒïùø¥‹ØêÒ¦åö`ClÀ–¬°ytʤÓÞÞ)µµÃ)@ë`áòÄØaec‡•-\^@Ós~¸<1fXé˜a¥n×ÜÎ![²³ý{hßw4’„ !‰„B2I‚¨µ-CD” ÑPÏÈir)/,UγÞ‰‚-`!’ˆÀl™RBÉÜPb¹Î©ía÷Üà3Üî¥ 9q¼jQªï¥ìÒ×.P¾~y{ç©öíÌ}xâNžþiñâœGLíõy$dO$ôï»Ožrçñ] &+AI!˜a¬M´ee1 @ ¶~0š åÃ4†TÐ>LL 8øYôY_Uß» k$9æ2)ySçüåàžëÖ$ˆ‚ä™tßÁœ\wDEò³ÚFZɰù!‡s –-ƒß¹ä!ä%¾ó#‘=÷fýÑô>ú€ÞϽYßÍuº[ú¬=€°}µ°Ù®ß;Oz§B ñ ŒÝ[¼L’Bp®»<Ú#òµÌs–›sͧAUX‚1 "!r©2Á|›çDr>~³nMDâ±1óá-ï‘/rÎˆï¥µÕÆäîÆ7V«ÑÆjc'Ü~:òcLî.¾0³†¥ê<Ù°Ó)v¾¾âühE‚c†•~ÞéŽ}ìb$¤Èõ•$$ Iäùš@9ö7Ê:À~:·,dn&09.!‰QÐCUJVѲÞ18È}.Ø’Ë™yzÒ»e^´2%¾X72ߎj´¶¾±Z›ö5Æ0, Ábò_Κ|ûY»Ä‡t–B”‡]˜ŸY°,>nxÙ¸áe –PÃÎn†Ö†@ñGµPBÄÙûGñ2Á$'5z·¤éEõ½‚!)Ÿó’ì1—í.$±Æ& Ç!!`‚`ËA\??¾;€ÜÞ¼¶Ü:f‚Þ›±8!¼’xäÐG1[ ÈXî â9Aš ò ’D ¦©·ŸŒYßI 2?2¬Ï0þ¾[1÷á‰$Ô{°9ÛÎp%"BYôd¯ÕÊá¡Ùòw+öÝÐÄÖ'’ùFRP»÷\¼Ñ pëàfáQÉòÊ@Ž¥¢ö¦Ttyâö^M„EG-&ŸŠ2î´wGi6̹ٹ9˜ì(˜@ ¡ý/ˆ€oo÷ûª;k†î=ˆ½†tšÎœÀPß šíýQº4;*AnؘÿSPSÒ:8]þAuý¸†ÞDÜ>èc~0ßà? p"Z\HÁ`›Ëýˇ&s_™Psæ‡Ú'ÊbÿWG[p îyY·AöŽüÿì}yœU™öóžsªîÒÝéÎ’ξBˆì" ‚ "AÙTÜÇÑO¿™ù¿™ß7£?gFÇqÆ FpWEeÄYɾNÒéõ.Uç¼ï÷Ç©ª{{IÒÄÐOЦººnݺuï}ÎSÏûž÷%åS5ýŒÙD¶§´CþŸ>Ö~ÂŒyú°PüWÆ,pSûÈPq¤Ê/ ŽÜ¡ðeà÷#÷b sxŸú}3|' ÅpžßAô7Ÿ ~:qÕ†ñ{Ž)7QbK¤“«­Ô©ø$ÚÄDJ”QV`ÆïU[Ýyõ3T%ÿrMÔ:³×ôD šêØÞq­—íDä©1þ'ÚOú˜'þBŠ÷äîWF(~˜~G†ÃŠ—C¿ËÏÕ _¶åqÞ|§„Í ¤ÈwFJI_A.Þ>ï—ÓW··ô<9Ë’çZ)é¦GIÍIdy2 U ”÷åÉOÆÔDpR'ö¼ÿɸªbÖDZp NJ“îk$žy2©N÷GIy¿a\ðß³û%½ž‡#ý=Fðra$?òh@ìÜgír7î›C¢…ÁN|®al]²ød•týüçç]°âxcðÌâMí“;Å’¤ý8jU}“¬GQ¥A@Z‹Òາb"èþÈrïòLùöqÖúd(Q J‹VÐJ”ö?E+ÑJ´–ä׆U­F«âjCűY̺ۄqÈE xñ^Y˜3™–)0,KŒ$ªñÂ[8â¿ °ÖA€•öÅ«ýdx·%Iª!‘(Ž{núšÅwOé …æmÍ”¤7¦Ó“öxR "†Ò¤ ‰ŸËJ@>»¼ZT¨åßùœ?g©þF_{=7*]Kå¹|vÕf½B½{$É·ü‹ù݃õa9ÌFpd`D¿'®ræàçõø]K?¯Wø`ûBíÅr*™k ‰³8?¥Èrl9vnÖ“­ ÕÜÞ©ûºŽíèhmgÀE¾Þ{ªÍŠ@J2QÞÝj ì%[D`>÷„EŠÿïxòÅǼB‡Ò¢syT®¨Â"å(l@ØXW@@ÝO!Ô³Ý{˜Á frnhü¾¿‹åÕzÏF°Áç:—¾ä~¸~¸Fp$c(³®Føý(Á‡ÛïG艥ëL’Œ÷!Ä çÄZÿôĆJ®cf‡ºg´;ñyé5‹†”ªÍfR ÆPxlY½j/ùÛ%á?,ÑÐQA ‚P™™™|Û“~k´î{¢¶ø}z÷JÏ^fô—n IDAT8&ÏïŽÁL‡®ß³œÖð ðr¸4à #c΋À{±Fü™a¡¨´èÎŒdëµ\õ4w²ß&€.ç6Ž®Ìê(ÏirdžC"¯ßE@Â)5[ä§U˜áDø£KµNm*IH5Øúבü¯fÂ$ªsfJûRN —qR—xˆþ̯|Øë1< kxžÕ°ÄH£¯ýÚõI¾½'%â=ù©”¨ú-ZúþLD}MηüáX ë¬ùu£^´—™­!rS©?ãY^Ðî|ôÈØ~ò cS½.í“ò¾šZÏ”4IÙë¥}b™eB2&ÝG=2.ޑד*Á¤Êï÷v~ÿù½»l¥¡˜xÝfðÅåÛ6Óž«NÊŽçþayùÿ,ïúèòLÎG%©–ú$>:03œçw¥U`´ìÆèÀ¨[ž÷‡û˜=îE]·uÄ2óÝ/õ‘ð#8Z1¢ßS K=s€“úâè>¾÷Äû$ög|u.ò¿‚¨ÔI$”EHÌI爦C‡$mœ²'•;¦™«6›«6Wnm@æ™Öu¿€Ž8G£ózΘ¼™ß- º·u`ËHUMùÒRtß°”ì€y€ö¼¹­RѤo/iâ£Wî€A+•·ô­ž|u4…oç×€ðA{)(ƒ×ÿšûÞþíC»`ŸlYʶ|¾c¤ÄØ^jœ0·y_êú̯¡°QCÞ8q^Ëö©yDâµ_»Þ7¾hš à€á¼9ƒ„ÙUÒD;%úR‘ …@ZKÌ›6¨« ¸äFUomm¼nsãµ›ÜèJ\tè„´ÂØYq³ƒš3AÄ8q1ЬNy20XsÅÊŸ91âοY € Û®z&®"Ž0óçóúÒ:”òÈ’•Ûòk-Dä…d(ÛgF°ñ~`ÜÔ¾ü+c–¾²êJíßÃãgÚß‹|±µï y `ñÜæïöR_Ñ!<߈~?"‘ÕÛÊÌ&N™=i½W/á)%z€*=D¢Â"€šó$=W“u@ª÷Ó[>°©0³šßSW„:¡;²Xñ,È%ÆNCA!£  V_q"¥I’:Ç @Ë¿/¥ió•Ïù“[wáºnÅ1tgÎÆp19“³p–Ø%g|“§4î¹ü/êÊ\¼j TxÊ̦W"Å&,ž3€ø$ûÿ‹Ëʇ 5#Fâ~zmçá:”WîO®é8À>#ü~Dâ¡oÆ]Ktív ßiÙðÞÎY"ÂœÔøòûy5)ÑK¥—(3ÙÓkҦà ù}_™.‚ù?}"ŠY¬ß€0‡éS’úa¿ûFÆØ±;åë–„òþ©±€ÁŽyêmóµ"¥é©×¯*TÂÀ¢£Pµ‘A±§@ ®ñ3­#çÔüs¿à—‡raÿ&fÁÕJ„|8¤]xƤ,æìQó¯^d²ãŒc ¸àô‰Ùê â‹éSÍ8ËžK’ÂkýÊ<ønYµ?ÕÖ%=”ø~Z铦½µÀ‚bN ä„hÒ/³ÏÔ±¿RÆçák+õÌúÃÖÔ+÷‡š_2Œðû‘Š“/_þø]Kÿ}ìSÝ»øËcžè–QßÛ3”~Ù×xÄ@ ODEB•R²Ñä€:ÓT@IR°y3´NÃí·á=ïÁ9çàéA¦þä Û/= 9JjL.Jbø"l™hñ} Ñ?tâjŽa-ÙÖ™Êé§Þñ¾»“¦Þ2JOÍß–’ ¥œ“<ââU“ܽ`{Ýu¢ÙÛ&»ô—Þ*‚ô#ÿ¡Ö¥ëŽÝ^O'`û%«¦|eÌÒ»ì8 ïPvu‘ŽaÊ®ô_Šëñyå~©ùŽ~?âáˆ?/–¯åÖÞÒø¼]Ý3[’vÙ€‚pRÒÿÈ$|JôDBQ…HÈ„uĉğtãæö»'Œ»p÷ƒËè18íô–1n<‹ àŒSÑdè¹·žT½íq¡tIT|¨*°03û:ëɹZ‘ÒêîxµÄ„©ó¾û•7_õÁŸÞëSŸYv5´DÉ{ÿÂá: O€_>ºëpð°À+÷ÃhŒ`b„ß?Ž›5j¿w¯/R0å`ѬQDHî]« ¤aÃZ á¦xÁWƒÕ$ønã†÷”æÔ˜],¢ œƒïmškEI V™Z^ü _&B»¿>mìÿ{‚@£!ñãpÿƒ¸ðu B¹K°çª“F}syó·–wßtrbÏS¢[MD*ª Ë‹u¬@$ ¹hË\­HíÁ÷W}éŸßÀ[4/ >¬z÷‚í÷þ¾“¾nm½$}¼ø_þ¾?çÞ„å_³ôâU“oˆ ?‚#‡Îï'Ì5Ð,“!܆r À⃅Pã íP••xjN !Ø‘LltèÙüƒî8a|Ó¬þnq½ºª2œô¼†ÀS¼(€Óu–­ 19!La AAˆ ”0‡ '¹ÄúñòëöÍôͯ¦z ¦Bð³–­BG¾„L€0@¢É…é$VYøãÅ}¬›$6+ŽaeNkØ’vÑ2i2l‹E…I!÷±ù_žÄ??iþiiMÅ×5ò ªZB&D0¨·Â ²Ž/¸ëZ­è— ‰å=>ñŽK¿pÇüúµKj3›_Þ€“ç ûëÍO-ï÷¨¿0Qrfð€ ñ¤CxìFpxažZóа|貂é{c~ÛóÎÁ:X'·ÝW}™ÎëðÀhæÍQZf€¼µ«õÇÍ›4•r&@ÈꢵóçØÿd‰êGPJtO“íDü¡H²-ɲúŠ%D2柨”±ç'©|­Ê â ˜:Íž‚|B>ùFUéeBÒê,l]!MeÎ7 k¿¬ž½É-¡ p­Rô‹K‡Äò½½ Ùzñ>ØÆAõb¬îÖàuñdÿëÐoVç;F†‡a†#÷ì_}ÿ[ó~åó·•²7¼9Ÿ­/;/×XpZý²Õ š¬÷ØáÆÚÃonZ¯@7ôÎ͘5ÅMDrEïqΉsbÙ9Çâ˜E   JATXH’fúURû|׬›¨BT!!÷4íF¢*ð›ö¦AŒšB“ªôê¸OÐGÈ èÚÒœ¯×ü§Zù!^¨@¿¾ì;C¼¥R1[¯wÛ+<ÈÆAõ—€€v×n[‹!³Ãüp›“‘zP#2†ðÁ:ú?O×]’ðå”ÿãÎ>†Ì×Z¹ùîÊ-÷Vn»¯zço«,xÕ<·ŸcüÕ1šºGS÷!<ðäË—_vÅ=¯>þ"77¬ûNÃó·6>o´´´úþ¨¤éªhcvì;ëØ:gAV€ rEM3äëëŽÔ·1RðÅåÕ2znXšfÞ$Y= 4üë¢8Æž÷?™:ú5éóMÉS«ºëØ9¶Ž-óu¥¹òêÙ_/ûÎ.BV@€wå-¿ýZö§úšÀ%$Ë …‚oùí×.;õã‡ð ŠÕѱ|\¸YQ·Âï¹.žLG²ZÁpÃÑÌïW½!wõEyÿóãÊAwÊ—ð¯„izW·;¤qŠÞýb;CNW“?b£d&n-¬ûnq½1šbpÊélgyN º$ å >/’вkÓ¢ °ÿë“Q•âžH9B_r‚­’­Ökö¾ŒU·±Ð\µóaËÎ:gÙ9H¬‡|—)}dø;Ï^ZßÓ£"ÉR¿ñg×ÊFfýK*Jú±À³Ñ4C|BnÓAâ÷ñb#8Œ8jù}Ùy9¿òÍŸœÜ<¾F‹à­¯Úìõà VàCncŸù BuêÙ‘Eâã>àÑ ´ð›ùÕVøÝ•9–e¶ì,;ëØÓu¦óÉ,Ð!×ȽFú.†³Âä¢2¢ (¯ú‘»¯<#„(B¡Zá¸\Wµ¬¯ÏnŠý(^³B–Ù9¾¡g¶p”u290ˆ¨Ÿù^¿T,û«ü—[ð³ƒÖÇÇø_Ÿ®N×`ÞðÄþÞÙWå×/Ímðl4-y éððWÇ˨hFðRá¨åwßýUõ;÷ô'÷O_±ßrš~’çKE¢H¶¹ñŽí'áø%œivIðÜ*0*ŒVƨ Ô7bev±œ2Ö1³ÔËdÊ7QN¯ÍgÈ ¿ØH˜˜É•>ýLT¥ü¿ž©´nJõ;ÉßZÇ´õªL®ZæjY1jü“ [jOµsS ŠE»öΡ_ÉÝÏ&ãè–¦úõäT¿×{ñ½½ 'OúÏ¡?Ý@ dgM vÙ¦÷N¯Uƒ:5¿Æ¯<]þ—<éF°?µü.‚ËÏÍ ¶]>µlIL<¹N³àMg¾¤~¶Ù¦Àìd ªÝ† êï¹ôæm³^³mÖÙ: (AåÿR+øêÒçœcvÌ"’ð&Õô2õ#÷¾† âªD{rïøøÊ(¨/- Ü…Ò5þ„m.&ÿ(†«”\µWª=ýMýŒ÷‹£)ïý–ƒ^ÀújÀ¥R±T*޽åÆð¬Ëz.«éßú]M¿ëwƒëwÿð!¾qCÄc•ÙpŽ×üáÚÉ=ï=¶K€+'·]6vË™…Uþ¯Ë«3ë÷éôû^xø½^'þõà[õÃß¼µÈÌ}ì­ù›.ä$~öHÄŒ‹Oé(^ATú}Þê&˜ªÛ†òÀYf;AüÀð\<]ƒ7½ýÓYøÔB,$qfjÑT¥P3gò£úKêzC&.#ªxfgGN)ÄŠ«}<÷ñUA qDqßRJ(é'ÅÄ•n©t÷ój|ýù”âûÜ^(å™!v^íímðúÝU±É”ï%üÀ‡ãP-øA?Û­˜lª•]›™`! îJG‘â?V潨ƒàå‘ô>jõ; x`SP)uÃëlíO©2è_,2÷ÿ ð$“ý:tùÖï3çMüû.ÿ¶eþÏà9yo÷l¢ìMMسÐÜ Å3­_¢’09OÊLN+ÚvÍsQD㾺d0ZâJâÏ€dá/Øk.X+^“c8ñ+äÊ]RéêkÔøÜ›±ƒ„[‡xŒ¯þ´T*¾êµp‘4¾qpó} ÿ–·$$¾Ù]Q*ÇWÿ’g} µšìuÍŽi˜.öm°§s'#ùsééÓ«ì÷ø—Fý [¹LúÒc(×ê(æwáEDDX PJyŠ¿æœr¿}îùCÄ‚ N= ?Yï¹xþ¾·Ÿ›påësï¹0àý 6)’U¶5Û²ÅM0M¤Öàl³ õvJ¶åéx¶†, Ö=tåÿÚÄywŽe lWP…¢S^E5Ñ]3d¢’TJœ‘»k­6¾cU\¥IßY”é$²Šd†*% ^mD½&6ZAI2N¤*ÞÓ}©“K’©x¿Ò8®ÅSš~^¸ô¼ð@RºuÒÊL}_¼óÊmŲӨ_ªÔgé÷× õþPÿ•»¾À’Nà’cFµŠð®›4äZÒY tbçƒ}1÷ÁaÃÑÉïo>+ÁO~Õo¼éÍÌ"‚^!¥”RDŠ”ºòŒþ5 õvŠ·œž8רlßñ§7õlÚàÚ‹óõ©úÁ‹î~‡2,º"VÄßµ”I®ëœ)}¢©5Ó£pñÙ8õ4Xz"–ž˜lµ•Þ”käNZSSµŠ¤¤¤)I"«õ¤Ó—D€8FÐ{씵F+¥©x&/äºïíàÒ>É(^€†qÙ8D^¿ê*ƒ“ž÷O¾ÿ62j¸*Hö›Çl°ÎöO—®·z–ßðý,I&“íá%Þr.Ôã%]Z‰PéN ‡„Ü¡Dk¥µZñ†U=ÍøÉüLé×h=M¡ñ\ï;¹ú²ñ§ÿi¾ÐF>«Çh%^È#Bœ qlzÚ]ï^ÉŒšÆñI¸õ?Ç<ãÉý·ÑrΗð3÷\óÜC.‹Ž¾ÐR÷O’©¢ÏÒï¯3ƒf/ ±ö®y‡hÁ§ê;Í&’tÊ®/ð3¾µIË[dL®ëJg¼düÞgÆÚŽRõ èןã˜!,Ô¨T¬2¯HÍi\³®§ïúÍcñy'çüö‰øÀÏuÑi¡ïüü“‡#`ô½ÛAy›e¶ÙöìF§7®™?M‘ðÇ–ÍßxÿP£ÑŽ9´Ö: ³3<§ãëZNEß4“¨7èoÌÚ±bÑ,zúöòú¿»ú–cÆLÕ¾2Í8,¾cb ë Y8¨GŸt è‰'Ê]Â!fˆ3ùÒ3ÐJiEOœ½-þÕñI ¹¨$¹†4[’Ù/ä;ƒÀƈ ¿»öüõsIŽYÄqÚ@ iÝybH÷Þd½qš&PO[ñAÒçr7óafôÖ+ݹÛ/¢ À{úgºVû^Ê~%hæ\q|ïHsh>{!IZ¬IÈkÈøŸê²J•6õg3bÎ ;¹áÑÉï«{ñž°;‹(RŠ% )¥¦çV®Ä´ì!²¾þ”P¥ô³G/¨þ“°Þ `Ù¼v«S`‘U.ª XŠS´Ö "cÇÌÆfëÜ¢Êò•ù¥›Ü¤ézçt½èŸÊ9Ïl°ÆNpc!Q"J˜|øXÖ9žñ™ÏíݾNb ký‰éSëñId+ˆ;YH,~ì#fˆ"ÒJ¥´R6Fo.Jìuß ÔwhÂ@Š—Äˆ@’Eccr£æØ}Û~9mí…Ûæú3œ*’ôn•#¡ÿsLòîp3 ÊŸþ/üÓ8-þüäGæ…Û oº{[›Îü@é¯ô¹Ü{Z¡Þå.·XM½v\$UíÇa!á䂤 TÓñÙë|¡þú}#8\8Jý™90uüž5^½+J<)¥ú\–ÇÌxÍ’Á]šÌ½¹çÑÝÖŒþÓ”VÙÖgãéÏÄ3KmQ‹²w[TÞÂñÊhceŒ6˜åD¬DvŸ>Èíz¢¯o`&ñ‹  XD\“ÞÕ¾½£´WC@|ÇÜîÎ=|¯oµA„è¸WÅUÄþ©Þm÷+JÃh2Zi­Dª=W3ÖÓ j}NdmrS]Š$H.x~^a×¶žñÓlL?›°Î»4þ')²çíwõŽÍ¿}"#w®  1xÏ’¹¿0iÛG`ïtì~÷·§{ßüo?›‡ÃÊygµmÞVF[b‰ªFªFª¶WujŽ " ¥ÓK—,?³ûu—´•ѶuzÛÖéÓÏœìõì¯:³ãñS—½WþíàtÀËbÎuâÒ¸+h£Ï6O"!dÕKS¨`$Ž;d¹A‘—#ÿý¯±„ûÌD}׫KÌ™zA J©DÃ×Ñ|¿ã š+ùÚ¥Áy'îûsôË?Fýÿ¼ŸÇK›âbКϵ3~:ôˆ6Zkí­µ §g¾Ñ ¡kSýAæ›-¬±Ó®k‰EÀÊ+wæd‘Icf°ð 7úçW6ˆo»SA5ޝ1riŸˆ‚(¸zr‡cbí™Ý(­é7 Vï 8èÈŸý¬·­$KìYí[ÆïãÍÅpVímžíbº£¸Î¥µgyRšÒyÇp“NÝäOæKcV|qÌÓõäþæU§Ww«Þ­*ÿx‚±›>9aËÇì˜ü_ž=#÷ö6´?p¾8´m›Þ¶mú Ÿo+á7Ÿ©ˆ¥(D" ¥2†#!ŠÒ×Ò©¥¶ÚJX°å¡¶mÓMþKÙÑæ†Û_¿{¼·µ$o»sÂàþ¦*+±œ5ÛN†D¥c4ŽdÁðÄÑéÏôËŒLd{j¾'¿7²ÉtÎŒâßvò®>>1{àÃOÇg,ÎXü~E କ~ÿ~óØ~}ù­nÂTÝ6U·ùùJŸ|{“1fÍÏÛ|õ¶}2iR+¥­ƒ¹×iÇΰafÚ9¶–§äÖþ9žK$Ô÷…øa㽪RÕÄíDqÊòÿhTq\¹Ü¹ Þ{cÓϾéSÚ+ Ñg*èáG™X’V¬,J i­T²Ú×ÔÊÝk­Õ>5¾ÞŸ©v#?ÊŸOÂçÕn¤@M® `-b§f¿æ;«ïo´{Åm…µÂ`yWÏ,ïÈ3dê);{^ÝÛôêU>•¾ØüóoÄé€8 bs*vPFûQþ Ç"f/Œ”ÇÑÊï}M™½žåI(É¡©£ø7¬’ëŒÅ‰!óÀòƒD\ß÷:tíTJÑÄYEm´Òzßû„ R$"`¢2D@E­ÑžÜkÃŽEXXpfÓæGº['`ocçF`,€f3€S'ŽGÕ·QåÄ–I»‚Lha;ªQ¥âœµŽs;Ïç¨ïÝÎðV»(˜\Ò¬I F)­HiEZ©Ÿ³&¶Ó›z NúÔ–÷﩯_‹¬J&ó½Eóö}ó¾:Ú­{èêùç~ÀÖ;Ï$ñqR¹¥q½ƒ0‰ƒüƒ^ÆÊ©õµƒªÏ8ò¡d:} Øû?¨¯ &صu: ;\þÏ…/¼ü¥”£Ç@””ClD@ÁýÅøÜÒ®´ÓŒ?sÕò§'.|lÃcçø//Â1{ü„Û†h›øúmIÚ ÒäSÅ`ùÈ—åÏ6Úh­ úFγ‚þ=é!u¸1o«–¯îX:¿eéü–å«;ö·ÏQÈï瞈àþº¼©§,O’9ð䣬Д ø‚ÕÿþðÓûeöwŸZÖ6&±[È“¹VJ«®ßvRã• D»÷ñØ1ߢvw°ÖÆçØhÚYkffaæ×ŽßQFmšAf7Ya11%‚]”±‹¨G’ ï–µ±³ÎÚŒÍõYg¤ò»‡<× ˜ˆêe»QêcÖtŽš9ý”[Ûr–Øf)1𿦩Œ¨%ÏH¡œ®C@Ò´sCÇ„Ù~¯©ËyÃ÷®wâ˜ù¿ƒçêÞ d¥µÜ°¹Ð |¨½¦ÖŒ%Cx+ÀÒO¿ÿÃOðÏM¹89/á«yAÄ€1"‚hÊøü÷ò»ÎLì¼¶m}è¿:j^n[nð\4uŸf5Ç I(>qc’«á’ö)éHÉ%ÔiPâ²éÛVíØß±‚9ÁÎÌNßOòe,H G"ÅSk:–‰­(@ IDATÌkY2¯å©5ûeÒባß™šTóÞk\O€"EèãÏüžóK·ü¦v‡î©ç´E†˧yÇÒN­”6Fi­•V:¹×ž0kêÞÍ;voÜ>qöÔÜ™¹ò#e‰ÿ®3HD¨ @Š£Ùé„âã8oÄOlÑm]n–Ùž£xÖ¨htÃd©¦´¡E 1Èç À9—ôÞsN8¡xNÓ5AïÌ0XQ¦ÙÉ(u[a½õŒž ‡u]Ýlñö®¹1úú3ƒ¤Ð$b4û’æÒ0X¸VâWW|ã‚;¯s„›ÜB\½Âo,¡4Ó7˜’'÷à· 1÷ƒ"‹#q‹ KI/8€ JÀ "¬=€¯×ÓÐÐë 7 Âä3}>ûÅâûo('££#å¿ôm ¹­•Ó+}×îÓo=%¿nÙÉWŸýåºqÅùõ}«¢c=¿ûFY9ßzPúûPý58KÍú&"Àf”2„AÂ9ÀÜpG2f€<`^¸ÀšhÊ:L±bmçâ¹Í‹ç6¯XûŠèúׯÑÈïxX‡Z¸¹‹*@žT¬Õ(žæ¯a`ªe†«Níu±òáÈT¹+m´6ÚMD"DZsvw7§_ÿ¤WõÞ3šÆ5«=¬vÎhÃÌFömQ™ÁN„‘ÕN÷R“…½?•$ÏxyøþGÎ9b‹ÒFqÌ ?þý”ºŸI¬’ eHÝZ\¯Aªcœùg ÀêûßË)¥Yíðü¤¿~GæÏøø²Ÿ±)¤žßÏ —Æïìj )€1[ û š­8qƒcˆ`üb´=0 H¨Ï{モ?<»ÈqÝÜP!/á1  Ð…çUPÆz@‰žôè5DDÇôÝiïtgÒ¯sùÜoËÇ#å÷Eá–•ƒQ<ÞÊ·ý)H-WE²«–¼«Úc´6FM´ÿO[_xOGT¬‹kç=pFô X±®sñœæÅsšW¬aÒ£G!¿dä §£’M݈hƒ¥,{Æ[ñÌD}§G 0u‚ÚÖÆzÎÖo¿ú¬JµÄÆÏæÚhm”1F§\ÌÜéÛV=¿sí––©ãÙ0ÅÚ31@IR+D„5ž1,ìéiž5®óÍ9nii•*ÀHÌ$Ì.uaU)%äžÁ²Ëw{E¾cœw4þî× Ö¤´¢ï4®×P¤A š²ì‘Ú çØ!«“²|êÏT:¥ÐR×m¯f7'"YHùV~5¾þöA«Ç·-1³W‹%q´ÝÉ”“ŠÛÊíS*M¥œ-ÃVf†8°õá°„_P$S “§ê¼ªí÷P. ï Î¨ül«~ÓTà÷œˆpÉ%¿øE¡úš2±Âä× s ýôó „žÒÿÄÖYg™¹Z t^qe®óËÊÍÕãs›åÑãb}@D˜ Déýû þ%§ú]cÒž•)‹(¸¤QXŸï_V§WÝïrIzèiQ§àÙð+äo,¿ñÂòO~”'àM—Vüü'E!„÷£sKå³zgÚ†í£zJ™G·†9§Œ5ÖXkmÕUÉ&Õ@ךnKÀâÜ&+ªÓk+Ô•8pIÄ"IHˆ®^µ6Ê$9²·=7fqn¿=x½;” öýÙ/Ùiìï8ÃÔIL¼üþ—½œ ûò¨ç}ø,Æß¯ˆO[dN[dþ°2QÜßüµ¾æ5ÆF®>&±à•Ru&|Tq¢ÚÙ2;ç@äÃ0€‰³/ÇŸoü®¥¤’ñˆ$Q÷\þú~á7ü·Ÿ dù‘’WEÄ[ðufV½?SoCBòÛhù9Á©ýÞÊ»/ýæ?Ý“»vþQèÜZ–8„<³{ Ÿñ; Ä„ŠÀ 8 ±À61߃8†­ÀYüèÎü¥Ë*o¹4ÉÉùéŠþ…M<ÛL/N‡‰U¡ù‡±µÚ:m­ÕV£¬µÖU+6™FJˆN+Þ¹n¼çÓr›šUo'7ø òÙRþEÔnn8¹4ðU xë½N¼"½ ·dCëAsxôÐÌ™¼Bð2ðûSk:—Ìk^2¯ù©5ƒW½!໿ªú×Tî=}‘aÁŸµ-jsb‹(o’P‘R¤€Dħ ,ô;ÔÞ¤«=ÇlŒQ^­¥Rån´–rÝÎÚ8Ží¨cÇïݸ}ß–]d&бÀ1i!ÞÛ øtI¯ËrZ9£hf6âØ‰€9±àEÄOu'+öSI“Û?H¥–LÍ¢ sa. r…=ÉùŸ|ùrß1ã„eOœsû»´NêÜóöo¸œr½›ÿUõÜ- ë®ìž™NÍÌògúZðýü™ôÜ\jAß`β³%W:ß,»¹`«b+°‘4Ç7Gœ°»¸{L¹gF%ÿl¾N‹8Ú½c$üÞ2í«—†@¤K€gy*œ_•ô\[a'ýð¶ÂÛ®,øñ÷ŠþìÇ/;Lioh{ñy½lÀݽomõs­ÖV«­ÕÚhc­uQÕ‚t'UËæì¹sÝt'ä6%Cé-I=‚dÀã$B–w”MtÒ^íKÐÔ+‹ãs›kAŒÁòv®/ˆ|­R{ÐIùçk_½Ð¼z¡ÉLsj3ÛÄîñDO‰ªeÁ‘ѧ®mþÜÍKçlÛh¢_Vz"9/¸t¢Ü“ɧZk)÷°u±ãØÚ8Žcf÷ó¶Â›ÇW˜½„§TÖB‰„  )c³6ÆwÔc–‚”…ócŸê¶ô´ïí*ïj¢É‰3C%œ"D€cg½;ÃÎ9Ö saïë9ÎçÎûBóY/$£•Ož¹ç²äžK“,ã#ógJíÒ0”ööM‹L'¯ àü­ áëöáké¬ÄÅQ¾À˜"Ž%©B»ž€:™K-©9oÁÇH§!“8 „ ,JƒM¢ßÅQᵑD$Ïl…€$ß燷A¢±‹@ÎA¥£yø`Q©œYÐÙu €Q£îÖÖjm•v~Å:Ubi€ˆèsöÞ±nìÓÕéKriIn£Ðh+ãóÜk“U½Q“J¡¼(0æöU-éµÅâÜ&©£úg£i×Ìê9%.Q¬È*ÄŠ,Á*°’ëñžÄ¸ï¯4Ô võgƒàPGWÚ?>kœ²ÐH_ubBmBSK÷ ‘–YøvþáIíEt´Vˆ@¾äuMtoB幉;ç™Ý³¼‹ûP<[bOý–\ ŽŽ"39†}*HüŸlI,ÄRÓ4jžÑ‡ÙÙQÛSÙ; 4º‡&μ⌄廻/.—ÞæÂ0—óù0Ÿü³Uâj%Ž*6*ÛËçty:Ô:4:T)½§’MÀê)ž@úÜÍYq± Ïi²‘s‘³UVÊs¯òÓ}r›ØØz_ÆÿgS%¿ÁN&H\nÍÍÍ%í=œ¸^éY¯ßÔZ+c”ÖB$"Ì86 #mÆ‘¤Er(õ¬‘Úîl™A†AÏåòA˜­%NS%oHÖ>;-‡ ÖÅ©_þÌS7þàšÖY§õýWÜò«·û—&ЭÍo+’ {v…’ÂÀ¹'ÑŸZ“ðìõ»í³4 ±äYž½~/¸Â±lŸ rOd;‰¥Æ))§Û„ÙÙÑžg²”þDÀ Žt/ 0aÆý¤ýiçÂ|.æäsù< ªU[-ÇQ5®VâËçt1“bRÉä&?ï7õµ’ŒQI#°>¸ªji‘£t¹CW>ÀUsºPQRURQˆ”T•ÄJ ¢hÃìõ»f˜ôWð(m¯ºß œèw;‚£ÃÅÿä•EŸ¿m)é®9§ì˜Ùy7ÛY—ÓνiñÎr?øóøyl•} xóYá›Î ·ìb:Žj˜E{Å™ªwß©”@ÇÏÔ"òÌ·x¶¶Uk#g#'ÊG$VÉ´<ÛÈ[î6#vÛ8ö”GÀúÎpþd-,,šÄz] ¾[]e„@i§Y›27i®ä£É+•v"Êéñ¬X”øé3DÂB PUê"«ìm÷ ¹°ÔÝ„ÏÆ3å]!JâoÞVÌø<#t§Y;vÚiÇÙ?¹K—¶……0WÃ|ø»û¼¢­»øØ À½v¿þô¢‹™C¶U—ÚðÉw­>¢ZÝF¸ØqÕ¹ˆ•öyî:‹©¢@ ²=)©Û>â]úÇËk5žîÞÒxż^%F¼r÷Mâ dɆm´cVlIËž{f'‘H§èCàã±Þœ1F‡¹0̹\ˇkv†‡ö…¹çm7gëÙÔ¯Ž^›müXû’žÝhš_ût¿‡gQÜ xëèõ÷¡\š]Í=›SŠX¥þwš+ &°$S ¾¿³ 8½; J„!ŰeaN(·0¤ÁµPAÚI„Îõ¢LB¹ÉõÕÐé]­(Ôj‹Ý³ïöwVûÚß+ƒcK¼—F‡wÅ‘b½yjûO·Î;­iÝÖ=mÇŽš”ŒVI?¹;óCŒÿp)¥•Ö?x¾å̆´²4õŠ9)ÄŠœBLb˜„|ºH%ÝþˆJ@!!’í›LœÑê:­Ûk1‚>üî\mýÓïh‘Fp7ëT°ë„ë%ÝP'æ3»j)'ìäÚ %̇¹Bî‹·W,_k9mÅö«GKç¿*ïBÎ%!‚H…¤bò]œ|±1<»É-š¥/<£¡ÒÙ˜ÊÈݳ;ò!WJ$»µ™|·±Ã|ÝX¬·Sf›mç»swÓ´pç>Ñ Ìôû ! 1 $©Фff@{‚KªQQR3RjL!ðæ;³s,¹B¢Üƒ\°bc€•‡&Þë‘õãwÕ¦¡þǘ§Àùê–ý÷ì‡]ö>çÒóîÙUTXªó«ÁŠœO§ñ5q‹Æ×™É²h8ñgúñ»Ø 3ݹñJÍœÈóÜ8ÌøD/€_l@T¢ïÜ2©¾ˆ2¤BèÝ3ªÞq r§nG$«Ï“¤Þ½/ éç¨%©W_ì~\­ZzóÔ}»:ã-‰Udº¥~$DÈ«„3× Ôï{æ.›Õ%‘"OëVÁ‘ö<ž vJh=‹¸F!ÚPÛ¦-©ü­Õ¸ÁÑŒá9•œ—­ ûÿËŸ|{)2-éA?*wÌì8ÛàÉ^Ñ—{«ì„ˇ—…a>üÂ-=þûõôz·x¶6¡v9ãbfËiÑàä¶¹Öå€Óõ ]´}…Ž«ÎEŽ%Üï 3;7vÎÔDµÇ±cÎ7äï|ttß—ŒÄ÷לôZM¦³&36뢭*ÐÚ±‚3 &%0€ø Ĝ弹0ƒ0„ùàÎçÇtá°"£ïsN÷…OM¸} >¶ìfÍRû¤ @±% ÄäÙV*x‚Or%YÊ÷åò¯­Ê‡̂úÓ•üȬÈîP•§Œ§Ï°dÀ. ì|*c2Ï–Ý[ tj2‰€ BÒt@½³*DßQÜm*®çOPiˆJaôŽ‚2 e@ZzŽ¿·sû9ò£~eµÖUË1„&6ÛÑÙζhœÔÝ“%±ÉŸ@PZ‹ëfRûââå3º¤ªà1‘Sˆf"a(d&Œ/7D.¦Îô`lËdgmGÏn@z9<-¿ÆAý¹2ç°½ñ#82m£á£ßûø36¶J)ê`RJ)­š´ôoÈÔºv’5 óÇ‘Mêë:0Ëß¼«áßþ·wñL½x¦^±Þ‰ô¾áÕÎéjÌYŸ‚“UŒhÕf7]cnkh«VX²ÌÉzr+ÌÌ»Vmôc ;A)Z½+Xt ™ÃÆ5ºJÕÝí÷G‹.˜Úå§;ÕúL¥…» ´)s1²³D Äô¹PµÌÇJ« †ù Ãïo·8Ø`E<ó¯ñ~ ð7ŸÛòÐ{^ûµë¸q?8À'®x·ãAʪLî(úI®»ZJb_UeaïÏIbË@ÁùW˜ê÷ÁøÆ9‰4rÃ뫹q’+;¾U œ¿ëe†í"véÍ~GLõê*  ZP±DPçÛ:G › i( kDYYħÖJ9¹\?;~ò¨ÌÖuhjN|7ª‘»ï÷¡´Öº´³Ë¶ÇK&¥ªÈbñäQéŠ7ܤP›Væ…w[笭³qˆBlíï«óO˯pr~½å Žèr#8ü~Ý%yëðÍŸ×Ò"ÿý'å\’#_B]‘êRJ)Õ¨ Z÷JÂå©€Oa?+È1W{«â<ËËGßY ¾÷‘€gžwžitÀAȵvNfÁS6ߛܤӫ»]ìÆLœôÑ Œ6šr"®ŠµQGqdw¯Ùäë)ßšZío¬LJ¹uƦt‹Ÿî'íüÈët@€@†4¥5sI¨H4Š)3ŸíîØ…a†A˜ ¸y\ötÆ|³EÒÄ—¬øomª?ŠÒ~±?rÿô»–96<¹ˆKI)Í1¥„öM,CwCŸ]……XŠÈÑs¡ÌŽÔ¢…ì€´ÄØÔ›z7¹1ãwÙ©e‡¦©Õ,gÆ'JÅqR­ q™Ø%Ê ÞÒNIýpE:„àT‰(. ý)§´…2 2ðœ®4ÈJ²ñÜ{!Ò¹ãµFM~BÚJí´u.ŽœVŒ@#&']Dõ3qiJ+¥µƒ×IÔHV‰ó•'üܨD¶{Oò^ƒ¤o¼—zµÖ9ë|`ßZ[ëb>Kòäüzÿœ‹Â-ÄPGhÑà2†¿»Áæd|éçÕ]°c¥)!ßnEJ©"Œq©.ÌZSñ)ï[®ôTĉ8‘|ø†Ó‹,¥g7ºÏßÒñ‰wŽâ˜ãrœäÅùÂÜš”R&Ð@iû®–¸·°õ©u{7l=fÁ¬¤qGHI$ÎÕ"ªÓ&Xkƒœé|v§¯/¶`r2Kéöµc\ñºÜ¶6~øéx®ÙzŒÞ{\n󯶷¾~r„ýl¥Ú@S'वnPÂ@ç¨ÊÐÉvJgBrR䯙 rA˜~´eÜ’`ýhÕm¡;¹ñø`ê<ËNÏ6Óõ 6¹4\|À±ãÿ¾çmYØcPä®>»ç+!ÉDÃÆ¼äz¦V¢}ÀœU,iŸÝ(µü™ÙŸé0ùÚ^["[¦î(ˆ ´QhC¨ È€ æý} ;”·(<¿«ñ¡”{!Ð~hÑ:E‚ˆJ? ûEƒê¤ºÊ~j(‹ü~éê“2OÍ6YCÊE±©ÒhTÙ)¦º[ûÌ‚„a´¥SZ‹ÆH‹D KjÂ$ÞzN²¹vé= ;k­µÖZçWâÈ9k­Í*!?^IÚª, ·ø•“sëÛ¹q˜÷u’ñ©|é0,OêàüîßncKŠˆs\Š„¨—)äI”‚Öã8‰ÄêÔ§×,Þ-©V"vâœHA.<½ R~n“ûÂí]»¬ÁZ?ÉßÁs‘"%DLIj‹œ‹xâÜ»ÖlܹúùÖ% ½rBl}¢Œu±µ6Þòð³~&«6†(™ûäk]Öú‚Öš6›‡×Ž@²äšTéÕ…Õ÷í˜ÿúI=F¬(I¤I-ø´Q§(2A>Š*"NLšY'‰éCâÓÞEDr¹0Ì…?Ù6îÌüJ¶Ð¾ÜU=f6GÏwö™ÎºÚy_Ì2Û´såÚäà|;uekûÖ#ëÆZUOª„ èc¼ª'°±ŸaÜ¢ ¢šÁÅU‘¼'Y’d dòºæ(&t𡲗v¿õü“$%Vºb°jÂ!Ð_ïoá)Äùv€mþ çÒuã7ßøÂ§‚ìþöï|Q~Fƒy­õ•ZF k7 älŽåbÖ{ŸQq¤Šm¿>°ö×g›KdÏÿ=¸è˜š{™»ôÇK¯ðmÁL£„#"‚(Á6…·Ó¦k2Ë=%*Šr§¡„ìee¸?'o„XÏDu—zwA˜ýäM­›?]í¢êjhÎT‚ªaW>9hǦµ¡­ê"á÷ >§™¾9”y˜hH»%Ó¤[2¥úB«’ÌG¬—Ù8mÑyBˆï"Ê{ßËÒœ²©ûG÷ÝS¬}*{çlüÀijß}À¢ygî¤ýì{Óf ^ˆÉyßk45lŒ‚AÂÌ„”À$ÆZÑ^®FEƒç¤=7²·¿zàÿ¹}ö]?ùöW /AA*&R¿ù¯“ozñ`9ëb«*±]}Õæw>øø¬ºâ¨†l4ÁÊ‚õDˆ­acÓÔcàƒgCÑ4ØQÃÆ¼dÙ16›%ûÂÍ- IDAT[N˜™]ÐÈØ°—‘>* êzÄLþ§ÁŽ$JUµØúW$–UÓ´ÃT›,øj~a|úkΛˆ² F…XHA$D{çégÇJ‚ƒþbê̸¼Ñîð虼 N‰ß~ÓMOÜ?WÞwczÙée*Ucª•þ'Z¡=¶gŽYü¢bï½¶;âCi—üöL“àÁÃ3NÎÂ9ø€vÀP l¸Æ?üy;òüüÈßeÉJ1å$üCIz£¹ aâ‹i2¨dA–pEIdd»³ª&Kª•cEuw|âóƾ$NÕ¢6˜é¡ø"ÔµVeb&áâÔ%fö“6€PJ ÃÞ«hQ–`¬Hà o{UëýÿÞù§/´_q}C\ÕÈ ’jˆ“*€P 6acŒZèÜk/Ùñ¥{¿ãþe—^ðÐ?µÑl‚@ÄDÅ'† ¨Š„¼# ×µ ckÔ˜B™Ùk ÏcL«¸=m^œeF§ðÚBÆ·ÌŸ;ŒhY_uk7AI!æé4ecÌ‘ébÂn-WxõêI¡0ˆÝðÜ…¨ïG,¤Z½oÊ”@1×’9æîz2ÎïÿÂó‚˜Ýpê¸ûöíØ01˜§Ú‹öJûšê{ªÆwtMÄ¢¨ 6(qG>•AIE£~fÕ;ÚS¨µX¤C1/ðïáæ-ÀÌ1¨@ ŒkÇwG:_‰ÑöÐÙÏÚüËÖ¤0)™ œ‚S¤› X!×Q(pM£j 殆ö³øˆ×U4iI™R ÖÃæoÉ —ZPØ}!)ä¯u ŽRjE‹šêj¬Nʺ{ZhQ~éƒs¡âï¾Zð*Ä”µRbº;¹6>ï‚ô@uÒ˜;ºïlüW‹gßO˱}èóÅË({Ù ©*Ûê–qì<(;š×_•1&¼R¬»Ö¾!0æ6Õ(b‚1A‚jÕŒJ÷ú›šIš@‚h‡™ˆZÌs(ù5½édžCg:M™¹ Ëñ:]œ¬|Î…;ï¸Çç¾Ñh6 ѦîÍ0Ïè7Tô±Kà…½ #Õà‰‰4t fcØ4Ø ¬ì°ñíòÅ"Nö¸|½½ç1¿c¯êú¯üäÓ⹂x’Àd”c‚†9k%¾˜™Û b6ŒÀ’Œ8 A^DËÜKд‘p[I…ŽüñÎ RÑŒ?NL f%µÐ "^5¬¼jÓŽÏÞcšH©¡ ªç\tÛÞCUú„*Y@‰Ôfb¤@3eÒŒˆh6GPñ®‘&Äê0 ‰æÇ½4Óìðµ•à {ÂîäVç¯ïø=¾b°ˆÄ²$À+˜,«¹p¸°f ‘VÄ<ÎwÕÊã>6*¤TAq¬ÖÌ=mê €¡'¨z×ñòç®yéuߪ”ª‚K_q᱿Ú]%`zw­÷GŸ·{…éÔÈw ÐñX§¡.Ûùä§RUx háèIt „6\Àê•Øs'§!da 8Ásß7{Ç[ÁÚ˜Õ)H‡àf ¢*•ù|u«¦ 7¶fœ""þÄŠŽ2ÀUþL Cu "¨oÁ(´M ¥”xeæ´4a6}\€L4Ef0Ï„ÍV©-XyŽÂ¢o~-j…LŒKðqW—¶{ï Ìd ›Ø°fúìì&xlH÷u‘ý¬2Æ¥Œ>Q¹öÛ­â4- —­ÿ6Œ}¾S¿o;žì=ž-ø¾qµQÅ=[¼*Ö­0ëV˜íûçàË?Ü3ôÚ˧$&CÌlZÌl`f-Ú†Á VŠ0r0W(¯JdÈZv¡ô)Ÿ-íÉ©Ÿ!³^œi&É DŽN—¿>œý¶r%SÑ|‘£w<>Üœ7ŸtÚhÌ`fÄ&(%€F›ÇØ^ŠºXF•$Q%ÈLYƒÆt»‘Ð{§kéóÚP™Ä¼!Ç„ÀpL€v²}â)%°ê¨ù™4´sG†F qtõu¿SO¿p-©GÍ @»O~n^xßÉÆ•écî.7<ÑþzRnqå«×Ý}ûöû?òðçZ÷½öØuÐ>À•||…@:79Ó‡ìÔ?YÛíIÌR?pK™ Ù{“Ä®D1 ;€‰i8Ù¤h6—XÕÀÈŒ…1Æ”ÇÅÿg†"°î7f‰ÁÆàÑ_€(„Ô«TòùÖs½0²·iR˜ &ÅѶ Ú×¶qòEóý3ˆHÉhÏè„ì`ãOK[”†Ø ”¼<šç;4E:ÀÒ$!Ooþ%ñ?T»¤‡ìqÒÇÖ}%yTaC[gbUÇ>}üØyõhVØòdü¾GqѺ‘îatFÈ9ýA¥3¯9аˆÐül êɾ۸÷±‰§åuP3÷{=ùD+<óø~ᣵjbËž`ëÞðÂkÒ›¯NOÕtÿA"¢KVùÈš¥ c˜M·Šjv_gn”903“<*4`¦“îGBº2o/3Ù ¹ÉõñYÖAYò°¡ÑÉâ׆›¿"¨‰ZH¦îÙßh4”5šÇ'Îæ'Ž-¢Qd)ƒU J´' P«¼µVD(P(4` YG‡1;qf°mÒ|´d BÇNY%4NÚ»gí¶Uáí$¤¢>äy0„¡*ƒkb¾|j¿dõa@è‘çèû×$¥x&¸¹ùÐT௕›¾›ÝúÎÜ~cZ™Õø\ã˜e×@v] qâ¨^€xô/‡ú 9bg¾ÎñPùÑÌL´áG÷cùˆÖŸ¥C3«îžÎlÒʲ·]Z‹Ð9Q x €D#5‹ïUwB”Bj͹×ñÀ¡{=ßüA"6Ö»ð±`Å™ÐÀkf¥8K¢iX¼:{b&Ý•ØëF;ïJ¼hþr½+ Aƒó¼’xRgŒ&–©Ù ¸€ª›ÚÐxnŸ¿äˆ"/\¯ÀUµSÔ–Õé ñÔÞ²¢‘›×Ž<•Jsþû^ÆÓ÷÷o}Ú42÷û¶lÿd¡ éX`3 ˆÛ­&æ©5‘™Gì–ZA­TK±«¬{DÅ5÷Mîðµrów³7çï>‚>©Ì‡VÜ÷²-ׂõàè\×°üä€Ä!MQ3Î4ª Ū[oÈ#ÍUôÀ{øÞÃ{$}•ÝÄ"ff±ý †[H Ɔ1oó†ÑHpç/.ÿ¹v2¨É€î~Ï€±XciöH ÞQ¬)Ý}ÑOí2þ³ñ]šw dÐO|’Œacؘ”~>ŸüCðñ ƒg¦’‰!·z@VÀؽÉö„Nº'óÔä Äè´ÿ(k½ "Á{ ÍØª"ÌdšiR¹q48­†¢ÌŒŸœåá¸ïŠEëŸJ®ö{ßÜ~æYÊßADæþ4BóåxæñéÌgìOÝU¾àÊäÇ.ï(Ò݇¤NVVCÝÜ €®ÚÈ5VFH¥Ú(Jg8Ž; g«ÖCø(MßìÂ_X eXâ¹Øfÿ5U Rse ¦¹0 3´g/o ß÷ž†âu‹ OëÁñ=¢ `Åâ5€†nWRÄQ n@€Èß«Çt&¯e =Ѐ©¶BÁж9â¬óÍÏ`rû‚5ÕŠÚåÂÝ‹T]0š‚`*bÃ,ʪüï߸ ¤Ï¿¢vk|n¼ráô•;²÷œgêôÿÑ{°à¢ZT¨}@U’‚9@ßw[üòŽ›&Pp>`ßL\q>üÖßbÞÞüR0cjn—ɱû(-þÉûÿr€˜¤žOÂ^‰™Ô÷yj¬¯LÍ4?n‰‘~a |þ,büÑñ,(Q‚Wõ @)Å»ù¯Ÿ²/õ’}ôÀ <°¢ñõ† µ¯mȾÔRÖ£Ʊ¨˜·þWÔ\žj O –½®]ÎeÞÁ;xÒ¢Ó®,.Š |ãQ\w1®Ù„=w›t<ÖJÙÿIàÐß·Ø 0!ʇ"į±˜ÚS§kæ|"V2(Ÿ_ÍŸißòÉÄš~K‰x¨•á6Ë@ÂìFJ+‰š{dçÀûA(졬³”EÓ° &hXÞBQ–Ò)1ûx±@¢'Ú'Yãð*ÍŠs\œíîÒô‡"²Ÿ³ñ-ã»Â÷o«šÜ­¦ôÒõ£+ûŽ™KןºÎâÁiÂ`)ÐüaÙw/¬Å£»°ù\¬¹:Lm1Í…’$áøÇœÀ$èÒvb€‰¸GäGχIhbûœt ©&CÒ¹ªçG‰‰'ÎÓ#óì˧§gïgÀ QRVGG e²Bîž- XA‡'R†2d~2Ó}ÁÓ~^ó‘IiŃé¬äÙxêa¯Ø8ú“wgXë;Íúu¯óŸHÞ³l¤M0õä3L¶íØ,­S9Dš˜eô‘ù]û»w*½ÈœOWCàPK‰ç4Кf µéhI°B  lUp„½€Ân$ûß`…Ò®»$¢õ7]=ó¥cs0•(õÝnÂ[€ë$Jô` ªª‰0QÖn_ è,À–dýÅýüy£…Õ<‘ÒÐáá/5Š£(ÒÃŽ%‰aËÀlüMª¤U½Uû²1µ„gÎo ‡BTjs5U§ÑªX‡Z ¦ÛÇOLŸ¤æÖ¿mÿçŸé[¬³ðù586ïj+”À ˜ ÆÉí¤C«Nãò(­þ3å ®” ~sfÁNN#¼àüÕÁÄJ‡¯?†k6áù—â¡hç0LhŒijqÿïšDÙöÃzÿ­Gäã¯æoÂøŠÒšxÖé\ÕÐøZƒ«s=ö\Zð•êêy… E§X4j~ê¸}w"PB"Hã?Çà`J ·á÷Jö†$LJ@V/] 0úá=Ãè›0qc÷(Ís{B΂ûÙø¶â»âïßq59ÿû·LX²îßÒ3ÍxÎ:"±“ÔËù:6•‹m°hb«Iñü¤ÒÅ…Ç‘“}T/Ž Ó‰í¶n\ý‘© …°XÂ8Ý?è“öÚTÈÁ'5PÂ>éλ¿ ¦u×?3ÜkÚ^åÁ#sŽivŠÖ— éýļD'Ú¤PÕi"RBÃ6K:HÁŰ[J8:2Ûrc­r¡HÃB6–ýŸ)ÞÒŸvŸ³1ÝUþ Ñ'S¡²CEºÇ1Þ*ªš UJû@§`ÎÝËÛþô«ßzÍß]ûªj9ù|« °1LˆŠ¡¯S»!ž—öÕWë~e&p¦*ÈRLB#Ãü€ñch˜™Ál w<„k6aþ§›3W»ĺËeh¹ÐãOJ`0r}œÈmµðs‰|ôn .†±tüáxŽ©7.£|ñ§qì†îq1]ëcAÜ›&‡áðÒIú¨ãi|4¾@d¼³J%J±U‘h`R d°’ÑWž3ÍVaqûãÃÌW¸8Û `s¶çlfæl<õ°÷<úLÖ©Oɽ_½ÞöeØãBM>è~RUç,ìMI…Õ_˜B‹†úda [PÕ±Á¿ôcÚ `QCd :zŸfH‰ê ,LD8BÛŒ¿çw›ôRç]wq¬ZݲhúãÇç€xÅW O¥.â ªÉjèñ÷¸ÚXm7ÑvL:4dCÃ)ABÐŽ%²PXÚÙ¾Fi2·Jž.t¯™à€ˆxVu°J>‰ª( ¢'ÝUëR/hmyA5{6¨öA¼ŠV+.O·5OÔ¿ªÀ‡¾¼ó+÷ò},Ôøj+¿¾ @…  VíbºTt~ú4x4ô¹ºÛ~c`Í/µ¡xì7®û?3ÓTÉØId„üSÍã÷€,²ÒÆ€YSM‚/,Ëq*OPœš%ã`1FÎ=•Ë÷§k_Ž£P~Ã,€Æ]™{ɧÑ;Yk}$ÖB`ÑMƒbQ&i$2©U%À(”u©)O {xGWõD#0¬B”‚¾fÕ4Y!£ÿ°môÁb5€ÍÙž³6žz<“õÕk.´î|¸"ï×mʪôJÚGÓ"Bk-…¯1]÷¬5¾õoP:€…£ 2¦¬ÐŒØyðgÏ[òWjI³ç´Ë÷–©¡”Ô+y RÑD¢mDòÇ~¼e~\BXùœ‹U„LUgjnI_Ýõ_ÏÜA5çž+ÿ© ú q–æLq‚™ÈÒüÁ²,&ü_+ûÀÞÊÂGZyÊ:&¤Ï:Àrn¢‹Gè¹ Œ…Wއ?o†:¼"„ÔóÂ+u©éþJUêëᄤñ½xîl^ƒ—§[lNv>©ÿÌ·ˆ—Üw½"¸Ç]4°,ˆ'q\ÃTU8OIÚ£}”ÔcûoD<;ÞÙZþæÎêÿÞþÊ›oxÏ 3˜a ]ZÃšï²æYãG‹é=ˆŸópþ£r†ˆ -šo˜ùÄ”~}K àm¯ÎÛ…µI«ù–EÉÏŸœz¡W” Èab “ üÁÿé'O-ž7¹ïøjº*3®[‚ÆQ¬æÆ€òk3O'EÓRåÑ«|nw„æ’kVî¿|^2:{tzêÀ¤ˆ4Óy­_8>ˆ`#ÓH94)/é8{s<½=ð‘Ùæötú'm&Ux“)‹j®*¶Å"CªA‚¨ŠHÐ ª$"‚ÄùG'”‡zÑ‹ôò3-½¥§¬Þó«& H‘´ Ó ±Þ²ûµa;û²Òòê?>::zõ‹ÿæ”ýû–ÁçÊõØ.³¨®dÂÄꜢ–Fµá !€=ø¾L Ä!tµ1Y_Î@q‚£ÿн¹˜ÿ’âØ‡³¯Þ6xÃÎ0Á ,Ð7~ó¼±yŽ+Or:ŒbHÜ8S‚lLƒS-mraW")€¬÷n¦¦óßïRfš8·£Go°#uÉA¸ÊºO*é¬4;~½Ò iíWà s2[L7ÌÁŠyC…é½MU¾§JN50Qb70Š 0ʦÊÛ\±fô;'lL÷=ú]@ü³”’>K?Ö÷e<“ø~çÃþªöªöëöê«wnñ®\×v³œ§äbÎÀ軳!ŠÍª†Ï7ÄôÑ/W÷½z¨h†Ù4ÿó†>ßï\Ï /X:ô ‰Æ®Á#â ß½Å]½)‹ƒ½mßÇ¿ö®Fªÿ夫{BUùóW˜NÖù—hËîÿÄcydéü™Ã“Ö ‹¸$1&5DX8bŽM†Z£d5õóE'm0Írä{‚~RfaÁ8™æ#3é‚dz~rŒ(%ˆÓDDUª‰Þ™ABôÆï‚»¨xÑ ÷ ÖeCÀ ¶ßjR‰³„â$RâêRbM˜f§¦FGGlùÊÛGGGGGG+ÞŸñg3_yɽ׷d@–\¥ÁA w@=ÜŒV(H¡ziÁžÈƒ=Ä‘zh uGZ¢ø¦ ÄS9N"h®" xôóvÓ¾ä%È@@ÌÑW/¶TÓâfû^÷ÈÎÊüà_=Pt R5ÆË*Ê " †¾6>yµ0ž¾dÉà½K.X-^ÅÏd/Û¼ûö)¿tëÈþ¶)¼ 1Ó]ׇ£¤2ë?yÈ´zóG>³7öU)´96$3–i È  bRÕSR8œØ–a)… 'ÁAü]4 €X¡jt‘^¼H:ª¬S$Ž¡Síéõ+Æ"jK4^;©€>øP?ø…òÊî‡üì_5>>>>>௩ÍÅjè¡oøè–ÄÐ_¿p“MŤå¨VkLÿ]>ô†¸üo—õÅ÷^Å®¬ éçZšÑ-Rú"GÖ@Ô£XS¨‰hH&j#AÏ+gw9`éª:‡ü¤i® ™qå¶Ä¶të×̼MáØgS6* é]†Tñ8›ŒÞ’«¥Æ‚PãÎn“-÷Æ"ÿz¢c-áò¬ôpV1Õ¹,žºç¡dä«P,>þbìÉ*—‚tø«jF¶œÈ AtØZ½î:@Iˆt°ã§Ù²ˆ”|8I ™žJXkÓfIaJQCD¬‚ªîêˆE|g£?³¢ý7ûZ–+6¤û6¤û{†lÅÎÆ³<è­ï¼ï?ÿ]ûÍz.¿À¸wë©sźqÉÊúWU:¦{O¡ öv» IDAT±›ˆ£©$‘á.¾OM{=,¡×ÝÜ*;¥÷’&i’¦l™ª¡ã]i[ÃÞX›&+½wž,§­äÿq*¾ï/ÿÔˆ/]ðR”e(ƒwÞ—ND‰Ñs &œÜ±¿ÿcÓ³D´êê AÔÞ5áŠÒN‚¤i#kd“u¿]lÃZ0Ìhvzr ·gäž^duuÆ1&¡T>¼wdCcCÆ05š¨ÝÛ_{þÉŠ¼‹Ìž¯3Zº§=TK  üÄïDpÿ³ëÏù©O< à÷žo’`R1‰$  œáP²wÿô‡v@é=7o°©˜Tl*ŸXþAé¿‹ŒþpxiÜ?vïuk:Ò/´ôûß~àh\?ØZñÐÍgäîO}]âÔ‘8J›PGá‰+/zU§yn8öùÔígß!›A©<ˆ -{UÇ6Uö¼¯Á6#³Ö—S°«|:¿*Ÿ$6*÷-­N`Ä0?ò¹Ù™ë4›wVò[]é~dÏÀ¨QÝr¬ à’E¹+}»e,&gõµ\«aÈS§-A'aUY½óÎ5­±Ö›ÙꀸW«S„X LBñŠ FÉ(Y%£}¨`Cº_¿#çÈ‹ÖàiµŽyZâÒZ<ýL9MÛŸFëǧ%ž³q€o<›ý#ŸˆÂwã½ö¢sJ —iWâ Ó¹ËÖã#Ä|ÇýùEk ]QøÉ››e^¹K’„á„c A¼7‚1Á8'LÆ’"Naö¥wŒÛ^;ô®Nøƒ÷O¸íuC6a—ïsÖ—Þ;çK/A"Ȭ\D&ÑÄöÉ@cþù+Š¢pÛ.w¾t®ðI–ΦèS‰öÄë ¤¤V_LÉ€È`5JDû®åI‰Â䙯\9ù¡½+lNw„!n?7ûæ¤ >¼+>œ;lŸÊ"ˆõoäÏÜõç~áõï^ñÀÿ–€ÔDâzê¾!ªì—‰‘ž“&^rôÕÿ¶èö|è DéÖÇ/¿ãF\Ôs®ºÑ—Ÿ@÷¼U¯Ñ> {}™ BîO%@¼ÊîD=$Æ€¶P‚’¦vŽYø’Võ2d@F|<#OåÈ(0ÊVÉÂ}%ó?Q‘¿(£¹5«©¼òs¿€P}<‚’Q#0º¯ù)2úÉýÎKg¤3ÁZD!žÝç|Ï*?K#^OœËÙ9)ËBĪkÎhîÚ†r%¡@0•zT ˆIÉdYìÂP÷@浂ñ ˆQb Þ« ™Ó<¨©2±zM"¼zõäí»G*ÏÓ—N¸à”G’‰¿Ž ™ËnzϹ½.0!Ðß¼xýÏrëÏ|xçû_»uóúR4oùש¡?¾ymp¨EŸá'&_ùÏ#bž³+£Qpãk 5Ú£ªÝ*x¿„¼þbP$7É0郙„hÛ[ M—@CE¦ãͶTDÁ w„UÀ#€€"VNž8A´!£dèì1:OM†ì……ò¹†v6 aíNÊ#¨ 0 #° £ŸØ;`]zhA2=! ‘WPݺQí©hþ£€)!IFN–¥a"ˤè$®,D¬ˆ$b5”Tç÷x.×L©€˜¥QbQ0 à˜<¢­€-åò ÒÎOn+¿¿Ç6- ?ï’dd€¶î ŽNè­·4óNY´K"ŠðÎ)GC ABÓ † S‚TÅ´¤º2PAÙq®ãn¹®uáyfóyÀC;ÃÜÑùìEk¨Ùj6³Æ`Öl6›Í¡Fc a$Lí>,"éÒ>îÊNQæÅ䔟-ÈcáÔte¢Ôµ ¤ÉT~†|ðnÎ$ŽØøeHáIÉ1y†ã׬žð€[û•â¢Ið[ó[ó°;÷awî)›k‰ùh\XuÅïµ’„À_¾l]jèMÚÓ¿¾iD ÒÔPjˆ8ΤšƒÍ¯ì¼œ(Ðé㟺=DFÁZýd%£Ñ½«{òëÀéøÑL|Ü+· ¶}76° p ¶j,Ø‚bßSN”SpN@ È€m]D5 Û+'ŸÏÆŽ7cÎ}zm>u~NB\w®j*”*¥‚TÊÇ÷®O÷oH÷/²“¬²aQ3x|@·:B•ÕbE¼j‚0cc %„ \Q”eQ…+KÉUj«j¢·4ª ¥F‰EYaDYÕ*ùM•ØrkyN$ïë’ƒOõ¯…΢éS ª}>¾¿â™ÏÏøæãáÂ5fùBÞwT6¬îûˆ § r:cœ³€cªàü•À–=áȸü¯7Ží²heÛi“Z6ÖSe¼Jà…™Œ!Ä¡†Åá Î’s¡cÚ‡ƒ÷eǸåšÖ§îl_¼ÖÑÛý%kí§¾8óÀŽ.c% z•ÕÍÉ,€ †lQ3³¾Ì½w’fY̾vŸÓEÉ‘”!45¥”êCÙ…£m"xï™Ù¦¶g#¹!G{bE 8ä ÀkÏøà®Qœž^9S¸ÑŸ€xm`"VâÈ>53t:ˆ˜˜Ö:O}zœÂßcÜwå;.»û{[‰¯9T  ¨©F85_PJ€y,ó^Å«V#¡DÃ+N%ïJºÿ/Z‹ß:KÅ€–“ÔZj(ZM‘µ:‹ð‹ó¼Ùèu»-F LlÕdOcÞz:2¯ `º¸J¹kN©Äs’oÛ9™®O'Ì7ÓSÒúf¾ê¥þXðABH¬%¢j¤­RMÞ«clË—^“l;:U.ž7¤"…ˆ+J±AEÄ+6±–šÜÕPJC9WR0‘¨0A@¬±A@èÍ™þEQ½ü¶rÙºäàºäÐÙ±ÚgÏþàáá?îv§Lùˆ±v¹9ï³f¯YÆç.åÕKæ|æå ùœ…Õ#¬2Óýâýîð¸ü¯7Ž¢èäE§t¥3ÖvÉ{üSˆÔ]EÈP”1|ön0S”‡à)z §õ€b_ø2÷eînº¶ßñ’u– V.áK×ùLya²KAëÏ,;åì¬w¹+s“DÖ07z©÷Á& 6i(a!PÔeHÄqïœwN\@ïòŸPÙRVÍS$€gò o~rÍ$€‡Ü]˜ìz¢ÍNä—ÚxéñWÕ2ð(€?yÉêÌÐÛ>r`ÎSX‰5µ”ÚØÊ†ô s`>uW2{“‘ÉÈ6Èf°lF¦ïÆGÈ6(½¥PÒtk&â‚*ÉEsÉ»‰7 Ä`£ e«‘°›aåˆ-Ta’ú)6^@ ªRv)|-³=b;d¯]‘õ¾—+¹âØ–C“;'«TaƒÜŒ4¾™¯zÅšqïâÌëú"#Þjk….s¯eî˜t-VÝvÿãrð¸Ìt´‹—E§ÈÛEäïÆc˜µ‘¨Cµ ñÈÙ:Íjˆ"!ø ¥Äall˜ ³efvïڮ츽¶Õ-ì?&«–ðËž{æ"ÁʼÈË¢(ʼ(òÒ0ÇÌŒ1¶•Q«@$˜ÅÌ,bâ‚!³šj˜‚óÁyx¥¾)|}y†grLŽo]ó予CþåÌþœôv®‘=v0uWx÷KWd–~é#=¤° üÚÇ¥ï|ár°á*ÈSÝå“ïÛøWoý“.¬ÛÆ”· ’KJ½¤ çç dÛ™»T?üèš.¬G”×.ÐÿLZNpýÍ|VÀd«ü ' «…7 ˜ÊY,æd´þ×ÜÑÀôF6ž‹Æà?hŒü Œî˜Ll-ÏÙZžÓ/V©¥z_¹vöðñ’C¡Ä T½Ìôêó&Í—“ÊMeY’5’,³&á2B|Qº"weYä¹v¨>‘·Ð鈕^Žõ^9eûwÉûš'ƒø³ñƒÏL~æÛb ;NƒøU}~ÿ±êà¾r£p÷£ÀÛomæí¼è¸²Sc#y·ÖPÆPÀUrpZCTŸŒ!Ï”w|–RðÌi "ʪ!®lXDÊ<€A„Û~jð]ï¯|ºW-JAxÎË„n»Ö¦d÷ÊÁºÑâQ¿úŒ°Ká+O}W©%®™Àï}âx–Ò;~h‘wsô'"¼?¸ìFØvòm¨“3‚Ä¡;4 ‘{x6ºrÝž}YŠÑ= ï3±ÂÛ%ïó7ú30÷© «`• i(šæpt 6I½”òÚÍÌD%õgiH+/wÕ‘ýÉU«Ì«¾îÿéŒM•CeðYðÁ;OQóRǪֈɈïJ•k¾ö^ÂR¸8Ûf£]³‘¤Ì‹ÐD$X• !IB kiˆubgèÐä.Bœ½}†ëá§bZp6ùþß÷ø~í…À×î±Å|¶(:¥+\šfÖcØc¸ê؉òǘzUN5} E"†1¼†MðÞ›MÁÂ\ ;ev¹ëÏƒÇøú£>žl&¶<¾xŒÙ$e^–…ë´1FÔ¨VÙ"d•Ä€)¡J"!RD?2±WR4È”3d 4D% SÊ)Ò>E<+©BJP¥ ïþü-\¸‹MÐ=,=æW¾ £Ü¥[\‰šþ~õ¦…ôÅãïúÔ‰ÛnžŸÆZEmDCt …×ÓÅ3FÝÊr ¦e\ÇD`#«7ìÝmfU±à@•–¯ñ´¤TÍïR›J*£Ô›Ù7¹·whc!©Wká/¡<¨fdÛ&öd Âô%q°Y-¡±•p·[µ[eUPÔåêãg<ê.ÊöÔíÌÁñ>Öè¹XÐw2«Æv÷ÕÅ«ƒfBZ£ÜððΉ WCUQ ªRt ,4$"F¬¨I&µ qh|wýRºlàÜOiñsžÿâñ}™ŸéÆu%ÄÔî?{KYtŠXSµlêB¦åªyDö !H6 Kl©+n%“„|!ˆx”­cªBk â²ã\Ç¿ões4ûw=≰hŒkÑ)òNÑi* ÖZcµ}èxeÀ±ºYéÁX…TIÅX6†Ø+K_–Þ¹àê Í‘9ö)âå#¿ß!ZL›;W6?púfÜ5óf&ÿàŠçÇ|:±òiÇÅm7ÍÏ ý¿Ÿï)ßkp¯ô6õÙ®_ü¾¯õúóíÀZ3püÄÒ#GÏ9|h¹x’@Äj1‰î´³¹GsßÚ/“áevÙôÄãÕ~Y¸™ØhW*ÃS»1½OÉ*[T? È‚Se˜5§•d0=.&&ml-~·2Ve• ¤cs Å*w¨ó7x½üô-¦ÀªáÒÇpÞWâ ¾êˆS¹¬Ùz}¤ªÚç1Ñ—:›âàŽ‡wO§$kTåÖ4K¼wU–&/Ê"/ò^ÞF§BÜ@KÆV.Y¥§ÕWÏÆ÷ žj£o5`CQ#YI#BUÈn¢‹CárW¶O¥«'·ì$¢åÉd™—e^úÒר^•yµQMu`ò ÙYABÄ€ýÇ#FhªS‹.2†eà ï½wÎy'm_C zYVe}é¼øïÿi·]sÛ6¼ÀÕÇnï¸zàoΰC8‡9Éî.ª·p–Ðc‡ŠÔÒ[o3iíí?ˆÌ>Røõc¿ÏìŽZ|xÿRñ¤$"‚±b¬¸õ[¤ Ú«vÌ,|jéÎt°ÊrT߉85á>ù8È*ÙøSÉÀ6¢Ø´l!±æøl„-L“ö >Þ¸ÖÏ œH9^\Ä붨ˆEeC@•´¿nܪ\¥œ‰FutÚHÒ^Õ¦pÝäM÷uãTÕGö̺Îlš%iÃf™M6_äQwUEÿ•EQyÜBaXÕÈ_<›dÿžÇ=žpÅÆ'DÒgg|ó÷SšÊn}ÞTÙ)T‹˜uXn2 A"Š €-[~p{ м!¡˜±1ÄL±ÄBÁk¡ p‡ïþ\p¹ëÿHؽ üÿí½yœÕyîÿ¼çTu÷ì›F+í’4H @,f 6˜M²CœØ¹×ñríð±cû:¾±ßø:q.öϱ'^~ÆxIlc ˆ]FØ @ö! ¡eÐ6šMÒÌtwU÷þqªª{z4RÏLõÌù~ÑkõéÓUO=õž÷¼Ç;%Õ™J&Ó©dÚ²d—?aùÁe@¦H(")‚ÄÂ÷ÅÒ?öÛT €ò“Û¥ER’´HJ¡GY]Çq]‡“^háÚe_¸J¿ýsNßúßOnþèmÇÿÌsÄ’ã+—œ\ `jÙ}9=)Èäå³fÒ0ðÉk«.›Z sI³B4]ß¿k¡W¬+—”KD,-%-¶úÆ‹¾ñÃDö{OJ.M¦®J:×vêwL}—œÈ–}Ùâ’l% ãDg’ŠŠ' ¡>“„ˆ±Œ…ÉﬕÝMéB 0¯4ë¯<:{½ŽÏŒm*bÂMv}Nw1+Ö¿ƒëºþ0 «‹úƒ«Á‚.Fit}dnkŽÇÙžšàà™¸“ìˆÅcvŠÅìXÂRì¦S©T*™Æ]SÉd2•òÏ?-DϧŸs"Š–ÔO†Fßó²_]·È„u[3òšìH¹Ž«]² U¤”`FR×OTìy:;’,’„¤{ïkEx@$HJ"KxÊó\Ïõã4.ˆP„hô|'AB§ÓùïפÃ6h‰%B:™J'SR´¬ á]ZÒâb¡/ÑEŠˆ1‘º4 VvŸ0ß>À 7^Ðo÷UIKXRJ‹”繎£]¼ë:"Å ‚€*ÒK›2€¿bì×g Å%‡<‡<—ôÝú£O˜^ñÙUß›½·õó$Ü]S/B.¹¿—¦2!+þsYùò]^¬e}îèopgïÛ¬e]›w¶¶¶’Ç,Ämªjœ\Û>Iÿeoǹ¾Ã½©=tîͻмº.˜ÿ¯d'‡I(+&„òC1bа¸õ„ò“ß»˜wö:ý€â•a®§_ø†X}ËÌdãÄΣµLP„O_Ý6˜Ùs]Ïq•ã9>þµG&ôÎÀu1lé¯Ï+àÏsÊæ©I;S“)ÚßDkZg…I5Ì*Ji#ï‡h’IE`>Õvüç+ßí@1 gúÖÒöïB 'Lì$Ó®Ö]Z¢Hà—>÷‚øŒ§„ ’ôÝßøKƒú—iI]sØó\O¹Êõ<×u=O%€Lš¤$!…”BHZø™Ö{݉‚êL±bËw?ônY–ƒ SLBùi‘L¡}†Ä|û€ÎÔ3£ç3Ñ|û€”BZúCÉuÓ®“vÝ´ë8®›ONL|âý»åsÿòÉ^½+Pö\¡_pä÷ºÁ³k‚¡…Ïw`l;ҹ툟/„h8ÇB á±bAîì½ÛóíÜ…¥„¥ý»’–Š[·`gÞ^Õ<¹üÄ”¢WŠ/ù†þô¥A4&˹‹¬8¬Y °Í$ÐÆu$QUaëÕ±µšKmã“Þ’¤·8¥ý»JIç¬dÀ1N_ß‘º¶3yUgriRçüäð£Žsa¥ô´U×qur ô”gAL™Ô1³L~d þ=&¼øì îhH–¾à,Œ%bvÜb¨tª3¥Ã4éT*z¢}ÆioóÞµîžaDSÀúžœ¹mþ‘À°[Á¨ª”R˜;•ò<Ï»³RŠÁ$)È©ºÞÐ(ÿ¿ŸŸ #ìžëyž«<¥\W[x*¢À¼‡ÿ 0>|y;I”nÚµüÖd‚3(ô)Wç¹+á›w™‘~ðý×{+ÃM…9ÓÒÒ"Ë")Iióžvô¿ê¬£+R5¯ûk]®ý7ËçÄ¥øøSo(—|Yï*ôóþqÞáu.®ý–î7š¾,„·wÖ_âý¥çô“¾ÏŸ‡äU›Û´Ükq'Ê„¹B8B8aL†=±d]ÚzpU%ˆÇƒÂ÷Ê%åùI(ñJ&u”4Tµ¿3ª=˶ânÃJLÀŠ#uqJY¶+Š¥„ ÒÛõ‰Á»8Õ<*ÿ: Séùiµ$©.ël©IÇËEIR2!Þõ@x.½9ç/gßcСÎrœa½ì.9~/ÿé_Q”?¾JÀ/wV0SXœ2‡]ÁÚÙsb ëÜú±+·U¼„T2™N¥^¶–eÍ©·>&Þú€nÉf÷þÍîý,mXeüdý+O]ºþÉÅëÉâä²NÅë‹‹vMEDôØ¢s5ïsb ½‘ž¨ógü‘Õ D#ÀÈÎp*«Îd_냘[n~R/ìI_´'=ÀÌXnUw]LòÛl™êþqË1AxfC&ÀÆÞýÈ»°ô°ªöïZÙu|†Ýÿ\îB"aü4³A‹Hå¹ÊuýÏó”KIˆ’ CR !éôÑBˆÇ¦do°ê†÷XEÄV:å>ÝÑèÏýaAgƒï€ŸÚñ‹†’‹­C`ÚÝUܵÏÚåL~ûL"ÕqVJ¡£ðÚ¼W5} ı2–¶²²Ä]ßø÷6}å÷‡²Ÿ‚¯ò”íè§ìÞƒ ´¯>!Çßig,Ÿ[M’}£ œ Ñ8±¸F·në1mÞ ,-%m%l~“üÅš>óë㻦Ô%ô'|ù¦Ñ_»yÜÿyïå‘ò„òèLbòÙĔޒɩŠÉNµŸWóCuÝ“—¾b% dÅ!ã€@‹=í΢{NÑÌ Ú®_¦¦=¼Ù½_ßfBzvúìq–Y#‘—'<—Þüøâ—È⣯à±Ks‡Oû L}ñ—‚ñÒpù­ñÄ9É‘z˜~v4Ñ÷Æ{äM-ñöÑìµ;Œ¾6x½3dõ .°£ºg Xሪ6ï%Äg3Êî)3gìXî·ÖEÕøÚ̃¿ø}ÑÝW&¤çIOz®r¥']ו‚< )Hp¢!Aá¸Ú%ö[J{{é:`̤I'<Ötà¢ò™à»eµ˜ß<Þ _v·3퇌·SÂBÀM;Ê!°pÚÙ.Ö“q€Ó™ˆ· i+Á¤‚)±Ä žCz‘¯ÛñÖ‘ù b]¥K3²Î‰BúJ-,V.“`fÂ?FJD +p—ñ̦ÝtæÒɉ´‹O\[‚›ôߢ<áßp„Åþ´U‹ŸIo¾!VÿWW>ó\úŸeÒ‚°©y|'Ýá¢ÞúÀ›Ýû¯:*»/JIt4Mè|ÖóÇHuzL8dzî¶½ûïáº.é¾Ò†þ\‹¾N™ÑB¯]wùs5v=a50ïYÁßÈ3ÿòÅ\5i¬$†].SÆfjÛ>¸¾ŠÁ®ïß]å*幞rцh¨ùð±Î¶³ì©9Ö¡œ•4â—^^¯ª.;fò$«üZPa5ß¼$ßß0Û:Ìè¹DLˆÖïµó,AÂ’Ž“ò ¸+Ò홳ž`»XÅJ•´Õ·–MÄèŸÖínð…Ŭ(;uò¢í Dîá…ã‚,øŒiµÕ÷ï˜1JXxüíSe­Ë‚ÜqÛ”G@˜-ÃÒR¡¸¯ÚܶêõÓþÛUUŸ¸¶ €—BFÖóø};sÁá¯ÖŸYq²°âáŒô+ßnvï_çôàÁf½˜ É÷ó£‹rüù«‡ÉvÀf‘¿ò*?m±‘ÞéïçîuƇkwØ•®Ssb ðU БúC¡^ßuO~Vh²¤”¢”¸]…Áåq˜÷þÀ¦²œM]½ÀnhT¯è"6Ývxi‘Zu]ˆU¹žç¹(ö—îël=CY)ÍÛi[œ›Ò3sæ ‚mI[JÙR4¡bAmß$G)"Á?m³Ì¶ØãöšS¡yÃÄ ÙÖáu|©”$ˆ”+”KÊ!åˆôYáuúò£d%Ø.VßüÓщ}ë¹ã²[Œ^Æ”*Pyá9„hrkÿj}×·:°î'…´\»ñ¬ru*§Ÿi%”°`åö–•[[ØÞTÁÈ–õÐøK›¥ÍÂb}CÚ÷¢îOiÎé8äLˆG§ÄÀ×ë.¯Z41a[ôË—[ËB/öt×ÂJáÏ „ž©öµŽ¦ËãWÄk^ô3Pu¨½»6ù«y--E’ÿØyÜ=ã{öÛ'B°2€Gv5CâιÕ]ÂîA9øìl™=Ó¯PÊ‚Âå-OýÖ~$çûkΣ|ööY?H}ü€ ÉYnšø‘‹NéÌ™§Ïν¢èÍÖŽƒÀ,dõÛÌØQ€ö¦{\ðÏÀœvòIAê{å™|ó~Z±b¥øûOö¬Ý·\S ¥xíë=¼ Üð697.±o\b¯}­ËË~õr9€OÜèxžð\Wž•R–¹Ùôšîç¡M陋ì}‹ì}[œú‘ìªýb¯;q†ÕPYYàÇ×ÔeÏþVöm%$[‰.êüè¦3['/™g`Õ¶’ÁzL’Éâ÷O® ‚ZHø¡˜Ì&N/m@ùó¥d©±£é´ßï3]uÈÔ>÷Ò”eÞE°v‡/ñoÎX¢mû[§?ý–ýéþvË@£sWt|Aü €×’ÓÃgÿ«aTxû•ÎY—%ö]–Ø·19#3UlàO?†"šç¨æG^>—²-|øæxöƒß]Õ)„PŠ¿³ª½G7 àýWù³\~¿±gõÏfíkŽ$ºù²JôÝ·ÖÖ#¶žë¢ŒD= ð ºFiδmŸ{Qeeeee¥°˜9£¬ž+šÀN§ðÒ™¹³¾ì’º8ËæV—%à醯5ÇOn{®„Û|ß³blÅ2É3A}1‡„³kì‘-eþúëKÆþIõÅ=8)¬ÞŠ,¾cvrº˜w’™<™·çÍÛ7k€…'ÖíkýÌùõÉf§œÃö‰A³c sc‡lNMíãÅ™ßz ›5Œð¯> ù  ý;€_­I}ôÖÄGoMüüÉdøàÿ} µ·ÜquÌSxê•t¯ÉaÍÆôû–ÆÞ·4öô†ÜwýxœÏß%ùŒ²Ö§Ÿ_­É=Ód‡h¦Yï0ðVVUƒþRYY¹xñâ£.Á˜È#R$?-Uã`ÒœÖÂãTØ €Y 3”“98w޶íoœúÒ¹c%ýaॡZž9£Šè¥õú`SrÚ¢øÛ‹âo3ü1ös,K0’1”G Uߨýiû×ĘñÄËýwÍÓÒ·^»õÊØ“ë{xï÷Iɯüyn¡´Þš}î ñ‚„¶©ÖQp/([YY‹Åˆ"ø«|ê'¬èØÄ™Bx$<(¾JH„ÔÕªÚR[áÕUY$<@ áê®À@‹Ð_ÙbÀ7ïþçv\s\§ÞItIód²Js•Œ§ž”Þ;a´—fD¨ìï,š¤£í;Žÿï é ÿÓØûéØžô¹ÆÓ¶¤¦\?T~rW{íÅÙ.ƒ¡†Nß/ø4ý“ÕÉ{Vݳ¢è+;û~åŠëâžâÕ/ö[Ü5O®Oß¾,vû²Øc/õ¼…þMKöÝOÞ‘ÐÍëñÅ*¯eöÕßsŽÿpg[ìv÷ÅX©ËYC¬ÆÌBK;³NïWúÀÄ »á´×ЖžX•B8;…P€WtZ¹5éö;ÍT‡ ¥ÜF±m4‚aÕÉû'" ×å{²îBß”6('“í.¥ÒæýÈ%uÊ%fR óFÿ¥,Å+™õ¯¥XrÖãÇÜ}tÈ \Ù7©Ü)r}³=5éZ:QrêM`Œñï†Á¤€ý;€¬ìüÜÝEŸ»»H<®‚çñí"¬¼>®]wžâ®yì%?™ý]·sÏŠ"ݰÞ^ð¦[7ÃjXl¿Ù¤ÊßöÆË§÷¡YéSO)Ç`—¸ìé™Lþ{˜1áØ^0¨×S KÂ_WT §Ó§bBEL¿·c&XŸòŒ †t­p`¤o8(€ÄÚIY¹·”ºþ êE—ëÕhÅÖ)%[JYÌR)‹ÙRJž‡•«÷îèwgåƒóKIT$L³#L]æ\3Ü:}ÏÓqø¯v~é/Šxž¿Åß–ð<žb׃b¬|¾çáÖè}t]zùµñåׯW½Ðek½¼È¶Ei—u“úþ¢_.¾—vtt'}cå;¬(ö$hÿî‡* ¥E ïz‘Uò—_o8›:Ò’žPg ¾›¡÷ ·6ÝRÖ*ɳÓq[H±g/®¢Ëë²~@:ïsêîƒÙw{µ·þT-¬ªxÈnýô²ìh4GæÚ«f·¨–­M^ÿ¼ÿ€bä}¨¸lnUok4fïÀçòU”Ú`,WÝã³…Z?2›oÿº#ûîÇoëR>ì¡?œ³¸¿[«V½úà{â|OÓ#?}¼çÀw^xè©»oˆª­gýŸî‡«ÎIӳɋ²Ø·éGÉ}K¦OiwP$‘ЧÇZ+:f›ùqyb0)"á×s×Iœ$ybyìX2yÂéW”Ø/Z¤ÅzçÕYÎp¥ £êkÛ9Ìüþô!?âN` jÃ$§!`ט#N’ž”#¦È"³Ó÷ŒîÃÈhnnPÖSjbÕ~³†q-ÅWæ«ëËŠ-÷¡Ë=ÒÛ®”¿¢¯eÑûÉÆ]-ïþ¢sC;÷ ;›{|vêû@óàsý¸ PÚÛÛ­Îv]²±Ý›¤÷å:õrslœÇm‰×’PBpùÙ&–)üª(¤'–ú«§±|QYlÃÙSG3q  ®£^mÇ›Mm”<Û¡¼V7¥0ÃlyðS‚ô¡¤]·W¿`cü¨“¦e4ŽƒºóJe×Bðo‡ß¨ÛÁØÃSï=øçÏ,þÍæ5ŸjZó©ú›œó†HÚ÷ž«&œ‹fUtŸUñ.ÂC=¿¦´XBKs¤È£Ž¯íΛ’jçþÚμmp0ú^¨¬æ͇Öÿù{oS.•߈pX\×Wí²ø^"BµWUé’¿ª´í,(³¢Y¾…ßÖvZX\fÙIr*ݢќЪDL& ©+»²e 8Ìú\Œ;¿¤e€ âLsÓtsõå’PPYµn¸ëm((&V”åñÑ]šnÚûáµÍiô¤Me%s·Ç»&øë–æ(n'îö²°}Üã˲ʺiŠãÀ‚™Ù›õërRî#Ý_3@ôuaßO^ßÝ×Ô“~¡ûëùÓ呌Ñ÷BEžÜÖÑÞñ›õ/JIRˆ?½r©?]–‚Ø:¡]Í bbTØ»ô*©$¸sTKK·v2›O ÉdLÇ+ˆ¸ÉF$k‘`kŒrÜvÙØé¹*PF‘IÕ §g†ÎËWK=éé©ãž:qR9ôþ‹j…Ç*[ß³£7 ¡He•´Döíàã:ð–£øK÷,þö^Ïî–¨ú÷¼±åͶ|mJŸó(͆¨aô} X|qe¾´¦¤H¨ŸÓÅ“^}çÍÿdíó–Rˆ§¶¾nIaI!%}ðêú@èÄ™Zß…N¿¦G^;ª[¶dQ&bÞ‚òJ0 ‚j)Ñ,;›¹³¢Ê8³£Õàqù‡_}´Ng½ë/ú\Û!–-gñ?LFà’¯/ àé'X}ð”ré®5”ËÝõ]°T™’Å]Êú©}ŽÇiϯšö§÷¬Í‘§%¾û‹–fiç¾moÞ¤Ù0œøèÇ „1Sø7|ÿäg¼é#O/¼Œhô MjNé»þÀ•Ÿ}aú~ÙܪóV°²b }nw£¯JÇyRÒÒ¢(F${¼ŽRð©÷]«]ùþaƒ^q‰­Þ°EJaYâÖË熲®i9í€Øqð`YÑ `ÁE N³q¼¬@DÕ\äMPk¾¤hÔ«íÇæ¼úhnŠyMÛA tcy]kÇ¡XÂê¼®¡èù‹àïµX{ìà)«ö4ãås«@ °r¨A FËúŸ:ìx¬ÓOõyë×w^ÌLÐ~7Ÿ}ìÙ—“ù½]^l1ÑÒv/\r7PáPRªsWQîAy‘+Ü‚uÙ¼ª!É9Èë JÞÈc«òh!/S `ó]6¸°=áOYᎥõáÜ¥§7î”KO­Þ°CJa‘¸j~&×dÇàljʼº)šÎbTÙýÍZ¥5×>iE:í‚o-pÜé Ì4U=L èTy"n,¯ê¤T§WR‘h¿æDñº1A‡’žfuû”QÐú¬ÜÑE+V úðÒ¡¿wÍÉ´ËiiOñOWLõ½RÊ,0=|öðJdîíž,{ÍãÊv÷<7^€üüC÷ž÷{0æB²|–Ì­B^G·óÂâH^ÝwgQÓ¿’…pJ*ˆ­ˆo¿f®~Ùs›÷êv”B$lûàñF"¡Þy}öOYªÑå/ @Mƒ‰_CO¤ÛÔÅÊÁ§›½”b,¢+&…CŽ©NPTf™ÐMxFз—Ï®°jG €U›Ûˆé®Åå ˆ~ð\SÚãT:HÜ$&â]>!Ô¨@ßõ§å³U]T²´ÍãÚè`Ë‹/@gËÚ,WžíÖª‚åû·ß7p/Èü™Õ\ƒFH&€I ¼ÈÓ5³¹¡~€·Ø×prvÝŸ<¶Ä;"R$ØÿW(!XH,0¶ê‰¶Wâ€Aà±±b-ñD &H‡eÜT1 €^ź7'îŸÜpòHǨ §¯8]þJ9Î{ü+%&€vM*ˆ¨5饃 :$ðð\èõI˜ ÞNäg}׿=3žk6Ë‹-0®¨wÏIìÔ@ºÔÂäûu(‘çBdóg"ê&úl–_-H„ÆîXèõ=iʼ¯m=Ûùw‘*JÚà'Rž  =™"0‘®9ÿAÐ>Kv )ˆ R…æ¸kÍ ö¥%JEŒ€V'É à`•L#\Úzší©$Õá”{˜{æL“Ç ¾(«Œ:ßs=t` Àq=>Ó©Šm1¡Ê U’À {YÉ9¤7U¾ßÒE%Œ«°ídÿÇWiu%šù3ÑýQ ð´mô½/f|èþ uÖÖ¯ÛçýÆìÛe%£ºáãêˆͼ’ý"x¤åLG󢊒D÷c¦$ïþY\Õ ù[ Â+NÌ%‚ 1޹@¨²âZˆUe’Ñ쯸¢Ø™a·I§\•U[§O¹Ý»·¶ÔÎ G(å°Â‘fwb•µãÙ±Èrý"8Ù5-Ö>œý=bµ¸Ÿa8Ý3O¯ ¸¾ŸýÐ'zÔ¬%Þ%.zµ©M—XL[à·*²0º¯æƸÑtº]€ˆð˯ξqѬÃþSœyQ7(XtdtÅzÖ è-¢Œ*Ut²¶)q éIéÙí¶½stègš—.*•Šž§c.¯caëx½@àÉË fS–VÍnwø¿¥Iœ™Å€$]ɘBâÍrÿ½äÇÙ÷×5 *8w/rR@‚Ù#åÒ˜î±Å‰“õVõ†t§üì§þö†1ÿøô fþÂïŽûÎñÝÞå7þ‘ꕽoÏ`éXӇȢºµLz<ám®þÜÂÆ ïþfÝK n¿¼Þ¯H ¯~íP×ç׉4ÚlC¡cÆW»RP¢¶ð¿x~.Qð¥KεíiaåH?Ç|TùF"tTÚÉjIä RD\~k›[L«.Óï*’öNZÜ>Þs‰³ý{PeÌs *b½à*H]´íÌáùUMW”V¾ÐѵU=k½nð?ýÉÄ/>qÌàÐÿ¿bJöSONüm¿»Ì`8_ J|Œ¾0ÛÇ}fã¯VH!XÑÝW^¥2£Âb’Îq³eÝÜ_âƒÏŒ.%R–ã13•îw­„Ás2»´ïö™ xÇÐIWaœò¨íÚv(«òÕâp÷g€ÏÕ!xAy""(e±Ì%]K2(=уÐð­›'øëÇÞúèƒ~º|ú€tå€Sˆâ04˜žÊ#Fß f|äªëDÛuYà@dzVãó—jòoh¹wƶ ¡â±S HÇ+y3ÉŠ¤íQ0OJ¹aÞŠÿiDx¾ã8@×U.)W¨nÁwçèö0 Ökj“@ÝöƃóÆXR;~ËÉLM±ŒÐgé.õÿþÞŸ|bïGÚ—öÔo–ÏY;ûWùíÉ%š3m#Ù¨èR€éï0ù@™H—dåC›6>¼y£,,R ‹…ÿ¶~0ü“,,NíLkwÆ%˜Ý’*JŒÚÛR¶·C¹DÄ$ü\É'6*·\TKYÍx¶å„“¢ëŠÆêSXIVòùöãȾj”Kþ‰‰X¹B¹¤<š²û(³ýΠ‹¦J[I[I›¥­¬˜²blÅôçÏIDAT %mD`&å Ï®#~xólýžS`ûí0È%4˜"z:,DŒ/xÔ˜E„•] ˜…€WÚGwÔ(A^¦L|E*…Òºoy.i«›d Ì{–ñGDÝ`å’ò„´•öïNŠžM¸©z ü÷øo6”€§£4 ÓöÚ7s: ΛÅl)eM{c[¶—gE¬Ø/$ÉœýÔý·ÎaEw?ºÖêk›‡¢¿‡Z¹Ð ̺3ÿªÏ•––¬>‘Ø>æ3Ú§Ë,·.$·×ÈöÑQ"†àmí_Þzöï·œþ¥,¥,m«•§Í;ëAWì{A|†Àㇽ4Ý2v´r…~}ãR‹•5j3»iòÒ”mSoŸ:JyôȾ&íß¡-¼GÊ#åÒŒ}û•²LOÚ7«~ßì%VLɢ’Ù7ìœ?þÌ+Çkkk¸›¼ 2}f_éRŒÇ”Ÿ(--m|ë-ÕãÇg$Ÿ®,9]Qtº2¦¶IÌÚÑù·ÛÛÿN¿å’q_WÊš´}¯ŸßâÇ¬”YU;÷ÕoRÝVWë¥õù@€bKû÷÷Ží¥éÉCA‹€òˆ=Òà hãi•W¶Rööc_ßyâïõ{vO½r÷Ô«wM½.ŒÒ„A­øÏÎüÕÿÞŵµµ+>µf0»÷‰æÞnö˜øL!p×ѵÓ>Ôyâñο>æsUrý(~ƒHé¢Á»ÓŸÍyñ¼1ßTÊž¶g«ç ­¹ÂÖU$YÚ _žõ:1ðÈžf@,Ÿ]­ß샘뉩Ê%añ­“jÛê±ý§nŸ>Êÿ„ÍžDá=" R³ßÚ¹{ʹµÿ¸«ñk»¿ `Võ÷ôû¶M¼I_[\zôq}šy¢ö·ÞX÷ùÚÚÚ9×|/]j0ô“B¡j>‘ÿ.3ΑÌÜ4ú>‚x½æ&¥¬‹ßz1£° ?gÆ_ø%hÇ‘”`Z±¨@0¦š1ï:ø®”ƒ§”ã§À/_På/¬°js›´»Zøðì⑟¯,¥z sW÷î„þ@ŒU6 FßG SËîSÊžèÊSÊ%Aó[üõß_¾Ü*€Šîª/ Ò»rI'Pœ_ÇlßrB‡—ÊËìNw-¬„¢GRbêjá Ê#/˜ñ¤<ª?ö¥ì)¥?²Þ1D½ªóes«†º!Ó9"˜\òs¥¬ ¿Wn&0ŠBçN‚eÿ±¶é'/4|ç¥ew..@€¯ì:Ò?°vÜGë'’@`á…òÈKYð„».­X0±H‡ôƒÌzÖ5²ü»‹_Ò¸F)kRñÏ.ô«FrÇŠd£üêÜKŒ’_ŒþÔý—RVý±§ºF(4Ô:yñ»kNé×;.ß÷B "8I #ìÊ#¿H™€Î(%uÖr/•‘²Ö¤÷Üžv2”Ä~³ìQVݰ˛ŸTÊž˜ø¯ ù²ÑÔ¬Mo´¸tNåP7ÄpþDó$Ý7&ÿ½¸À½JWm ÄÝΰ"!3æýÛÏžLÄé37ÕóþЬ?ÔMfÂ2aš~½Ÿ©”_¯&,)‘¢4Ë8.+Åß_Ûœrø ï…à\BÄž+H¯"¨‹Kêô…7Œ"*£mV_ÿÞ•Hž£õjÑzåèþ2Þ~X)kÉñ•Ú){…Ì;¾þÌÑZsÀÿ|ï(=£ÉñØñØK‰.A|&æ`ÏôùJY$³ž!”KìeBêz 5íáâ q |ó™FêîÏŽÒÏ%åÑ;é)e³îë+(‘ܵ"Ù(CÞ2}ug @—Ï«ªôÈk;[,™­«ûóf¬\­”}Ôù@`ÛÃÈ»?àùÕgŽ€@à¯ÞQ®©”v9•欰»6ïAmaÁ:3rÛÑo(%\<"5 O ^’@H{ìxüÙ›ª¿ô¾!ùëO§, ŸÉŸ >À1÷ÌöX¹zH{.ÿ%5 >ærx8sÜ»C4Ý'pßêQÞräCþ¨+„T_xæ@Ü¢¸Mÿøþ±ÒV¡¸àxP.²ÃîÊ#+&À@—%üp Öå)DŠˆÜ;nf=ݯÝ^ûåßüÊ“GSß{sÈxA@âñ1°[Ö8•5f0 +L|f˜ÓØØØØØ„G ŒÏ<½_?{ïmAF`0S)N»{™É¤Q†eÅ´‘÷ƒò¤-|¯'v)å°ãÆÿãícôG|þ‰CA]øÌ¯nRsssssî‚®ÃøćÑ÷\ ´„doÔÌþÊÉ“o¬û|¨¤Ÿ~fÿí¶ÉÿrÇÄÌ´Õ ˆ™›¤´Ëi7«œ¯'HAéàí—±²Þlþ[a ˆ ¤ÎYQš´Ãi—ÃŒI"¾÷ޱÿrÇîyâ@°º“;pèõ¯477—MýâöÙ€Pˆê0TàDш2”ún"’ƒÃœk¾×ØØø÷Ûè/Ùýñ'ßðƒÛ§ Á‚25ô¡ÇTÓ.Ò.gU#àp]'ó tì7•²vO¹*§ü{~îcÚEÊÉÈšLøß¿³Àÿx|ßÇžØú÷Ík>ÕÔÔµ¦±±1íñÏo›=g•[ ß_»¾.§]N{Ì K Ýü;=‘5ÛãXxrt¨ÇËÒw‘ùèŸÞ5íwÎp÷ê&ì>$%Þ˜ü™ÂàÂ/¢?õµÍïÙþ—$TöœU¿¦iq÷SÑ—YWVdI‡UI`cí­P8Ô‘In Rhu&`å’®C–ò˜vÉé±À*‚çµÆHð¯ï¼øƒ+wýé=k/ôF#£†Ág(ý{DcÃ×Òüñ’ÿÌV?éð:»dC¦=ßtg^&ºd¾‡$`Ô¤/1Ë-ãßç²+ü銎 ÇóSéÀ]-¼>ÍŒ„ÅT#º·ÎTŸY<;ëWÙ±«(«‚X7wØyUkkkkkëgÇŸ$ÁpÉQ¾‘']´@"r:@„Gk‡c1C>0'Â|aô}äòPÙ#=>Ͼ;vþ?Hǽ^7•´ùÆî˜úGق؊ÑêÑ+óÐtCž0J:¼1únÈ¥Ù,<èõQ1v"Æÿhø’}uÓ³yn`Ab”Ô0ø}7䟥·ý|¨›`0Œ¾wÇ-ƒÁ0\qI¯ë^ô‘ìØ#•e1W.¨éñY£ï¹D³x¤9é:frÓ°t~ÞVª,³Ñ§4 }ŸŒ¾ ƒÁ°—÷ËçWçË•—ZÈ«4ç…þ:ë>xe{ÞJ-iç¾~[SÏ}7 ŒËó·~dy© àòÈ)iÞ¤tÃŽ¼)©vîy”æAÀè»!\–?Í*+¶ò»Á¼P^b!¯Òœ'ò¦¤¯æOIµsÏ£4XŒ¾8.ŸW•¯!†²’(*iÝ_¡<*éΖ|mJ;÷kôÝpþ°ñ† šûÕúmM®\г’F“!Õ÷(ž#Šé*ÃÈá•íÍÖï4\Æ¿çb”Ô0 DrÇÒ«/o”txbôÝpþDR²°ag3(ršµa{3€¥Æ“2ÑÜáûÀè{a͈¤q†¢à”t(é½³Œ¾FI „QÒaÌÐæ¿›ýÊ0’ˆà%ÌQ8œ1þÝ`0†'fþjÁ`l–Á0´p¡„Fß ÃB; Ý0å”ó©i0 çD4%«Vÿn†Ds 3¢ò`¾˜ñUƒÁ`žÿ^8÷Wà˜Ð0È}ï†9 CáÐG4r(õ=¢AÒ¨bÎ;ƒ¡_ÿž‹‘Ñ~ÍîŠf« †AÆäG Ã9MÉ2ù‘†#𻼡À1»U^0ún0 ‘­H6ÊŒ¾†#Ñ-“Q`\†TߣyF#ƒ¡_ÿžKdeÔœ ÏÓW†ËÕ‹F…·¾çb¤Á`N\µ°&_uU… `Ù¢šsKÝ›•ìokk*bÜUÖCL~¤Á`ÈW.ÈŸ’–Û®ZØ%(š¢E]Š)¿¸åTxÛøwƒ¡ð¸rAÞ„¯²ÔÎïóCþê¿¿¼µ)_›ÒÎý¥-yÛ`^ÐÎ=[ÖCŒ¾ }qå%y¾Š²(*iÒX¿-o§{¥ydbô}$’OÍÒî/Ì ¾’F¬UÑTR}¾Éã ÑÁèûÀ’ÿëèá«Yy„(o‰Pë·%5*ÃDßû‡éïa^Uf!zÑȯûË£f]R“ß æ…h¶Ê`d¬e‹j†*å[‘/ëÏù ´”ˆ8“ÿëh£YƒáÜÚüÈ(‘_¹Ð\D †á€5„¹>Ú¹¿d†È C!ÉÁ&SØ`0ÑTÒc0 f‰Ã`bô½Ì1xîD4i0Œ¾æ´Sø˜ßÐ0˜}ïsž+¦§ †¡§—ãpˆõÝ„#û … †þ`ü»Á`0:=»?“i0"‰Q‡s¦·®2þÝ`ñDRI#Ù¨Ãè»Á0Ò1J:\1ún¦Ñ2ŒxŒ¾F°ú…I72ŒxŒ¾ FÞ C¿0ún0†9×Ôçm‘‰Q•q×Ö׿gsÔóefÍœnÕu—æ¶Êè{§l0œW/Ey:~j*cà|Js^È×·Ë/ÔK³Œ¾ I>«®°ó»Á¼@È›ÕZ·ùT~68÷67ækƒyA;÷ç7å¶Êè»Áð.Œ%Í›)Õ‹næíÜó(Í#£ï#‘| Vy ‘mXþ.ï󨤺—ò¸ACD0ú>°\³hT¾òô¢iý"„QRƒaé{Çaj*b`\1%æEtd+² 3 ëÚKGñ…9Ì ÑšÊÀ‘"GþvF€’ #.¨‰sC]?2RšÇq`Ç(©a¤PHše8w¬6 ¥Šéd#3Dn!5 &b¨`(lt¼(r£¾15ŒxŒ¾ ÃðÄè»Á`0 O†O~ä°'ªyæ%šÝÑÙ¦UçLaý‚ÿi»VÂÆýIEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/submission/screen3.png0000640000175000017500000020546210304371203024623 0ustar pabspabs‰PNG  IHDRõõ`*ðFbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ+‹C™S IDATxœì½w|×yïý{žçÌVt…;©N5ªÛ²zo.)NâܼŽäÆNb¿qÞäÆé‰;Ÿ›úqn'¶¯cÇŽã8±Š%[¢(JV±%«X…jÅN ‰ŽÅ–™sÎûÇ™ ’(Ä rÑgØÙÅûßüæ9Ï¡_ûÌ ˜k­èLØ{0?W;\ÖžšpO÷¡ÂìvµtI À#³|x¥µ¤)àÈ@i¡_ÈQÔT¯ •ØùåçùÆm­¶F[cŒÕÖô®þJ>»-ÑsyKï­Göwh[yÄ7,Êå)¥Xt¸ã›ÚROöQZ—â·ß{xÏ’ÖU½J,‹õµ"kèüwe_z"g¸øÆÓvÝ¥Ùä ffE,“%îÝ/ÖÚT@cŒŒéyI°ê¾’„ÇJ‰(ÅJ‰<ÿ@IØÔ'2Ñ‹ÜÝ3\¡¿¦š*¡¹ç{E5ôKÛÆ¬ý”¬¯ró~*K”‚‰ñ-Ëm‡ÖXs0ó°¶ä/ÿ^K°±wwGËê~E$ÊSb}X¢s¯®åûÃÆÀ3,w®¬?ðâèÀpÁ¢Î,Ãκ«)mŒ6æ¥ï êÜÛÊž]”bÚŸP"ÛŸQBîµõçs˜K»RSMó¤EÆ÷Hèã|ǶFðŧǟÕW]æÁXv '.ãÞ˜ÎÂ-ÚÚòt¯~Í´mxåÒ®3‡3‘RÄbEì†k¶>6´í©‘µïȤ–hb&ÊÎ'‡ÒumL`Œ6ú•{KÙp§¸Lfä@RH„yû³9bF y ϦjªiZšc¾W"œ9¾âž=b½óéXTV½šÃ™ù‘(EƵڑ=²ól`,¬YjßÑ-?&ÃÞê—÷¿q€åç²b"K$Êk¬÷ $â ˜a˜À ‘Õà´»š‚ Œ~íîƒÏ~·ä'‰(%ÛŸ!&/]`fòÊï¼£…`È‚¬%Ø»¿[Kçkª -Vÿ~TE¬ø¾A_…ªtøî$¢@QDcA&´óao`í2¾Èx¦{d»·êÍÞ]uû^éZ~~1&Kd‰V]–ÞýLBùþdf ²‰áœ"",çÜ÷l*úEYyÅÈhÿ¥”¼ýøHºÎOd§õ:¯»³™¬%€Œ%X²ÖJµl-¬½ë†Ô}›ko5-¼N*¾GŠh=3Yk»O¢ë±'™6?> àÚ«BçÎ6tî`KÖh¦óËÎèÜÕºÊï}ûÐîWRë/’‚o•+Û¹1õò÷Ö_ßffj¬OPáH*ÓÔ¹Tš¯­×Ÿyë–úÖe;8ÑæoI'¿z_ºî˜¯ê²»šÉXȲ X?ðÉ‚­ÃÂZ¶ 2p¸'‹Û®áï>6£s€šjš{ÑÖGÎ83}•ùcÜ™:öVè«9Ÿ™ÿ>AW^]¯ÕÖ¸ºIcŒ (­6ázöÕÁ¨å&”ò\åËè@‚ê$ÝR ‚€·>g—7f[z_ ËïøöЛÜÛ¿¼ÔÑžjYØöWï‡àsîj)Ús¸1[ X¶€µ [¶í([ÈôøM÷Í›~ óùŽÕTÓ*|ÐÙ:±ˆ>Ò‚³¾÷céW7„X/3][Sæ¾ÕÖ(Eo=“Ï‚¬œumš•Râ½öPaåuu©&_?ÿp±˜}÷Ï~åðÎ4ZõªÓ}Ó¾²ð…—½¿Ê—ê\®2¶E1 ÀÆà±ûMDü¸gc1;_f}øÕG~”^÷­¦špêðÝÁ=Îñ8îmyг@ ¯f¾7Ö+ƒ Ä÷H_]¯5ÖD¸wˆW´±/>’#+9ãº$+UNp½J6úÙ7ÿ³XÊt\ÑÑ9üÕb1›XÞòú“g_|Á½¥R}¡Ø04¼|{ö—µo¬o‹ÅµF­­ `kÀüU]K&Jcbv~’Ùw–}ìÅÆ…}ëj:euræïÓQœõeÖGƒ…ý©¬¼§%Ÿ·…¢ýîc“¿úÜ÷‡\xMƒ å‰ Y£ikŠ>ûš¬¶ö­Ç‚m[üŽhn£Âå +ZºaI$½}Ç;Î×sû´4e‚ÀŠt¸·¶¡eàþ‘¡Ì¶íW&ÓÆXc`²g°µ6´%½cïN*’”HŠ$’"©p›û6÷ýu•ÍH.¯+ýÚªÙ¼H%@±Tñ«…ç™+—¦ºÚ“­©¶¦dOo1þ=w{v—íñ;Ö¤‰XƒH5¯‘¾fô õZ¸”§37l­Ï=ŸHަrÛ˜M“zK$èlSëDо!h^e›¥µ¾g[Úó†´n«o tJdÄ“\RåÒj4›ëI­8ô6°,ù¾¥À’oÉ'ön%’’)%úø­#f×S°Üm+Z‡÷õÖWú ¬©¦ š³|¦šù>9œ™RÎÈÛ(¹±8ÔW<ÞN@ÕÌ÷Æ:`p¤âáÌ×6¸ÿÑ~×_ÑT,Úb ¥’-•lɇï[ßÇþ‘ ºà¦&mL¾¨]hãÒ›¡{ Eã‰>ý†#íC÷e³£ •)Œ.imÞ)¢{Ÿ><Òv¤Íp®ý¦;ÿíÙž?‚tOw«õmC[Ékµ>ül"þDƒÃ¾¬¾ ŸQNØÇJ$Ûûšï6ù, Y[L£”†Ÿ:£®‹2†€gw¬®ôÛXSMqÕø>…Ú[“@,¡€9f}ïW_Ò àûÏöOþÒY«ë#¾û  ´-˜|üµ­¿¦>^cc·½Ähß6¿cMòí-çžýÅHIÄìªËö–JéB©Ak/ÕUw÷×~mõÍ«­ÑýÝ€660%h c”X«5Œ±Z[£½Ö‚ë¬9³›àÊÞÁ°0Ö]bÝe¶¢”B)EA‚üko][‚¬ýñÞÓ+úNÖTS\s“¿W3ÜOP‡Êù@{K2º³­<>| ¯f¸Ï›FFì±¾ôú®±–^KêÇ& y6­ l tA$UÜþØ0€µ×†é¼>óÂìÞM\xÛw'6œ‘y2“â”ïÊ[zûW%öcuaçàЛÙΛW½q_xhéÚØl¬ÉõHó2ßmµ&böb^~î5f™Ö0ÚS*ú0ØñFWü¯[½,–ÛÓ‰ÁI»³ØÇF[Co 2ÜÂÛúL ñ5Í“æÆ¿W9ßOÄ¿Um1Ö;k¸– ¯f¾Ïy?kU€×wåšêqäÙtÒcc0< c¬µ°°é:ÀòkêYò]ý_L§“ɳ悶»W,iÝa-°}ç;Ó§wú±/âYñA¥ÔŽûÆ^ÀÊ Z`lsW)4ïÆX£¡ÃZ|-Œ¶ÚX£Ý T ¢¯:ô¿½'„øúÎW\ÅäÞÃ`à ,À®Ás˜©®¡6ª¦Êjn®¯6Öy†*ŸÒÎBsw£yí¶l:<ʦU6­²i-Ììzl&%fú¨ùQ*Qñ+««Úš|Û»gwõu`¥GJEVÚÊ–O‚%îÛæ÷nÃ꛽ÔÈ›©äH]éµ=û.niÞ àHïZ%¥ÆÆ¯ñ=¸ôæñK5‡3˜xQyîU(Ž=Ã×%J¥¢)“ÝAÜ4îå;µ! øÚN@¶-¼™ï;’Èy:[ß`êêz“»JÉLÿÐò¥—{‡ßHµ4íIu‘ß·¤nCºcàõ½œ^ÿ¾”κ£ñõûCÄKª:ýÂÅ¢Í_ugóÖE[càÊv´1 ¶ÄDF•YŸå|¿ä¾MÃËú‡o^daˆl*c©ZDSSu’ó½áÌñÙöô(sUŒòÉj¨S¨dæ–B}©d pË•¶äÇhNeóØß»IÃ(isú° žýŃω²ËÚ46¦yOŒV~n¾xU*ë Åú×ÿQ J©@'Œ±½Jí0 îhÜZF<-y…tVe^Þ\Èç\w{cˆu6Öh+c6nàybAÙ¼'$Œæ·v¯;¸O¹ãf¦Á?Ö;SSMs¥½¾ZåáÌüóý¨Šøɾšý{EùÎ¥:&1à†Ë &fÏâM,“±f/6„8DÈÁ=ºs×S‰þÜ0Yq] \_ÏVÞ’&åiTß#¯¯¿±tèê²ýý+2Û˜Øqo_Ç% ¾_ê{±04¬».—ή”[võåâdUÆóÈS8<œs÷ÜtGÓ¬»b[gŒÕF¿´³@~0cŒ5ÖÖ·0¾È²¦š*¤“Ü¿W‰"‚G _Ò” ‚µU ÷ŠJf5¬dr®¾p¨TŠÜ÷¤œ}ÌËS,g‡(²À[O×è÷»a u´±íËB¬7Ö{nP}M•ÓÉÌ÷*1ïqM}kcø9ﬢÏyEÍ»Ö À;Ï9ä—ÈÄÀ]†;ÙñãÉÌŽ——hȪ#ôò(+‰¶ÓÀw­¬'ab;¸3D¶•¯—v^Üäû‡„Þ »®IÖ5Ñ![bðšëxçÓý‚^v6:ic±ñކî·Óáá\WkÝšÎúdoìðÀýaáü»oo²Zƒµb»ù¨<Ó6à %ß°*›SÀ`¹š?â{c½ç–y¨¾¦¹Ö ñ½ÊÙjÖ‘Rkã¸ÐÆÞ]pì«&ÖÏ­òCiÀ¤ò—¶Ï÷–³—“ôȼO˜@”:û‘’IgƒdÖ¦ÈòÊe«Ù1]B¦ 1ƒåµò¬¥e7:È4%òþ2c-´ú²³e¶,Ä«® ðxZ›Á}»ûW®l6ÊÚ—ÜÞðìã¿¿wdMg=€óOoxiÛØ—î}`ã‹K E[tôÔÄÎ ‘m@ßTÔ@_Ó\édöïÕ,÷ÞÁRì/·4„㾡“ê£>:˜l¦±pÑê·K~<Š‹×Ýàík£G­=«›Xd6~AšmUmÄg™^†ûËß+¨_¾±5üúöÒÛ›J–^ÎV[.uûãCkß‘bC  ¸£1¹æðžÝ‡V¯j7{ùí JÔ“÷õE;ÜypÀù§7\vnã3¯Œ‹éÇ^sgS>oŒš!w‘ôM“@ëk:1´|¯ÂpæøŠç3ßÝÀýóú†¬‚ÅPnŽÃ™Ü`°ÙÆâùË^+•&äìd Û÷Ÿå¾sÝšë×ïB™Ú;¾eý,ûibóúº+ ¢ Á]œagbùq캨?š0F %?ð õFµ-W Š€ˆ±0¢Õï,ízÊÓ¶ÃFѹ…±¸ê®–ÇcˆðÒ¶¡ËÎm¼êâÆLš|bìprîú†A“ÏãÀàÑÑ, L=j¦¾¦Óìù^ g*§È¶G ov‹þEû9'2Î^òãR‰"¸o?rAŸ¿·£u~¢k%‹œ,LrzTÒ9U7¢ Y.e-ÑJýnRâ¥D´%¥ÆBvf3‰<{ßX‡2]Hö´wdë: ¥ 0ApÇFm- A(ä;$ĆÙv-§5;wíÛ½nù*k,Œ‚ pÍÍ}g\´g^¼êâFゥùîMýw^×\,R.;ÎxFn¤8Ê'›úèkš¾NZÿ^ÍšÎKè›#З?ç‹ ôý‡Z㌶çöàd–aÄ$“Ï/Íj°kÇkbrÿÇÌDÄÄ+†~‘˜I ˜•RÄB–Êž=ôøOß?ž§¥`/ÿéL)(úA`‚`÷ãŒpQ=Í (Ÿ!"!"Cd˜×\^Øùtª«­½»wïŽîmë—žnL`À×ßÙüÈxÄ?þÜ €÷ÞÔü÷´äG©X°O½8ÎæŸ &›ú¦òõØÅõPÓ‚èääû¢ g¦T”Ï4ONiÉ Uõ4÷RY¬—Ô0+E°LF, YƺÖbA9F‡e* ù‚&f!’åžb!ëœ;ËSã Õ“”hØ+~*] |&ð•\3H׊&&âÈ¿3½õÌÈ¥ïõ ±!6Lvý(›XŸ· cÀÁÝm7ÞÑüðýûß½©ÿïiÙüÔÌ™i2è鑾¦ùÔ,ù^ gJýGOiÃß㉯‚ÝUÀ‡ï‰T8ÿÎp^ûËå ¡ÞXºâà.!ÓË5Ž.Ä=ˆ áþýñn:«2¶(­Lè Øõ¤{eQþ. L¤˜â×^2²ãÙº®†µÝ£¯íè}þ´–‹ lÄeo¹£ùÁIˆÿú=}î­«”\5¢ôÍ“@ëk¯“Ó¿Ÿ ŸÒ†¿ÇÆò`Á—Ã>Ž^>tž\¼fgèÐ9V·.ñã~xÕó"Ú²R¼åþ‰wS:ëû¶hó\v[ƒ”Üy_}Eñà¡€ bÐ å‹¥"@bÅdˆ\!ÍÎ×m<»»øüÛCOÖðNk]o·ÝÑôÝñ/ ÇŸÈZ¶|ÚÊã|¯™úšââ©¿¥¦9ÕôÃ÷ék`8p[tOc½j¬W®{Õê¹kž}{•'ÊO)ÏSÊSJ‰ò¤<ïô<å)QJ)OÔ–ïM†{[}n–)ðŽÛL8ÿ¾ó‡)Ý‚üUË;âæ€Dðƒû1+fa^·q€ÑzEò"HîíÑÍ&ÐÖh·C£µÑúŽ;šæïÊ3Ž¢þa?Ú¢;›ë=·Í×Ë«©UÕŸÿÙéä ß§¯È¶GF¾±.ì/>ɨ•6¡GÕS¯wºÁuÄg'EU\vôžR[6M̸×tÖ—|[*Y×üëª;šJAÉ¾Ö |@bõ¥¹î#>‘e@ˆd ï(Ú|F2d˜IbEl˜×œwdçKKÖŸß /bcw•µ\ùcÅ]·7Ý÷@x¤©h¯MÖ#SZ'›úæzˆô r0jš ßká{õk ôe ßPм¬—= my¾À-Wš±8>Lo„„}dÀH~ÜB(g®ª/„m¿®½³Ù÷}Z‡æÝ>€ýGv2ËÚ¥ë%ÌgÆI)ÀE4ÌŠØPX. ÀhÝe¯;PÿôŸëýŸÉî‚x}ïíMw?0PŸU†ç÷¸x|M}KC8_ºúSG ÀÊÎôž¬çE•gޝå ±¬¦1[6õÕ„$§ŸtøÕwÝ”tÉû=ßP—ž8 ôüÓŠEëf“:•™Á=ØõBãêƒÝÃ%q¥î+~wRB„(D<³Ká×n8´ckûú ‡º†ï<Øú͉]—û`ù*ëX·œyÑìOú‡}‡x*ÿÔèQcýÉ®ð3¿²3í‹ô§r8s|Åó™Ær¥GTòQ… ¿oÓèq¾zÙ¹…¢uM¾œ®»­Á|C¼Ö±*KÎŒÏßQöïÞ?põ-uŠÈŒÕÒÜ1C,%‡‰È ñ«¬ÎËåâ–Š‰N`÷îñh>Î÷š©?¹ÅÏô•i·ëµpæ$Ð`.Êq¦7dÕ‚„ïÓ”3ï.œ¹å]MnÖh¼½×M·7š Ðo´ot`´¯ç‹-kÎïÝWxޤ¸®ùB!rW'ä3žGžGÜ"«å ­$ÄëÎìÞñÆ2£uÇÞrrd2cWYÝ…Ö >#†G+µ‚® J«Õœ])í*E[tgkcÂmsõ,5UƒBû!>"ûŠò †ò¹Òü‡3S*¢ydä­œ*ǬYë½75»¾]ñÆ^·ÞÑäûþX2Sæ/ÍUQXÄÑ6!ŸQåZx"ÃTNáY1•SøÀA®ŽˆMħ; Œ?˜{Uòä B|DöhPU¨5ÍN¯¯F _±A_ gf­¡\'»StÏ‚ƒÞîïiµñf^ÅJZ—=uè 0A°ã•¶5çô0y¬ .œ 7š˜Ï$ÊÎØu‘t³XCį?}ïöm+O[¿§sï´Þ÷vý7O~ü*+P½5ˆ“Ùc)¢y ô'“ŽY?³÷h WBÖÚ]ÝÕúêT¥—«>5dˆø ,0² ¬¿{Óĉ£Fû:K!.WÎ!:ü3¯³ïs¶‰´å;ãZÄxaC›¹¼¶w]ß|ß#cˆ¿ë¶†R%3ñ¾Ñþö×:מ¹Ÿá±—gföq¬â‰Xæ,̆Ɍ¯¥â·Þ^}Úš&Ÿ‘AP®„ q8s|M=çJñ§²f0uïÁ¼ÖÖÄ*ÂVt¤Vt¤–w¤–wÌŸ¿8–jáLå4<ªÝgz]FêÒ2¹l±rºï‘þ\×_Þìn¾÷¶¦xÚ®ƒqe3aÔžÈs"ïÆBIW$”LŒEðB\¾ÐJáŒVâu«Þ2:XY¸ÈÒÛŠ?.__ ž|¹®¢?2U<ÝŸZ½ƒ%·Å™¾¤)á¶|a5M©Ìor—^ãÅ6+bX¿¯fê›fT!>2òqÄO˜47ŠYØÑXÍä¦Gù» ‚µëw3¼žå_¢UG~ƒ=«xü;…Åï†]þN†œ‹çm»×¾r› ’`2®þQQ%Š£JÉÌìÞTïÙ¹îÔ•â'|©¦êÑ õ'ˆç3ßÝÀûk /k1†3Sj ô1¾×¥ÅÑ87G Ï¦@®îm4?{].… ߃Àè`ÇöUë×ïa"I2Ùv÷ÉùLdÞÜ{ÿa¯¥!Å´~ù&0¶”°Ä&© ÀB°•9¤U½Žz"î?©þÿLdÛ#Ðw¹ÅþC}-œY@ű>ƒþ\±@>vi?Îô±â÷ X»f“:¸ìk”k\ÕûˬÊdɤâÉ丛嵜ŽRKóF÷™§/{mOnø­¾ž3[:Œ®lvBU}I>Ôdз5ÕX¿°šûþ‘ûz ûz qóÞÕžrÛœ?WMÕ¦‘¼Îåõ gÓâ¶YìpB×Ämû »rI#€°r¬ÕL`‚`ûε 0‘m´¹FgÛ¢èë%S´fÅãïùÞ »uXˆ…ÉMz:}é«F¶$(yîgñMS©ÂY[Lfg÷p·<À|.æwd td 4èmÍ ·ÍÛ˨)®éú÷Éáû”Š‘}Y ñÝ•7õU¤*öaš~G|Dv¶°Àhaêg̤“¾Ó5ïC!€ö¥ç|ýzs3+²H.&ÉAïZ|œÊý="ÖG|G™Ý˜èã*–Bðmú¡wÕ…£:ÖMì­=§¶rÛÞÊ…ºuêrQ‘m&¬Üéë÷ô¸î²fç®kÊç±½{ÀCO2¾f£oˆÜŒV>½ýecp ß>àŸ¶´‚>‘ž3Õ£ˆæã@Oàp_ñ˜«iŽ4¯ýßãž=âûÒòàÀlA_ ß‹r1”G|Ÿ)èŠCªÏPý¨Žš—Y(ÖYTÙ¶SÌ¿ƒ¾}ÿÑ׿ÞòL?€s×5èji,Q,Úá`è±®:¯ÏDÝá™K ]¥WVç?œ™RG}K˜‰Õ@_9M‹ï³g¦TdÛ—Æ@O,ºÔH=Oªto¬ã(¢ùdÐc*Ö—\î-ˆÂwçâO[öÚ®ü!áôMkÇl{Ø™€îß\¦øÓzåí]-îfÒÖû¾}üe¸lýžhF«²ÄÆ>¿ïìÙüäÓP:? Ÿn¨ÐþP‡ûK!âË…Lè 8Tcýœjá×o:0 ôË–¤Ü¹éÅúZ83¥&ƒS™ú ° ãJh¶Üpæ²×9H3ËÛ¾éIfp-t_ؼ,‰z&_hûýÎL5Œ^°ìuC´lIþPof`Ð65U¤„†NlÚj5G;îã2ú–±‹Ûíåq ôs¢…ç{¤ôË–”ý’±¼þX¬¯…3¯ IDAT'F–Þ¤“a‰W¾›N”Ñ£YŒf£Êw£ƒÓÛ_Þ=à3'Oïhæò:M Úüôì‹7Š4Œ'©Ž¦Ÿ¼àŠÓž5–ÈjWˆ¥'Â÷ÆzÀ`5…3ÇW<Ÿ‰øÞÞZ}o ô³Wñ=R”ÏÄù¾´54õ5”Ï•0œ™R£‘Ýièµ¶¢EVß:t~J½(’8­=S¶íxüŹY›Ó#î1:˜ðÒžK‰m›<ÃL•à{67 —çõ»«B‘møÞÞšt§H=5ÐÏ\Só½áû4÷ìK[CÖw–ÕúZ83k9šÇ={Äw70y*ù&êIpzë ÝýÉ0™ž~ci%^U¦1ÀRö.Öås”ÎÎùòÄŽUœÎLgŽ¥È¶G ïp‹žZt3mU£?ª”iÞ=3¬=búQsà(ŠXïøn­õ‘ѰzoîbUxY‰¬n'~´}eE_I]S @¡—ÓHÍ}š©Œl¬ó Ž,špfJE ïˆ@]%ª~“·°Z4|tpè£_|µÁU3@]w°…mæ>;9ÐKfT¦ŸÝ~ñ¹ËŸHæ_Q+—ê7Ÿ;.¦¯€2)0Z0élEž¨n¤ÀH]K%v¾¨}º;b×cÑÙü‚h ¾/`83¥ö¢ß®SGk2Bê‚_–inH诅33×äpæ¨*ø€_*±MíÏ3zdì±Óyxuê+gNÅó™ÎEÛΧŸ4¹r&2ò¨Z"¶Ò»GÏ«o0ù"0)¦Ç¢ýlU½‡‡é‡ïÓ×ä³ùÎÖ”»{ úEÍ÷ÉŠç3ßkÓä&kñ†3qeêýBN¶õ_–ÌŒý bz,*Ð×0\¿dvo¨S©`׳ªÕdЇtJ_Ÿ;ß«9œ™R‘mo›z‹Ãý}-œ™±Øl}pÔ‹œ•}&ɰ­Øq¢Μ¸"ÐGwÓ™Fs²j±ú÷éOk:<ôÍeG_yÐ×ÉZK“ÖÖ˜¬T’-P˜C§S¾2‡ ¯4}EÃ÷ÙiISÂÚy]ã)ÊgÆM£)OÐ/V¾ÏBc ožz {ä”j[º@±ÆOµä@Å^Ûq@*IoNàgo¨S8UÙãkÜ4šIsãOnПB|Ùöˆï8•–‘tK,ÍÿªrDd­5F3Ïß’ÜGÕdÐc¦~šU=³Vãà!ƒí³{øÉÑO¸Ò:0inüÒ%)wVyR6*?&ßuø>MÅó™ ËHZ w¶ ¯…ïÇ1[£XŽþ‡—J0€Biž¬ôqæÇbÞýÉΠʜÓdÐGËq±~Qú÷Jô›¼Œd«Xôž\ëb/¬˜Åè@k_Ä[è×2N _xs"=Ų À`®ÎÌXŽâã׆ë Ø¿ÈA¿(ù^QE o@ß:ú¾Åú… gâbQZû:ðEUâfúÏÚóðë³ï‹Ð‰±lH`‚ï,,€9Y#5Nö¸å}&Éî)G Óýì3µpf÷ìëW´§ÝÀöõ,0Ekþ}ÎÔ?TŠïä@o ‡˜õÙ´ÈU œÙüèÍ$É Y6z?)³GΞ‹Õ6, —9n\‰0@–Ø‚¬\tÆçŒ.¥êZùsaßµn7ŽFö 2Æž}áuÀ–§v¬v÷¸fñ8.è—ôîp¤uʼn¿ÎšPë#¾XÞ‘@ÀÞ}ïs¦É•3‘‘GyÕc,ª…§ÔÃÝÌÂ,™„ˆlÍ ¼¤3ï!âYºx¸†ÿ ¼Xw0Û+.þêèàÁãïßžØIK˜³o¼ÞÌä8å3ߣr…9=ÄVw´SÍÉÓŒÂ÷é+žÏ8¾XÑ1ýùdýD¾×™9T<Ÿ‰øîZÌ ßô[H˜$A`3˜ &bkfÀ/æØ2l-aëC[QÈY+ vWS-a"Ïö²K¾ëÛ Ûr¼’•r!æŒkí£4†gxyÖYxWHÙöˆï²©°=CújFà‰ÉŸÊÓ£|&Î÷pLØ[yÌÖüû<)²íèÜÀÚyè 5·áÌÃ÷ÞÎâ“ 1˜ °3x–п»‹¨d,[/0¾1Úó`KË–BÆ2–l®w'€lëši<ÉÌz¬œýDÏg"¾»A&ɳž[ŸQ­…ïÕ®¸gXïÌ4*é§k|ŸMZSú†ôuáo¡ú›ÿ=ô_·‘‹ç¢˜ð_0ABD F9Ÿùè]IŸ»ïxM˜™=0X¬óïİl“Ê lÑh(周(¥‰Šj6^ü·¹ÃÛ¦ùš­µþÓûïøØ=S~çd²;ó>£pã-üdEùL6%Kí0º|M–€Ê\©é¨ªP83E¬ø 0׬_4|_táÌ”Šò™ˆïÑÕÇú¿q;³°$œOg0HÌÄ f0CÈ2aõoèu-«Gúv·Â‰àd,[Çw›`¥uÑëy‰±ZI±=ÿâÏä½ Û~Öt^ùæþEõ½Ï¿ïÖ_ýö±¾§ž}Jå :[twšE Ý@bNT g¦T„ò8ßWv¦ÝŒÝFOü)Æñ½šÃ÷“XÊã|wuo¨ŽÒ·¿~³WaQHy!0Ûò˜èËŸú€L}'{)u-«?z×®ãXxfB¬‡ Lˆx2’ô|]Ô¾M¤= ‹gùž­ ¤;6Lç•où—_ñ maÈè‹ï»ù—&"¾Bd?¾…Ÿ 1GŸ£›ºôX åc±¾>£P gN Å‘±~ÕÒŒœè?÷ìßë˃áÙ‚>¾Os¥H~ù.R"œ "&—Ã0ɲÀ]Y%úê_}Ù}?'%•h›þþE<çÊ-¹ §pµ1nœRÊ×Eí›D:éÜý9ý~±ç¥iîü‰/}\”g£‰¬"}å' •ný…ï`<{\‡vèi»„å3q¾Gã“ÀÔW‰0œ™R{æâ£Ï鉀¾Æ÷9P%zŠE¶½~è­>±u±­µî¿èÆQ[õ~÷ ïf/4éëîj*˜‰Â$_ýû/à„Q‹Õqiõq,ü+ÿõ9aK–ÃÎÎÂ[Ëö±åw+¥ÞuàŽ’?:á¥Îºè·ýž3àu\8ŸTT‚K°ÖÄÀ ‡þý®¿þÔÿ™’ì³ ß#MÃÂsÏq”×M2õÕúZ83kMQ"¾¯ZšqŸ°]ÝÓýß«9œ9ùÂ÷ékx2è3âÀ02sлE”&Ü©µ ‡×Ðñ{ÿü>a/*|tÉ ÀBŒ°Ô¾öÿyB@yŠèÌj …=[C† g0mYúMJ Â^:ÝPòóºŒ÷¦iº¥&Ï|íXÜÁƒÈhH ,ˆú¿ÒÒvîqöPñÕV§÷#“L}]Zœ¹«…3³Ô¢*Il{úÕË2(ÖßÔ×üû¢Ñè3åÏy&–Òƒõ“+#‰hBÏ-›qúà?½Ÿ˜YÊÕæ–™”Kf Hâ¯ÿË—’Ð …ÿN÷ÓÃâƒØFUí`ã‘ö¯)(¥ Ïumº´ç¶TZuþ3räÅ"ljõÔ]½^øÆgDy–@€HQ` †|Qž!€ð»ŸþÄ_þÞßmm^2­v:Ž…ïìyÀÁŽuÓßÛdл5ÐÏDK[Sˆ­¾½ˆ¡ÜñÝÉAŸè覾Æ÷Õü7|ò™8ßëÒ¡©ŸE]<Ÿ¹ãc÷<øÞOQI;1 ‡.„o~õËHÜóRäÜi¬ÅËQü±"çß Yf]YÝÒúeGv!Q±ŠÙ[·ñ—’½Ï¢¾¾/±&7t8Ûp¼ˆÿ¥oý=«rg"×C’M@‹!QØ4Dý}[›[Ž‚xwœ|®S Ék‡øè0ž€¡…f}-œ™µ¦¢ÄQ>ÁÔOø*Ïh¿ ¢S9œ9¾FFu´EwfÓâ¶YïV$)œT’I*I)"I±s$Ö5ʵpI>ÛhÚý¼˜=¦pöˆ½-Mÿêà®HyåA¶vnà1_ÓÉ­ÝFw3}õ²ŒÛñ½¦Å®‘¼ÎåuܼG”Ÿ©£¿ñ#_Ry=Iy’T’ò$ýÿ|èã°šÆÑœì˜Ž9Û¾®eµ›î—ˆ÷êÞ[E<µ©îóÔÁƒRP¹pÆS囃ÿÌi¾€3ï#ƒGAü÷|E”Gâ±òXy,ŠUÂý+J±Š ï‰$³râþÁJ!~²–|‹`t®Ÿ«F ›wGùù½µÕÛsañ†3SjW÷¨Û¢{V/ËÔø~BªÂÕør“@ŸIq´Ms' Šœovþ=" pà0qܹd­¶FDÛQŸÂSĬ24º{çå¥ÿ!´íPŠÄCdÞ•bïµ!£Rý GÍ6´ì™°OQι+%â±8ÿ®Ä±ž=–„H"Ľ—ø“Ïÿa÷΃å <0ô²µsÎÅÂWC£ÁP.ˆO›hȪƬjœлuoúªéqª)újç{-œ™µŽjÛ3)N§8=è¯ùåbò“"RDÂðJÈûð¯|ÒèR³pÈza·’Æø€Æhßh?ÓØõÑ»’Ãc™½ òćÎyíoÏÚúiž‚òʶ]A)VTV¥²*õTßGãû¬klßÛŽïý7‹bq9Œ3ï+—ÏxÉT–•2ÖˆòD)ñÎÅ1…© ¿:£7yvš~„u"r”ƒÞQ>>®¦êÑœ‡ä»ºGÀš® s•†ï5ÍZQ83Z0Ñ}5äh;êÙ”b¸äÝ#RLŠÈR/¨ˆF¼Áop\þž¨fe‹,Êmö<{+:ÂõGööÔÌò´T…áÌ”ŠÇz:Éî¾kdøÎþõÓÿö{Dacw„,QxÎGˆ™Ðœ7HDʘdN9VŸSßÔ9©”«Ç"X”ëÌ‘WC¸…Ûú¤åBÉÎ/lº=›Ê¹Ö9"íøÎl6¼t›°}û’'òK˯%¢¯ú¡—Øð›¹?<#û)_üî CFµe:»çO@ÝLÞ#ÙÏ%Ø#&0ÂŽÅ brÑ%b%DAÒ×Ë×~Ѐ½‡__ <Mg­PedT ?ýù•S¼FÄ÷Æ:Ï™g½M-|Ÿµ*TÁ8vl{dä—— úšfªUÎD¬OE O0€«>ü©~õÏ-¢nbLDn:+€†Uh<âøfˆˆÙ£>ݼQ‰bXʪúR’xWK˜Œ°!²ÌFÈžýÂ%›GÿãeÛ(|’ðÈ =-·áI‚Öš¬¤ˆ)IÞJrÂ2\á~ØR!ä»—ÎÔý‘:¿Øµî. 6Çì@9‰¿îŽ9ŠÌOd5íY„3S*²íc ¯÷ÜÏZm ×4S©i`ÜJ"è—O½ö×X`q†3S*Êgß) §:…Ôd"ìcŸýìg?/dº£|@ëVš¦‘`²jÜÊ¢ñ_øÜãW|ìF±`ÈÁçºà^b Ä@9QIg“V¬¶.rON`"ÒÌ)b²FC¤9Y¿âð¥ç ”Ú°õ×·]ø/î`!’𨰾ÁË´]482ì8?þª~}%°åXiNïÕ Î¿±ßîí:ûvV)¾¼2AÕ®? šaÀñ®¤ï›z])÷÷¾ÿPeß©Só÷Q myþú«ÏÀôרsù € ßÿ;/ÿ×߇ 9ôÃLlîÆ:¸‰,ˆÊ½£Á>"¤e9€#o¥ŒÀƒ"@Û®Z™ÿa1׆áìv:› ¦|ÑOKÒ(ƒ»X愵BŒ½ÏìÙkö]Ùõ!KÊB ©„$HÆüûuGu±©á¼’†µ@ëyªþ˜ëq»cÔ$‡={Âgd@SKúzFuÃ,ö0ÿŠò™¦I ' ÿ¬¯…3³V妗ªé¬ÏgºÊ¬ïj•}M3ÕÄpưÑ>fÂ÷ñR°ëÖô(wŽdÞð§—mýãgŠé ŸvB¹ób¬é3e“)xW~äfaQ" b\³ìÖõge¿þƒûµ1.7¯«Oi´XL¸5†´Wöï.«‚Uî"ê/¼óýÿùô½OüÊu]?gˆ]DCL–ñÁ®ks~>Åð`ã%ïȱ (œ…?gãD öžá*¬ˆ“ðÜsÏxóáá9˜çä~`è4—ÇÇ}MÕ£WÂFùLÄ÷eåAw ôÕ§ÍOß ¬~°õ§/?ûëÅü`2Ý8Ó=œ÷“¿þê·ÿ™ÉucXrmßÙ8¯ "¢ñF>¢!YAú)$/ûЕ¢D±X…,Nhò÷lëÿ÷þ<þæžo€))I‚ÀJX²âNˆ˜¬ ÄáÝùt2é‘yüð7è;æÜ—¯y÷ÿh.FJy?Z|ƒ®dë—ä†OÙÁæÄ•‘¡ˆì›7=`Ý9×<ú½¯Â6ïYqÎìö9·‹èÎHñ|&âû"}•‡•;lÏ~¦CdÛ#¾/k+ƒþp•¾s¥E¾³aÃp2ݘéÍô¦ëŽ™Ns'¤ÂI—ÔLv\á<•;ϸëî>3¬\üóW(QJ)eE „µUŽùÄvÕ²6®ïºø¥ÂÛ`ŒäG“âé/?õ¿|ò/~— ‘a†•(~9ï*HéîÃùß_5Bò£â½¾ ÞK¿ÁDß:ò, ’’?¦cYø)nÛ#²ÇÞ¥…/ž9AE4ƒž™Pë)6+M¾:‡šƒ™l‘mø¾´<80[ÐWùñ¶šwy›Ÿ¸Å#+0üä³?uå%ß<8:x0ÓØ9£}žõÞ¿ùcb€¸\uH–/ù㫟ýÓï—RA”¿»û†Ý/|ÿ¥n®‘"(E¢ ¢H1D,âãÞ{žøÄÇßÿ›¿ñ“]í®þøÇ?õéOèZ¾lÿ¾î$'Âu¼‚r BJ¯\Ú–N$Ó™¤oŸ}ìs_L”.¸ñbDµ4e§zEÉÄo˜Q8s|²¸íÚ÷~w#7-M½‹àQ…)ü¢?ªÎRsÙŸ ûpÁmÑ=KÛRKÛRË–¤Žó¨š*'Gv2ÌPÿàùX«­Í >CÊiA=”þ1ä,üØl{ %ÊÙ'ïÿ§øæÆŸ¼Ìµ ‹:AF½!â)³V0ëøÇ?k횆löñÑܧ>óésÖ¬[¾|9ƒW¬Xþ'Ÿü³„x Î$%‘à„û÷Âî&Ï É‰Çç.oã£ÛV¾öã-¼5pàÍþoôu¿6÷Ç2M-›7=°yÓëιfÜÓùaX\{ë/œ‘}r¦o>4œ™Rýþ1Ö˜±wµ¥!á¶Ö†yš5vu¶¤ì;ÍbE:QD¶=2òË–¤¢_~u.{8}-–pæá-71+vW!«i‘lS×HßÝu-«f´·Óîü¹·¿û_[÷Þ’D1IÅÓé  ïú㛞øÓMAʨø‰¿þyÅjïSo ³›‘$мC”((†rF^ Bš–…î?|À9«×¹ƒFS"E„A¿ ) K^9‚×$¤†À–Ìi+W0S1_Òl’ÊÓÌ !!°yvË4ë ï ºÆŽáƒÃë›&žÄ<ûõÿ­MáÚO}p°gë"§)Ì{dÛêÙçLUì@§ð-1¬·6$Üñ¿–ÞLVEÃôkŸy¡B»Ž+Î÷HÇ}•‡3UÎ÷Èèm~äVaVÌŠX +Å,—]ô¥‘ÞêZ×ÎhÏ;¿{ íØsY%MGå==.þËÝïð[?õ—”è‡zÿ­.›¿¤ED)Qn¬D )%ìn+ÎêIÜö¿~ À٫׆}¢U¦È¥#ôò[Û>ûÿÈžv%4ë[p{ëj*‰¾h»fÖdÒ¥Dv÷XN|øöM†¨oZê~®7îýŠ1Em‹Úµ)j]øý?ûå®e«¬Õ0ÍG/WŸÙ3ù!£é~ï«o殜ћŸM €\¡ýûd¾Çñ=Ò<ƒ¾£5 §*yRi¾ÏS'¹îÊ—–㚥­!ô«å‹NÜ7=t³ˆ ç ‘eXv7Ÿ}þ#g¯üÄ”»š¬5·½g÷C¬_óã;; %E%Aéc?ñ5E%aŸ)ØtèëJT‚Üy˜œ…WV¤¬ "V"V)ØO|ð7œ½j ¢µ ¨¼””o'“ Aˆ>#šúä²J(O33±&ˤI‰Ö¤…´aÅÄÿºéÖ€‚Ÿ¾ø+žx*ÌhëSdEÖ0[¶†­aÃŒò¥÷"ú¾ÚÒ9±èÅ2Óôìq¸¸öÖ_ÀL_ÍpŸR½e'äÖur÷ë=RyÐW3ÜçA Ð)4²í®×>ʆ‹ôUnÞ#VÁŠ«h$+pˆ·DlÛi¹Co佑m?sf»%!â7×âü·ÿX¡$( JŸá üÍÿ])q(W,† ˆ…”€è•€…<ÿöG~ÀšukÒAù2&•™î|;…Þ‚Î?ó¬ÿù‘lݲù¹={»‹¬„ÂiWDD†ˆ‰®Y¾öÉC{B¦3;¾¤…µ&%¬}]"²÷>ù‹†õé{¾˜Cfíªï“a""ËÄLš™ù/þâó¿ÿû¿º¬k9-ô„„ÆÙvWÏ^©4fñèøæ=®È¶G _â„#Õþª”*»-d'èh•Îè™aí¢ÏèV›¸…Yˆ¬@f×MÑ ~áùßÜxÑßö¼:Úój¦cåØÍ—žðɧ¤¨(Tò…|FIÈßrðžˆ@Ø#Oˆ¬ vÁ‹¸üåw>þ[V¯]MeœÇÖƒ¢rûár×÷?&¤’×·uI¿±{·ptE"†½fÅÚÇ{vk2BZ }eØX À2@ÊkêtÊ,Ùó+E:Èh$E¬Ù2òµl‰¬cÂ# ÈÂ’õÚÚÒ¾aÉ>S 2)ý’ôMá€@‡&®Ó{²jEGÀÞž 6f¯ŠNÿ'¾£5\Ë­§÷TùeŸ¸b%L`=;„ °L–aÝ@d:ÎÉ÷¼’ïy%Ý1uÓóh%Íæ³)òÞZúÙ Gnͤ´m|lÿ—•ˆR¬H¬ &¥aÃ@†X‘!ùäÿ÷ûV¯^v­q¦,E4b|O¶Y,hÃùWxî Ðp6ÈÚ‘ÝÿN—ÁL$U"  IiVZS Í"$ûŸ>pÇ?³ŸF-xÛ†?z›tÀÁÂ!£‰XÈù³¿ü»?úÝßìZ±<<™22Tóìs«(Ÿ‰ø ­)üìŸ: ¯œª‚ï‘ö"Ä;9л“˜C ÍúEÎÍÓ8?Œ/ƒWß´ÔË$-ÀÚÓñýs¯x¼ûË)%žçY(b†xä1I€@ „Ø•Êüþïý€U«V Æd2L`˜$0D\âFK BØÜÞÝ9 IDAT&\!Šl4ÈgØZ2š­IöA26Ø÷ &"XW"qãŸÏý“ff-¬ˆ°=g×Úµç,ùBò?§’œ'+ •ÖBòhëWÒ×ùikÙöK‰YZÖ¾ì¹çž;°ïÐìÈžÉ4sxš~Q‡ïÓT<Ÿ‰øÞÖ”t‡ûgùÁ?ÅÃwÌ[ýÌ45¹r&Î÷ˆE‡úôUÎwçßïþÆ-,Ê•Í0+f!q%4"¬˜=baf!öÎÝø'¥ž$:.˜°·ÜÐ!„“},Ê‹%øÒÃ7Š%%žÇjà‡=uÈxžRžJ(åy^Âó ÿŸÿGò—ÿìB÷ë~ýlX·ÞÙu£›Å1;“nmƒ%²n)WÇ÷¨M9Ž^Ø«?Þòý­í MÖ°1d ͇þËy&<½oOX9íÎÜŸÕÖÓÆ{ºÿ¹‚- ŒŽ^Õñ¡2¯žÿïšu@ÏZ“8Ф¯ê¾=_6¶ÀbïcwOÚÔVo`ÚÚ/™ÅïåX|wš²–Æ­š_{«z4ýð}Šøi¦ ¯f¾ÏC8ƒjóï“ÏgÚ[’ñ/è«PîßþÚÍÌ F¢…-`ÅÝ á-‘%X7;)ÑqîyA÷¼ Ý®rC‡ã{ŽÈîD`aņ‰¨íË=ñ.Þy‰pðΫWv,iZÖÖÒÕÙÖÞÒ<<:Êdoøø‡œ³fqôI%Vƒ¶\öP ­¬uÈ÷˜yl{ì©C^ Xlt  ÍÆð²ŸMõÝíÕ¿sýiOïÙ©IŸq(0–gÙÚ_ûå;^þ›WÅÐH©˜"Œ$½d@³0iÍhѤ˜5YÃ?ÿž»´t¶Xk, UqúI©(ŸikNNÌÚÑŸjªv¾ÇÙöômn`çã÷]åæ=”eXfË€ œ¹3ÜMGv˰üÚ Ÿ9{ã'%\ž£Ã½¶|–”m8ÊL_üÞŠ1³%2ÌÄdˆ) Ò?zb»ˆeÿàý‚1öÑáá›>ñ¡³W¯£òôÖXÕcxiiµ #Ù±ñºW溨?òæJ#ʈ‚c4Àíï'kšG¾ÃlW Ùµ¤ .>çÌd2áûASsö‡…oýÍG?>÷@!7TÊ}½ï Í,¤7¼)oî®›l¼üRk5ÎËœyIÈŽÈeŽÙÖíø)M5›÷yÓÿÏÞ›ÇGrVWÃçÞ§ª»%ömF³{¼c¼Û¯Œ=cc 1KX/˜%„l„äMÈÇ—ð9!Àû!ù Øf³1‹ÁÛclŒãe¼¯³4£e´K­îzî}ÿxªªKû.µft~Ïo¦T­®.uK§n{ï¹ñ_÷H¢'Ðøwóż/–¿Çˆ?Ñúšå {ßÿúeL>…1;ƒ¢«0Áy«˜”ë/ÔŸ1´ûçØõsÔž`oEV&%VfcŒ„ÛO®GY‚úŒožik=aýQÍîsÄèh¢\ÆzuJ¤DJãÓú˜ˆñ˜¡l,xÛ?tq*›}Õ1¹çwZU,>ô7ŸìéÌJÎ+a/4J3†!óæŠ³{óe]M¯ºøg¿æ5– ГÈUÑ´z¸®æì}8‡!æUœ£‰‰ho±dÛ`aĿϠm5ÖgFßÁÚÖYܱöÜÁ‰3P/dÀŒ¨§IÃúH‚«dÇѤLjòÙÜÓ¿þ—M'µ§ú‡ž(ÏíÌÕž9Á 9B'%"%§Ò°ÒÓëvŸ¶Þü±÷¿vã(ÍŸÍC0;žþjéŠÝņ´äÐ#ã½ÄWt))sr3³Gª$Jª¬úÌš6Bðã¾øüÞ†Ùx%~¤©zÊufªÇX,8ðRA*O—VœðöÓœJy)ãùl|ß÷|ãùÆxÆ÷½´—J{é´—:vÝkÎ8êu›/¿.O%ð˽’Ú’’•%¥«ËV¬_±bSEù±+¨½ë·S?¥².@'gbÌØwl­‡†ÜŠ÷4Ô¤WÕe˜ŠWœ™ÈânNQDñûœ Iñ1³ÇÕµ3n“[â»:W%Šï¬®œÄ¸¨ÙåZó¹€Yˆ=¥Ó¥Ä†Møk๡ÿE³F<…G iŠ•)mè¹Õ}'·–ÄO‰œd¥°F„t}Ìì:‹8&Â@@žrZ’/œN“ †¬°ß7V|_$g¬ÿ‰ûÔ{o€‹ ‡ƒÓÃféÕTŸ&6Ol†y Ï3ŠY|_xqfRŒÎÏ5&Š­‹‡ëLœAñðû|xеuìž7t!Œ/ e%æ¶/¾Øñ»çÄw¥vCVÑ9a¶ÄJäœJeˆ=D­O¯<þåM§ýaoÕYÕ=V÷<úÃü—=r¾Æç¶#ñB}†È0¢=Ì!…ËŸüþîÏN\¿QãÀ=¦ñLCÈì4Quö°ž(©zG]‹=ß„ ` Èó}Ol žg¬§öS×Þ°éÔ£«ëkÂg9Æû†Í̦NË*Í\¡õУø¤qcmÆ}Qü&(sˆbá÷yEûh¢¯L¹û£Ãó4îP…ÃyθùJ™ÉEñœÎdˆ 9%G JÛWþÌ#ïé–mŽÐ7ó‰çâ¨ßÒ.%b&Qâ_Üs¿Ï^èÓ"DR<+‘‘½ØœÐfFœž‚L¦Qaûœû!<ë¥ò*¤ê§Ó0,«±Æ7¾øVÅg±*ÿöÿ÷OøX¼Ïí-sÙ@€þÒª9=j± ˜‹E¹PáG¹]áàú#‚ßcˆ>ae?:×/q&,‹$5 KhÔHÎ*C`ßO».'Gý4ý$ŒÐa¼ÀfV%Nyk BÊN±'W9ÃaB•¢ \ˆ† 3ã•s÷gþý§'nØ “iɤˆÒªóMUîè<ƒšÞ•>ø]Xè¸È3¬±F=ÄSë©°·õ·ë_ãÞÇ9|õÔË'CøÒ4C10TŒâŒ·ÝULâ̤88ÊÅm»_Ä–…"ú…gpØäW§‹öî\Gwn¡×FCgë¬f€²sÛÞÖɨ«ƒ46€"¸ Þã´oÒ‘óLX©VÃ%*¢*ªV5PÜ3ôLšýKÓ'©@ETTEX‰¤ˆ*gÈ)32B,áGÞ»êÜ{Hù% Ny/0ûÜ‘û‰§]¼õ„=£÷Wöàqen¾üº£ªËûi“)1™Ìs¹ï߯ÄxÓ¾ïû¾ÿO?øÌ˽ Èàûé¹;Á™dÑ–­óÙxÅ;WÕeÜZÄ›¿/â@Žˆè“\_³´ˆ¾ÀÚF,$PWßecR¾—B,×8EÞñ{DÜ"o«¨ZQ«?é"mü7–¢ŽúEá‚ôX|±’ à`•áE“ÄJ_ÿÆ×žÛµ£4“ÑíÏqÁ̘ˆ™=V]/ÕSû6/ö}?•&Ï÷¼”祼Íq.ó ”!^Æô0-qfR4·g›Û²-à ½©>ÓTŸijXAýâó{1ÃÅï‡zr½ùxÅV9¢/÷«Ê”ëmw•Úoýð÷ ùÎØ•E†Ê;BO„_†1»ÛO0aß‘(D)ï"¢Ñ±V|yç}ã–qÚòüœ¡P‰t8¢  N-{ÇkV¯VUUQѾWü≺gžz`ž fJM‚ZUÓ8ñw^rÙÕ+×¼ /žá”acü念{æCûob¤¬*­Ìxùô›vœþ)mÞnD÷á@R…€ ×¼:Z£ÇdùØiÁ0°RŒòûa)¾O Žå›[‡õ«§@ô /Î`Ñù½˜Å™IÑ刾7Ÿüu¯,÷+Wx•+æ2±!bÅb÷¥ñ|ã¥ÓøpÉÿgó}.„‡³3QO©“àÚÐ\ a=¥Ë©J!Åþ X«¢Ç­gu÷æ»û‚îá„^±Â«\áU–MãjªŽ(̆»€M€óßûÙó®ÿtbšG¤Æ °B¯10À—õüq¡ò=Qìö7F!lT bMA2 _¿Þ[xþ¦VQ€”èžß­›e?žÔ¾íî;¶Ý}ǦW½nÒɨ \záE«.8÷×íÍêA `ÏØ^ ÑÕ+/Cø'ÿeÇ/_T@aAJLĈ›) &.œOr:s¸¦‚eqfIcÿÁìþƒÙ„¾¦1³¡©ÔZ`qËñûü!Iñ‘\ã(> êû'êiVHŠ3£ñÚënðÈ7?¼¼àãXÞÅøDʰî*àª$õاÞëÑ8ëN3ö–ìÏU•§ÊKJ³Ù<)‘à‚•¸ˆ  QrÚŽÑamM³Ã˜jÌä1û0±­¡hâCê!ðTðµ\QqòVœz{öQ_ü¿{ìŸ?‰ÿçô³^CDªæZ1Jqá·%éGl`:Aýr¿‘¤ø8–w¼Ãá﹤Å÷i!Ög’!|E´=&ÑÓ¬iñ¬«ÿÀãßý ¢úEW_Ⱦ ¤" †ê1Ïþ­OYPPR€ô®–›}61Å÷÷ UW¤*Kˇ.l8ªˆTjU…ìzò…¿0…pÏïÖ??ñ´‹§õ#Œ)ÈL“Ù€%`&U:ûì³|õÖ¯ŠPè¹[ÏÏjööÇÏ㣮/ÿè‹/Ü0g‡ä®ò{ýêɇk—÷uè]Q›Ü9šè“Û³Qo–cÅ÷iaßÁìšÆ W*×6–¸ Õ¼ýbò{1WÏLœ™Ý *ù½¢Ô ŠúzÆÉgŒÓÞù×OÞòùPˆ˜8¢xbÀ\>øá¯lJÑ Ñ`ôH¡R¨‰êdú{ùï_ð‰Ïü—£uR¨ˆ°aUˆ‚JJDÕ£‹ïsÅìH…T½¼mË *ãô³Ïðùïÿ‡“¿<ßxê_ñ9}û¶U¯<#h}øû?Àé¯9«¼ÏE3éÑfÄÆD_ !ü²83c8rß7œÇãX~mc‰û+Ù{`î‰~YŸY4Äa»ãw‡òh{¢ŸXœSÞñžýþG* q!ïJ;_>‹¨#Ê" Tˆ*pÚ¹2¹ÊH"Vº¨á”tÚ¯*/G˜Luz3‘BÕ_UD•3’†SáG 23cvjÿà`‰ˆ º}bõA”>¾ü}Ý­D@å©eåu«Jª^ù!€B!<†YÓL€©_ÍÆTo\Øñý¾;o°¬ÏfˆÃöѯL¨7sÄõ‹ÆïΪm„áщž•Çü^^j\ÑFß”y|b¼ê­à…݈°¿‰ŸÝ}YÙ4åJP¨PDÕJ”#e† •¤SŸÿ㿨¯©ì¬([áøÝIî¬,ªä†Q 2ßa‰tFûè°}6ÌŽ0xéß)òN~ͩƟüËíŸÐÛuÀ¶—•×à†ÓתwË]_pÚ9§…שy3ƒMô&*®ÜvÇ7P4Ì^Œ ßÅ,ÎLŠÑ'ø}]´½gvD¿¿â°=&ú%…è~ö\Ü•×xù'·ÒIë¶›=»OwQ7 @«Ü·ÿfo(å™”ñÿñ]×»¿ñúäW +°Z(A—EºÚH Da¨Ðßa‚~ΙÝE0¸á”@:é„cõôTô<4jÙ¾\¶§³™ˆWT6$Ÿ•i<ùhú# =ìl‚ÖÕ=ékUô¶è)¯›Ù©ŠÀ$úmw|cOp!€Òôâ»À»ªßîeqfúSœɘ=æ÷u+CõfOËLˆ~™ßÇÀ<‰ïÓBLôI~wÛ:qfLýÆwìøé÷]]͆ O3óüo§¾ò® † ~Yî¸ãÞwå~ü;7(¯Z9ú¹e Çoß³xò”sNZ˜à5®ŒŒ™’pŒh8ˆÅ:‹usŽ8l/ýªB†v÷”¹~™ß‹ɘÝñ;eÑ|ÿLÿàzÃ[ìùÙ®ãIUŸÞô¯™¿„Ó'´_Dª.¡zÞÊ­Ï>Þn80Éí¢ qâ;‰*D•Ü~TõöõcL‰fD?BjŸ+f@bYÃSp§Þ‘xð™§D%/¶:ÝtæIëÿé]Øöç+¶6y„²Ú7—à¡gN>ç¸ÙŸÏÎÛ~2LŒÂö˜ßã šÅG˜aI‹3“¢@ô« êÍúh{R¢_~_ßg†¾A› ç½ /gÜ­{ýöm»WEu0ìhÚÝðÐÎGwyìm^ùh^)êÁ'U«ª*PRÇëpú{Ôà9^Èð}DØ>—Ì®Â"lÖv…vf­X+V ˆŠª…ðâ®SówÜvÔoû$+W/O%—0KqÆÅì{í…c~ˆñΘßá>z"ÌúfnR,‹3Å€¤>óûúU¡z³»yl¢§ýëöù?·‘(f~¯­Hè(ÊŽÜ“}’ßmÍ@®íél¾åž«LMÚØvÿšhÛ9}ÏzÖYŒ±†• )1±yL†É5©šã¯2iëùç^ÊzcÌÀ:®ìÁuëÖm¹ô ÷å´˜}…9ÔTg{@ª¤–EH$èÝ3``ÅZ+"±bE¤§®;þÉ®W¬Z«ö•ß¼ø“[vÛ|îø7¿@EÏ­=o}˜Ë;̆ßï»óÆ}öBL3$ù=Æ<}‘ó{1ÇïSßg†˜ßÃûT†õËúÌÒF’ wñéô§Fô½]-D„š”JlA!ÄõAxrí~ÈwJs…’’ªäJm$lß?á¨u­Î¨e|ñÝq¥ózœAÌÀ–Øq^¢â¯DÈõªÄ,‚ì~µV¬…JfH{=ˆ%µ,b!'­Ù8¨ƒ` zo,†,r=·xÛµ½í¦O¦øyB,µ—•Lú½#ôe%#SñsUsUü¨¯I+ÐV”侈©|CÓØêÍ"ð{1ïK…»øtHô%ÑÆdDOT•È#bH<Ωð/(Pò@ò̪>Œïñ‰Ø] 8´\QˆµÖ2‹_™y¡ÿ|ÜyãæË¯s¾1Óý³¶ž½f ¯¢PEn TmY:“7¤±QʼnÇóôï^²d-‰@DE u9 Te@pöY›´´´\|Ñ›ª×ï^å=WnøÚv>çñÎjÁû¨ªö™7ûÅa{Ìï+¢â«¾¹n—[FqbWóØêͲ¿Ø0,-qf< ÉÀ$9½$Í%i.Iñq÷vP^µò·nSU(0»‚Éh.«G;…½œ¼P¯/×ñ®Úh”ªXKZ°j™xÚꌥö@ ¬"¨•@­´æ:úû:ûÕªHØžUW_+xUÕW·A^%©2à’ÍoT»žFÂxÑúÏwŒ<&Ü=ÏÔ„ûî¼ñ…þó“…í3ïôM oкï)/5nÍø˜E.Î3æ[œ»[ݲäRAlþ>- Éàp¢Ï¤8Z ¯û  -ĆF’`g/!¹Ç5î"*j-Å–“{ªSû*ÓM§¨"¤BR™€Ü'æÊI¨í+…µj­®]Ýtâ1Ǽê¸cò*Ú@mÛP_{¶¯-Û÷˧Ÿ*ɤjQN„¨"u|eêăғ[o*@Ø”Y_U•ºêª«Ÿ{þ1Ñ@4ßúÊ£*ö­W¤¢çV±ºŸ|îpß7Þ7Êi`E©Í¥¨Ò7`ÝŠ÷„D?ÓâG}MG°83)v· 2€5E<_j1‚|6—íŸÙsC¢Ï Si2)Jû”ö D,ˆÂªZW“WÎ;±{Ø\'«*OpEôT•ú™GʪI:Iü>cjKªKêk´´w´´ 4±Z°<üüóû;*©»ºöÔ\.kƒ\g¤CÞ†ÔÚºº†ººz…¾ë]W½øÒ3 Qد< à­×|¤ªÿÉ×ï²TÙÓ »¢~âÓŽ™}t?êœïc¢wÀºï)/ó*¢5?¯¹Œ"BøÇ¿¯u~eñeñ}¸÷‘ÍDÞÎù)€¡Án«bKÊ'!”1‘MP|~° Då• ÎzˆLe`ˆÍ2af@ÎmŒ•Dˆ±.+õ*V‚XRÉ–¬ö”Ç•&U´'@ A%J\Á拯´Aå¹^º3×+Î{ž”…^½æ˜=̓¢¦‹w¯®95yãå—©«oˆbó(BwîÆ°±-ó{ßrTRˆŸŠÁ@¦Ð=eS,‹3³Àb‰3#ÀÎék2n-Þ)-ŠV|¿ä¬ûTð³ßüÞ=ê:ºôwíŸñ1ûº[‡rê—ÔgsýáOß² ¨Z;è}¨ È‚¬` “Z%0n¼ßYkÖˆSL¬°‹›Ô7¨Ö&UÙ“m¶wW ©@S݃}ÝCýj AymEYíŠM«× è€Õ”ÕTùÀ«=?ãùiÏK/•FǪUk©{Ñx¦w¾ë/¿ü¢Â®:&$âÑBü ”¥ñböe†hî}CÇCoЭÂ9¬ðÜZ˜s˜,‹3SAø‰Æ3ûš†ŒûëÜ?Ï}¡ˆýŠÝ©"Ð<2T5 Õ•…„8PËnH•DLâæ*8K/Q²2þÌ©™…ðšëá€íršQ^3€f)ßTs@ž—êÊ=[ç•ÙÜþ¡C¶ÂZkìFB™3eô‚–U«7`ƒâ:mµà¼jÓ0"žZ¹äØ—±bÛ'FÁÁ4böxcÆ%—QT·¿)æ÷³'ú"gj+S:º‹.~°í×[ {Écòˆ Ág2¯ÞøÏŽßIR)m8aÒ£õ÷´(«hýÐÿàŒ‰Zœ<ò<ò<„½N)1†ŒÇÆ ã‘gˆ=ò=#ç®[fò`L_õ[¬ñ¬ñ¬ñ'¶ùŸJëƒÍ@,“ vvú­Nú¯Ëºý• IDAT¯语[— ‚!wÍ2^*zYUÝ"¶6· *k€#w*ù p®¿‘(¥™Lí_ÿÒ¹[®ñÒ#(>yÎU=­º†¿“Óbv'’Le˜× Â3ŠØð½˜ã÷"g0Aý̾ѣÁ2n5ŽêM1“{y©!%¨ÄÂ# ôÌÎ~v׿%ƒ÷ƒÏ|z²CŽÒû]ß?\´õ"‘aÙÔP¨‰zŠ ªV¡9Å TäÕ%ï+jE­ue,qÏ27²ƒµyûzŠ *³uÕC5CMjmI¦€ç¥=/ÀyÍšœä¶6ßäÔ¥ÊZ^µzSLîmÝ52ÎT%Ídj\wý½íæ‰Ofb•fRA&‰ÊQ±;€ž¾À­xOe¹¯E<±(fr/*L®¸Fƒ'h=¦øæ#G½YTD‡ªäúC“5 <ú!fïŒÓ?`àà³²Ÿb(AüÆÓFª¿§ @Ù¨z§zþ†Á MÝÃFVb&U(åD‰Ü~QeUPd €µ…¢w%a¥H…Ÿ°VrŠ*MoÿK€UR&eUãf–¯\ñRv°#k3UšØêê €ºÚ‘ã[Ö×H|ϤkG}CSlj-~Afêèé Fze¹ïš…»zŠ4¨_ÆL#£’Œåc~7Ím“}‘‹3Å‰ì™ P2HÂDf@°ýÑ0™SÏø€ìÁ§Üólëv†RÃîËþÞv•ê´|¬ë¯6/ âÂ!eÒ°H&t/p#=TE.Ÿ)¿o ó’Lõ/õ6¥Únj|ûTTø© ½û!7\IXÕPè„Æª#o@‰}…+×§šêÒºº„%AÃÀl¢ø#Èýºë?8Z¥Añî²ô–ó^ «¢aæÌ^¬)Ÿ1+gbƯJPÑ 8‹…âg0cÿ™8l/}}€-“ý2¦×|¨¢pT+ B8 R&„;Iúäcÿ‹á½úŒO¸§ÛÖí¨í1&5„ÔÆÑ/ñP矱ºÙØ Ð9—œ÷ð½¿q UŽü ȆÖ4Bʬˆœj^ÛÔ$"©÷ªewk!sÂ×Užã6Ú[!5P!utÏÕ«‡%l®o``¿€@¨­ª¨­­S•ñÌÛS©*4å¿1¢xÕüú.Ì4f¯,÷t÷‹83)bºOò{UyØ@ѹLôŇÙö¯6·f›[³Éà}U}Æ­Yy!QÌâ»ÃÖÍÛ  ‘?L— èŸ~ìÿ}vû'˜†Ó Á{Oºë‰ú`GòÈ¿ìøc+62Y´**"I>,ˆL44i¼ÇªZ«"b!VŦN]…ŸbÝa]ÃYµ§‹ Â%#™QÁV‚ÚòÊÚÊÊÚºZ$ÇJÇ#÷)ªð?øõ]U:<™i¡«7¯xgu…ïÖ|¿ú²ø>uÌYÅkLñ1³ÇËýœÀéˆü‰ŽKrñ<ƒœ£#DP¢gûW&ï„Ó?ªõgôöB5¹WTt=bÑ-Ù/18âA{*†R  Uf«BJ$†X9|4<+PR"H¡† N?’´akTY& á§…úÕghÝýXÃú3F<ÔÛÿb}ecM} ñDlGîîKš ’ áÏyôG:óÊm~Ú{1ÅÇÌî6ÜwhY©_T˜³·|`nØ7¸wÊ•—yUå>Ñäýb¡,c*:³='Î¸æ—¯Ü e€™ )ƒ VDv; n¸5ƒˆ• ÔÑòÛCno~´ºñtª<:_²:mÞ=pG_°s#Õo2;l[Øè­æ—›Ä`3ÈX&0ƒÌJ ·øü•ëMò(@í~ÎT"y†!ã;þÜ‘_×»ûÇ9eŠïLYÕÈ¡K^ÐR–^QS_õâª%¥5ž_âù÷Ô„]öD8åÔ3òýÿY{ÔÉ#öŸxÊy;ùòPúUkš_°¯éø)žðTUøuõ£83³¶Õì¸U’§Œ(I›’ŒW’1ƒs÷ÇUVâ(Jä¢ß1¯þï-£#úº(¢/¦,k]e @{‹3*@mD¤$‚’˸.š÷(0”Íqó‹ß%ö6ú‡ßÌþ&fåKð*¨^–>)e¼û?ê~ܽÔéŸùä½Û•D™80a3TÀa¢UHE”`(@aÙ&ˆÂ»€ g­!1ä¥-1C5"Ag‡;áÌ^ÐRÓP™ö ycÇÂŒ{]OŒŽ¿þ%£ ᢾÖY ´#]‰˜½& êk*SˆH6Bè²83-,DGrK[v„=ŠŒë‹ê J 3œD®ÜE•áæåÁí$DêPn`Ò%%ª‹ï7¿ÁŽ^îµÏ2Ì›JOSU@ß^sVÚøß:øªh4sO,Ø9½CÕqºË²’ÀBXÂ;¶V ÌÔ*H…UT%Ÿ³Ö² È/ ÿÚ3‘Sq’èglJw¥NŠIõ™‰™}Ø¡¿9ëÍSyÑñNåH@¬Ï8~wpé.:ºŠ=¨ZêX~wäžTá“ü¾²6Ü>бLôÃg¨…ê¡O‡E“ G› %ÃÍX õ H·7ý”…âŽ|÷ßû¶3˜•¯©?_×7]öü¿å§sÖrà®"nÐGX~ãFËÑ“’»¹ …‡'"!"îç²V™‘à÷Lšc¢›â´©$¼ ‰®ÔYbÌæÕ1ñ7\õ_øöl^«hé}ž<Å%böÚˆëk« A}ûÒçúbg°Xþï-íÙxÅ;WÖfÜZ”S*~¨( ¢jD¬Jμ×*´PBã¶UË© å—@ððÊ;\Œ•p‰µbƒpü´ÈÍüFó/\²õ³ïýp‰I§Ó~&å‹{]«Ñô …¨hTWcÅÕÛ@,…“@DD²;¿åBxRe±¡é ›·’?`Iš÷NÝÀË…ísBî7~ýKS'w‡¼á*7ÒopªÅ›{ìèÎutçFïuU)·&xâ²83],¾c\¶ÇÌî688ÿýRß\ö¦»î¹ý  ×ßDD*…Ø¢DšÏ™T*Õß4ý„m\*%RUF!šÐ.þ{÷½¬lÀdÕ).¥é”Çfh0VŽÛ„@&y£òÏÝ#Dñ{!„'åBT‘>d ÀÞàÂûî¼qËß ×;& ÛGˆï`ê‚ÌhÌ|XkÑFï ‹$ÅÇÌ^WõmK௲È1ïü>Zœ£‰¾1ÚX¢/BÐèºp¬”ˆ¤ Ò„ÉU¢aõ‘L*`&(~Ùôc¶¬PuÉPN!Wqâ  ª+˜ñÈsß30÷H=²eFEKK2>¼Áþ¡óW+¢DêZgVÄDâ?ÐÀW#N±gUf±¡å8w}&ú­4&|’D?uµ}b̘Ùë:öh¯];ã—^¦÷шõÇïê« ±|ñs}Š3(Îù|:²:²INo¬ÍD+½ˆ'¶ÓVP* DU ªÉ ×–o䆆HÉóR÷7Ýê/b"“heŠöJaÓÚÀJ°é±³ÞP÷*¢‚þ¾l__6jk’e%7|ìOœþ޳U8—1µÍÊ òX!7º/ á'þÙ_è?ÛO¾‘ätcÈ­9$÷s·\3³°=‰·]ûÑéª4µ•)€:º‹Qœ)†í¹öÎÜB¯¯N5Ö¦™–Å™é¡ù=ÆÁŽìÁáD ¡&íÖ쿄ćÐ8ö¨j¡m5œˆ­ åXÉóR*¸hïïEÌ+ïN!¹Þªs€‘è?×vª‚¨m}½ƒ½===.ºÿÌ'>Jíê("*q㫵\×+ßd›7AÞéï#TøIŽ ± €†ši¨¹iÍ&"WU?î³&gf µ„³|Ÿ.ÅO|òˈÑÖ™‹W¼³¾&íÖ"žØÂâëïSA’âcf7ZœKº¸:sTYIÒp')…m¥ r¹V…(˜TpÑþ·Þ¿ê6uމq_§«’T`#1Þ=tÜã—)å…ŒŠ&\hÈÕ_ž’Ùô7ÿâç?ùÿù©¿ðgwƒ†™® ž”,ØUQæYX"(Ü9R…µ’4Ô<:lYrŠs¢f#µÇ¨kß  ½næâÌ2f€¶ÎÜN¯¯I‡Ÿ?¡µc‘ 8ÅÌ7¿O]|Ÿ:b6Aôªhëñ.¡<)ó¥ Wß{ð[¹‹1©þäWhïøï¯ü€@TH]omØ¢ö»â¹;Žß`ÛÓÅ ?”)Ušª08Ú^èMèç$lAãtÀN‘â—•™¹B’ëc~ós—‰@‘ëïSDë¡¡ÖCCIN¯¯N»ÕP}Xét—½ëÀyFj(Á»/CAF“©W7ž(ÜPQ÷m[Ú®e÷°~XŠUT€F„mØ·ÎnRÁ ç¼ímçµvt~ñ¿nwù^q+¬¸W'É«ˆŠ…³•´¶6{&õð`Ye.]ʰX¶ÁÄÂJEÏ­çn¹Æ±óÄP W §qÇRû\‘{}ûmuëÆ|t¹ÖeÌ Z;†ÜŠ÷4Ö¦]EƼzÑŠ3˜×øÝ9Â/¤§XLñõ Z¯¯Š‚ú®¥qIŸd §«$çöÂäʃ\@`/åC¢|ìÆ>DqGë–CïÙVõµp†QxYËá}òO,ç LÄ ŽÆz ô!ŠŽÎn@EJÄ V†Â•ÃC¡ r÷Ø[›bÐêªR©Í×ôßÞYúFRV%&UaV6#ßž[¼íÚN÷­S ;x¿ñ?_pÑë¯M>šÙó¥¾& íP1Þ2³83uÄa{Èáíñ‡Cü>mCnµ&ƒúª´[‹xb³‡ëbunìa´®ª¢ä*#UI ¢q8¯ñwºÞEã‚-ï»bH#jT¨±`¾xå5jc#xœvöÚö®Þ{ï~F]ÙŒU ( áð]^®§5fWµç®6Phh`«zÌb Xl¸aƒdÅdEÏ­o»ö£1¹O1„wˆÉÝÅì#Ý÷È­¿ùÎb‚o˜,„{ìí2&Æ ÚVv (½‹Ûã“æ(‡=–°þ>EÄa{ÌìõUi"¨j[Q¦¹& ¹œ*¹Æ""˜¡ +³Çƃ“nâÖV&U€CEÑ“âÒ®0›»Ë¾×û÷ôÇ9†2¤–`–ˆÂKé{.{KgwÿŽç"%W©ÈÒ󫳆a„ÌV MTÐß¾¼ãì5yjË»ØWqe¼ÇQ¼²+sOÅÛo»éÓ3Þ…íI5&Iñ1³û^(ƒç¦Ñ7´ïÐZ·~âo›@ˆ_¦ö…Çè®I«‘í|`¾ø}áÅ™I1Šè©.áiW$‰ÖIÄ™hP_4kŽfjmÀl˜LÔ‡…’põ‘DˆGý®‚Ëú>tWéºÇ­zDÖÂ2Ã(¬!±.ì7=}ƒ­»³Î¡§×`%L`†°* Y†þë‘Q¥ØÀñ›)¸¬óUWFB’ŠãzU–Qr ᯻þƒ¼o.ÆÝë¯ Æ®¯¹>&úT´1%¢Ÿòµ`LŠw9ÀåÅBRŸ‰ùÝmЧ|Nûœž×â‹8à9ráX>¼»ÙÑM‡ ÑÏ‹>ÓÔÐÜZDâ̤ˆ)¾`N]Q øŽž…Po*Wxºû&šÙvùûn¿ë+ouN®ÕÂϘXºQQ0HˆÇ;¡ ˆœÕ˜›Jª/í<×G^Ð)…Q%󸦛r+”WkE\I}8A*´ ÊœÕ¤Æ ±î怤 _ïÞýÚµk4LPXÿ®j|»òíʬ¤‘(ﺮ@næ,F©4£«ÚgSZQ¼&Cø˜â‡òÒж @ký†©sæî’ËXpćðЧq©¬ÈÅ™«ËÿüêtKò{M´}hAˆ~b¨¸™|¡$€päj4ÓÃ¥^‡î’ÑõÀÍð _%²&ÊLœBEJKŸ8ú} fb£†…ûóY;™%>UP),܉*,]C¬kh´¸¯ãü²ª”¢×'¡ˆ¹å»:ŒâUI²é+¾wóÿþý«?ÚGü=~¿ÒÄǹ|AÞ‰ù=í³gx‡)~emÀ¢ß‹YœYxÄÍöɾ©!ã>üýK*fÅ‘P?3±83éw&cö˜ß‹‚èÝ >rµ3Bä#ëàpbXU•B3`u-¦`…Fšœ‚8̵>¹çõiʵþ7D†É4Éßÿ”þÅѵ?€Aäl)U»REŽv!„G‡T¨ê ƒú„«ÕþCQ¦â~জö—›Þ¡¬JBÊ.ŠJ½á{ßüß¿õ_¹yPç^rõxjLIÊÌÍåÌ塈ëÓ¾» " §MM~ì²63,â@dmˆ$¬Ž6– Ñ›³·|`nXäâLYÆ0 Y·Ülx¸Áði“IqIÚÌÕ´h7µnh²£½ôÈ-3µØÀ€˜È2ˆ L`€fŠöPØòäuµ“¼²ò•úª=¤Äa´ÏÇÑë^Ð_ð€Šª-AXVNU¥(RD‚ TÁâª%Y¿[— &`¡vSÝ!“Úé¥v†6fa[,(LùŠö<Ëå'Q“@PkŽyቛ^uòk%ÂÚ£NSˆñ=!gÈ,Qwp§*ÔmðLøÚž!·&}ÅO9oÇ#_ökN¡o`"åm±ILá·nQPVâœËËö ÐÛ¸U e^MEЉö(^qGf~u68Ô“s+¹³ºÜwk6GžŠøîP˜É—p{O´;E.ÁBŽŽ£TjTWwókTŠãêåÏATŠsÕß´¬RFJPberË•A C9¯œr+P‰1Ìm>òš—÷íUg Î84ÿujE%v‹­%±QÞU؉BYœihÎ0›·zÚˆnC†F̤8“âLÊdR#Ë:c\ó¾¿ØqÓýÔúHÀþÖ¬[ñž5%n-âYM€#KŸ™º83)’3{uE¸Ñ9¯£5•T PÈc†¾’`rHàPùPVRãäwüÅ´^¤·«åªó¿õ퇯U8[â‚Þ‘û0ñ˜îШ†„œ}¤*@ºÞ¸«ŠUE‹ \ îN4L#¨bhç73®rB*+³å-·~ë³ï¸ê/Ccä‚Á{(Ê—¦ €¡ù‹òF^NbŠ…ȤÂqS£e¢kÞ÷w7õ“E˜k-fñ½˜§­îoÍŽàt÷%)@Ø[4×¹ç÷#PhìŒþãòK%ù0çèìÉ»•ÜYUî»5Á§.θâÏpé¿5›óœ Ûñ–Ÿ‹ZmjT;¹ÖX+ÖjÔʄ (‰Êä\£bÙ¼;æ§î‘H™-|iUlÿ+7±uƒý,Yë j\eŽs9#ÕxBH‘LDÊæì`Î&ƒ÷’´)͇첻äá}÷ÜÛ:˜äôµ%n-ÖYAúÌŠ3“"³ÇÌ^Uî»øt–·Ã6È‘ñ®‹€¿|ëÏ?{ë%Qµ»Bbç`…~NŸ«pôeô(Ê +¢ë¤aP(︊IUaõ¾t“Ç^§aáw¿óùw¾ëÏáZµLø®–e €þi&̧õ³O1ÅÇÙx׫U¶éÚžbrìXgæ 1ÅÇ̾¶±Äý&-p>öHʯ.RaWoÞ­xOe¹¯éÍ9 Iîå¥@ï€}ÿw«‹³E-¢ªÚ„›|aZw<°ÛZ—E ûXÕj˜bU q»ŠÀÅò!± ±=ÏÜàVkÉ^ðÝïü;Tí°?ו­;Gœ™ƒCV7}å“e›®-Ût-€Uu™xÍå‰.c1õ¶¦½ÝŠ÷¬]YâÖü^,Î`nã÷#\œ™1Å'i½²Ü§áN†ºFÑX’ÇûÞt÷W~´5,T {VäaŠ•ÃÌ+«²BA.¼Wu®ñ Wh>'ü::Bøºê_ ÚõÜת·3ÉTƒ 5,ë0¿ÊÌì uJÓæ¦¯|²§âíqØž¤õ#ÜšüˆB!¢˜}]LñD{Zæéu }¦xÐ=<–±1Áý² òŒ7«BÌYâýWÞóÒÀÇï¿ë¾DAÍu´*DHaV.%`•x eçh‘rŽ–p´D*g‰YPlp蹯՞p½2u w^Qâè,ƺr§ÉTw]OK!|KzZÅü¾²6í.#ËSå{Äú̺D¿nU©Û˜s¢?Rô7Ñ©pw÷æMžÀøêµyÐB’;TQfˆÐ;0Lݶb/Üra4D#5&L¥º¥QÒ´hÕ8ã ‘pÚŸ(ĪZQ«7H$ʸºd­ZHû³ÿs¨ag?ÞIgú:2½íóþÞLgKP}ü»Ç¼ r8Бu+ÞÓX›n¬MÇÓDçËâûâbÏÁp%8}ýªR·f|ؤ8ƒ9Œß—Å™™aôŸÙõÆmt÷æ©hêì¬ZRœ·õü_Ýõ`è^æÊ[ˆ\Sk\"©`U8;š°",%Ì T)ú‡ å†NöqßDªÈg÷>ø1óüž6ãµzÀ¥l.?ñ¯»=“ (lÓRRí¯Z9'?æÊƒ;h¾íQ÷DU5ÂD®N’bךò² ƒ˜øº–þç§þÀ‡þú©ÆU6a·Ó—Þ[9è tå¢ “qÇb…*¾ÒöÄÉÎômžT®91z¿Àª½ÏX·÷Yö¬yÕܽ'c`̰ý˜õe^ÚÝ?Ö3fˆXŸ©¯ ‰Þ ÖÄЂe®HRù†QAýxD?7ü^ÌÚûÒg&Æh¢G¢WvNˆ^ÇR E% Þ…Pä J'_|ú“ÛS8ã…JYÆ70†˜Á†Œ 2½âýí)% ¸áãD8NÊ޹ëD_XI¤¥éÁ>GèùÜ€Q°ÏKCðØo¶xÛ•EE40hY÷êÔ ® òLÔðÒo[yÍL~ø)*~€± IDAT`ˆ¯­L¹|tÇÌŽŠYœYÆT°kTP¿¡©Ô{qw_ò;ç€ß×4fì[g¦I9@UA4A†ÿ¡&½ÜÆŒ‰ÞY]÷ ·Äùå¡?ŽÅ÷Èž"¹†Hqòŧ¾|ÿ³FI±«ª@ÈY©ô÷ ˆÁ Ö4+IWw/ q»3† Ðç›,)‘Ò`º,5Ø …pZÝë‚@ ¨}è¡'€Ïœêii¢4qŠ(EœUK¢!?3r_uàe-+žà{ÆÔdb,ŒÎè¡’5±±âÐdðbß‹pßh¢w¼ÃÎýýËúÌ¢ÁQðü벌“ûÄG›¢ëuG‹ïP¼£úlŸ=Ÿ=ßóRìùïÝúW_ù<+ Iï@Ž•¡ÇìÊÎxÞ*‘rgo ¢L*Qóª QX ¼ŠX¿ÄäóP¡è¾ÁMœ €R ¤|õÖ®ëœfuã1q*õ„å~·³w>^zLÄúLM¸4¶­î\‚D¿ŒiaWó@’Ùÿù«®Îl‰O‹ÙG`.‰>q‚»Ò¥©“|2žñ|6‰VUèç^¼sýÇûØ©?mû»‘€š¨’t¾Ê¢D@wOÈ™•ÅꌂðLSÎß]Â{aÃAàê.x˜4‘}]¿¸÷ï<íŒ4ù@JlWý†ÞéŒÃž"&ÛC,ž^y(¡ÏÆDÆvsšžYFatå Ž~_ÆDñÿà+˽¾ôôeåÑžîG Ñku“!㣪¡­£â[m¿¢P¥¡ÐWFÈŠ¹¼îª;[¿0ˆg„SID ÷7ÿö¢Æ³{úU!A\0ÏÏ®îvuóì&ˆ‡œN–Ùy‚+§<ÂåxyÏÝ>púY)ÀÚ¦V÷2&š¼  y,qfŠEÒ^KøèiøCE…eqfnê3kW–ÌØµrY|Ÿ96ÊMô?¨'€@©Î§2ùr&;~ê‘aRQýiö‰ó"äw¡XŽ·jDqYÝU?ký¶ª6tC¡jóÖW÷ôüüî§ÝÜ)…’ÊSkÛÙenÁêJ%c‰_ dŒÍs¥ÕA X…” öWwß àâK¶6–ÔÈU¯žÛ7pŠä~ÒÑžy¹gn_}6èœÑ5~‡Âø=v5+Úq±3@1‹3nÀÓœvš ºÆÏÇî4› ˆï!éïN>«»ò¬?çfeÓ°°:*¤‰ò®¬ÅïNýÅñÛ_§$—Ö_}÷›#fÀéïVÁ½ýƒD ÔáÆSëºröpÊv¢P‡HAJ\⣧?—íW“À*áK;}Gõ`ÿúòÙûÈÒ™)i2й–¬«7?zÎLÔÓ<•\ƼaLqÆÁ°÷À`Ìï‡%Ñ/c‚è9Èx?ز8³ÑßöÔÕ (A Z"õN«*÷™@„ŠŠ=÷Ä—¬uü˶Q a7ÓˆJ5Ž­z+ †\Òx-Cîm‰µðÿßùÝw¿îÍ¢ÌÐÇ7¼HÊäBüð @Q  ä eae£6Ÿêëp×€Þ²j¨@ñ³{¶¸hó%û¶×®9}ºoNSËKšWƒi†í§[ àÉ»'ýÎEÁ˜—ž®Q2½Ûvß»`E–Å,¾/]ŒÌ¯Ž&zkK܇]V·öŒ™½c34“)ÞØ˜¬ë*³Ç\\ZVo– Æ­ŸIê3k’SÝ£K‡èL öµ«X¨¨ d˜{{Yýqì£D†ÁØ÷]‚\• T5œšG °ž!8qF’²I´©44œÜ¡dÕS²$D° "µJòºU×Ü¿ÿ& b‹Á¬µÂVlò‰¡ó… MDßäü%mtȔÔ"ö  øñ¶{\øºÍÚö7¨bÝ„Óäþ§žûG.ñ †@PÃ{ ) q D$…ü$ÂiLaE{>o j5IÁcˆï4ŒUO”–@ªBD¤Â°­¾ö{o±µ2˜ ¶o|$©³ªqÄFP į¤dEãD.˜a6Oä9»I‚üè®{\pÑf‚¶íy´~Ý™SyW·¼øïÛ~„ij21N?¾ Àöç»fðÜ…ÀŒî-â°=Ù9U[á;­g®|oŠYœYºâ;¦ëO0z.¸“àŠGˆ?ÒÄ«Qþ›'®²Iù ‚á4« ”Â2D·M®í(v ¨€ºôŠq ˆ VnjÄæPFdõUˆè * D}"Qe+YYHù‚5ï~`ï7øÁ“?¬:weâªÝ Ćœ×MÔÅj@ÖŠô}r7Tvˆ”™ŒJ˜Tp§ÅÁÝ6®Ÿœâÿ}Û^÷ë–Û_ÿç3üŠ]œ™’úLmÄõÎÁyŠwËXH̰¿iïÁÁ˜âcÄ\_·.{±>ûÞ 6;£e×â…ÃÄâ fÌïŽÜG¨ð1¿¯nÌÄ,¶<±o°å¬ûî}øqîŠH)LVºyÕáÌ$R"¥˜hÀuÌþnªµ¨ˆ›Å¡¬bA‰ï’,^,¨+Õç_ýŽ}ê#§Ò@a LdYñÄÑ·ÕdKÖŸ½á &D&µ°„†TI¡¢ÄĆmN ÂlÜ)…*Ml·îì‹APºõŽŸ8ï‹4…;^¹ñìïž«“¹øÁý~ø†¿œñ§@sâø>?˜§¢Í¤>›YÖU‚ú©Œ"YgæséO‡í«ÿ/{oÇuÝùþNU7Ð@¸$p‘(¢’ÚWK²­ÍŽlÇñó<‰'ÈLòf&É›7óòÞ›ÄÉÌdAâ;™,N<¶e[¶äMÖbYÖB-”(k¡HŠû"î;¶F£ëž÷Ç­º]è ½TUßnôïQ½º«¾uêwÏ=ÇeÚȾ¬ô§ û¥Û®ùé3¯Üæ„ê$Cº/ÒÄd/<"¶ƒ\ûy°Ãw’}Led, l›ïÉäaûìÊœQÁ;˜ §n0ÂÌ&“EHL à ‹½µâŸ›'êc«;g "™DNJ»!ósdqfab#$ˆ[&1Y6ßÙ}!O6 úæ~`ó 7ö•S â]S©QÊçõºù¶í8_Ê‹ø§åÏ,jMõªT}é=§j*B¾ÔŸq7rR|ïjÈ ê˜Ï —MŠU+K­”q‰WºýºŸ>óÒí ÃsfaŸè˜è™aÀ„]äKΓʻdÇ´f°ëþi滽~Õå¼ÂáZkjq#ÁLbC°ñîŠ/Ó”‡½fŠˆ‰È ÁÉ“,b`“`%L2™Ö´%OE‘‰ ±úfÃK»‰ì?Å ÷)ÇN‡‘g+vWïãû_íè¿6%½œ¿8éQ,Ypâl@–ÛŸQ|Wͧj]d½Ò¬æ Šã{Fs&›TØ®@ßÙžtoŽž[îß‹oßòÌO_¸$ÀĶC`+ÈØ™–´ÿ%’¸7Â’m2X°€³¬7ùÁ?[ò¨Û¥‘'ˆ´\x;´Ãâ:†!`°Y' ˜‚-bг€+† l‘ë*ÁîÚ'Íw!`ÈœK#:~Q0'8aÁ¨KºCî(¾öØ÷\wýõ²Ó+2e@>ð£?ð½{·èOžtN,ŸTØžl.8?ÝkÎúJ7gd>úÎö¤{ÓÙÀÀñ9zÿtÛ O?óü`²Á'IÌ,oPç'º “X³á<˜ œ’jyËñɵ¬NʼA0ƒÈ|ªùoÉ•æÈ`$Pg²I”`$ˆ ½½âÿ& Cºç‚‰ S0¤M”œb•o›,N`BXDuãÁˆ3¦Z‰A‰8 „ìè;iÎØs2pÿêw¿àº-×K¸¿òÒ‹_{ä1ðcýµÿåÉg~íå ¼úÎ9O^Ísépm‘l.èâ{Ûü:Ó '´¼Ú®•¡>°ÛŸ‘|Ðá ˆqìLñ¬×ÙœY0¯À¹K¾Ç,2©Ñ±eäm΄*KÜS²,»tá…E \ f!œ,xØm²ØIU±_Üù÷Î ¿FdD†BÿAn ÁŠó%àĻý@Bz;–Å‚˜ÌP¶ÃD$¦Ù‰¾íKÃ6gH˜u¡ÑóÓÂÞ|Á ןB„"$“‚dÕødo[I>ÿo~ç³û8€¯>ò¨¼ñ±¯|òþ_ÿªÚœôÚ>wÌ®X/š¡kN³†ÊÇœAÙûk+Fñ@ç"{\ èç²î¸ù™§Ÿ½Ýæ;Hš†­Ë”p9 'ÇF>’AN=‘dŸ*œßnÖÁöIC&§“mú†¤ • „“I­&Dwß{}âˆL‚¢IóBû–k¿ùúgBËT{+Ù F²Ýf°ˆ cüÜ´tљ則Àõ£çÁÅFãá„GÏ]Õ9 Ã>OÐßû‘Ï}üá¿äbR'}÷<üào®È.%%àd¹A_æ úŸl/è ™ïÅIñÝ}ÌÏõµø]éégî›™`ƒÈ0ØL"Ó`C®{"»&Il†I0!ïbƒˆqaEà¸1LÛ±mSìÄ&Éäy’™é†äøî7›‰½¯»žKKï¸Àè…ãš[;Ô6_wÙˆ@ _û“«ˆ ›£.>%ó~äÉH0ŒÆù` žkdLƒÀ˜Š.TsªFJ§Ñ“Lrà.—S5M\ãî÷dqŸóæ+Øú–¦æLW[ÀZò=}¢â»ºæ(×±¬9ßýŠßˆTTØÞ±(ÔÛcÆñìi:Ã=xÝqûSO=u—S+[FÉàPß' Éìòw‚™˜…yºeuðÙQ?XN³: ŽœWVõ>·ëЭk{žgN&‡/½ó®lÛüÊ»6%7¯_ø·ø¿rMŒ­‹fã±É©³ÖôÙÄôß÷&˜B,¦ÄHu£§bÑ…"AÛ×#N*±ä;ìÕXvó;;è[ý±_úíïñ9ç.ÚUvé|m‘.Åzy,»T³éå w”ÝŸÉ-wÌ®Xß±Ðγ ,ß˼KÝyç“Oÿä.iÑØÞ¹¤³`™c£ª0Éò0òyƒBfÛ™…2áÐög,û&a'ª8.<9Vdb^³ìgÒØº?t·Ú¼æÖŽÑ 'F/œhnM­Õ¾õí³îýËO +Æ|ÄàÓ&f:eàŠ}¸æ?¿àWÿÇg'Ÿ ! ‡?Ñ~&Ö”ÅS2ÇIï‘^åô‚-€„ßüË?ñ¯¿[臬3@—¶G­À5†*JS|°da½Ü9ý>ö5ÞóWa|_¶¤ËÔúC±¾c¡ ú% “Ñ}e±>HÝñ¡'Ÿúñ‡"&†"b'#žƒB²*M†+)dþ;@®GCÎù@rÜ´—J1ì3„mô; b¡"{DÔ{÷GÓ7¯¹uI6Äß<òIL\˜Ö;Ѧ3‰éÓ,N3Ÿd>þ½ ‹.ÿŸí†l¸ZžþÞÉPÃɳ_ý_K£úò=2;Þ©j`‡öMã£0ÑÐ*kÝ|ý/îÿÔ¿y,ÿ÷ú ¼øæÙüŸ¤t¾¶È³H”ûr|‰Ãzuì×üÜ*Ì—Eá5iíäæ;Ã3?£éÜÂyuο+=õ£{ 6"’özˆ8骛 ƒl³Þ$V‚ 2ëå¯i²KïÞpý¨DI0ˆÈvä]f=`,¿÷¡lÛ6zá7ßoÿò¿ºhM_LL/==mpì[ï¼ø‹3êÞ6,ZùÅßb3$L“Íж'¾Q4».`£iÖ_ùÿ¶åùÉ|üûú‰?ohu‡ö¿ü{çùô6,ð‚®|`¶¬h•RPûêô•ö-BšÇïùû3Ìw·–,Œì)8š¹ðå…»ÔS?¸‡Ø$2‰Õ̪SóÝ0Á&`' ÁL+!#ª·oQs§0 2ÀfÊlj2Þ9D0V|äáÜÛ6vñ$€¦–ÅîïýÁéß÷êÅË6Œ ëÑ;ÿ£ºý¶ÇþØ2B–a ÃìêlfˆC!ašÿðÖtwFðÿùõ|>–?þEß¾ï÷ þ@7.ðüö3³>2xu/ipX¿£UÊ“"¯êj^‘>ÏDŒÜÒ™ïùÃù3ÚÂÀ‰³±”pÞmÛiÅúr‰…°çTÝÕĈL2X°\¶j¯ÁtÀBV D*A\^ñ³Ýf/9›ª\xiñyÛÀG?϶5µ,»xrìâI7âçMÇÂÂ’ÙùW?òûã,ÆYg½It‘hýš;…aî“ ­èmùôïþ×±»æ_»åŒeõ;ôW0ø;³Å.U wèmÎx%¶«ù¹Ò³«u†{¡Òz~5¥WØpó]ç2èïºï'O~ïn`‹†ÿ.˜,0ÎrO5õ*ì©WY‘pŠ‘©US¦iØ963“ÈWß÷™B60FßxðÿºaÃ"/¼yæêG~@ó¹.™!„çÞöááÑËZò¦Õ‘°Ú¾ý·}LµwMY‰›¯Zà¹7²ò—R¶¸@iŽO'~=ïРÂöΙ gšÓkã«„ïér£\ñ½lËäô8Òîzà‰'½Ç^†ªêmÉ_…„>día¨NÄœ\é*ŸäL¨Ú’‰(†“ɯyð³m[SKûØÅScO5µ´§ß»íá/¼öèôŠÍa#4ß0[ óÜ«_2]N›a˰`YÏþ[ò)7]µHnßÍÛœ´N<÷úi÷Ë–¸lU“¯5£z;<6Qî ZÇÒ@ßÑQ_”žë¼òWAæ òç»ÎæÌ¬R¬W|ô²‚WÌJÔ]ýøÉoß#“eI0"Ávf$1;‹We2¡\¼j©’“²Â°]žÌ~°°W°Z÷Я·m9ßóöS­Â0…bæ9èŸçÏ|Ì4pÄõëϰýæmêÆ›7¶ÉÔýç^?óðc à‘ûÿ°¸­…ÞrMÉe4m©¯ôÕdΠ:â÷üËŸ*š—ôšè®ÿø'Ü+˱ KÒ Á¬`m1 €„%ìµI,­wdÂÎŒt*9….ÿøo”²m)Ä”¿-yÿåÉhë©å©}9ò”;fW¬¿åê¶®ë³<#/É—J¹ ÐG:›ï·Ïtû3ŠïívP_Ý ˆò廾;KQÊzfÏÚ˜i«=ü£'¿ùa»†°S+X‹ì4v&f&Á€ f„“ÔNLB9`R5à¯ø¥ß.qâóÚÇ/¿t*:¯ýÆ ‹<ÿæ¬ÚTâË*)ßruôòïxv[a¤&*ÍÛñSý]Qû󾊟;Ra{§«/…èúBÍäÉwÍ­JQ:èá4«„ý*eÓ2 [ßõ‰>ùõÈÕKI³%ÙÈI@  6\õ&íÔj&¹*vÃ'þ­W×>~éôø¥ÓL‹¼zÍt-þÃß9|óÿp‹sË­ñó½¾1OÍ8šUÇÒúRtµGB¦àÐñê™´¨Æ“Ù}µ¨øî!è5Ô¯üÖS_ûëÉ|Ë. OaÙ•™ÁŽ ï80ÉŽM,ºê—ÿÀÍó•QjfõgÍßo½ºœnÞ?}-3ëåƒ ùkBàæÌ¬Jo@$-xT… _ñ|— ¼]½¦h^õ ÿôoÿä_þòC B¶;"Cö=b»<ÙÕä!£u™à.ékãªOÿŸžoUt^Ûå=Öù³'ß9dzûÊ_Z0à ç¶#-sæÙ4иõš6idÿôµSîë /_°ïhÍœ)LœŠ)²KÉ_‰*ÿà"ÌäËwwg?7-+è§/ÌzmÍ)ùÉÙ ùìÿrI‘Ìq„ö$* ä´í¾î3äß¶Í_¸øÂ¹Sã£g¢Íž¹4îrð)Ü—íaî¨üÖklÖßæ dD¯¯õ®÷Áª³Ò3gܸ_æŒËú‚4;ß»;®"Oªe}kүχõ:ŠàîÏþð‰øHrQ+œºcpÌ9ÑJ² 3›>ûŲnºúúCÿiÖÇ<ëø3·¹@ßÙÖÀà¯ýèHöç•S:ã]7s&·Ü¬W|¯,ÐÏ^Fg¾ËEÉžTœ(Tn¾+¥€^çø=¥Vþ% ‡É253v#2¯ÿWÿÍ×mSëþ'FÏhl^XúkÊà=¶ ¿×¾=éÒ®Û®iëjoŒã§'í(ž52âWv7Øsx¬Ü’Y:ó=ÏÌ÷en‡€@VgΠ ü÷rÉrÅz5¨€ˆ~¦Åp÷çâÞï$Ò$ógl±Mtãçþ2€MSCcó‰ѳ£g=A<Q²ŸÿÓ×Nß~mêò+iÙ3ãgåN‡¯™3~K…íɈ~‰Ñë—a8 ßuÞõ‘¢¹ôòH;¥ë|l:îþÕÇžøÛûm^vo²¯¶0só¯þM@çµ½-ö±!üéÅÁOû#›~ý_Z0XD/áþÌ«3¦[Õ”ì-±³.à¹×ËPwLã¤üjÞÝJ‚Þá{·3‘&Ì4rßMÛye4g²éô…)ù£nik­—?eܪt-YPàDÚ‘v÷¯=Æ–ÅB€ Ál±Ì‚™oúr0ÛvÓà"?wŠ266/ÑÄXñÝM•#LÈ8挚q-QÏn;ýì¶Ó)ÁûMW-ºéªEòÏ N:±Uª#'&åûÆîŽFùS®­’ª`FçÝøô…©¦Û¿ C’eöÏîîßxì‰ÿþ “ÿN`qÛoþc€[–AM &ÆÎMŒklZPÊëˆäÎîW”û3WÌ~ÓU6Ùo\0˜žÓ߈~ ·À¾¾KMÙä^ªÈÞã Š^6U´ùŽÜ|—[VM«¹“¤yFÚgÓßý›ßýñ—bfºã׿ôÛg:÷]MÅ=•ªÌ÷¯}üÿ‘7~iÁ`A.MFs&›T³þ† ‹ì›Œá}Ë'#h<7àGM1åÏô¸AOp(À¢ž•¿ËâŸE—ð^3Xïð½}¾ÔlJΊK‚Aw|þkÁl’ŒyžV±½¡iþäØùɱó Mó3=ovýªøýxð?Q1É3ÅI5‘å쥶\¹ ¶¾åèuå'–¯5wt(ôò›  zs¥ò½¢¥Âööù©ÕÍ}>+~ïýíïHL}4æ TˆÏ¼Û@á!|‰}ÁåÏl¹ÒÎÚ¼~Óô¯¼Sü4ÃÚ¾yÞ;p©´m¬É/%Aßiƒ¾·3éÎgc})æ rð]ss†5ŽUÒÍ™lRÙ5Šï‹ÁI Ò (ð+êY|ñB6'%ÉÝJÙÓ5 ù#þökÛ9osfV½äø3›×''®½| ~íÝ󅿤Ʃ35¹¤ü7ßÕØÛ ¾"ãwY°¿šÚn©°]ñ]õœ"¦ç<ýK5>5æÞ´†èüÉñó“ã碻4¥g¾ûô±m};³_{¹ÍúkÖÍWAýëïåÇz¿VÍ™26ôp£¼7-¨÷ôÙù®ñîRÅRa»»ì’ve»ÒA_`Ri ;Á¬íR4DçÇÆ/ÄÆ/D¢­9–¾BUdßÓó á_uü™kÖ%O`×´Àö²=q]3€ûk™3•ªƒiA}ogc]Øðþ¡âW#gÞë¥C䪰]òÎ)ªô9‰¶æá Þ¥ŠÈ• L¯;aûàjûï½r EZ7Ìx{ÏEhœzˆš9S¬›p“]JÞ"¿í|úseÞñ5Þ[슕޽8©³nПåRðŠß÷ƒ›7.çšþH´56q16q1ÒØ’rWÆÚa³ò³"^ƒÉËí»lÐ_9ü«×¯h‰DLfÞ¶£ˆ)ÙšôUÆÌ7ñû»¢òÝŸ½ÖEÆï5Oý¢d“øl×9Êóõd¢&WcqDêŠz >º_쾨ÆëWج¿lÅ<ÔÈ„JO IDATïØWK‘¬Z¹qßï°¾iú)¬ÏPFú>äÞWŸäÂ¥ W¥?;~&–ôŽE‘ζˆj_•Š4¶€(69ƒeƒ÷ßî´B?ã 5þížÔâ3ÙŠÒl¾¤·÷^œŒY±©äüºþæuË›×ö7¯ío.ã†IÕÌÿ´ÿƒñýGÇS€Þ¿4ª~Pqñû\6grËxË+Ä÷‰] Pݼ± Às×4Ì‹M^ŠM^Š4ÌËñ0wf䤅y·ü+³._Ñà½Éˆ~ ëkúš€±³V”¦zåF¼;–ï_­0¾ku™¬­ëß;Û"!“ˆp踎ъ'ÆQ¶®nó}‚gñâužhÍSî,I›ïÀê¾fÙq¶V}¬"Tô²ÕÖ§îì5s¦hoÎÌ*¶+Ð/[Ü@༾âÀ ûbg/# ó¦&G§&Gë²z3âwQðB'ÍÌðì<`Ó|µúž&\ZVužÒÙœ™ Út< ·3zðXt[ïl8vJSsFçÙËc§cñ¦aoeog£AD„}Ù'߃Ñ-Û€â;Õ74OMŽæh¹çØ'g‹ß+KëW¶vŠdnír@/ù`eOï9\Ǿçªtó=OÙó«½ÑÞÎÔ\KÝTk]P´Ô¼Å‘““‡OLºƒ÷Ë¢+»£«º›Ê·u%kænñ|âA5þÜ•ƒª­Çkû1)ðÚ~¨[>wå`ú³RfY«l¯Û}hl÷¡1wð¾bYtŲèò¥ÑåËt'€‡"Ö·×U‰5ÅÜ 8xl\Â}ew3ï=2Ïç%JçÞc¥¿Â9ªz›Dƒl7 ùÕ’³úÈ sf|<É©Á{¦3>KéÎMížzYSs¦íq¿Â…õåK£r‰l>kgjÒ_öž.ýÙ|=Ÿª•Á««=à]͙ʕ:ô&CøuË› "!Ä»{ýM¦¾åêE~¶Í›ÆFS±ÑúHóÄDrv»í1‘áF%÷³æ”ÜñÜr'û¢¯+ÊÌ(:›ïËÚ9Uåæ Rò#§©‘”OU+k Ry&•î>˜¼f_·¼­_Õj "÷šåÉ‚ôR†Ž=àc×ýîWúu—»,ðD¦•¾úÓ¯|ìºßýÎ+>ãV]/áQˆùž¿ÔdŒZ') À¬o©ðšr(¹§K‹&e¢Õ]ÌÌÞÕkßtš*Μ™U;ö`f Zˆˆod¯bX„Ø; ¨.Ò¹ÃðOÝ8£¬X̵;~ÿÔƒ_Þž•UÏUY’wÒQ}Í?Í3eþ*tw4Èõ±)¤kòVšï˜u}Sz13xÑ1¶PÕÌ™réí=e— NÍ“«Ö´ˆ<)xâ-£ê"MããÑøÔx]}i>»Ú{²¥Ðd°à5Žßƒ‘"@wGƒ,[Ü»4=ŽžÌ|HêlÎÌ)å›,æögz¼k ^“Îr÷ozÓñg®rÊÕ^³n>ˆðÊ;E‚þÖ«Û<»­ÈÌÈtm\22cru¦Ïž1~wk|<ºqÉÈë'†½ÚÿtŪo½ï‹i–Q‡•q’ïR]í9[AKÊçŽùŽâêdèÛѨÃ~°^ï µàûØå/Ï+:(Fõ ¸îòD ™­ˆ“;ó],2„wÇéÿôlrüÏã³·fx©ô)Ö§t]ÖTFqåŒË kÈ–j T´¦4ykÎ@ñ=£ù>«ÒA »£Ì{çÓé‹O`ñ€“s¯»êAqÓUnó È9×½èô R2x¯«Æ§&~åküÃÉ»b3–Œ!¼;ö|àƒûvwUt¡‚¤,ÓŽ¶d ÃjE¢ô7‹ùÜþL·Ãúî%öu\‰s2KGÝé«©ìzÅé*·ùŠd«è6,$"€Ÿßžôž›3mSO ñtý}ò×ÑÇ·ºïMÙ{Ü¡ýý÷>öØv‡¬O¶M=Þ†ÇÕ‹è©àÍ™Y¥š!«4jpB§ÐgN™3ð£~¤ògß,sƵÉ÷ ÕÑá‘o}+éÏܰa¡ÜtÕ"@i½;¼¾(³£ïz¨«o´fFèS3ßÎÊnÁ¸óæv§Vu¾œ=q6¦È.¥šJbN¶Ë_ž›3ðµ>°ÛŸQ|Wb®üâsÖœ™U/¼i‡í7]µHnÞ¸HFô^­fÂLó]¢™Àoüão~xÆ#sÄïne\ÅZSþ’4wÇìnÜÔn¬¦Òe×Cáæ{ARaû2WPßm'Z͘±IQÍœ©týü ›æ²€[¯nëXaàëOñê]Æw¿4x¦4‡=%S/[ œbýÞ?=ýÀgïгVð•«ZüB's&·Ü¬Oi7FÀ±èýTÐÅôÜþL÷âµÈ úš •œò Ìœ™UÊŸ‘Î;·]Ó¦îýék%ñ*î–ÿÛ÷³­)HñgRâ÷»>3àÉÞ®^GßeM•¬d_IÕ‚fQÑÏ5óåíߤü™ei G%°¾fΧg·¾õñJ·_ÓÎÄ<ój! ?ÓÆŒÔÆßü‡/l½¥ øÅw¶³uñ±ÑM4£åçÿÚx åïÔ¯W~lg{®¼åÇ^üRÆ××H»ïéæL6%[Ð,šу4ŠH‚”æ;4éÏw$ ôrlšÄÌz¶‚Ö}'o½º œš9sû5íöàZÔ€g^-ˆ>Û `Çö‹2p_wW €Çþ¸GzÑxH>Ä0`…â)k˜“Ìî¼wðÔ$p¤@ïõ;ž¼`Ç“×ÝÕ"_mÐDW´`fíJ—ògÒ›Jb.%Yú“Pæ{þrÇìŠõÒ‚GÍ…/DòhÑs‹3yžyÍF¹Ãw9nOgýÙ^¤MŠž:Ú ±÷/B· †n¤^J„¦0sš›ï§&`?>òW¡È_!åõõá{+½©$Jn ìÖ4g„´]zyää¤;œ‡ ôЀõù_ŠÖTÜþÌí×ÚAý×µƒJë“w¶3ÖÆÏ~èýßâo>y,/!XḬv«ÏlÎ]7ˆI,xe;€SGR_Íæ{MÊrÅ÷®6;ñ¦*‹PõwE_jî‡s¥èúö$èå¹îh Ÿ›æ *‹ïn)Öw¥€žƒ8±×Ì™rÉíÏÜq];Þ¿€¼)— ê’þþM{¶»k°¥môÜ…:8SÎ$ëÎÇmç]é®MíwmjÿèÍ]É›,ù*!×ä‰ÔÑ­@¿´ÝN¼Aíj>Mšò=ݜɦÒA_»‚›zú•S@’ÅããÑññèé+·lýËïÝzòå“òv;h«§<ýKõƒ˜Ä̵Kiî‡¿×ø>‹äÂ¥ ˨£{i»Ë¶u‚ú#•sìûg¾C[¾¡$èÛSAÏÀ±Êù¾«X7_µŒçÞð¬>ÁþäM÷¯Ã;ÔÉñ›Àà{Í÷îyo-€ßÿ9€D"à¯Zîº0•2¼¦&šËæLn¹ýÅúeΠ‚@Z¹á>8“?î;Û#òÇ“·}-ô”Îæ;û”O8w)þÔ˧Tð.ßöÇkwøoÑ›‡ê2Âý®Í‹<¹ÕýDDú/¸(¿ŽžŠ9s3}Y{¤¯³1¢9h¾CÛøÝ“=ÙxEv5(:¢× sYÃÿs„‘_µW¥>å ¾!ré¶«=qÙŽ»ß]÷_£7É?êU]÷ýÊSðæÌ¬RˆW¼žEP|5g 'ß—-nÈ]t¬)š§‚¾¢Z‹U¼ØÏð=KP@`¹Vößâgÿ¥ñO\öÎäd3¡P:ÇìŽ6¬nðæ.]Í/,ŽœŠ¥,£‘¿ÊšÕö ÒS:òÝ×Ý%èÛœ G•z¯-n\È@¶vþ‰\žÐ¿›xÀÏ^;}ý† k:‘˜…Âwm^Ü×ÙÈŒ¯iYÓL3,wîÚ´X®šiß×”KwD¸Y¯ø.ª.ÐkÇwY >È6OÊŸqó]« ¨×D\s&Ç=¦VBX‰'·ž”ˆWºkS;ÛOå² \Ý `»¶æL%K…í ô=Kdî„»ç¨OòÛ|‡†|/£Ü(ïH êõ½Î‘”l×§š:y¯,oj<—~ãØ„Ý'Ö0CÂJÜ~õB0Ü1û]›’«dïÜÔ.OL©¥ojÒ^ù/£Q ïQ wúHzÿTã{fO ê;Ú"¦AÌ\[6U„t­U,•š·ù¤+f¿sS²ô T½K:‡ÅIù3Šï½Î„ƒÇ*Œõzñ=xsfVe½S¨Z»ÝZgˆú¹mÃ_aäË`¾Ólol˜!aY"Û”?£JßÜ~];Ì\bÏ©|T3gÊ%¶'ùôvÚãÒA€9Ýø®sžÀñÓ1·Aè¡ë5ÌAVÚråB^úEÐæ yr5E¦iX–%„eYº²p…í·; ¿íš6€ü¬ÿ ¯©\:èògß=½¯Ò‹ï:+½©›ïúõ:Èï ‹\“«³¼÷›3ðô+§ó/½ûŒzÕ?öÖkÚœ®%ü³mžU_ôyôVþæ{þR4Ÿ z‚6-’R¤ßåv%¢ºQÞ±¨ÌîÖÞLAtÑþ—!#¿’)3 Ùßša°,TìPþÌ­®Fá·ll“ /´rFéŒ÷ê3ßóW&ÐGå—u ÐcÎ@+¾W´Ž§µ‘ ôº×+öõäã‹C¼”ÛŸ¹e£Íú›¯Z$çmŸ/¶žÚUkZ¼±³f¾ë+èí&3}Îä}¿ìB¥ß5ŽUÒÍ™l*/è5Ôæ+ØúV†¥9¦àÍ™Yµ&è X¾Ô&þ¾£s >Îáe2oË+{zågô>üÈ#ç0gr9êw\ÛÆÓ¯–a1êKo%ýÉw×^6_`lÛ¡{;@ÍÍm{ïØÿÁ¸"»”ûWÏY¯ßåR`}ë<ø¹»T7è¹ÜGZŽüwM{uqsíeóå`ãºùÍ !fö°‘¡Ç*÷×Z¹’4wsÜÍwσz-ø^r‚ÙY¯³9hégê½·zõ];l߸ÎýUkZ×ê Š[F“‘õ^¾Æ÷Y$Û€Ùž;ô¨Ì þšËæ3ðÚ»¾¹ %D‘·_Ûà™r˜3³êõçåäªÒàjô þÅî‹eÚ.[ùç ×T¨ÍßW,MfÓï=R0ëËÏwÝÍ™òÉòŠto|ÍŒüÎ#Ëe¾#»?úØ3tÕšVæËšd•1©+ZäÖ¿õ~™A_S¡J7g²I=f…˽Y±Ìçúòó]s/O“­ËèÞÈ…–Úö-;AµN¿ñÌ…n@ƒ™ìJÿÜÜþÌ•-r°~U Ìxgo  ×÷ƒ«@sfVíu ßW:ƒ=³^¾k¬®ö­ ¾§ƒ¾caD¦Ó)ŸaãÚVfö1};ÌdË£¸õš6­‹>æ<1*fý*ô—¯˜'ŸôîÞK>o™/¹d5å)¶¯L=aÏáÌ /3ß{:һƦž:q6&¯ ÖÑ‘]áI³s’çþî#iΔÿÊ"»ò/øþ¶ãÏH¾X·|˜Aر/ˆµ35•K{ÒAß´qܬ/3ßu¾ÖÓ\é™3îòôKKôS¹Vyø‹Ð<÷›ÊIžQ*b‹ßqÂöuËmЯío– Øu Æz-”¿ùž¿’ wñ]÷¯ù3Y¥¡9“[î”Éw8kù@AwÅò·,A~Ê>¹ª±JظûlЯíoV7®îmf0»•¸iš«úÌ÷üåŽÙßWvGËÉwY:Yó(ª£'mÖÛ|º—4PPáW·ú—µ=üØ#÷ÏbΙã÷[®^ä}ïäɼï{û“1ûê^›õ«º›@`æl^í¬ª™ï!õý–™ïºKcó(ÿeMÊŸévÃÐZÌß9ïò왿@3g®h{œá¾Ë©cµª»IV.‹2Àìo哚ÜòÜ™U¶?Ó¿4º¿öMÏ”ô7T\R×­îÖb2¢'‚Ç;€î 2ÇïzlYù¹qï¶ý5)'¡Ã\¶êäj.›39dÇïý®,ú`X_3gJQ)!hzk1¨õr„}…¯‘KÑ•-ìJãóVÃ?aä#y˜3™ü÷«×Í¿4îMçß«?z%7ëßk OWyÍxR_ÌíÏ(¾çúš Uç¢4«¼ÓY ¿n¹ý<ÈóyZ±¶Ð3[嫘 Í$Es7èåwuÄÐ×Ì™Üò¸>¢y:èQ ¬—9媹8»4‰Þ3iǾQñÉ­¼l…ÍúwJ}¡æÌLlêl€è¼iùWÙKý²ö¦ªZ^Yò«þL:èå8¢R*ÍqiÌ;~O©<~™Sš\Õ(¨ˆ,‘ᧇ@¹#ÿÌÈtiúá­íof°»"X¥K~™Šè«ùز¬ž›ò½¾ØLîM%®Š“¦  }_㌉3ÊŸqñ—¯hÂ;{|LtO®®_9oï¹,õïkJ‘ògßáJ«¯zЗÝ|GÀý=|0îç1³JÙY¿tq€£Õ¾Û•EnÆæ;°~¥=x{VÐç]3Ò%v8å}4Ö[¹ýÅ÷e‹¤çVt!„šù>«廄»;¢wó½ìA½Ææ¶îZÓßÌ®¹ÖÜRþŒâûú•-êÓO_û:üì#·fÎÌHŽdN½E#é»ßùÑâF…íIÐ/i¶ëc+We®ÿîF¹»nmú½s\šWï£¢Šª«°]ÀúU-òµÞ*­Èš_]·|À;ö]Ò£«ûš¬m =_?±$è—$[ÐÈA€^seç»[éukƒ½î™3º«¤ÐØíϬ_e³þŠU-hz546‘(~ËôŒÚè½mIù3Ý) 'Pvû¥fÎä£àøžnÎdSyA_S¡ZÓ×Ì®O%Jù3’ï÷ü˧@xþóߺ ‚ÊÊ«àSÚ2FïzK¦9¹XépèáªgYCyÒ(~OWÐÃsÖk~ük¾y>Hú37ÅfDîW´€ÞÜ«¿«ôgV÷73x×þQ ?¿žf0vÒÔœ)£ÜþŒâ{^sAòÝó–Cs'¨ïl8¦ë²ï<¶_9tê7 ´áÍŒ¼‰¡\x-gVk¥Šó‘¢¹ôáRZÐø,}‚ ­ã÷tù4+gxj kŠÐê¾f {¹àÒ5óXqû36ß «íô’ì232½QŸÒ¸ÑwÌ™Y•ú¹á­€ø.ky[I8›{ÃŒCÇ û¾5>Ðt—¯1èð C ŒÜ93Rù3ŠïV·ª¯òâ8'·O¿ø}ew{Ö«&‡ŽOº¯ã¡Ù2šK£öj`Î ¸øÝO‚¦ƒ¾§Ã z åGrÅ(¿ÝF…í ôº›Lf~û}3#k*Zéhgضr·);èuP…ù3¹¥¾QÅw5@vÖך7–"­¬·?sÏõ 0À+{¢ÀØ{D£`Ygó]Cs&·f°¾cÆÕ<æ0èƒà»lùdãV…r7ß«)¨×D=MÌyõe-BÃ/ 0²¥˜šb+»·í9Œ•ÝvçXˆ6ÞvÅÀ¥®œ*]Ë—F™µÈ¯¨><žz5ßç ½Væ ª,~O—åéA½þ ¯™3E>•xŲ&€Ý1{£“T}Ū<ûÔ¼Ò¥³•w õ˜¾Ð| ûï•‚à»&»KzPßÓÑ2‰™÷kû}kòÙe‘¿CI;;Õf’Ûø‹]3VNX¿²…rÖËV$•,YÔ7€Öé@èû»Ôe=Uwûhßù®Oª¿R:è«©ðE`ZÕÓW÷mo5üò€‘ME|'R5Å2Ü«üwÝ›ËVÌc;öû úþ®(s­+}¤@ïâ{²½hU~#UîÏäÖ¡ãnƒ3F—õ]‹#>е÷¿Á{1“Z¾4ÊyDîº7ªÉÚåÍ2m>Ïr˜…JßyÕ9#÷õºâûò®¨¼d,:ÕÍ|Ç组»Û…wó½ìA½ÞÞŒÏ*í§Â—5)f­ÓHvu_31˜<^½¥qâ pÆK=”¿ùž¿Tؾ\~i›â“ü廆æLn¹QžZÍNƒˆ^­ìŽÂ·>‹Ã¯ ¹¦øn|œf¾ç¯÷œƒri.€Þf`ö)SHÍå7ûè—Ú ï_•çº "XŠætüž[éÕ삽li6G—A’ =TØnóXÕÝÄ„RÖöv6\[z£³Íû—ΈèSîM‘†æ üæ»ÎW¢éæL6•ôsQ¥ñýŠU- öðâz÷Aô«º›ä`ew”Eä°knÎè,?Ì™ÜR»›ïË ^7”g”|¯8sfVå=Ï¥òd+–E™ý¬ÞWßeÝ?lä÷°}ew2²³ Ϋ·ÎxŸËæLn¹!¦X¿Âè ú¹ëÏ”røglD ÚŒÍÐû¡á7†Œ\U¼ù@V&ðd{2JM<¸#»¾NÉú¬öK÷’F0ŸÐÔœÑw^U')Ö¯p¾.lxO¿>‹s—ïžìÎnFñ½tÐkn¾k¼¬ ëW¶€qðØx0i îÈ®¯Ó>à{;¥{“‚rkÎh®àÍ™Yµ× z€•Ë’'û=zÔ¦Ÿ£|÷cwQ4/ô:GRË—FákíÌ™òT=;pÌþLTå“eKd*½4=t¦{­ÿpÑÚ{tÜMv)û‚O9fyÊ/¾WŸùž¿<½Žòóä3ü‹!F®,!3Ò|9Yªâõ½/mo0MB%”<ª©PI”§ìŠøjª(ëçhüŒr€ÙY¿lqÀ]ÍuùŠy¿³÷Òå+´ˆ•ÕW¼´½!epôTåŸéƒ’†æÌ¬R¸wó]ý\ä{ð»K:èQ™A}E˜3—¯|.ý®wöÜ\ÒK—¦£§&٥ܿ–õ5sÆ?¹Q®ø.k7Á·òMJ¾ð]ÏTtÄŸùØÀäkT<üö#ëK̜ѫ刔¤¹›ãn¾×‚úÜÒy:*£9“MŠõŠï«zšÔÞú¾Íý‰ßuþB´QF÷&d0‡êSÏPi»Íºåó˜±cߥõ+=Ú?•‘õËœÁ‘€A¯ñ+‹úVY&¶+ÐXÕÝ$“¾<,ƒ1çü ½¼tÐkUÆR©¯+šç*žòªÄ²ÁK±~™ô”L¼ñUr¯ÓÿÚ±*åögÔêèôKaýê¾fïù¾bYðsqcõêȉIy°¹Ã)j!øÈÍáC00²®s†Ó¦#i —kJQ[Vª¥ 2gf•ògÜ|·ÇDªHFAšsñ»ÎÊI¥×B€¬÷Z¥Yk—7³SúQ·ø=Ý|ŸU ôŠïj@^O„jìÍÈÒ¢¥ªåŽÙëU‘;¸ "ͪ¹Åw Í™Y•±BY‚zW_ê¼m¥H…íŠïpÒ]02^úº¢@X‚þI±ÞÍw5zi IDATÎzYÝÚc¾×ÌÿTÞÚô¾Ÿ½‹Òt‹ß=ùËÜþŒâ{é —ûO^ú/oÍ™YåF¹â»jQ­Ç|Ÿs—RÞ© i®•,Q‡ëð®!FV—`¾sÖ_Ê+é¤{›£h>ôÔ¦Iç†ëßÕ3Y?‡üÍ­Š;5êãÞ”¤Òâ‚Õ}ÍìÚ­µÊg?Cž  wæç+æ«Ï®š93«Ô>ïæûê¾æH à»/xÉ÷€/Xjr+·{ƒÒøžÎFézjL‘nþLJ‚¾¯^ss¦fä/wÌ®X¿¦oÞŠßu—w»³A½Ÿ™‘ï0²ªäšb5¹ü™Ê¾žÓ^:Dz»Œ®é›'Çžò]󮯉VÝ ÷øðó,¨÷ ¥}-r®É=û¤Oü^ÆŽH•Þ)¾¿+ `¿¶æŒ¾,„ûΗ ù¾ª»ÉÒZ©æåÔww4°瞤JæÕë~˜~ã¶öøm*\’æî˜ÝûÞŽFù½TÖJïšÒ•:¿šzäQ¾‚.XjÊ­ŒîOê‡ 0ÒçÕ²&@ëIt­åf}¯ÃzÕH6hÐkü%Vë²æÏ¸ý™Õèå½Ñë£2š3³ê°SÏR)Ÿþ‚¨Ds¦; pÊ‘¦Éâ&Í™Y¥üÅ÷ Aß¿4 `mâ× å•™^¾‚@¯»9£qÀ™^2Áãþ‚%vãËHr=Ìw#Ðts&›ÍËúš UŠùŽBóß3‚¾.d0xǾbÊ!´Š“‡ý‡0ÒíqæŒ&ñ{•)è tà˜—Á“ÎÇëÊî&{*'÷¤ÈõM3@O—¡5x¨ë+eì/˜r—ÊÚé[“ø½J•z}ö±ï-èk*]¥®_}ÿИ»… \=¡ëk^^ÑÊ¿že‘ /1NËòt¢?Í;Þyò¹ƒÅ÷¾Î¨|õ¢._EmÕUQZÛ7À{.s¥ó]ÂÝí»ù^ö ^‡£}î(è‘Î;?Ìw=’ßIãýNæÆx›á®Âö$転ò#ÐwÞ«pUœ9?ê»Q®ø^vÐk(ÍÍ™R•zÌ ê‡?a¤«”n«ÙT~¾k-?O=IÐwÙÇ{¿3¨&ÐWü­©h^Ð×®õJ’ È69m”ò²}]QæÌ&@Ùã÷î%ŸÐô´ŒÔWÓŸze?$klÑÊhÎ °þå}M…Êz–3‚zr3ïÊS9^‹ß³*øKÆýi ‡ÃqTÊ+ÑœA‰|O7ßgUЃ5­§\“·zàÍOÇ€‘Ž¿[æÜR0èk ¯4¹ýÅ÷J½žâ,‡DI|/å >ôpªæÃ«ÂùúÎrén¾û+ç{)l>ÖQog#ÀÙ>ºòç¿k¼×i"Es7èÃaÙûD—]:«kúšìÌôÑ•¿ÓŒùX‡ï¥ƒ¾i”¹–YŒ|j6’C³ÎǺ< ¿Ëz vw48\îú‹¥aH‘ôé÷–]²W%–Ô-žï=Mv{ZŸ@Ñ<ô ì-ô:Ÿo粆O0²8sæLéëcË¿×T¸öw“]ªæÞ”®òÇï•úΠPÐk( #)·ü=5æýêÝÙu(ÇeYyógjQEqʘ9ãêí–ªáð÷V9ÌhËw%úi GvÖ×­Š–ïõ, G`:è»¶)NȃßLúóÛ?Qð[¥üëv/ÍCŠŒJwoP]q^ÒïJ{Ó@Šý²µŽòü¬g9|f„‘EE.k:rb²{f×!÷¯‡O”=ù½&?äÔÒã<¿ýÊ5ßQ4ßý0ßó”ûë þË.]}TT$å™J;w¤Ï^ºùÞÝÑ8^ÀùKÓ%½MMÁª «íô8oŲ¨Ú­æ`‚uîx¬H¾kfü²Ã!ƒ5N´Ò¹à{Og#€Cþ{¼þÓSX/ã÷-ö^]½T%š3³*ãýÊeQy|UÜB¤â´¶¿À{û³²®bü™Üšñeˆk“ïsN‡O,ïŠhW'o™?/L@OGcž¸Îæ{uË}ɞ̻sµ-…õmΠ8¾¯îm°ë ŽóÞ#ãîó94˨•u—Š®žZÑ>?ÂÈü"Í÷¼Rˉ\’¿Íw@¯šG×ø[ÝJæÝ¹ø®Æs$¨w«¾ë<‡%áî>¥»ù^ êsÈ÷«øÒÌ™|ž’ü~áÒ€CÇ'ßÕäµ ¸ëöèÖA€"2 èþ붦mš¾– O©nn”+¾'¡OØ£}cÑY5«9ƒªñgrȽ딽ð…¾ÇY*߯Âö×|lÓ¨tЗÝà&$ŠG{e €û¯{IÞ%k²kÛY)€B±~FPßÓ€8—÷Réæ Šàû@o€ÝZš3³*Çzè@¯{§o?5|qÀH‹ß]šnFñÝCÐ.ÞÐjl@cC]c(ÚÀÿôÌœŽ"rhFP¯:H;ЯhŽgSõÇïU^ÐÏE•Æœòš3ŒÅHÁ}šÑØpƒJÁÕÜœÑA ån¾¯ênª¯3¼³÷by6Ë#UUü^ C2.†ö8¨×ùÜ视dž@iòÙœ)¹¸˜}oè¡_PÿÐæínÜ{!Þ×™ˆSœ)ÎvûÆð3¯Ç°mÓðýo—{3+Fî˜]±~ §YvÒu=MNÆw3#oêÓ|lÿÒ(rƪ)›ò½ªOp0 ô(0¨Rx*NÓ˜ˆ ÄF¾Å£[Ú¼Ý×-̨2š3³êýÃcŠìRî_ËÎú<Í¿k¼öRö ñ¶ÛŸ‡6½¾œþæLùäþLÒƒz}@Oœe€kû[._Ùøî¾ñ/ýð²{6¼šíYntÊBy=%iîæ¸›ï=Ír—Þ}P÷ ¾ªüÿT›-ZÃC Œ4únÎÀçâïéA}og£¬W\Þ…3vG^&a׋Bäú,Ù¬™oîýƧ^þ\IoŸI™@uî*ÃW,ƒw àÓ›Ãÿòò´Í{¶—A=´ù ùà™d ºz)m;*ݺñ¡Í¯³Í:›3³J±^òÀjgè×õ7Ø‘‡9ƒZü^´‚˜ DUcÎß¹éà± Ev©À@ïÆ4Ä3Ã$Ä'qÿe¡ø4¦˜Jàç§<~ÍüY€` …®ƒ\[2™3Ù¤ü™Õå}>*€ï:›¨~˜ïyJ«õ±ÅÈï9•Àö›À‹¿Kš»Q®ødD¿÷‚]o âˆ'œŸi .0§-¼sÑ’Y3?Ì€L`PC‡Ã¦ƒŒöt†IÚ•ú5½Ê¯§/•c£€Züî­*±1¼¯Ž0RW¤ù^е…ÈP4wƒ^m˜§yèðºV„ç-Ñ0ƧF|2x—ˆDêÌ·z~ŒÙ$"á0ÂaùéÂË*UY«]ÞÅw¬é'Áƒ>_¾çîòWSŠ2€ÞUÕrŸ6{voWÀÁª(™ Uó¦tÐC®6" „¢?Þ~-<1‘pùqcÝ#/Ä¿÷ʦ®{¹”?¼&©.Fñ}mï<&F -È|GuÄï²”„žá’á]á¥b}ÙAï{Ì«CPíÒÍ™Œr? Ïa½,œ ì•-D b"LÄÎØþPM" !,¶PWOÀŒø Åu‰øËZCï^Hì¹0ÕÛÚ81mÕ…CÒŸ‘Á{¸á0‘êýºòUþæ{þRaûZô2\†ÿs5ð]gen ¿Lë Þ+ '†@ aÎлp5àògßó½czUïüwvŸpÓ²ÐØ¨Ø&øtœ…aatŠêˆÀä<-À ŒÇ…€11-hj…ÃÕ!汉—-žþ¹Ù5WO#ï9 W|_Ó×,?Œ÷ü}^|¯™3ÞJ¡ÜÍ÷àƒzß›{ìÏ”òÇ©Ï\ñ½ß¸Ê‹23>~“1£ñ˜16Eã±Æõm£ïî³°@}˜ ‚i`ÊbÃqÚG§Y[@c=@Œ±i!³'-P8Ž%…îC8 /=þêf"Üw­ï%Éʘ ¡Kè×ÊÙ¦jΠ âwÍ™YåFyzP_}½ÏÒ=~O—}ÿLÐÿù£Ú¹8†ñÆbŸÂx ßx)AŒ{VšS1˜€`˜ “`0ŽŠw-®«¯ã£Y’b4΂Ø"¡%Œú:ZÐl²!Æãlš® …Â‡ùÛ6>rµîõ&ý–ÌgrUªýÚ¾ä|ìÚ~;¨/å•ßçêåT©*hYSzP_ÑîͰaÄ(~Ùj¡]‡‚Œß=¯Ë¸&èãâ˜uÉ÷‡®ÿïO€2lL†Éd„8 ÜÝÎg/°E¸DDÄÍ ÆÅ FÜ"bXL!ƒCa ‡ñÌëÓ>rumZµœrû3²¼ œ€%€¾âã÷ê;÷çÞøúÙ•öâE4Ð=Á·7“ g€KcŸÂX S`ð_?1 † ü`WâÞ¾AdL&“±ëb"là¦ÞОcdĘ×Jõaaœ5€E‚A A D‡iÛ{†{¯zů¿g¦jæL>RþÌ:è#õ!oì<_ÐKÍÎwyáà“ý_Ý*¹Ý”Æú¾¥Qü[~5ómúÛB!vy)ö-‘—/ù+ϟɨï¾| W÷Õ_ÃØ,Á!™À%Æ÷&ê ˜„a¾‰z;CS 𦰄°¸>Ì]|è;GÄ@C$D”xwÏùp(|Ï`ÖdsJÁ›3³j‡ ôdæ9wå•J_Ùñûªž&ïki¾{[aÃó Þ×hwCF0»9c…&åÖĶ|@ä¥ïý¦Õ'ð{^šY¸4†©iDÄÄ-kmÀ  g.MEÃxõZëQoaâbÜ4,B Á2 Ž%°t}Mæþ1k|ìR]SkƒÁç/Œ¡÷ŠÐŽý£ŠìRy‚¾²ù>åkPâV®[€ÈÖïþBÜ!HD QO¢¬HH4"B¢6M±ÍD¶~¯Hw»*â÷G·^%?¸]§§V´ÖS¥®ùè…)Ã@G}ö*Ú~XŒ‰Ðæ^qê4 Ë0 Ë€ XD¨»ÃaÞµL`ˆÆG/˜ž´æLÍœ)Z’æn”+¾¯ëŸ'WE¼»/ègá{þ…äk ^Eõ¾7Iç;Xˆñ·Ý h ŽÜ@\o ‘·Ð‹ æ ÍbjóC8ô“¢Þ3 ¾ûlô'ÿнâéw¯l­ßs}óOàÈh8Nl_ŠSO‡µ¨Y XV´ŽÃü“÷ë-ƒ„ƒ «WóÞ7À¦Ï Âø$*1ãÈ'ihÎä–b½ýeËíô³ð]çïî˜3³J»<Ëó}Ë}ö “11ˆÃ† ú¡žÐ`üIæ·ì÷Íœq7ÙH)ú(µç€:3|vñK\gbUMMó™I6 b˜a†E‚\èÄl§äÀ´@+§E­æ{5H^ñÝ úYø^}Ù)U¯²çY‡†Œ$Üæ; ž’p ÀXÆ ã@ˆÈ@Þxîlèök»z>\÷Á#ù¿é}7ÿUú?÷;¥þ1eUƆJúÂÂùIÌ ¡½ ñGLšJð™ nˆ?w$ óhŒÂáäêÕ»Ñ1ÏL&1€* €47gt]º9“Q*lwƒ>ß57gtþJtP6÷&2ìö/!*ý‹±}`Ã)TîðÝ}Ë”)ƒ/‡'¾ýîX|ó'ë¶~ï¬LI¸¯o1/$(žÑø¨ž\o"6 ˜Øv²©>Ì ÓdLUbìÝÉ„e€5„ŽŽ'PëÆç’,ê«IÅöÒå½`Ýò术©`‘N§Ÿ}GÆÕœmë_•?ló4AVº2˜L{`r26>xᚇΆa`’ž|4J0¦7ÿržo!]ˆ”Ïÿ¿Í™<ƒ™ KàØ%Þ}FL'OàR{N&,ƒ„A‚(Šƒž wˆ±„ p8\­l­GóÇGiuHÌ ½»ï’=¾ny³ü)ï寞&»µ4ßu.o½ïÈx|Zħ“Þ«½'¬®ad:iÎÄn¸„߃’@™ÎÀ`2ظöc§·8»þ“Mò‰¡Ž Ä:G.ÿ»ƒ>MÒëê f~笸¢Í ,p>†ÕíX¡·ÏsË|¶âçc1žäÖºpÈ„"»™ÿ._gekA¼ûüôŸ>2ðûï^©æiÈã‰PÕ*3ßS”:¿*A¯ÂyUéF«YV“U5ÏÈ-Eóâ@?0›±l•eÌ8Å.¬3â 8mê»HŒƒ×‘%‹˜‹Æücß""€D×Ãr5b×ßâÈ Oäxëà›k—Q,(baáÝãâ¶µæÓ;-"L ><Æ+£ôÆù…uÖé8#l\ ´ˆaÀF< !ëB€10?¼ûüôøý‡w«·»1*vO®I*sþŒÛ±‘R ‡f¬¯)忬©HÐgŒÓd±™¸ýCqB˜bL1Àq&€e£PAL"ù†)]'Fì†ld}5ºíÙ$«Ý³Áüñ›ÖÓ»²¿G,99;±¸þ¬IÆéh»˜8@LŸID0C8=?À2iÒŽèæ‡vŸO|ñ‘•T£ø^:èWõD¼_3ß —Ìt̸05Oeæ{º ïæ»žA}M³ªˆËä §t³"í b×ßààÿ -²pæcŒ)æ)`Š76­ãýŸ‡)Èø t90ì¾t8òË>™ äk‘8óçß[ãÍÑæFŒŽ¡½9b@,mª;66uh\ôFþÄé·ÆA@¤ÆÆÏõ,i%1ö 4ËyèwO&RÞBÑÜCÐk¨ê6gý7Êßk OQE›3¹•z¸º‡ï;:>02žRSŒìøSŒ¸8ó´Ø´fŒ;†ŒëÁn²Ë»náµ…aDÐñçÜýK ¶å#€6"[¿ÿ‹¿ëc¾vO¾ºÀ‚¦FÂØ[‚[J¡{6¼à‡o :qA\=ÐDÌêçÅ÷§œ3#e\î”ôT ù0:O x¢bê‹)š§€žþƒ^gó]wy±;»ýÅ÷åK£ “&8wŽöÿž5ÞTÏÉÀpþ÷pö‹§M+§¼y&dÆ) ÁiÁ;AD œý !Ä ,àà# Nô>–/Ûü`dëw=øÃ*Dßí:®èk<IÔ™Ädȧ\EÜ+˜>|Õö¾~¶íãî ?Þ6ɲ\…ÚôÚ¬ï›ô=Q8ûT6û¥fέÒÍdä{þ)’é _ã }MI®bõ¶>ŠgÝ ßÛ÷€oÆ…F@®3˜™-Oàâ~e{x]×ô #±o*„)°¥ÈND,sº%èÏßG¨Ç µâUØËíéÀ7ãk–= ¶ùÁئ}+>ùpýþiÚŠ­Qª‰úÕ™·`8p?x1Žì S?¼ñßm3O¼>)ó$ï¿®àU¬IÐ÷$¯äV9ã BùšÞf;«:Rô¦>°ýš4ÐÓ{J:UŠ*ºl‰Úwt|²Ù€ŒNŸ‹¢ùÊø—ß[y›%pø›!;U  éŒ^‰÷v5t·M.iJœˆ‡`%cvÈ ÆnêAqçÒ’°äBX€v¾,Öá™ÍC|Væ KsÆä:“ëMJdðn`FðžM½f+€Ç^Ùrÿu/•¸%nFñ½A_ÅÊÄ÷“i °¶Ï®wS:èúšák픚ŠÒð¼!#—þîÒe7BàÇOÆ·ÒƒÝMĶÿÎ §ú8ßàÉU¦yËÀ…®çNAÔ!æ&ÖŒ‡G£uÇ¢.,êLŠY0€&#$"S°ˆ @¾%˜&©ýè/#†ª%o ™©™HÅïsÏœÑYŵßI=€Õ½MxsÁNpùŠyð.©4Éw ;{¤ƒ^gRš|ߺ›3ˆ ¦%8A`ƒ daBŠL~yï¼ÑÓó›AMâì% ÐØèT³iÅ"¡Ip`Â0AD¦ÉH0NŒYf˜ °ç@óšÖÑîÁ‰Co4rƒ%ƒwj&`0üH~_±4 `¯–æLuËíϬvX¯Ž}â’ò×ô5Ø©=ü–Žñ{ºv¸ä6è¥Ô÷]^Ðk~­ç«†Û†ü;ÿ¿½3’ëºîówî}¯·Y ö ‚ Hk£)Y»HY[dI–R6J*Ù†å-•T¹*vª’JœŠmÁKÛr,ˉBY2)ÙÚlK´µp7Iì + Ä6kw¿åžüñº Ì 0˜éå f~ìïôôtß×Ë÷Nÿî¹ç<η߾¶¼ëpHà†XU«ývV6’Pæeô´'zÄăâäŒO\ì‹x ¯.ç^EÁxJ(—& m×k]7-Ð!9ü\§ˆj—“n¹kQ8ûÉî;Ûû<´^×q%»ËTógêc»${iÀ>IM ¾'p¿%ÔÒ¹ IDATÌ…¯½Þ)} Õ s&‘˜Š?£`×û¨ô½•B9pp:°2Ä‚Î0{Ìj€Q¿X‡8$‹X$#šTŒ@T¹³l0 AgÒ "VCÆI§l];ˆq…uGÿ¡ûžÿÌ¢èò:·9º4*½“kRoäúOwïµr=„ä5g¨ñ}ýª.`OšÌ™«ªöz=3¬oª±®3l¢$ ;Y0’ÖԖ);•˜“3Ö•ÊbÄ8œí_ *É@N° ª±ç³Vá¹Ú4¯6È#õæû$#©æ¹7m UR¡º&÷ø×J÷|`Õ‡9üwÑ|Çy9åtÆÆ¡Äh¹7ê[ÁºX0ˆWX#VÉÝ']ÏàW ÌÙeüƒŸºÆ˜Wü–R£ùHÐ36ë'éí4]3_.Ƨú˜=a½L³®¢S!~oлyƽi ¶/Úìx¥¾ÛªØŒ¬þh‡Ó!$8w6ÓcÜPà̪Y«XC2:*—±¾;ÿ6ç}OD wìõþÁ_Çt®žü>ôLÍ ~:·‘™ŒöX·ò’ ¡ö£´¶(˨j†9Cúù~ËÊ.EÞíïºwoZjÎ\*cò ˆ3/8wÚ÷æÏ’!0H8_±¨Áx¶s-b#b“¸^cï»b|{ú¾Â¦]ùÌ8á~m=êQ>ãÞ4ViþF›Ð¼>f¯Çý­ÕqÛAßXy)7gš­ »7i~+·B#øž{ü«£ÞЬþ…7rfçíð_SA{;< ß*­ŒbÁ‚¼Œ={_ÿÛ\ç¹ñ~oûä{G©üÅ¿û/ãùÛQÝ›Œo½/§´äÑŒ7Ó(Õ³¾Æ÷ë ôÞ4ì€3ª®3÷¦©/êö%Û€Çÿ쪷nàÖ³Ù=’w “)=´(´CoízÍU ʨÀÀ[_çdw#ø/þêxî¿!• .½@5‘”¥!ÎÃh’j4o è›dÎ^š»#&M\nÎ\USݽi:®åì¡@Féuâ#C*!‚UÑJ2¥TŠû*h÷£Úyûnî£Ñ”™_MÕÈž¨þÇT±>mJ Å4£Üãä5ÒœKc‚^ÒU›kœJ»ÿÞ^åÞd}Lßb–×ÂwÿÐgg¯þ#¹içÙ÷á#CÕðZëÚ§ÕVÙ ñÉgn»†¹4´²ØÈ­Cõ|OgP?£fhЯª­ÇJCš+µ@©æ{zœ£ë̽™Œ¶/ßì82.s¦*§Y¥Ç‰Gwǰë?&†Lu?”&ug:nß[ÈlðþòøïºI•ßk•õ3 Ÿ>ºúUƒ¼¤… ê¢×<¥—ïãÿJÕb8ùê›DT|•²2¤‘J#L—tÞ+d6\3®›Ó¹iTÕh^úd«Ox _ÙšÒlÎ4JõþLïëWu'AÊž‰ÝÍ3ßI3ßS½_ª‘Í[_÷¦šàëâ²åÙ³çïÎ`$ÂÌq׉ƒkBù¹[2€hû5N§a|]—‘ _Sm(šNþNg5#R¬…íA¿º;ùd¤jE3Å|Ÿ:J•{ÓÜÌ™•Û€/_“9àúe`Ùí Ò7å¡g¨ ðœ„2ôìÚEs~#ä?ã&«úß×ÔuŽžF¬Oq^Sgvô«+ ¿­:HèSÊ÷u«º€}SpÁú ¹7-}ÓëYNî³²øÜoÃdž€põ]Ü/[(÷|ÚôÝæ¿ô‹˜å5™ƒ«¡¼žï ê§s¥Ò)¡š?sÛ¥ W±YßTs†Ôòý:P{AŸ~ù‡~©6Wý‘éÛà¿ô ¸ŸFæÏ4"Ò«GùÈ þºŒèG:–éQ’ýÒÊÍJ»G€žöõ3|¿6Mà­<Õ³é/Ñ$ 8Æ·ø‰‘½ªÆð½«—#ƒú5Ë:“§`&¿îUòÛÚäÞ¤‘ï)ìô]ÓdøÖŠ>$Íô·ß´ ØqðšÍ÷Dk–v¢¼Øè„ÂftÖn¸F‚~mu0ÐϘ3S]#ƒúÛVwrè“{Î7ïqÓÈ÷ë^MZmzIœT.¡5;ÿ½±ª~íÐÓV‚·|•üSÿ÷Zÿð–å]hJû\·Þœ¹ª.½€Ê†›z’kvlü~ɾ_ƒšÑ¼q*Ùô©ä{CfÕúÔòF€žk ê¥â–^ßý1$ÿÔ_·lÓS»õ×Èž¨þÇF±>|O'Hš«”ƒ~ûÍÛ€&hά]Ú ¼Ð„ÝžS+~©z” ê› úâ–TÝ’òËÏ?ðÀª³ xAwqó'ªCÉ?ýÅæÍaÚ*¡y=Çëù^ »&W%u|¿uU7°7Ý»~›ª‰­ÇNOs³5%72¨_»¬3›±4áË{qËG’AþÉ/ï¾ç½ñݸWÌØ¸Hb¬ï.øQP¼ëSùgþrÔ;¹ey°ÿÈ@éöíP©$”ÛùûêÄ”BsæÊ•õÖTÝ› >u|ŸQM­XâúÌGÙìú'_Ý1ê÷Ÿÿ]#fmïç.»>…«—£€~ùÅ÷À “3»‹[?‚Jþɇ€ÝËßãNçÄEâb‰q±y.îtÎÄ1q,oºÒæO¹§ÿ(Ýñ‹•’AÈ>ßå^­?E–6~H 姨j¬¯ñ=ˆ*°óàxÃß¾WÍ0ßÇ©¶ïݾn°c_úÌ™kÞ_ìû¼cÌÊÂdò2[¡ŽÖ“=Qíš €¾¸õƒUCæ¡}…w¹Ø¸WŒ‹«'.KeàŒb¾ÿæî7Ÿë(Èð–˜`Á¨w›{þ’sjiãçJ?7ƒø±4ÒœKµ°½z`ãM•$œ«‚>]|O³9“’¯ð£º7M¯W<Ù´÷ÆÌb4]߇‹Ï‰`3Æó%+w‡‹¼"ÿ٦ͭJP~Çk|¯ Œ›õù'þØß÷.wNœçÄ!Š8Å ‚ÈÆ7*U^QWaEî¹?<~á?.-_Q-…1úU]qß¶ýã2åÌ™«ªÞŸ©ñ}ãš*è_™éâûŒÆ¯–®Ç¦ää6B×XõDT™ïÝåeÔË©—Oïz-ÿ™þ“›»ƒŸkÒ ›¡îëCû›«ã«‚¾¸õƒÇBŒ ˆ"" ÉÿnysI+Ϫàâ»Ò]øî¾p¸ï#ðº?«*Z’òÇ­÷‘SŒ¢ *¨ÊKýŸ^µó•6þJiã¯ävþ^ž€é®ZØ^ã{mÀ¥¬Ÿáû¸´ne7°/•=9›]¯xûúmÀŽ=5gÒän+D¡`’v¯,èÜÐïmÔØÖ«>®¯ñý*§yQj,§÷ê¿ ípYy~—–̲×öйa5¯=ûYÑ9±\pô•ß׫*¢"qìHÒ¹E•CùO/ÖŽfþŒj(¯ç{}PŸ"¾§ÙœÑ# éõŠÓ¼ãGó³'a¡¿9A¼ZÔ¨Y>ïŽ#çž>_–eSgÔŒjÎŒ¥ZØ~å´«üã_+ÞóÓ‹°Ç¾¡rIðž€]P\ž]‡:¿ *¢7hŒ3 ë±éŽ[ ú >M|O«ÚÕæ»!j€M? ¾7ßœßOŸý¼ Zwë8Œ„!jDÁÕßþ;Ç6+.¹ù;—>Ûà)[“<±Žzš¿ey׳¢h…ïÉ ÜKoa]n˜’œø>ª£Š‹+}Q‘bJœ‰#ãc’µf྽8 ß“*N˜6‡ðÓ^;_ì7Àm—n“m‹n]Ý ìMAEüëU$—Ú57¯èJ.Wø«í·;vNÐ|o¶Dtäå 7¯1S! $ Mš^±à“_}çØÝª1ª«½Œ1æ»'îjþq4]ì?2P­“äÅTá.2üÅŸR‰¨Š*ª1ê*ˆW'Îá4vªÃwéÐp•Ñ2ñjÉQ–(ÿjœÕab1«¨§æÀ]Ÿ­ÎgD’æ(M)53ªLò¿ÛnêI.­yÔ‘Jó» Ís›X½â±@? ëÓ|ðÕÙ]v©zê+(Bu­5Œ$ ¥hŸýîñ{¿st“j´$Ê.ŠrjÅZk­mÙáÔ+Y/Öã8µÿÈ@þ±GDì¢÷©ˆ }R†>¬âTb§(ˆhçÔÅ8‡‹q ÎS‡Æ¢eÑ2aE„$8#Á-ߺØ!qá‡qÅĈSqjr»þPÚðk=Š+hš›3—É»ëN&mýŒ®I“ÁïHÐ32¨Ÿ$ß›~zÐÑ.—èô¹Ï_„»ðjøÕWÊ)ÌîÉÄNNŸ ÃPœÓ#¥®¤°8ÎҨ‘›okí£§ïmê1Œ®æ>u þ<âÔÄ*NÅ9‰ƒË NÔ¡±ªJ‚xKùf´,Z-IYbJhI´,Áðq'&Æ$Iô£jœŠCö½ó[#¤; iµ*þ{ ñ5²×#~w“¿M$­ 'Ü€¼©jFsÞ´i¬õØ~ ìýñŸLìn›^çÒõÕO|aOÕŸ¹[D©äïéB>ùjùYAnÌÞY[e}¥üбS8G =V£ÎXärªÄ$÷"X§V6®Ž÷Îüàìß4ç‡Í;VKT@œ+±˜縩7£G¡94‡€Q’ÜG'¸ê¡VlɳVÔP+Á²%ÞÑãqᆃª£ ºú• ÛõßÛ}À©P²u’%îI—¯¯Ž}ý¸Ù O£R ¤¼^qóž¹'îOØó±?K€î®`»¿RúR¤]þ@ÁÅN1å8ò¬Uå|Œ-y³†]N¨ú%q±¾qzè´YÒãrŽ’!Î!ý8‘òÕ«Q+΀E<4,jE r~±Ê+Qþ^¬êŒÊ­QÈîþvîôÕ¸ò#¯àÞ4ôi‘×­êö¥²S`³µ}ó6`ÇÓ©W|óòNà@sÌ™8޵k‚*ÊmP6Å~ ÊR.K9UD´WÎag…,é¶çTÃ2çbéšù'%Y<Œcºbq¡ÑP1gœŠM6@adçóp[»mƱŒª~ !ÿw¨cè ?i¼Åš¿ Ba ò €JÔžˆ•N{ºOnT+â‰F¢ª'Ârt›/æd²ÖTÒ*gTUëÍ®µ>A3@¿~u7šRó}FõJI½âž{°vyXì3AYʔ˒ ‚²¨`pŠô¼Ý¢ršççâ¢1†Ž$$ Áp¾?;«+î êÏ)¢5¢µÒâ¯-¨T:°v»%)ñ›d¿«Bþ¸u ÜldŽêyÅpþ=sp%ÔŠX‰z¥“SF B"D9—]¬–ä‹–:9Nþ„ÑÅwÌÈîþoÍ;ŠË4cÎŒÔ÷75ôi>ŧynM§ÀØßÞzÅIð^ì“rY‚*܃P,êPA<úŒG§“Í8Ë2¨8peÔâeˆ’­õà'âWl}ù·¥óM=¨Vª´ùSôwûFñÞ=èNümR YŽF‘Ì>u1nÉÏ’ÒùXËbDÎ2pv^aþédÓ(=î„Ĉ"ŠÑÃFx½cÔ]}*3j²&»µéÖÍŒÆPSÏ=Û·lvZyêj4o6èÓûYÚ7½ÕÕ½Ég­ÂÓ{εfפé\Åbû½Û€5¬¦Ø¨¬Ÿè“RŽß>º)ùñU[ ê‰0+òALR«6 ?EŒSã˜Õ™r*r²Ïe Q9бàŒä}w*ò²ÙœÍ› ‡Ë™¬7Ü™{²1OÁø´nE°¯Ù™H‚õbÊIz*•Úš¦R$é@:¡ƒè¬æ{é…b™á2dP«bq‘‘eÑ §TúºÿPyÏå9 èWUÊ”_÷Ÿ²kzhjû̱ÕêúÀ—€^@¹muM(ýA}ûÕôzÅÍ 6j4Ÿ0èß¹ìY`ÿË[Ø ™éD³#[$,àuÍW’’¶$Umã0‹ s»ÌÉcåÐcIÁÄ>çc×ësrXk»¹ºº¢0ìôýëÒIPë;U‘ä ª¤HVÖW%'ÒHÚAÞ! Hg‡Ë”-ä‰1ˆ…s™eÝ¥yVòˆhן«*'ÿN¼×ï}0ÿØ#—=üEÐWùž d©i^Æk¯ÚVÿ}÷Á¾‘UÌj¬ŸýÕø—' ú[V>ñ…ŸýÄÏýöA…c6,eµI1 K§¸q®—¬VþŒ#NãȨӼq±h9ÆÅDŽ„qËçÆš-Î¹ÑØNžÝU¶v=5‘§ Å*ÞýñÐëßu´`¬K¾Ú<ûÍ®MïXô~јã_WÓëÄtà:D:è<…öPÚ-Æ(‚gA0–Ï>ñÖ³vÀvsè;oD1ˆv~ UÕ䟫©¶×@¿¾:˜Éao”ÚÆ÷î—¹ð5¾ÏõWPs3#ß° Øñ£Ö|¿èE®dVˆè_üÖêŸÿíƒËœwAÔeÈdTC ÕlvAEÄ%qN³9µ¢Ëçê…a ¾–ÉfÅ88uZç“ýíP³}ZÁúNª93–FõÓÚ½iæ7øíoÞìø— š3Í(™P£ùHÐ_¶Ñ³äQP3pÊÙ }wä¨ ÕEÄb”E"c8:Š%£ˆ¢EœmOðÞ ‰’ï.#xRÙÆ$"¨¨¾/¥w¨8eÉç:¾¯X­vßVª‘{bê,xÐI:ª$X¯‹ß ÿø×Š÷¾¯¸õƒù'¾:áY×ü™Û¦ èÛkÎÚø}TµÆ½IU'ØG} ¸oõÿiÑã¥8‹x$èóÙŠ—T*Ç@6b²¯e³Q,Ö…žJçXÐa’ÖÚÕи˜W‡ ° #Ù¸Yiû•E ÎsÀ G¶¬]ÞºäÈdFæŸürqËGלë8è‰'â‹&pW©ÖÉ#®Ú[O1–d§S=ÖEXð€IV5v«`Ô9E. áâÖá2“{½vú|ΞJeŽuÔ¾O#Wvo¸î‚ú~éãùœ·nÕךX¯8Å|¯i$sY ¨ŠõŒZO4¾0«3¾ø¢zén0Áæ°ÔT~Séì'¼¨ƘÃâÖ¶æxª³jÄßäuòÅw1~'©GÐõ0CM85æ’€}áhµÿ(Ï~«Ë¨3¸KT Cqë‡FÁ½^£~ƹ]S)~U êS…¸rùkÄ÷—'×|ñGܼà7ã±¶ß· ØñhŠÌ™+«DÉ —Iú+I¹\>w¦c…¯TÿeÊ„«Ë~’H#‰›£˜ ¦ ^Ñ@%»8” ëïya˜yÅ”¾stÓ;–=Û²ÃiòO~¹¸å߬^ÔqèÔ ¨©ÔœQ‘ê ûË ~JÓ®/ `ሗ۔'¾Ùkq¦Î“AÐ,d¬3·|Shþ‰¿iê±ì>Ô_ÿ©'eA^ÛÍÚÂ÷ 7õ»]Ÿ !A}ªª=zèãÀŠÌÚ|‡æ;È´w¶}ü€ûÑ‘OoXþÅ?^ªÎlWÓ¿ÿ·©KAô¡ÿy̹3½=.ÝK´ò V6:Õò9,¦€-pÏ<÷ÔËÞË1._ªîçi‡qêÐE.w¢Uˆoùšyňw>r‰»ž r"cXð€&ei<ú½mI:C¬Š%£’UñU2j²ÎdÜÊ5b\â+Í>ŒäÓ=ê?'_;Žš)¤)¿ª”äYNRåà0 „Æ*¾¡yÃmò½¯ú·Ñ”â»\:Û$Ò÷\aГ¦sE4ø²W áãX1lSÀ°Y$k©]ÚcP«êj§eqœ;aKß>ºé×WŸò¡â–¬ØÐuäÔ!Õø½²_@Dn9áDà’÷Ä]¯=ú„ý¹þøØO¼ñ èS?š%_MFMF“€ÝdÉèÊÙ˜¸4ÿäCm9´QY¿¡º²Õ OÁª|oáaOõ<ˤš_9ãK‡1å@§Ò„'pû[·;þi‚æL‹ §\¢ËvH&|W§$—ÎØôÕßÎæ±…*Ü xH8Û$]A7â¾—D¹ã^é;Ç6¿céÓM? *ÿäCÅ­^þSÇtÍß.ÞóÓ•5èŠÈ?öõ¤\L×Áß÷7J3ç z·¼å<ÉùUT`ÉO@¿&è &ÿä—ÛvT—ªöé®ñ½• ß¸¶ØùB› Ój¾·«Î=S-Ïò;û~X™[ëÊA…ò8Å·¬Ýl³.+î¨jϺ1¨QÑQæ°¼” W•}ãUȾØ7%cΙl·ÊqVh.ž4ë'UÄûØï\ú¯Í8vmhÈ?ñ•âÖ-}°´(¸äW}ýâÕç%÷ô_VF›HN‘ßKG(`ƒHÜëU£ùå —¶‘ _IDATö€¨•º>ý™+k,÷&—³¤«ž¥T* # Bc°nê5{•ž8ñ‰­‹¿Ô¸‡jØ=µF2FüzhD‚ø~›8\ˆ1F2¦nƒîà`ÖTk-'å&©‹L¥Z}¥d^ÖàÛÇ6%,} mSþ‰¿)ný &@ýüã;ò£¶¸É?ýWõ?ïþXÂúÃ}¤ù~Uú5 p]²~:ò½¦ÑÝ›tU¨¯¤nç²^9 ˆã\§‘@O…IÞaë)uyü^ñß}ÏDaÂe5ª7R8e†s5®÷ô ιóÍà@·µ•NÚΊ´zK/ôÇûS‹®+‹&ÑäóóOµjsFãtôu|¯¯'з”ïm4g®ªÝ‡ú*ˆ¯¢j<ýǯo¾°%ÙIbŒ¾}Õè¤xxϦ÷¯–Š9#+rk]ÕUˆ#q*`~ÿÖ¯u¾ýwú€_1ιž‰+žzâo]ó›|k×£õ?ÖŸ½j|Ÿ<èo]Ù…¦Ô|O³9Óz=UŽ“”ªÖñ]ÆÑ²k é  ¯ÿí×÷Þ ¬Ÿ«ïÉ%râ*e­*fKŸÍw¯ÍKäBg†Ý/Nq*¹ a"~eÓ½ìÙ\7#ÖHVá –¿® ‹à»ÿ®øÉÿ|8….ë•ýc^YŒ}Ç’F¶Ù«Ñ¼ôšVR_jR“ñ¨ÞŸ©ñ=Í oß7ÞÔ ì<˜Šº.ÕHÐ×Ù£À`¤‹«lˆW ’ŒR|.ƒ[æåp¸0öLì<'KV3Š:N¿"ŒÁ©Ä±ñìì‚Fzã ¸Éä¨V@Œ…uùìþRðÈÞ;¼õ*[r¶¿g°ãï§š93¢>A¢·Ìý1ð¯Þ ÷Ë ªÍ*æ> è«6Π觃j4¯}GÞ{þlÛ¦u©Z¿§8HœÀF‰Q5r=ö?=¼*ùQ#5â Ù4c²'…Vâ ·™|åÚÕU!%‡„Hï\¢XúÏ’œ$Ê%r~¥vV\µ¢“eF멟GÊã{ÆSüº\Mc~|Û§€oÛôë÷½Ø—ü"èW^zÆf}š3#ׯîöJãÜR¨‘ ¿}moí·Ï¿p¡ sªjÆo ãÖ¿ý³‰~X‹+$/•˜Ý "ˆCλA,%gª‰2Ñeã ’(84ÂÆ¸˜lAGT"vIO€S+bärdr:…¹=>]5Cô×ï;Øš™ÔTƒõ%ë±3Aý´ÑÎúêÉž¨vM[@?Ã÷æ*Yä|xϦî¬0©Ö,<ã-ºT²(Ù¬â*mzlTŽ ±HŒDà †ÑX]„µâœF ,1q•ìŠdsêçÔÏÉxv@mß6`Ç7¦ž9\!~o».YÔ§ôiÚh¾_U Ê/ãxïm½¹úM&­kº/¦Ñ|¿¦N°V-•å°6è)7üº2è22¢šË!ŠQ5ªab$®ü‡ú“‹‰\„IJ4¸°">W.R.aâê+~ŽLNüï¸Ë3Æ$y÷cJÒýQ[¿ý™w zÙå·þäïk7¨¤¦¤ ýcïËÉ¥vÍ­+»îZ7+ŸóÒ úõ«»vϘ3 Òó/\H.µkn_Û[»4ûѧ{üÞ2¾½ý³ûöÝó‚+vÃŒ Ëɹ<8­ÎDDõÜ  "Ʀ3㬃ˆ –»æö•åñ£Ý‹µÏ ç¼( (EäÍźXÙœËäÄÏ«ŸãŸ‹AÞsóSG¦&Ücq5åéÞ¬_Õ]ûíž—Òý̨ª!¾ë·¯íM>†ÏhJP?ÝùÞJ­[÷ø:xxϦQrˆƒj1TÑq‘ž-ZÐS,E—Ã[fŸí?›4$„(d2êçÅÏé®ã¥d¡õÝk[׺åJ¯9sUí}y žì‰fX?¥Ùœ¿.‹å+ƒ›ë–dÇú¦ó=ÍæL[Þ.½¹[^+ï_¨^ q‡’m©@µD­Fh¤D¢Ž8–(Fc3<ž+R†R!8tF°ˆ9“fÇ?G&§OW*¼kÍU’¾·¿°ãá)i¾_yqµÝ WQ‚òË ^Ï÷6²~&sfÂÕ|¿ª.õu|¿£:~nÒ Ÿ‰ß[-ñ½B·Š3€j¥pŤQ %YM%Rurƒ ²—#õ•9YûB/èÕóà ‡â;;$ý9éÍç4“Ó@Æ*N9bM=Êfk Çžã)aýŒZ©ú˜½Æ÷;ªÑýs]’á{«õ–Uý½ƒo8…Ësv0p=¾©äÓTj‰ Qî.ß9¿ … &ˆ‰"–ì«ÃZèá†9æ™c ”m_¹LÙÄÀÛVŒ»FùTæûõ]>s†õÓ\µ°½Æ÷;ê,ûkb}sù>cÎŒª’+f­tç̰qCË…Æd³ä&þL!¯™@£2~F_:—"æfm)"vØ$MÞé¦UòÜqTµ³3vNî_öÌ8'°ýÛ€_›’æÌ••æ¹1†9s]õ"ì>ÔÈOVÊÍ™4›ï3g®ªÊëù~ÇÚ^d¼ÖMsùÞà>Uû¦&"ÆHœ³¾qÎàÔ—+[Q] =ó4 DDÅÐNW.¶EK)"V¢˜î¬8_:|ãÈeguuº-s¿{m3˜\ZlÛ_Ößü£1·ÝSk¢Æb}£$éþN—æW¶ÙS«Ù︖¬Êÿå–Tz²û¾IEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/moondance/what_is_needed.txt0000640000175000017500000001257410304351001024055 0ustar pabspabsReturn-Path: Received: from sccmmhc91.asp.att.net (sccmmhc91.asp.att.net [204.127.203.211]) by services.cse.ucsc.edu (8.13.1/8.13.1) with ESMTP id j7LFjvP3020907 for ; Sun, 21 Aug 2005 08:45:57 -0700 (PDT) Message-Id: <200508211545.j7LFjvP3020907@services.cse.ucsc.edu> Received: from sarahblaptop (12-214-194-20.client.mchsi.com[12.214.194.20]) by sccmmhc91.asp.att.net (sccmmhc91) with SMTP id <20050821154541m9100g6t2pe>; Sun, 21 Aug 2005 15:45:47 +0000 From: "Sarah Borchers" To: Subject: FW: Information for Moondance Games Date: Sun, 21 Aug 2005 10:45:42 -0500 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0003_01C5A63D.7CB72380" X-Mailer: Microsoft Office Outlook, Build 11.0.5510 Thread-Index: AcWYYITMHU7dxcyEQIyaVs9g5St7dwOBnkCw X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180 Status: This is a multi-part message in MIME format. ------=_NextPart_000_0003_01C5A63D.7CB72380 Content-Type: multipart/alternative; boundary="----=_NextPart_001_0004_01C5A63D.7CB72380" ------=_NextPart_001_0004_01C5A63D.7CB72380 Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Hi Jason, I apologize; I sent the email requesting information for the compilation to an incorrect email address. I've attached the message below. Please disregard the dates items are due - just send me your information at your earliest convenience. Again, please accept my apologies for the mix-up. Regards, Sarah Borchers Moondance Games 763-370-4647 _____ From: Sarah Borchers [mailto:sborchers@moondancegames.com] Sent: Wednesday, August 03, 2005 2:21 PM To: 'jcr13@users.sf.net' Subject: Information for Moondance Games Hi Jason, We are excited to have you be on board with this Independent Games product. We're now at the point in development where we need some information from you in order to best feature you, your company or group, and Transcend. This email will outline the various items we will need for you to send to us, along with the due dates for this information. The good news is that most (if not all) of the information you'll be providing to us is already created, so this process should only take about 30 minutes. Description Due Date 6 screen shots from the game you'll have on the product. These screen shots are for the general interface of the game to entice users to play your game. A screen shot of your game may also be used on the product box, on sell sheets, or the Moondance Games website. 8/12/05 A completed ESRB sheet. I have attached the form that needs to be filled out; please complete only sections 4 through 11. 8/12/05 Final credits list. 8/12/05 Minimum system requirements. We will be performing QA and running the compilation through a config lab, however it would be helpful to have information about your particular game. If you're unsure of the requirements, an approximation will suffice. 8/12/05 Company/group description. Please provide a one paragraph description of your company or group. If you would like to have more information on your company or group in the CD, you will have an opportunity to do so through the "Extras" section. 8/12/05 Game description. Please provide a one paragraph description of your game. If you would like to have more information on your game in the CD, you will have an opportunity to do so through the "Extras" section. 8/12/05 Brief feature list. Please supply 4 to 5 bullet points on what makes your game fun and unique. 8/12/05 Genre classification. Please let us know what genre (action, puzzle, etc.) your game falls in to. 8/12/05 Acceptable Name Abbreviation: In some areas of the interface we may need to shorten the name that appears for games with long names. If the title of your game is over 25 characters long, please let us know how you would like the name abbreviated. 8/12/05 Installer information: If you game already has an installer please let us know if there is anything special the installer does. For example, are any registry keys created by installer? Are any files installed in locations other than the main installation folder? Is there anything else unusual? 8/12/05 Instructions for your game. Please send us the instructions for users to play your game if you haven't already. The preferred formats are txt and html. 8/19/05 Email address for support purposes. 8/19/05 Optional: Concept art. If you have concept art that you would like to show off, please send it to us. 8/12/05 Optional: "Extras." Each game and developer will have an area associated with them for extras you wish to share. For example, you may wish to include a link to your blog, or to an essay, or a movie file. Or perhaps you would like to explain why you decided to make your particular game, what makes your game unique, or anything in particular you hoped to accomplish in making it. 8/19/05 Please note that we may edit your submissions for readability purposes. We ask that you keep the due dates in mind, as we would like to get this product to retailers as soon as feasibly possible. If you have any questions, please contact either Brian Kingsley ( bkingsley@moondancegames.com, 763-234-1116) or myself. Sarah Borchers Moondance Games 763-370-4647 transcend-0.3+dfsg2.orig/Transcend/doc/html/0000750000175000017500000000000010305077132017354 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/html/igf.shtml0000640000175000017500000000752310133730534021203 0ustar pabspabs Transcend: a new video game by Jason Rohrer

a video game by
Jason Rohrer

IGF 2004

The following downloads have been submitted to IGF (the most recent are listed first). Note that the alternate downloads are available in case the SourceForge mirror system is buggy (some mirrors are slow to update). Please try the SourceForge mirrors first.

Note:
The Transcend window defaults to being rather small at startup (300x300) to accommodate slower systems. Please resize the window before playing according to your monitor size and system speed (if you have a reasonable 3D card, window size will not affect frame rate, so bigger windows are generally better).

First Update

Version 0.2 released on October 15, 2004. This version includes three levels. (change log)

Important Note 1: The keyboard layout has changed slightly in 0.2. Most important is the grab/drop key, which is now D (instead of G). See the included how_to_play.txt for more information.

Cheat for judges: Version 0.2 includes a cheat (for judges only) that allows you to skip levels. Press the 8 key at any time to move instantly to the next level. This cheat is provided so that you can explore the design of advanced Transcend levels without spending too much time beating difficult enemies.

MacOS X [alternate] -- 10.2 and later.

Windows 95/98/2000/XP [alternate] -- Includes the GLUT library.

Unix Source Code [alternate] -- Compile yourself. Requires the GLUT development library [GLUT source] [GLUT RPMs].

Initial Submission

Version 0.1.2 released on September 26, 2004. This version includes two levels. (change log)

MacOS X [alternate] -- 10.2 and later.

Windows 95/98/2000/XP [alternate] -- Includes the GLUT library.

Unix Source Code [alternate] -- Compile yourself. Requires the GLUT development library [GLUT source] [GLUT RPMs].

transcend-0.3+dfsg2.orig/Transcend/doc/html/index.shtml0000640000175000017500000001441210215052510021530 0ustar pabspabs Transcend: a new video game by Jason Rohrer

a video game by Jason Rohrer

Transcend can best be described as retro-style, abstract, 2D shooter. The graphics are geometrical, and the pace is sometimes frenzied.

Two features set Transcend apart from other games. First, its dynamic graphical engine, which can smoothly morph from one complex shape to another in realtime, produces striking displays. Combining these dynamic shapes with subtle randomizations ensures that each new play through a Transcend level is visually different from the last. The second novel feature is Transcend's musical power-up system. As you play through a level, you are simultaneously assembling an abstract visual collage and arranging a unique piece of music. Transcend merges video games with pure art---it can be viewed either as a game or as a multimedia sculpture.

Though I first read it after developing Transcend, the Scratchware Manifesto captures the spirit that motivated me to develop a game.

Requirements

Required:
  • Stereo headphones or speakers
  • OpenGL
  • Keyboard
Recommended:
  • 3D card that can render OpenGL
  • 400 MHz or faster processor
Notes:
Development and testing took place on a 250 MHz PPC computer without a 3D accelerator card (in other words, using software OpenGL rendering). Transcend is actually playable on a meager system like this, though it runs a lot smoother on a faster computer with a 3D card.

The Transcend window defaults to being rather small at startup (300x300) to accommodate slower systems. Please resize the window before playing according to your monitor size and system speed (if you have a reasonable 3D card, window size will not affect frame rate, so bigger windows are generally better).

Think Transcend is cool? Donate a dollar.
Why support my work?

Downloads

Version 0.2 released on October 15, 2004. This version includes three levels. (change log)

Important Note: The keyboard layout has changed slightly in 0.2. Most important is the grab/drop key, which is now D (instead of G). See the included how_to_play.txt for more information.

Clicking a download link below will lead you a list of download mirrors---pick the server that is closest to your geographical location.

MacOS X -- 10.2 and later.

Windows 95/98/2000/XP -- Includes the GLUT library.

Unix Source Code -- Compile yourself. Requires the GLUT development library [GLUT source] [GLUT RPMs].

Transcend is free software (released under the GNU GPL)

Screenshots

Keep in mind that Transcend's dynamic, morphing graphics cannot be captured in still pictures.


Destroying one minor anti-glyph as another approaches.


Battling the major anti-glyph.


A well-established power-up collage and two of the resulting projectiles.

Development

Development is managed through the SourceForge Project Page. On that page, you can submit bug reports and feature requests. You can also browse the source code in CVS or checkout the latest source to compile a bleeding-edge version for yourself.

I have posted some rough instructions about how to edit Transcend levels. More information about this topic will be added in the future.
hosted by:
SourceForge Logo
transcend-0.3+dfsg2.orig/Transcend/doc/html/roughEditInstructions.txt0000640000175000017500000001406110130740707024500 0ustar pabspabsAll graphics in Transcend are generated by "object spaces" that map a [0, 1] parameter into a 2d object (an object made up of colored triangles and a line border). Every parameter in the range [0,1] gives a slightly different object, and by changing the parameter slowly over time, the object will smoothly morph from one form to another. To relate this to something you have probably seen before: Think about a color gradient, where 0 is red and 1 is green, and the color changes smoothly from red to green as you move from 0 to 1. You can make the color bar more complicated by sticking a black control point at 0.25 and purple control point at 0.75. Now the bar goes smoothly from red to black to purple to green. You can add extra color control points and make a very complicated color bar. Now imagine a spectrum of object shapes instead of colors. Instead of having color control points, you have object control points that specify what the object should look like at a given point. Thus, at 0 you could have a star shape, while at 1 you could have a square. The spectrum would then blend smoothly from a star to a square, with 0.5 being an object that still looks a bit like a star but also looks a bit like a square. If you stick a triangle shape control point in at 0.25, the spectrum will blend from a star to a triangle and then to a square. Making a Transcend level basically involves specifying a bunch of different object spaces (one for each object: a space for enemies, another space for their bullets, a space for your ship, etc.). In turn, specifying a space involves setting as many control points as you want in that space. The minimum, of course, is 2 points per space (one at 0 and one at 1). Each point has: --An anchor in the range [0,1] --A set of triangle vertices (each with X,Y and R,G,B,A) --A set of border vertices (each with X,Y and R,G,B,A) --A border width parameter --A number of rotated copies (to create a "flower" type effect, with multiple copies of the shape rotated around a center) --A scale factor for rotated copies (to create a spiral type effect, with each additional copy/"petal" getting smaller (or larger) than the last) --A scale factor for the angle between copies. With a factor of 1, the copies are spaced evenly around the circle. Larger values spread the copies out more, and smaller values squeeze the copies closer together --A rotation rate for the object (how fast it spins) Here is a sample control point: 0 6 1.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.000000 -1.000000 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 -1.000000 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 0.000000 1.000000 4 -1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -2.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 0.000000 This point is anchored at parameter 0. It has 6 triangle points (in other words, two triangles) and 4 border points. Each vertex (either triangle or border) has an X,Y coordinate followed by an RGBA color. Consider the first triangle vertex: 1.000000 0.000000 1.000000 0.000000 0.000000 1.000000 This vertex is at the point (1,0) and it has a color that is solid red. At the end of the control point are 5 values (each on a separate line) that represent border width and the other parameters that apply to the entire object. For example, this object has a border that is 2 pixels wide, there are no rotated copies (single object, does not look like a "flower"), and it doesn't rotate (rotation rate of 0). This control point comes from the file levels/001/enemyBulletClose and there is one other control point in that file. That space determines how the enemy bullets look at close range, and there is a separate space for the far-range bullet shape. Each enemy has a close range and a far range parameter, and the bullets blend smoothly from close shape to far shape as they move away from the firing enemy. Thus, you can understand why every enemy has bullets that look slightly different. One thing to keep in mind is a trick to ensure smooth object blending: --All points in a given space should have the same number of triangle and border points, and the points on one control point should "correspond" with the same points, in order on the other control points. If the number of triangles changes across an object spectrum, triangles will "pop in" or "pop out" during a blend, thus ruining the smooth effect. Also, if you don't keep track of corresponding vertices across control points, you can get some "ugly" effects. For example, if a left-most vertex from one control point is used as a right-most vertex in another control point, the object will "twist" across itself during a blend. As text objects, these control points are pretty scary: it takes *forever* to edit a Transcend level by hand. I have created an editor program for control points, but it is a little clunky. It only can handle single control points and does not deal with entire spaces. To use it, you must copy a control point into a separate, temp text file and open it with the editor. After editing, you can save the control point and then copy the new text for that point back into your object space file. Since the editor only deals with single points and not object spaces, you *should not* include the parameter anchor (the [0,1] value) in the temp text file. Just the control point with no anchor (from above, you would start your copy-past selection at the "6". The keyboard controls for the editor are described in the source: Transcend/editors/doc/controls.txt The best way to get the editor is to build it yourself from the source distribution. Have you tried compiling the source yet? After you have built the game, change to the editors directory and type: make to build the editor. Then run the editor from the command line, passing in the name of the temp text file. transcend-0.3+dfsg2.orig/Transcend/doc/html/images/0000750000175000017500000000000010305077134020623 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/html/images/screen2.png0000640000175000017500000020774710115436474022721 0ustar pabspabs‰PNG  IHDRôôD´HÝgAMA± üa IDATxœÜ½yc×uæùsß{‰Ì¬Ü*³ªXÉ*Ф¸ˆ”LQ)Û²Û-¯=½¸í¶cì¶{Ær(&ì‰ð6²Û=mwxÜ=Ñíî–ÕòÖ¶Çî ÛÒHmí¦(Šû¾V‹µoY¹‰Äò¶{æ‹“Y¹ à_dT$ÀÃÃKò‡/Ï=÷;ô‰ßz}.]xÝ»«g-žÿ:€=‡¾»#ç´{ù OpF?ÐÂsÎ} ÀèáÅ|N»WyæÛ²{?ÔÎAŠ—¿ ¿ÿáxΩ%.=`èÀGÛ××dPžù¶;|„ÒSŽ étY¦QéÉˆí¸´¡ÎÞ§|ßtµïø¾ÙË÷^Âý]@vþÂÓîðáÈëëšF5¦ càÎÊ‘( b9ø†:ûŽ|wGU\Útµïø¾MoLñ}{²%“ï=ƒ».¼P[¹ð. ;gô}1¶¡ÎÞq¾w  ÒhÓÔmø®£PëM索ÍÏ·}Ž;k›Þ˜>âûŽ]}Á÷fÈn”@¾÷îÆ³´§xùÑžœ@,ÚPgïS¾oº‚ÚQ¾ßX–Q– Húív›Þ˜­ø.¢oúÚ‘ñG÷åŸO¿˜§7ÒÁ[´z)Xº¾éK8N[gØŒvìzì ¾7ÙÏžp¾7Ov£¤ñ½poTcòûЧ|ßtµïø¾MoLçøž…Huå.XYD¢Àkí˜;v=ÞÈ÷Я›þ |+ ìÜó•Þ÷·A`‡¡öGàòElj§v´•šìgO8ßwµS)±|ß-ÙÅ÷nÃ}C½Où¾MoLñ}Ç®ÇÎñ=Þ5Õ&ûÙ7ðý¶ý_<Ë,?°ƒÐpò¹¹È=¨†öº{n|ºãø¶ÝA羫J‰å{ {PÈ÷ÖÈn”¾wî›® ößwìzì ¾7ÙÏÞ!¾¯ï†lHÙ)»5ï»Ú©Ôàû­“åûÛYcÕï¼õñs³Ù’À÷.Á}ǮǾàû®v*%ï¦Ïoß[؃;ß7uЯ5sÈþÀÿýÞ[>ïEû_=yôÄÉÌåÂî<òÕ pž?õ•ê@¥:°é;p¿ ªmæÆ$„ï±$‚õœïq‘ݨç|ïÜ›ìgO8ß[؃Ús¾‹h@°®›»åtxù¾ÜÍæœ·W dð½ö §èÄ¥L=€µú{8Aè(ª^ÖÒ‹7>×v|Ûì¸Ë2±$‚õœï1f=öïñ’ݨ·|ï}üÀz%–ï-§ ô–ïfPcsÿêÕo‰Ö-§ ÄÈ÷mT-' ô«Û<½µD°Ç_ù'Ïú‘êÊ‚hΘúûTê³A`¸X?T¤ƒ ½á‰‡Ç¾å8¾ãñvËĘõØC¾ÇžâÛ¾w‚ìF=ä{Çá¾Ûm¨ ä{›¹1=öï¢!š˜+³Op÷>ØNKl|ß¶æn9@B¿²é½mf=ŠD"õœáüþ‡ÃÐZ™;Û¸7ÔN¨ OqìÀ¶Û޳[&ößžð½Cùì=¯ÏÄ«^ñ½³po-` Q|%¬‡|7Uì•K 7ýf³3³UÄÇÂ÷mË2Û©M²— ×EÄÍO˜›ûö¼h ÞñÖ…›ý‘K`mx–íÔ î'.Ok¯»AÊgï2ß;:y£›|ïœmo¨'|ï ÜÛ‰ŽIßcÌzì!ßË3ßy»c‚´çßwê–±W÷óÞ~>û†rPÚ wpßGt¾;p¥Ä²ÐÝG¿`Û¾m¯œûx˯»^¼Ñ5¾wa¦RwøÞ²uŸï‚{û¡`=ç{ì)¾½â»å¤Eį­6~ÂÊ2ˆÂ µMÿmò½9çþŽ•ÕöÉ^Z¼"Z熧ÌͱÜÉ(²®ï€Èàô‡±¶¾j§ÄN ¿EµZP‹$¨½tÇÔðË-¿tC]˜©Ô¾wmZ^§ùÞ5²u™ï{\q=ä{‡òÙ»ÏwÓ3tðcíWWÖߥ¬ú&ûîó½¸Û©,€À+#®iyïüs!Œ¬0z»“ßÿ0³^¹òwÌÚvÄÉÀ]·|ùó=y±þ?ÿ-ûûÑïþ—ßý?jíõ»6-¯£|ïòÔÎñ½Ëd7ê&ßã‡{¼A¾ ¾wñ¼ÑM¾¯o|Ü|S¨e+Ë^ ¹.mð½©~G“ ÙWæ/ˆèÑææ s>ЬåêÍë3¼ÿAÅzõê#L!!r2ôÚ¥;_“ÉBíÈôäÅ£ûžoùº<µC|ïÉ„ëßcD|OÈnÔ5¾Ç ÷ND´¾#%ø¸Ô¾ohiO¹y]+/ßøHÃwQà5¹¨¡ÖøÞd¶ŒÎ®<øµö§åmøl‹"+Z³íæ®”Ub‡|P)½zíæPqÈE·üÆ]G¿ PÙÿ_ÿͯ?ý?ïöÕ{2á:v¾÷„ìF†ïˆÉÂ÷ìFÝá;}â·^ŒëX¾aàÞ`}‡Ôµyîô±kÓÍJµÕ%éÜÈVÏŠÏÐr6özo/wúf´tõ$€‘éãÛ?Ìxvwü~N:·«SzÇqfÏš8bn¦i@Möš›æól W!A¨xõ œþð¡Ég‡J‰eù/Ÿù¾Ö^½'doÈÀÝ€¾õìëeàÞ`} ê9Ù2p7 ï„bsî]«Ô…|7G¡vοoµ 5­«+ [=QÙ)ƒõЯn¿hƒvëß›qîoWcÚî±aÜ’ÖJkµþ~@ŒUW2‡Cû¿ãоkcÖŸ"-Ö뾫OÉŽ˜ü{BÈŽ¶KðÉ!;:ïßã{׿u”ïÝrÝ ¾o0Ð U¶}«mD›ªÁ÷f¿ã‚êú:»“àWKÍŸÌ/Ø(ñ[Ñ’Ö–Oãîe ™"¦hOö­‰îÈQ&yöizóJë è9ÙzžO¯Zæ{¢ÈnÔQ¾Ç÷.Bíß»Ov£xù¾ctŒ›‡H¥0»ýq,'³PÙÐl¾e™ù¾k3ÞÜìfZºzJ´Þ{‹¹i½Ã¶×•áë9ûr>u("Òs+·^©ý Ú˜¯²µÃ÷äØö†Zà{ÉnÔ9¾· ÷ž ¹Žï½"»Q\|o2ÌšèrafÇZŽk¾1ýˆÍhôðÇš)ÑlÿÄí1NfP^ee«§4ûZ~I„Åί€­çÓ˜aŠˆ"¢¨äí_®bް.ÿýS?F·£Öøž@²íŠï‰%»Q‡øÞÜ{Bv£ùÞ[²µÏ÷]Å=6¿ûßr\;å¼r°nÔöÚ™ï[;÷xZÚ×´xùuˆÞ³ï6sS‹Òò¶mçpÙŠê! y2V '™"æp¥R_n5|ÿ…_ÿ•_øGÿà—~ìš|ÝDÙö†vË÷ĒݨI¾'œìFà{ëpï!Ùbá{ÈnÔßw䛞‚èÕ¥+M>Þì'ÔVýæ¿=ß"›•Y¶!{ÊÄ«›<çú ­û‹j5æ” @¼Uøõ¿|­ÊäúÕåÕëbøþ©ßýùååáŸûûäüãí_4™d7jžï '»ÑŽ|ï ²ÅÎ÷áÞs²µÉ÷äݨÁ÷]!¾µˆö $;ÊNeíµfÄø¾™sßѳ§Ü< ^¥°ãKÿý{ãïÿµù‹¯ˆèÑw¬½&‹ðÊLeu¦X-x^±R]¡PíÈDþ$SÄ2‡ +Gn< áûòòðòòp¡0ôSûé­^:Éd7j†ï}Av£møÞGd7Š—ï­ô¹'„ì µÖÿž4²7Ô {3-ð-ß°2ÀàØM»}¢_[5h6},ÛkÓøÙs/˜8üöBMVc ÙSîæÃð<þKù{–ò¾òÒ¯Î]xÀøÁ»Õ ¨Evš«…í4h®ƒâ¾‰“™¬Ã*Ž ¥ Çöfj›\Òâåoþ“ïû;Ë _ñíß[ÿ€ d€˜MgÒÔ€û-ð}Dö†nìï;²7Wÿû®áž4²í–ï‰%{CÍlqj‡ìVæÏ;ÔÂs×ÅÉ îøàù>{ö9Gî377%ûïýò=år¶\Íý«Ï~yýϽr@*û6ßï¸é‹{–ò-åYì[ÊûæëŸœ;ÿ"€ñC÷˜Ç”—|"±ÓdgœÕ@n4 (,DAáÀä 7k3G ËûmËsl϶k)§–rª)§–NUÒ©j&]N§+n¦|îô”!»m–~ñ¥ß6/±ì¡_#V‰…;¶à{?’Ýh=ßû—ìF±ð}wpO&Ùšç{òÉn´=ßÛ$»Qqî€üøáÖžîWWLq%åî€ø |¿~æY“GïÇÖžý÷~ùžK—ö¥Ý RÉUkn¥š­Ö²µš;7§¾ùê'SÙ¡›Æž·”§ ÖÙ·”gàþä›?`öÜó&¿€_Ñ~Uçö¼VY¨pG]~mõÀèó©Œ{qþþÆ |}+¸gÒå|n.“©ºn%›­þÕ_ýƒGÏüÚ²^™H™ýç†où±^ŸH»ªÎ>!µ™ÌÐ!´Šøfáž|²mÏ÷þ"»Ñ|‘ì ×Ïš<ÚÎAÖšXd«j¸Ñz¾Ï¼õTyþµÑ}G·¯³ä¶ÿxéê-³ *öq[ULíåýGÿÛ‚„vÂÐCEJk¾¾|K:aä\¸¼`ïÑH‚ÂI9‡&žºº²‹)K·Müõþ©³7ßôâç>÷#–>zÔ½¶ºDÄÄʬFhHlYf½®¿ö‡&ïø©^ŸH[2Õ˜t:öògz®êì2l,®¶Àwuÿwý‹Ô/dÎôV.x+ÒùƒîêG²p‡T 窅sîðÄMvéÜHµ´P[]Ì ìiù –6‰ÁQP‹üÚV¹cîð‘ÊòÙÊòYwøÈõ7¿ `úöÿiû#_œÿpúl¹’¯”˜Ó.ˆdfùöëKS%oŸG=MÑ¢ÖJ„˵­Õå³'T&§EµjXõ‚jàW"ÛÝ8€J»ì¢ÊR!8®ìT“ovz.çóËñè¿–¯ù~ÊÚû½ª+óDœÎ 7µV“I´m°xþÊÉg'‚ÕKÎÀŸH5êìÊÝU®F•«ÊÝ×ë“jE ² ô¥§àÍÀ›¡ôÔ®Ž³3ÜûˆìF›ò½OÉnÔà»Ô®!V²UKó€dFÛ9ˆe§-;5Q°ß — Éî¹½°¾ ÞÑ@éZµ²šr\&ìT*ðª¡ç9éTˆa±²g¿×ÃÞ€Ç ËË–sCS¦÷QÀö+Ú¯h¯‚”ûÔRjµ ŠìŽouëÅåK™TùRé»Î^ÿàüÙ¯k ¨vý…À&¢õ×ÐL©m 5L¬uvgà@°z©Où¾aµù¾žì µÆ÷Ë2}w£õõ™¾&{Có'þÀØm?Ù‰ƒ7™ÁÛ¤¼rA Ù4^¸pé‘ÊòÙ•b00qçô­›GÑþøCÿ«m¾~õß}–34RSʳ”ï—g- ¥-ö¯.ÝižxõÍÇÜxØ¥óÅH[é|z`tc‘Ä«¬¤ƒ·R™\͹u›7UZÐ6—ÝÔ²ÎÀºÞ˜Òâ•òÙ3Xž<þÃëßp¿q5®ˆànj«Þ˜öó»¬MÉÞÐn—Xwøƒ±OÉŽuû›Þd/Ï|Û>Ì™©øh> 5úM‚üz™Þ˜©÷þ €Òì«›>ýãwýJ¹œ]ÿõ;ñùóßþ67jäûN¤í(²SÙ±(²V–+ ²›w²a{”¿¼T]\I Ø™¼}#ÙÍsjöQǪ êW6Ü‘q –ª(ëÿ§l ûÊÂ%“÷?”É ­‹Â@DúŽìèÃÉmº»3b;.mOvì~‹Óve™þ%»Q:Ð_x:ô î¾ïíõ¹´¥F}Cý=F¹ƒcÕâlueÎÍ7UØQ&WÒä‡~ÕdL®ïz¬Uª^y–taÃ{yðà¿"µþëO¿ù鯽NZ´fb"‘„~µX™2é7®œ| À¾ã67iõrT­‰(vGsŽ»ùÂf½”ä°/îÍ=Ÿ¨¸N!e¯ÚVMkK‹¥Å<ÊÀʤ°Fv;wÄ«‰xpt?€3OäÇg¼• éü!¬5É$y)u›®Ç>ªÏìØÏÞ/õ™Én´«úÌ–p÷®|N9ùþ%;ái+=HzÓõÕ~цÔò=?^Y™­¬Ì¹ù‰¸Žiø ð*åÙ§±®ë±¸pÁÉŽÛVdÖWOu^Š"Ë`ý+¯ÿæ‰+?˜keâ{“›•:Â÷wFnµ£­rcª+óWß|B€›ïû¡úOÞzѶƒ‚.#?ûÔïC„êÓõ2|Ë€Œ~øW‡zàæO×¼ì7¿z1 í=wüRkçiºzî>öüÍ3Ǧ¿"ÂÏžù‰ ÊÌœþö}7ÿmÙ?ô#W+×|•ËÙ¿_Y±ª·æ OÜ{Ô4Ï$‡ïíäÆ$‡ïí'‚%„ïí“Ýh¾¿]–Ùª7Æ`½/ê3;v=öK}¦ùm¨¨ÏÄÕ¹MŠofpÌ »²2`æ¹s™›ï±¾üwA#j‹`£`[TfቿøøïøÎ;ÿyÌrpÿbõfs´–uòÊÇÎ\ÿÐÅùû®-ßþÚ¥t3«¹ãÓ§ŸüãåÙKÏøøÕÊ'Ço|¥RÞ&ßÛ~ö–Û'î=Šu#úÚ9¥¸Ôf"Xbë3-( õ™¸ÈŽmë3uç¾c×còý{óýì ÷ï- Äîß7Dò¶ óÙ/¼ú5Ôë?À¬·`.œûZõ쳩ì¨ù9A€h²Ö,€Š¿‡H&‡OG‘Š4Ó{ÿãùW¾`âÐ=ëóŽ·óÔ§!ÈÄ?·éÉç~ã–«ô3–%ϼõ³cïæ,ë{öû~rSÿ~¶úCX7xÖ( þ=®¬Çžû÷ƒ|{èßc${C›úwFsýì ÷ï»Ú©”dÿÞZtLìþ½MóÞÜä Ä37*ŹJq@4{¿<¿ºà¿mÞqìšc×nš|mzÏ)óóåÒ¥'~§zîïqÇ!¨çÖ¿ÆìS¿?ûÔ§×^nëS¹áý^;ýä©oúëß~߃»¿ø³`$?;’Ÿð™/ýÉŸ=ú_žý…ÙOž,ýó‹Ñ?ÝôÈ=÷ï1¦øöÖ¿ÇÑÞ+ÿÞ ²c ÿ¾»<÷Äú÷¶¡&п· ¯7‘¼{oÞõÎÞ&g*]xå+¾·ÞÆ^)βðâO§2 í—çH„tŠI3GÌ©¡㯈€a¨fn DfæçLízð—׎7?>ûägš€Fá"ãüùO¦ZZЈN»úæã¥Ù×@tìCÿ €OüÈOxñÕ9æžúˆJ»ét†¯VÑ䪋U=53³wvvB©ÐR‘e…–YVÄ,é…©=§£êÕ¨z5“*ˆ@ºx>Zz“ÞF¹Y›5 ÇÂÿwΙptê‰Ã{Ÿ´ô2‡+&N²4ûú辛ד3j ¿öŸ¿999wôЛM¾ÁÕ5¾waZ^wøÞµQ¨Ih¡‰K ¾·8F |1|}øbåû†âÉVÚ-Ùw8÷¹'ÿ b*-é´ M.‚êâJí&Û m+²¬H©È²¢ß,U÷ØÑÞ3g'kÞ@ͨy9Ò¶ À Âhét´tšD“‰6 3€X\½üèКséÅ=ù+—O? Æöß¼Á³¿ýöþ·_üùßÞ¸Få×V°·È9Ø .ð½ksP;Í÷.¹îß»fÛ2|o}FLoù{oOøÞ ²ÅÅ÷é[ÑWN=¶ÍcZ!û† "’z&Ͻúès¯?šIe2i„®çùAmi¾tt¾xd¾xtaéèõÅ[‚šx5"Y÷2eúQA@´üV´ü‚8ªj«*†Lì9¯#þêÿð^~æ4(N…X7áº!&½Ó;ÙX_Ú^=Qã{—ÉnÔ ¾wŸìF”¿·­` ¾wñŠhï2ß;Gv£Øüû¶ðjìXW–™êiíGf³1èùSpS. V:ïÕüRñ:‘Ö¨ŽÍ\¼Ðäà axã  ÏÊ8ž5îÂ[R8m^‡€Hœ×¾ðõçž8í…Qdå=lFö/0ow¿ºˆ™˜Ú¼:Ç÷®Ùö†:Á÷žÝ(^¾÷ŠìFíNwl´Evï¾Ñ5¾wšìF±ð}ßñˆÈåÞxWËd`*$óOý‘!z;&ì}w|˜‰ô›’I»n&c¥ó*-Î{ËK¡÷ÖÔðGo1¹» ¶á•s§ÄUdR6‘á;ñš©÷¯D‘iDZª—¯/ͽñÚÁ÷~OÊM¡5zàþO±Þ…Ùu‚ïÝ'»Q¼|ï!Ùââ{oÉŽöáÀü`×J4]«Ô¾w‡ìF±ð}ÿmrùD¬NSP¾ô2Ì:'Ën îf¨41¿pîñ.<y  õõBAA§Yí‘IË>¼®’aÛ—¯œ¢µÅT@˜„ L )œ»zéÍË×—Lí1)¾ƒ{?E›$ô2 ³N«¹ïàUVDàd[»ñò½Wd7Š‹ï='»Qû|ï9Ù±ÛMLÛ«Ó[œº90¯s[œºIö†ÚßßtéGxÏÛ&½ÛÎQ¨•õÊçéñòŽVEô+'7£­…¡µ/„;Çî2ê–ïͯäÐè,I´hgÚ¼(“'N"ÐÂrÏô-€TýЮ£­îW‹>=W4x8¨-ý‡?åü¹ôÊÊ@~ȨZÍÔjéZ-ýÁ>V:íkÍ¿þÙM>½J@ÊÍ·vIbÙßÔ[²7Ôæþ¦„½¡–# ’@vÄâÜê¨ïò(Ôù÷žqø÷ïù(D.½þwæf{”žtõÿÏM©ý‡iò2TËP-ƒZZjé·ËîDF#"b¦×–_ȉ¢…ÕU‹Õޜ˂0dÜ/Â"»|À.°Bb&fziö͵2¼)Þ\-†DØ?¤ö YÉÊÛ_üßúCŸùÂgþü‘ß CU©d‚Àûž{žÀ¬¯_/”F·xÛm\jRíû÷„íù÷¤‘­ú÷„ÛÏPmAvî@°z9X½ï§ž ¹Ž}„S¯ÈnÔþü¦Âì9C‡Û$;€Õ‹ß²"[¢!Y ]íeB¯f9 ´ªÑÜ…åK¤13[ÌŠXÍûsÅ`ΕÊ I°j[Y"!` ¨®ª 4V®“²…4˜÷¤§ ŽÀ"k¶àåÜ{–«³ãY7ˆ,?TA¤‚Ð ´ò+«ÅÓßðK³Ï?}๧GŸfø{¾ÿ"$|â‰:u<öVKî—¿oÃ[ðʬގóùÌüÕ])9d7jm~SÉn´ÛNÉ!;b‡;:À÷žÝ(F¾÷–ìFmò}hâpqîÌÌ©/ µCvµË/‰òç—Ï ØC–b†&H.ªæ‚ê*;К‹¥Kl <ƒ¤@ »šò¥|4¿_‰ÁQi!Ñ YµÒo^y 08œóç&ÒãLX*®*è|f|„CÅd:.É”øA@¤µò,onzÿB>;{òÅðóŸ¿ïü›þù·‚ý‡ÓµZza´“«m9év®CC­ñ=id7Ú-ßKv£æùž(²#Þ²LC1ÖgzHv£Xê3I »Q›õ™ÊâiH[žÀÂS²21³åó !€I …DOÖ–§½¥ioQ3‰Tý_b"‹È _f†Å£E"a†ìó™À VP ÊJ³J3ai¥dAOgÓ–tú–zWκra}jf={ÆcÖ—fßKD ÂôõÂ{fWŽeÝ|`òÆ[¨•—‹moh·õ™d’ݨùúLÂÉnÔL}&idG‡àŽ˜øÞs²µÉ÷äݨe¾.=²÷ðm™á›/¼òÕ–_}á©?5`åµ-LÚ®úÁ2i1´e"ð]c·Gx;fVLLäVJ¹5›p6z•!J„!u³9Æ)¸›/DÙ³‹—-¤öd,ˆ­$²ÒÇëqcÆxó9C†¢µºzJóÒË€C´š]º)3ã#çßñ6v¹q©Ea00ýa4Ç÷$“ݨ¾÷Ù¶ç{ÉŽx»enT;ý3 !{C­õÏ$ì 5àÞd Íú:»Žqh-­÷F=òן"HÀËÃÿMóóù§ÿŒ¤Þ“ ƒ™Ù ìùÓù1…ˆE§ÕA4#‚h}eñ5fbz])§b[$élEQÄ f0ë#ú¸­òˆE_YzJN°‘¡›‰|&¹}èVHD¢ÓÎD/Ùnä”z°~é™»ªú)›,Å´@ ÚÛwì£w’Y_;fÞÅV“=v¥Zi¦êÄŠˆ‰™È,0¨Ü·j¡I>ÙÚ¦¦ÈÞЦý3É$;: w´Ê÷¤‘Ýh·|O,Ùšçû†Ôó/À¡»þÞ6Oyôo>FñĆ·t‘”€p÷¾ïVÅâüò™Î[14K¤ •#1ˆ××–^'f@¢àª—+ì\…Y3X˜åf¹D_[f½¥œQˆ†„óNV{ozãé7^;I5ÿî‡kA ´Å€XŠM$"BÐÓÃTýàg׿µÖྲpi âLÄÌj=ÜXN†êD`¾÷Ùæ}=âû‘ìFøžX²£ pÇîùžL²5Ï÷„“½¡[à7í9÷Ò—¾{cÉz=ö¹OÕ±N‹¿|ÉyèˆÀÈ–mEâäªá¸}7‹fhm«,C“虥7HY @,ƒ:T$³^– ûrå“Ê, ÷\/œ›¿´0ÎW2DBn‰7[·Äá d&“‘W/| ¾¯Iß5}Ø R6Ka-eLO ]yàñαƒ+_^ÜünñÊkÛ°Œ1'Zw7ÿö8@­£ÆÀúƒÜØßwd7ÚÀ÷þ%»QƒïI&;ºwì†ïI&»Q3|ï²mÃ÷­ºϽô?¾ûãÛùñÿï×AR'»ŽÀ0X'X²K©\U,&XJ˜È"ÙÊ%èÙÂ)R–A2 ³(¦%?KÀD® +¼Éš¥æb‰(ˆÂ™ ·BЙ’9£¸Í¶†¡µ@Ϥ†´È‰ _ŠHkè»ö&HÍ7ææÝÔmþø6ÞQîûé/Ëåì’ûý®ŸyÖPÄdÚò‰7À}pló6… bV›Þ»žï}Jö† â£(B?“ÝÈ_xÆ/]TîtbÉŽN´Bnª&û#“Ov4ÑÙ_dÇÖ-’Ûô³ï½eiæôòÌ鑽·lsäǺòæ£þÜE0™Í®"f"‚(²P)‚EÄ‹À„k~Åš”=U‘¬Ñb1A‘:e1ålO1aœxŒi¥¦À`…¡(bÂí‘vtäÀñcŒZ”"½b¥O^~B”­¡Ié©ì0a¤ °‰hQ ?ÙãQj,{ðóvª¥™Q\üõ…¹Òùâ­«KWV—®R£ïfí›Ñý·gÇ2£éÜH:7œÊßxq¢00«²¬6I>0j´H–—΢ŸÉÀ8Pºú-í‡ÿ@¯Ï¥]ù¥‹”ìôH“ýï=Q—àŽ&øÞd7Ú†ï}Gv£ù¾ãN¥åko™ºu›ÃžýòXnznîD=+†‰Ìu:¥´e§‹ µëyŠI1˜iZ-•k>Ãf†+Ú"aE”³ýH§‚(åÚ>1ßg²óa`11A1…:•#ª¦<&‘Å)†&èâõgY ”¥I¤§-òÂH°-")[ƒ9=D"™-¬çÎçÎfÏ­.]ÙË(P"jU4ïdïÍïÏ Oe‡÷f‡&³C“n~bÇ …>LwÐÖd7Jç-]øFä&ŽýÛd­\ù–ròNfd·[œ’&SÉîû{»ÚßÔ}u©,ÓÐVõ™>"{C7Ögú”ì 5ê3MîA=ó½w#Vxö+& &(-ää¥G×j22;’Äó3#¾dìp‚1Ye‚"YkdóIp­”}f0 3ˆ…˜ªþ ²n‰”\+Š(ð-& ‘€Q‰$Ÿ)R¢YŽDï‘«‹¯kÈ ¶"‘ˆt„ê{:ÔZQ)í(ˆ\-€Pz€h8ÓM¹ÔÊÄ|ïÑG‚À9~ûf?ÙòuŽDÊršy°©ÆX–B/FlÇ¥õuöžŒØŽKêì-çÏtAÝsîF›ú÷~$;6óïÁê%ÎÀM½<­6dü{áÒ7íÌp3;•F¦n]ºvjéÚ›#SÇ?,>ûUïêY³2­.™vòÅâES‹¾ þ$é Ò“¤÷ÛÁ!«E¶"òCÇœ0J¹–¯ÆŒ3(ïrÙÇpP³THb1H±IžAÊò )‰R–”Œ„¡R!A×ÏLZÒŠqü1¦%…‘hòúÒIb!…yfaaƒôªxž_ñwﺺrÙD ¬•&»*;pppbòǾ÷³Go˜˜ˆ"ëjé;Z»ÈQàPvª™7êìíDô\VP[‹(H‚n\AÝm>A7Õ©MLÛhÃþ¦>%»ÑúýMýnÛ”RX[õjFë*žýzñÙz{åhuqtñ줷<å-MÕ¾sâ¶[Å?¦ýwÌM°/UÙç”Q÷ôX­ Tjƒ,õ›¦‘Ò"XÂûÁ¬Z2HeR%›ÕÁ=Ahl°ÅdÌ×Pºè0ùÞ "²sÅ“–VPL¤(Œ²:rf3ÚÊèÐ}pÏ]¤uYÓykzHí²§R+Óc#ÙÑ4oÝ;õéɽ°¬ð‘Óÿò™«¿Ðµ ýjè×°{²››}:ÂiÓÞ˜nŽØŽK[õÆ$vþj·Ë2 ¸›?6û”ì /?–/§óûìjLóÁVàþóCå(“P4ë%"h©,Ä454C®.¼ÂlBÒ‰Yˆ)…Ìb˜E‘(ƪ?h3Y›É&¨ÌÊlÃAÀ,LÄ À&³³Éx{Æ`)½VHE`C`×{.^'ÀvW´¢‹‹–@4ÁÌT=UÉhÑýž¡[E/EZ§2·ˆÈrxFD :ëÔšoÊ?ø+?ñð¿úê×Vþþ\ТaýŠI²i2Žf«Þ˜X"‚»¦í»û¨>³c×cë3=pîFîä¥v-X½ÜïdÇÚGT6ëv“©õuö&# HG+/>6X]Ëß"Ä EŠj%ÓˆµP"컃$Tÿ €5Â"b¡¡TiÀYy{jG-ÏÚ²´R‘,-YˆëM•T «6‘Ť6Áb±H‘"r6ƒ³…ÐbÂÍW†à2e²%ZÑ`.}DépÕrLÐ *Sñ^=ûÿà§?ÿ__^ü×7í;uß¡?jáÚ^€ådÚ$;úÊ¿ïØÏÞ/þ½™~öú÷ž9wS1@ìÜ|.¨QéÜ|.hÓÔfü;GÒÑÙç>7RÑn:DZDºZT™Ö^ÛnÊЀ0ôõ¥×Mx€(Í,#ah­-¥*2¾ž™„‰H~~Ä÷–¢ª ƒŠu"¦!Ï_ò™Á`¥¥nÞ-04 Â!!\ó š$ë+1D@_ósËD~fUStDÝ)‘¶1Ñᢓ¼ÑbrDÁ/üîÊ¡CµLºR,ä˜õÿõ—ŸoòµUAi§Ü&ŸÒL?{òý{ó;•îßwµS)Qþ½gÎݨkóù:¤õuö.Ï׎Q[õÆ4ãßµ²…XËiÊÞûPöž‡}©è”ŠÒ–N)íX:¥Lÿw#¡klä m )!2¦¿nÆ…Ö îB•*Â*1´YeµE9b9P °Xï©V•‹@,±EÊ$G6‚$m¢°´<†£aä°X,6kKQZÑ$Ãa±"×±$TˆmF¹Öw‹ ùaèô¥?øôo ˜Ü;{ðà¥&/¬_[5ßÄKv$Þ¿ïjj’ýûn÷ &Ê¿÷ƹoXDíô|¾iÓÔ¾óï;v=6ãßí vöÙ¿f‘[îùxñù/°Ž´‰ * ú¼~J‘0A)FvÎ¹Ì «þsQŠ”é“a!"ÅÓpÍ_ö³0C‘")ɆV2Rñ–£”ÈÇ ­ Y SD*_ó<‘"+ ÅÑÍaÞ Âw%‚>¤+kD´ÖÀŒ;à¹ù²@^<÷ÖÝ{~ lÆ–‘;4ñ@³Ýe~µ"'kþw±Û=¨‰õï- $п·œ.Ѐ{o-|·[!±Y{L‡æ7uT[õÆÄ>¿©£j¦Ÿ½™,zÏÔ±¥+'¯¿ü¥¼;Llº\„€3µÇ Ñ傾ÊõåO&¢cå]«\ó‡®,¥-Ök‹„9b(1€“FY%°é 13)&ß±Éfs˜Y1 ËH9,й¾-, E L&­SŠ©äÔ˜0ÆG!Ñ×RÃТS…×.œ¹Z^¦(q…¶•b rîøêåÊ—ŸÏíß6WÌ«¬èÐÈÉ 4ÿ»h!] ™ý‘­EÇ$­?²ÜÓ  ·-’Ý.ËlÕøØ_õ™í»û¥>Óü´¼ë3¡åQu Á‰•GN­~ëÍò·ê³¯Óþx,’c¡@À"û†m*9¶…M$P2…"&½§Zµ$²Õ+6€ §hfЂ¤\Ed2߉…‰lR–ŠC1,‚¥`1±‹¡ùÁ@JÑPvÅR°KHˆÞ¼üøÉË¿õê†*ßwÓѵÀwAã_hˆž}ò?ou)ÌÔl&»QÒê3í„‚%§>Ó~"˜3úþž—hºêÜ·oiïÿÞL?{ÿ'ÖÂïvêŽþ]++½|ýüüKó…óËrVÁ ¼#UÏÓƒÞ£5 ˆˆD`Á`Z•}q#mö%)ÁüËD¾­jŽå¥,åÕñÍLL¨ÛxBR(gì  µH2&öjl–^ëÎ]ÀæTDR‘JycÌ™è=¬éÚÒ‰•êÜ<±Dd yBz85H„´­Q ‚PuF2D2ñÀ'6½^¥PÊÍ7ÙÉnÔf"Xrü{ûqIðï1f=öv‹S÷œ{3›•’ïߛߩÔ(»'з6ázÿþíG~í›óSÏÍ}ÕÌ8‚6‰XDBÐD ÛB}<Š €4H@ÈÔ€"!ÒdiUoyÔʶ4[š•° ¶D-æÜ¹wnЭÏ]˜t^Û:7”50'ð"Òóƒ®bRŰ)†RÄL¬bv˜˜if9\*¼5·rŠ-a¥¡D”ˆ%ZYïÛ{”êCVÅ$Ì µøÒV×§>2ÛÍïêªÆ’õ˜ÿWooý{ì)¾½òïáÒ³]ZPÝÕ6ÔÄ®¯¶¶5iK¬­‘½¡õë«}õÿ00 3Ä 1Ò%²€Ì@ HLãªY‹`Y ›È,^‹dñ¼Å°l²!CÕóÅ„^¯§Ó§…Í_€"0&K•%+§•’M¢db¥²¨²šD³Œ—ÊA“sDðMÐA^WüBñ£thR€ENûÙ%©J$ú¶#:\ ㈈hK‘ˆ6 ï#ÿƒ פ¶ºdÆöívªj¼)¾=\_=¢½'ë«Ëgïr‹d¸ô,ºS–ÙmÀ@2ë3-§ $j‰µM²xæñ?¾rá™s'¾xõÒs¼áòŒés$Õ"; ¡@)ß xÀWÕÐY£1©µ’‹"‘‰, ¾Ç¬ Â$ 0 fcàQʤ‰™ÝQDôÖðÔò@f9—ñ349ìÕ„Š|ª~ZÃU/ê”ÖåŠúÕ”DŽŽ!G"ÏRÂ8¥¢µÒZé]'G¨YŠæ|ÌF¾x;+Í fQz³\ßíU-œànù×±^½ªÏtbøF÷ë3¼ÑÍúŒ!»5rÇ{ËÑ1âL,|û¹1Iðïíý‘Ï}ÊT'ˆA ¿2CŒTn’H‚ÂŒ1Úuæ2üª=äé½vhÀ&LºeR$ cÌEÕ{‡ª~ÑgÓÌÎLŠMйityÑe+k–SE¡¥® ŒGJk¥5‹VpËé(¤è¦Å%M&V*šĺ…@H …ŸÃ@;!I$€ ^n‘à9åZ5= F^¤%‚°MÚ¡¡#\8a«N0ðö`&"jmžj‡†otÙ¿wt¬R×ü{wf*uÁ¿7ÈŽN÷¹· –¾Ç•Ö[¾·Cöoþõ¯™~4ï—g‰Á¤>†ˆ@lâÚåNøù0¬7¶£>‚CALÂTw4¦x¸ê+ 2J™¾0@ 2IE{àìØ,ø§‹ÅÚøP¶êDJkÖZIÖK…¤CDGE‡çŠ@4^,/’AFKå" PÄç(|±B ‚\ñ´`&½ê‹"G컢héj¹ºwôžëKÏL&»xêÀ1ìyðÍ5©–ÂÚȽݪ£c•ºÆ÷. Ìëß»9-¯£|_Ovt´,Ó~Ü£;ÐóMŒY=¬Ï´Y9tÛC?tñä·ê2D$°Sze‘D3[k–˜èvñöJD„”Ö&¹Öêäp}ZèÚôìzî#Ü0dk«@×[e@ÀlÞ-§rÚ©dìÀ¥ª ¥ŠEbF.•›Jž#ÉÙ¶XeiÀ£üªJ¥¼HX4K.Äk‘,ƒÓžti–’[ÕTGó· V¹xî%O—+•J CGª+ó&T dG·ê3Ý…Ú…úLX¾  ;„é\}fÙÑ9¸ÇäÛC¾ÇžâÛ+¾×Šç´ù¿úÁãÝtüᛎ=|ù䣖¯3)ah3ø™ˆnÓ¯íݧ"1]ˆ¦KHXˆ!lò Ù l§ýTÚ¤M#c/*§’²+)§’q¸>“˜ ŠKéf†ç©¼= ¦ÕZX­EyΤ2,JDI.HÛ‘ŠX[ ®3T­ #„BBÌ`“RÆÂS$)‹èLÊ!¦Ñ”3jYÃ$z9<Hƶ÷¸z0Í«‘À9¼pñÕÒÒ¿Zܳï¶.cwF¡všïÝrÝQ¾wÈu'ø~#ÙÑ!¸ÇÑÞ¾w(Ÿ½û|ouƒ{xÿ±‡†|5?ÿ&“2ºÇáO ¬—m "¡ÀâL¤ëñTëD€²3¬lf‹ jŽeJ;YOØÔmŒ ÙÏNg"Ç/,© ¤°â¶MІ­ £ÙœMÊK³D¬µ!X©T…õ­`F¸†`0„ÈóQÊ]Y ’#ú˜c@-99ÍD ŽŠ™:ð=¿ádK —œÌ­,\Z]¼\Z¼<8Úì–ÝrÝ9¾w“ìFâ{÷Én/ß7%;:QsïÜðn¶HvzòF×êﱓ½~Øg¿D˜qÁ©ÿW”²ˆëõt"(3ôvz ÄÌJµRY&!À,±šïë)õLR€{…scÃÎÒbuÿ8_+3±ªTµÅÊÌ»œɺQ¤‡œtÚ=uSg®ÌA¸d­"‡f¡õx¡,Bš­TáŬzyÂT!½NÑ´w3ñ°´ð5wPBѵSRχÔYÇ«¥&ïû@îà˜¹—O~‹M ‰1›1±‡ïÝê¢u“ì Å^ï>ÙŠ·þÞ+²7Ký}+²#vçÞѱJ]óï]˜©ÔÿÞ1²…`V1U™J#ƒûÜÑRéÚÚìk³ÑÔâá[*i©tŽ•Ã–ÃB|#’êEyXD.´ÅDçG‡#ÛâÕ²]ªy¶Rd›ÕÛJMréÁ#7 ¯®T•EV–aQ¨#0-WË!´ YT°3cK• Y«d¹+^Y󪦨Jó¢J.C¢ oS’‰ˆ²Âð"WÓØH©¦÷|è—@pÇ×!?vppô¦ÁÑ¥Å+kcP¥8W)ÎVK n~|ýEë Ù·ï!Ù«ï9Ù‡߆ìˆ×¹wg`^§ý{7§åuÔ¿w–ì@OV樳lpÁ³ IDAT•H,•ž™}žõ|GË"Æp­jŠ…K„YêÅw†‚0„Þn'“`¦—ÇÆHQ˜²éúLÕ“•ðB©ÚµRSS£)µ¶Çle[z:Å–xȧ2/^?ïQFG¯-D‘Ž´-VÑ¡æ@à!ôElå‡é0BII å¤¶ÃHªiŠ×MGjA "‡þáï7s}æ.¼LDÄŠÈŒúVÓ7÷Šì Åâß{Kö†Ú÷ïI {C-û÷íÉŽ{×F¡vÔ¿wyjçü{‡ÈÀ»zÖ|3^Y ‘"(e‹E‚ÁÁƒÙ½ÙÉ<¶3Æû–íêˆEk7»™dmqµÞK‰µåVº–Og£`53ÀÄ+ + %`íGUQ6[c#Ùr¡ÊU¸yyò$ %Ò$"’²mH©ìYUlxµ*Ðã«ÕˆP„jBÇŠxÕ"h 9Á–†DºJ‘/aêh$;H"ŽÍÕ¥kÙý[–\ÊMf‡&ÝüxueÞ¬ÌþJàUscwï6™ FµïßBv´íßEv´êßw$;âÊ–éòëEÐôdÂu'"$;Göâs_°§º›«Yÿ¦ójžïI&»Q“|O>Ù¶çû®ÈŽvàÞs²µÏ÷žÛö†Úá{çÈîø•Ê3_ã5÷ ¹÷>äL¬ÇœsÞPáHL{«W½Ò5cáë?Q-•Ëx«r9ƒÃâùùAIè3 ì”é•<;>U¼¾L^Õ °Öfƒ”²,;‚ŠÄ©‰úÿ¹»ï8¹êzü¯Ï93³3³}gS7»)@H€P‰RÄ€&(‚»‚‚ˆÆzQ¼¶kÃrÕû‹Šb±£r­X@B±\)’¨„Nê¦n¶—©çœÏï÷îÉì”3§ŸÏçûºóð±åÌìlO>÷}>Ÿ÷[-éyM?+ Z¶Oë£Ä³¬À ÚÊ##-yÍà084ñ|qZmÓÔDÞ`07_ÎNäoާ¦Š“Ùb±=¥r…kW”™~Z¬E/Nõ¬»ÎúOÌZv]+rà ôJ,®(1E<4ÜaÏwñe§4ô]Ù)õ|w*;\o…Dv3®÷GŠ#»û#ƒ“]ÕŠÓßÃÀ®3ðÖÓÎeœ+†Á¸Á8 F­Ô¹Ns6óEqÎ'<À8gàí ŸCSÕÊ€‰CÛ´-<•ägÚŽYùF#sVlèb±”dJPb£šÞW ƸªL)Ošn0^à×ôRjºØ™-d™Â5ž4J†Ã`:WzËkÍiMSz“ªt@1ŒStÎG§[Ró8p(Ý–eq­ð$À[âj^+N´Ö¶nžœ×õ¢Ï'öýºÔûr徦Çt-¯— z©8´ã.C׺–7s¯X¼)­¨qEÍ<ºqj3Éöå%e§˜¾ñòÊN1}çùp¾l‡Y–) ? žið|ae7c³>#²ìfÖg‚•}nT­ÄøL5†ê0æÇe•™£Ÿ2pfè ÔlÑç ÆÄþGÇ¢ÓÁ9£‘Ü?ð´/<õèÅ0v¬8×üÑ…ìÄäý×ǸVÐS Su®(LM-JÆâZr-Š¥i¨-ŸR %ñX‘¡Ä¹nÄ’†RbšÆ5 E <Ý‘7Œ¬†¬Î'u>eðædA+ñëqCㆆcNZ?’7h°çéDá@¡•ƒôÊ/Ò;ÉMƒJ)ÀÈÞû–YºŽþ£7º÷Ϻ–®K¤ZCøÇá{¨DCue7Cõmz ÑÚ'¯ìf û~  iÉ+]ÆÚ÷³òKÁF;gþñsŒ¡4²ÓÈO( %PbjÌÈ"oWôq½¨jg,«cF‰Ç•¼®†Ît¥ ©|¢¨q£Äy‘ëJ«®¨…˜ZÈ–% ÄñL1¾)°‚npMÏ 88¿­ƒÊJFBÕÍ-sÚÈþÇÓmó´bnæÍ1 Õ±<7¾G/ ¶Í?q|ß_ÕX|þq/QãMÁý#4Éöå“ûï× c]Ǽ"ê÷â)‰Ö>=; ÇY¼-´}EyH‰·Amu׿ènkߥbí»,²Sêù²ìå1Ô˜‹«†>6PUŸ™ý`¶4N$fê3“’­=ãf@Çâ3ÌêÍ,žl´³@1?Æ:¶¾ortˆ1×t ¬IQTE×b{ÙTKAÑ ®èLçÓݼÅЛ5LꆡkLoJO•’`¥’¡”˜~¸ÄÒI¦ÏÄ c±âd¬T,Äò †RÐõTCQ8›}˜ƒqE5¿ÆÚ?3vè©_q°öžçÎŒc:z²sùY ÙD²%{öw Î ÎtšajLk©¸‘Œé`Z‘OøT%—4cR×§4=¯©yƒg¡i‰qƒñáRK‘ñ‚‘(‰ mÕcŠ¡)†Á é«:[¤t¶cÁªÎãWeúNZ°tÍâ¥kú–‚ ¼õ9×¦Ûæ§Ûæ{Æ+©ö#{þ]ýç@u Î\-fÝK‹R^gwÝ¢@„TlqÚŸ€R¹«¦ï-ÛÍTû.£ì”rßE½<ùdk.Õ–MwP‡®(\Qgdã(Sž)œâÌ0ÀÁ ˜_™•}¦ï/Jùi–H¶ÐOÑ3§7¸nÀàˆe'Ôܤ65Ávç”\Ñ(âÍLNhúh±=¯ÅÆT¦)\IL` -»JÍ%ŽÃ#;–•ú5žw¼úüãp" ]1²àœc€‘Œ+ôhŠ)ÍÉXyÏÞ¾“Îgª:ðÄýå¿;ízìí¿ ÀÐλÂûC÷5wPåõ½úª¤¾×ÜøhúnŸøl+|—QvJ¹ïòÊN!ß=z D’½<ÙtÇTs§ÁzÌ,Þ+eW9S†÷=h0¶`õ%3-hæ°Îv-[ Ìv6˜Íü³®6ºú5FgFuŽálqdt¨ÓÈè\oA©XÀþbß6ðIÍ×yÞ@®ÔTb¬ÈHŒëŒi {‡¶Ü0 =ïN&ºM\O%X*®€<®²xŒ!³¦âì]ý…)ûŸü }Z¾Ÿ¶½Ëè{ͽ12ú^ooŒt¾[li7·½Ûô½ö U³þ®g §ìª¿Oü«šh—WvJ~|§V‹§ºÂŸ¯m?¥Dª˜H›Ò‰ÙÛjùxdàoëê=“¶Àç'÷¦ö'[©˜ÈŒ·/ÒŠ9ÆX¼)]þÊ-½§³…Ïá£óãSp(Œ±ö¦Ø´ž›2K›{â¼å0ß?¢•||?Ò-F±)vÒìÀ~޵0ÎJÏÚê‰a‰Îã§b©¬kÒJ-͋DzÇJ¼-Á9xLeçàH-l®ê!ÓÖ½tjäÀäð¾âÄS˜{Õzÿ»˜±ØõΈm¿b½ë1„Û~¥q‹öôû%øºGã♵Úô€žÝ/¯ìª‡†y¶;ˆP5fášËáwà€2ÙÚ=Ñ6Šìæâ}xàÿ8XgßÙ´„çLmë9`㦠öô¦•ò`,–HU¿æð' ‹ÖñÔüD¬´ Õ˜ß\4´ÑR!«i\STJŸ¾&¯»t h¼¨Ø2ýÌ>~Â2¶¦À¹Îuë%Cß¾ûþBë­ðı{L{ƈ ‡SnïZÃaþ{é‹Wž9uäÑá;«ï Ê»~¯YÖïvö³K±~· Õf ¾.yÅ¡ÔtO¬¹Wêû«Té8î ÿ{h)¯³Ñÿ=¸L´Ík_0Ú±È`ÊÐÞ¿q°Î¥çšÖLcm=gr6ã»^*0°X®3p®snpCgÌ`àJçñ€1ûà¨s›tx×Ý™Å+Ú~xç#Õß•Èw;‡•Ä÷ÝþI%Á}wÚ`ÀŽïµ{Ë”×Ùmžo0uö0çkû˜šwPMÜC±íKŽì¸ ÀÊ®“”òƒNGOBññýß›Yܱä\ænññÃ; ®sÃà†Î¹Þܱ@þþë8T0µ¤³|‰ï\¹°PÔxŸQ( NíÍ´¶<>ú”ª§Oì=W‰±î\¶¤$žÚžUb SE€ýëÐN”pú²•G:QäÀþBÀÍ_ÀFãœg.¾¥æoQ^g?²ç_æ-=¥ú2ÂÝ¢ÿLäqt Õ÷Û~ÅÅTGlûw­cÐhD_•{ÅTI÷GVßA h~S ©·7Æ4]Š%<É>ï˜ £‹‡»z†2½e›dTºõÚÞóv|ÛBM+´ïÖ§Ÿ8¤è 3§%Ô™×k,;±gi¾X,”Šùb‰µ-càÍË×eÎ~ÿ’NfGvó–žÂuh`{õ•‚¯ß6sýî®»€€ëwײ£Ñú½rå^ooŒ\ëw‹½1­ßíìz tĶ/1e¯ùÝyC{¨Ç¤¡•-ŒÅ?¯yÅù™³ßßÓɌΓgŽ)ÍMï 똢ì{òÏ’-]ÉæÎ¦æŽ©ÃÿÐÙ÷ÅÜd1?UÊOÕü-Z»û56=z ú[‘ûîo‹ö¨|¢?{$¾!;¥Âw—cö X ÞKwqn±zï#BýÝ‘ìÖ!Ü3Ë×s©¶Š ö=ñgKVŸKÖS­‡ÿï› ÎzWùeÏ>üKǞѠ)öþ§þ  çøsfúœjL179Óí—Í ûŽ%Ò¯›8 Õ6¯úÅ£ª¿4|#äú{ “7¬¿'»³þîþܦ8%x}c\¿»NäëweÇœõ{¢Ì§ÿÖì_¯@"ÕΘJÿ°ˆ¢¨æ¤¤ê:{"ÕšH¶Äg{™Њ9­˜×Kó+©¶yLQéÞoEÌõ{˜KøàÆ*…¹~z¦Rhë÷dGÙúÝÓ¡||÷¥#˜¾ûÕî1Bßý•B¾î½¯bÖè¡gR”Øâ•gѧŒ1‹a¤TOÇì(¥zYtÜ™Œ©‡ž}Èúj¼©9Þ”._¶ë¥‚®u­dèZ²¥KQãÅÜdõÍe{8¾=0/ßÙ–‚ïáÈN!ß½v\‰Öw{=F뻿|#_¿û›ÌòõÙs_ùi$©ùi²¥ Œå§G«Ÿ¾ÿÉ¿(ŠÚ{ºTk†1VsYm†)êäà¿Q_öòÄ©X"Y1}ÉеXSš1ÅÜÿSžîÂ)Á‡3 U„ý3~%PßÔâ©,c&*ß}ïâ•ïA´hß÷ –íf2ËÏcæýÕ¡½*j|þòþòk¨óLõs™¢²Ùb‹Å(SƒÿRÅéX%5Þ¤Æj,nþ÷&–H1%fèZÍëƒö=Ì!×úòë€|_vŠ?½E¨Ïø’ð}nøF˜¾*;€e–`xןFö?¡¨±î¾Ê~ë-à˾ú¡ì_|Vvø‹;Bñ=œyAûæ(Ôà|IöÙzËÔÈ~E÷öTŸ_¹RAU‹‚š¸ƒvÍ3¥˜ŸªØSïú€â—ïÑÊNñÅ÷Èe§xô=rÙá;îØ÷0G¡ç{øC®ƒð=´5;ͳナoéêA½þµê2ÃÛE­Ùu@"Ù2ºç>­˜/¿ƒÚ¹è8EUÇwúúKXÅ»ï"ÈNñè» ²S\û.‚ìwæ{øC®ƒð=|Ù)þúr5&ŸSbñtû|ó+5}'[ÀX©0m~…)1¦Ô½«9¼ëîX"ÙµìüR![þõ0+3/¾‹#;ŵïBÉNqá» ²# Ü€ïáËNñ×÷¨d§øå{˜²sÃ`L‰Ç“ª¯øVõûœå»EŬÆÄ›ÒŒA+õ]QUE w¸õ]4Ù).|PvŠ#ßÅ‘Áá_}JvŠ_¾G+;Å»ï!¯ÙÁ˜®—”X¢üÜ¿™jß+ŽªÖcº¢ÎK¤E+æéÓ–®%LQ§Çú÷kØŠSßÅ”âÈwae§Øô](Ù(îðÉ÷he§x÷]Ù)^|[v€1‹7©±D½ *|%R3™®Y–©Ù] –H2ÆÌF1ŠSê×s‚‹}ßE–bÓwÁe§4ô]4Ù4îð컲S¼ø.Žìw¾‡/»Í˜¾ñæâ}jd¿¢¨mÝ}å[ôQãM`L׊}ÏLyìø.¾ì”†¾K!;ÅÂweG¸ÃƒïâÈNqç»h²Sœú.¬ìò´„ŸÝ:Y½ú¶î@%¦k¥dKSÔBv<Èw]7Ö¾Ë";ÅÂw‰d§Ôô]LÙîpå»h²Sœú.¦ìû¾ .;%³|=?²{ ÝSeªÊÊ î e§¨±8cÌÐ5ëþA§žïrÉN1}/'^:Ù)¾ +;BÃ}SvŠ}ßE–bÇw)d7“Y¾^Qc#{¶ºV¾r·);EQc´öª2C©ö]FÙ)æXò]RÙ)¦ïÙ=·CTÙ&î°í»È²Sìø.¾ìkßå’’Y¾žìÞÂÔScp(;EQÔX"͘Z¯³c8)÷]^Ù)í½çñ£OÿÒÊN1Ç6'k Î$¡â¾‹/;ÅÚwYd§Ôó]FÙ)™åëu½86p"ÙâBv c¬¼‹oT!ß=z d–ÝL"‘P,£~#^ÓÔ”H´öñÄÂHFlÛIظÃÒwYd§Ôó].Ù)Õ¾Ë+;¦(Ý+.Söm» ®d*‰D€b±ÐðJÁC՘Εo‚ä#>ÌR{$#¶m&ÜQÇw¹d§Tû.£ì”rߥ–¢Æ³‡ZyÄoŤ;AXóùJy]êN7Q…õ=ÜQ廌²SÊ}—Wv ù¾oÛ· »¥–³uö%§]…z-$%‰Yjg>_@©¾ƒ*©ï5·Çˆé{d¸£Ìwye§ïGÿd–BÅÐÖî•R`-¯³×k!)E*n¢Jê{½½1Òùn±ñQ@ߣÄ@<³67¶SÏî—Wv ÕC©6*o¨³ä´wH=b»úª¤¾×Ü#ïÖ»%ò½á–vÑ|÷éƒQÓ=©ŽRÏ_¥j̼.=ô·ðGlû’Š:»Ô¾WG:ß-6>Jä»ýìRønó°’P¾G‰»Y‘z¾vyê3wĶ/©yUFß-6>Jä{Ã-íRønÿ¤’à¾;:†*Žï‘á^Qg—Ô÷ê;¨é…g‡9bÛ—Xì‘Ë÷†[Ú¥ðÝæa%Á}wzUXß]4Ä÷hp¯yU:ß-öÆHä{Ã]²ønó°’à¾;:†*¬ïîº è»ëÖ1"øî{c$ò½á®G)|·¹Ÿ]|ßCÖw ôÝKß¡|÷Ø,rßÃÆ½á®G)|·¹Ÿ]pßTÙw ôÝuë¡|÷ÞLß}i÷­ïï–©)|·a}wqULß]·ŽÊwMÁLߣ%Þ¯^‘ûîc#ß}wû‡•DöÝé1Ð>ý² IDATT}wÝ]@4ß]ËNÄw_Ú=’ïˆn ïoß}÷½E{4¾Ol w§ÇPMß…"Þ]ƒ¡|÷Ø7Fß=ÊN‰Üwùv¯ØU‰&ˆþì‘øÐð°}ŸØ†ÐVîî ïf ï¥uŒ ¾ûÒLß}‘¡ïA´hß÷à&o„ì{ c•Âó}b´î^ZÇÄ3k)Ñxo &ˆï¾$Zß}”‰ïÁ ßÓ÷ g*…æ{óÂð}Vv„°r÷¥)Xä¾ûÕî1Zßýmä•ï¾ËN Ù÷ Ç*…ã{8ÓòBð=´Q¨Áú^&;‚ÆÝÇvúîo#ߨ|¢E{ø¾$;%4ߨ´ïaÎA Ô÷‡\åû\Ù(î¾7òÄ÷ Z´‡ï{pÃ7Âô=PÙ)!øæ(T¡¶À{L@¾‡,;Åß«dGp¸Ô¢=d߃¾¦ïAU Ç÷d§ê{øC®ò=Ìe»ß}DvŠŸ¾×’áèðÐ|z¬R8¾‡30/hßC“ïáËNñÝ÷Hd§øè{„²Süñ½Žì÷Æ*…à{8ó‚ö=ÌQ¨Áù²ìß}JvоG(;Åß#—âÕ÷ú²ÃwÜC˜¨ïaŽB Î÷ð‡\á{$²S|ô=ZÙ)¾ø¹ì¾ ";ŽÃ_ÜC…ïá¹Â÷ðe§øë{„²S|ñ]Ù)}DvŠkß…’âÆ÷F²ÃGÜ#rí»ïáËNñ×÷¨d§øå{ä²S<ú.Žì×¾ %;Å…ïÊNqæ» Ùáî‘ÈNñÑ÷¨d§øå{´²S¼û.ˆì×¾‹&;Å…ïÊNqä»°²SìúnOvø‚{„²S|ñ=ZÙ)Þ}AvŠß…’âÂw1e§8ò]XÙ)6}\vJcßmËï¸G.;Å£ï"ÈNñâ»8²SÜù. ìG¾‹,;Ŧï‚ËNi軲S¬|w";<â.ˆì×¾‹#;Åï¢ÉNqê»°²SLß­‰_vJCߥbá»D²SjûîPvÌà>±ÕÅ;JvŠ ßE“âÔw1e§Ø÷]pÙ)ä;ê/áe‘bá»D²Sjú.ì”JßË@]û¢«P8ˆÂA4-²ÿ4e§¨é#·ßÈíWÓ= /SvJ¼¥¯45Pšˆ·ôY_)²ì”dû²ÂÄîÂÄîdû²z×ø+û×?Ü¿tÞ™ÔÓMü°‘Ÿ*Lë¹I%ŸkšªÆxnJ)Lé‰4s÷âéÎcÒÇäÆvæÆv¦;)ÿ–\²SÒÇdGwdGw”ÿ.ÒÉNI¶/§¿i…‰ÝÉöå’ÊNI´ö•¦ö–¦ö&ZûP8ÀјY¹·õÖïÂÊN±¹~YvŠõ»ø²S¬×Ùó#“JvoF}xõü_¸xzu ^FÙ)ëwIe§ÐúÀÔ®ŸAZÙ)´~/ø5àxÙŽ£5wÛ¾ .;¥¡ïáË~ä»×ºx–µï²ÈN1}¯ >ÙK¥ØÊÝúíK?üªaý #Ý—ìjmÉ(é6CQ‘n3Z23óGr.8í‹ïzåÛœþ”ÈGôùÓw©e§´÷ž×ÜÜ `zz:ê÷â5æ/ââkÙ U¾K!;ÅÂ÷HÖìqCoxÍ1Ö`¢žïrÉN!ßQ¶„hÍ^*ÅJ¥XOÏ~‹k’]­|to¡|bðUô•¯¸ÿëîwôƒLßå]¶›é^±ÅCÓ£;¤–³uö–å¯ED#¶}ËÄ6‰Å/§Ïœú>w·Œ¥ïÉN©é{TÕ˜D#ÜW<ðókÖ×üVµï2ÊNiï]t ÉU-7ûÛWéºúÙÏ~Ä0¸apNÙ˜W®X±sÅŠ J¿¸sÛuÇ-¼XŒ_ûyÇ› 2Ë×kÓ¹±RË wøoÍÇ ±ðÿþïˆhĶo)»‰Ú¼è- ª¶BÖñ]:Ù)¾G%ûÐ-×$ Ãâ‚Þ-›“ZÁâÓ÷ì¡¿É+»òýÀ¿¾_—í/=å#œøñžžý==ûu]Õuõä®› ë×5nh }º~õ'–,X²d ·wï)™o”´xI‹»ø¡ûîOuk>cVcdŸïQqUVßkmqê{­}îU¾K*;Åô=Â;¨q®ÇyÝ•û¼ß~1­šKV¸cÖ÷±ÃO”¦öJ-;EÓ4±XÌûK½ð¹ß;ï9?üÈ¥öôì?þø' wÃP Céê©0]׸¡qCã¿ßö‰»»~Ñ¢ƒ‹|ýëo3tö¡MŽËšf5Fêú{E]^ßkn‘Ï÷úù^ç“Ãý3‚'žY;qä)=»?ÙÞúî„aÔ[¹§nûHZ+¦K…‡Ï»¢áKMOOÇ[ú:¬ŽjĶ_¡RûâSÞŸê3/=çëííãO|ɑɕϽhÇÈ s…Ö\¡õÎ~H׸¡sCŸ1ݘKüOîýÒéÏÙÆ9;xpñ;/z‹£ZQg—Ô÷šwPeôÝbã£L¾7ÚÒnß÷ú'Tg}—zÙNÛ{šîi›w|$óµ†7Œ¦Z+÷Â-W§K…t©ÐbY“¡˜Õ˜¨Flû•ò›¨¾ÔßGG»üû}èkË´ì0øÌ_é-¿Æ'¯~Õé«Úñ,ý(ñ:74^ÒÙ\Û¦_üô[wüÀþ­yU:ß-öÆÈå{Ã-írønï°’MßÕµ/ºªî7›Mü ô©æÞ ¾IqbVcoò1ÚÖß6ë¥]Ë>÷Õå_?ðµ7Îc¬›±.Æ:™ò̉3ûs›þùcd¿>~H›.å&´bVÓKGvÞ ÆæûbºÆþ'ÑR½=ÆÎ'ë¤C…bÊÈŽ-›XððãíÝ¿jq÷“™–]ÓÙô®=«µæµíÌ´îÊ´ìîjÞݙ޽g×’ü”Q˜4Î;móO¬¾ýï_qô-öÆÔ;ß$`îz¬y¾IÀØ<¬dqJ¶/å}9Œ“c¨sŽ8Õ »æ†ºµ—™5{s30»—-ÕuvZ¼S!>„úîµíZqÅþÇ;9Oô¬nQÔfÆš™ªïz$£¨5®©q#ÞôÄ)/žN¤²‰d>žÊ'RÅDJK$õxÊH¤HxPÓ´%'_¢¨s*Ô´x§…¼±ØøH‹ws¯¤£Ìo}@vBimkk™||Á —nx¿¦«7þà³Î8e 7˜a¨†Á ƒéºbÊ)«þ¢ëêýÏþ§£gg×#-ÞÍ^Æþ~vZ¼ÓB^À8=†J‹wó¬“(qÕ`€ï´¯NÝÛYsª1[1±URß+Ϭ- ?T~(ßø¥‹:µ)ŠÚÖ¦¨£:Áç GwàyöÁyŠãÌ0 ¦|î.É©/ *`ß· )µ`åK*d^x6ퟑÂwë-íí½ëè|“ ß³ùVmbŒÅã†ÓuƒÎÃPÛæÇÇ–|ä|ÎÁÆÁ9N9áoààœ­ZýÌž‰‡ ƒq® ŸÞðgÙÜÏžY¾žú‹‰é»£“JÝ+6 í¼khç]úî¢Á@{ïyã÷ŽÜ+ï®dмèœéƒ>ø×š¾×®¹WÖÙ弿Zo{L óW³c‡r£ÌOpÆy)Ó§·/úç;¾ó×·ßtçßú 7nT?jjy¸¹ãHkw¼}Áö ®ÝuÞ‡Ï~ÓøÚW•¿Ú?@Wß9MÍ5œ,õw;‡•\×ßó…ôÔt‹¦Å4-¦é1ÌöŒ1Œ˜n¨ :¸®ÏÜ\Ýöï³–-Þ~üª7ýöVÝ­;íü G'•„­¿»8ƒjÖß…*Á»n#VýÝ­ì‹ú{ ÜkßA•Íwëùþæÿ¸£©µ;ÞÒUÌOrÅÖn}Ñ*cþŠ7š À/®ïâúþܰµM+´• íZ¡]+Üú®[k¾Ú‘»¿ `Éó.Ogz-~¨ø¾Û?†êÎwMɨ­ ŠlÁpñTÃPcŒ1ÜvÏçõÕ/øTgO‚Lç: ÜàŸøÐÆSOû×§¾ú­ ÿÎÀ‡§V4\¶»8ƒ* ï®» ˜ËvA|÷ØLß½ÉN©ç{%îV{cLß…'ÞΖö€|ùu¿;ïC ]ÿHp#aM†ÞÄ ½9þìÃíZ¡£Tè,k¾ÎÄï>`ñ¹×Øù¡"ûî´Á€Kß„f$ì9hý® qÏuN§U ƒÇ›”R)^*ÅÏzþ?èé{Žœaýú®» å»Ç¾1Ý+6²…Æ—vÑûî‡ì”š¾ÏÁ½ñ®G³ì.°ïö+WŸ9ýÃ\yýŒ£¸o{þÐÓMÜH€%Àà¬ëû<ù™;´b—–¯xnû_+ýò?Ì{ÑìÿD1}w×:Æ…ï*‡ €1f–eÀÀ Åà t\ç\ç±8{ßUוJ±?l¹ÀÎCgî|®õ‹{ì#ˆï~u‹ÜwùF黲Sª}?Š»Ýýìmý"—hœC ´þÞÕ»†>H:fWîK€O^ß «”¿á£÷”?¥çWŸ£ «ÿÝÍw/MÁœúÎT…©3™0S™tC5 • W& LU qµ¨*¥ÎÅñgÔÞfP_:‚E½#ôÝ÷íÑøî·ì” ßg¶Bº9©D¸‹´…Æuƒ€öGò›®è,2¥üô𞃧¸1 ¬N¾\kêêÛ®ŸH¶þsÍsà°uÌs¶Þ1ÉùS§_ aöGúÒîÑþþHnS€sÎ pnpnàŧQ×Õ?>üþCOå|ôº†ò§{^~D¹ áËúÛë1ªý‘uñ ‹dpÃ7BÝŒìfÌý‘ \·Žlýî¥uŒ¹~÷w Ï®º™ªíǷΠÇ-ud?ë»×p!;€ô_´dï¿ècÖï~5òµ¿~×4¦i3嘊ʌa¨†¡&[UCãÿùÁºú™/|1|ÙÑú=¸þì!¯ß«Þú=`ÙQ¶~W4/z¾ËîÂøî½)˜¹l÷×÷„¡7zÒÐצZu@ðÖŸU~ÍÛ¾üŠ‹n¼ÀŸÏ8®Û=ró¯?.~ôOˆÚw[´Ûô=žà\7й™ÃŒÁÜ3cp¥­c À«ßòg]S††2†Æ-_ l¬RȾ=y#4ßC˜†ïÁËN!ßë÷–±|÷«Ýc<³Ö÷üÀ{–4ô$7’†¾8øÙ [¼÷cÏûäGŸ àCŸy!]ùës_|w,=…q0 óØ=ˆÎ÷ †oØô]a%…•Ž~>»a&Ý\Èt>ýø;N\úÇÁ# ¾vã=»Õú¥¼šïáÌT Á÷ÐF¡ë{X²SšcÕ~ÀA¢«¿ÑÈ×÷üë>s~ÏôXøæìŸö{?ö¼Î;9?jýÂG·8mÑ~ÆÖ;JŒ•€X X¼{Û(À”Ø´¢ä˜2µâô‚¢j«žrý=ÐQ¨vêï¬4€ÇÛpƒ/ly¸£c²£sbåâ{šb“{®¾ëŸzbËÄê¶Y¼H83•‚®¿‡<-/¸ú{øC®©¿‡+;ÅóÊÑú= í¾¯ß“†‘*“Àÿ|úO~æA.d¯ŽÀ0×pÊîm|÷¶â¿ÆŽd³Ïl;ô×o n¿ûÀö-{·oÙ¹}‹çߦv‚rmgýÞ”È6%²0¹¯O3¦¿ìaû-éÖõd‡ûgüM@ë÷ðeGë÷(d‡o¸#ß¾á¯ïßûø}ÉZ_Ïgr'{^ëJì±Ó^úôiî:퉾“ÇúNî]£/=•/;…ŒshŸiê8@qì©É}ÛG¶ûðËÔJвSúžç ›SÉÜ¿5-væiw²êϺ¡LO' #6:Õ·åÑçBžƒœï‘ ¹öÝ÷Hd§øé{D²ÃOܪï!ŒUòÑ÷k®ïob5þ¨Ýɾrû–‚{ô”£û=ôÙÇž“70€-=U]vZ|yÓòþÖ5ë;2'%µ¹5ÑÝ·fÅI/ôø»T'Ù) }om[¸4ÞY¿2 fpåßO>_×bÓzï}m´~åH&\á{$²S|ô=BÙ)þøìðw„ä{hóüòý7l½îsÿ¨ø¢ëjLQçvˆ4fè¶*§ŠúÐÔ¹ºë˜“»2Mnƒº Svеï»×v´/Z|èÞG¯}pÛš¤:Ó*²~"‘â¯ïÊNñÅ÷Èe§xõ=RÙá?îÜ÷G¡t„յ싞ú[A‰í^=‡!Z¶À‘G~{hÍ‹HöÔÀöøÀ£’«_оúAìŸ _vŠµï™®Áá©å&ø &×jzLÓbg­ùY½W‹PvŠ_¾G.;Å£ï‚ÈNqï{Ô²#Ü ï‘ ¹öÝw/wPKj¼¤ÆéãS·ÞAì8ýbc–x hÙÿãQWŸk>×_ߣ’báû–/Ûòàeæ§;Ïý÷® ë-Þ#—âÝwAd§¸ö](Ù)n|@v…;ñ=Ù)>úîEöö][KjlèØ™¿ú\‰­Øö»î­w˜8ýâøéÀ§ÿÎâI–œ¨/91÷ìƒå¯à—ïÑÊNqÔæÁÇ_½îôV|QÙ)^|JvŠ ß”âÌw1dG€¸Ãgß#”â‹ïw=jjL+«¶s5Æ•”XbÛïr[ïXþìƒÏmŸOßÊ.^e–Ý+âÝwd§8òý¾GÞ\þ©P²SÜù. ìG¾ +;Å®ïÂÈŽ`q‡o¾G.;Å£ïeÓg7\vË5Ó}'›_ù÷šAA1%v‘=cÏ>´ï™­úúMOü¹ ¨Em:îy5_ßÅùUÑd7ãh¾¶°²›±y~UpÙÍXŸ_•Bv3uϯ &;_¹S<¬ßE“®ÖïdàgM÷ßÚ~×7þö‹ßùÉÛoúÖÕßÏgeŒúÒh9E…¢>ݳJQæÜ3,¬>·fYÆŒÓõ»°²£lýîb«€1×ïKxYd‡åú].ÙQoý.žìiåNq¾~Pv3ö×ïÖ²OîD!Ë ÓJ!«¦ãÅlS!›,L§ Ó‡.¾Î¼Œ`™^ïýØóÜtíã†Ö»ûŸ‡3½¯úî5ß¼l“ºªrô„òÔß L‰¯<ÓâíÙ\¿‹,»Sv‹%¼øËv3¦ìÕKx‰d7S½~—Nv3sÖïBÊŽPq‡3ßE–bÇ÷z²º–R£˜ƒ{ÿE5_G=@í\DŸ¾îcÏS€ß]ýýk6½~ðƒËodµÊ/ÚÓY⎲ŻñRàN±(ÑH$»™êŒ²SÊ}—Wv ùÞ„ÑdûreGHe3¶ë3âËõ™†Õ˜ÂÔ°ª¨Í½'¥}nòÄóêÉ®MájlFöñÃÊÈþ_¼ç§ºV¼`ÓëwÍÀdS Sjïãfà»!«cš^¯D#‘쨋UFÙQu‹U^ÙQVŸ‘]ví½ç5aÀøøXÔï¥vÂŶ|—BvŠ…ïÖ²+j¬¥ca²m€üþ'K»¶ò§ê×¾ÕØ»¿x¡ž3&‡¼ïK_ùåWŽ£À`¢©eRá˜3j?uåYew3é…g×+ÁË%;¥ÚwIe§˜¾K-;¥{ņ8žÝ!µì0±-Ù¾<Ùû*D5b»QBÇ |—HvJMßmÞAM¶ÍOv/5?=zwó#¿©¸FËM^ùÕKApýç6lüüK€íÀ^``ŒMÄbÓs[͸Nµï2ÊN)÷]jÙ)™åëQ8”Ý)µì´‘‡š;)±L$óµƒH4#¶m$ ÜQ×wéd§Tøît×c²gU|y?;~æ_ÚÌßÚwßwWÿéFúô_Ó^°}Œ©éŽ/ܰu;ðð°OµN€Mª‰©XÂêõ}®ýߥÜwye§ïGÿ$—@îðßÒ+дPêðf5&äù«þgîMT1}wÔð]RÙ)¦ï^N*ik^4}úËÌO×Þñß—úüÊ:¬¨ƒ³ksbý1 ~ÃÖcã©1…í›mjöã—˜ ù~èÑ[ ³ìÆÎ9€¯î¿u~ÿ­ó#˜æ1f5Fêuv‰}¯µ=F@ߣÃs|—ZvJ<³väàcFn¿—™J†Ï|ÝÞuo{býÕ+»·vÜäÐ rç–ò‡?r:fe_xÃVc±xGWß0td—?¿Ãlr¹€T*åÆÌ;á2ã÷užÎÁ9¾;O&ß+êì’ú^󪔾×ßø(šï‘âŽßÇžù$—À‘w)©ž®E'úÒ_lýO®ß½`ÅÃó–æË3äÁÞÀ±þ;ï| 8醭Ö~éecjb\36ÎìÜ4µªÆ,\s9¢±íWÊëìí½ëþ·ÿý8ò 8À£~oöS󪤾׌d¾7ÚÒ.”ïQãŽÙõ{á`ÔïÃSÌjŒ/ýÅÎùÕçŽ(Ê S+Ê÷:{>Ñ:ïC©–<àÃ+žsÒMWSã±øD,Q\rÒ8ØÁ×î1à4åuö ZÀ‡“ê;¨dºÃåG¶ràfï{cäòÝzã£4¾Û;¬$Žïábª•™‚LGà¾ÿL´©®³»ë?CyÎ_Ëæ§' Ócù©‘Âô`aúP~êôþš¾û¼Íï˜2ôÒþ'Fe¬¸èø)5>‹O|ÀÊëïôò‹Ô¼ƒê¢M´©–(?40óé&·ÒW®8ñ_~‹ØÙõh³M´±¹¥ÝĽ^ šˆãðjÝ4!&â•ûÑR{èóµýJÍ;¨®×ï'ÝëSF˜2ÌØä¥__ùížkdÊà+¿]ÈŽéŒéŒé`“‡žÙÿØÂ·~m’±IoÅ™z{cäZ¿WËþî~Îqà uæ1>pß…Íýìâ¯ßíV2Mq ï¼Á€ë÷(Wî5n¢š¸K²„·Þãtý~ìC¿(¦žÿëËlzÃd,>©Æ§cq%;>nècÜ3ôb)à¼ýÁÁ/0›†»¥X¿W˾¹»À•C[ÿ‹ýõÞ}_ð³ÓÞOß û]6ŠÓ“J®ßÝCµn!A<´Ž‰výÙʽööÓt–ð w=:]¿?»öU eŸ¿é ±ø„š˜ŽÅÛ®øVË»Òóž™é G¦Fžµù³Êcg?»øëwë“Jzìèƒö¿¿vÛW0«¿8qqUÌõ»ëb•à½5‹vý îVÛúå-ÑTÇ÷ù«±Ä¤š˜ŽÅÚ®ø–ùÅß{ûÙïÿesÇÂTk·Ó´RIdßkÊþíî~>»67k2† ÌžomçŒëî¢ùî±uŒ(¾ûÑî1Bß#(ËØÝÒîmÄGÐqtXÉËýÕò$o¼lJOÅâW|ÓãKQ\œA°>SSö›2ý®Þ à#éÊ¿EŸËn0>pßm§¾ß¼,Úxï#H}Ư¦`×g|mäI}&ì•»ƒÃJ¯ßCõký>‹OF*;Ä[¿7”€«|PÚ{×½þŸ_1¯0¾taýîc»Ç(×ï~·hdý* é»»Þ}/~ûíSj¼3RÙ)âø^¯Î^Qo)/¸Óà ùÎoE绽£õÝ÷F¾ÑøÌðð}w— óÝKë¾OÇ"ÈNÁ÷z²3ÓÏw–UZÊ îfÙÝL{ﺋï¾wñÊ÷€Z´‡í{c•Âô=žÛîžZÇã»Ù)¦ïN‰¹åêÎËotýsËãW¯Çh}¯+{W?8ÞU&ûûõW—eÞ·hÎ:ý]Ã[ÁñÍ®°ïõgß÷@‡o„ç{ðóÂñ=žÛŽpVî>4Àwï²SÌÛªŽ|ïLvJT¾×“ýÆ®~¼kdÎß“jÙËËîfÞ5²•7†è{ “7Âô=„±JaøÖ(Ô }'ÙK©“ÇÝ·v‘úî—ì”xf­ï[$í$ˆþìáûn±Ÿ½fuEWk?ê=½z>_ a¦R8¾‡60/XßÃrœï¦ìzåîs#߈|÷Wv3!ûÜä0}·ý]ý®©üëasån>÷‡§¼?hßC›–´ï!B Ê÷pe§á{¹ì÷@Z´‡î{@²SBó=è™JáøîbZÞ;WÕ(¸Ó㫬*0á¬ßCHp¾G2äÚߣâ¯ï²#8ܾ¢ïÊN Á÷p¦åí»µìu—íUûdêí™)Oõ|mþë |DvŠŸ¾G';Å/ß«eG@¸>V)ßC¨ïaÎA Îw×®Ë÷¶?uOª½ÛÝ ýçá]ýù¾ì}PvŠ?¾G-;Å»ï5eG¸‡40/`ßC“ïáO¸Âw²ü½3£KÊë09ޝQv7/®ˆï¾G%;Å/ß#—âÕw1d§xñ½žìð÷PG¡æ{ȲS|÷=|Ù)þúînÍþ©ÿy€>(§<Ï‘¯…»yqu|ô=ZÙ)Þ}DvŠ{ßE’âÎw Ùá/î ¹À÷Hd§øè{T²Süòݦìõ îo9«¿¼Âž55æÔÜßrVã]í¦ï^ˆAvŠß…’âÆwñd§8õÝZvøˆ{²S|õ=BÙ)¾ø­ìï¾»®³¿ñ¥—Òýdò@¾ê‹OAYÙÝü ù·Kxqd§¸ó]@Ù)Î|UvŠ}ßÊ¿pLvŠO¾G.;%’óMAÄô=ä#¬S“-ôAÅÞÇêš»Y™1ŸR/í½ëÜ•hD“æ‹ IDATâÔwae§Øõ]lÙ)v|·#;|Á=bÙ)ž}DvŠßEX¶›1Û¾;õÝõ²ÀôT+}P±ñq¦æ^k7¤ùë8õ]LÙ)ö}\vJcßebí»MÙA¸Çrº~BÈNñà»P²SÜù.”ì”ô³–hÉ^QpîqŸÙ²õ .yÉœãK·ÿ}få~ûßç¬Ü/yI?€-[¿ðÜã>cçÇÙ÷]dÙ)v|—BvŠ•ïòÈN©ç»}Ùa®ÜÝù.ìW¾ (;Å©ïÊnƾï^Ö즦f ,5 îewó‰¨Uv/ßÅ—bú^“x‰d§Ôö]6Ù)Õ¾;’„»–Zç¾ ';Å¡ïÂÊN±ï»È²SìøîQö¥-7OMÎÖdæÖÖËq¯Svo]Úr³Ídí»,²S̱|¾K';¥Òw9e§”ûîTv˜+w§¾ *;Ŷï‚ËN±ã»ø²S¬}÷(;€©É–£wSkÜ-Êîåϵ“z¾Ë%;%³|}E‰FRÙ)G}—Yv ù®úm~|—#ÙQ~CÕ¾ïBËN±á»²S¬}—EvJ=ßÝÉ^QpŸžj=z7uîò¼€£š+÷òçÚLµï2ÊnÆô]jÙ)Ý+6¤Õ‰ìè©e§˜cµqš³[ÆŽïÈN±ô]"Ù)õ|—KvJµïÞ×ì´™šlÍ·^àÅoî¯(¬çpôQñ­¿¹@¾õ©ÉVíÀ_èÕ¬ËîfÊ}—ZvJfùú¸1œÝ)µì0±-ÝyLVo‹f¾¶¯‰ç¶'Û—« /†CßÙ57TòG¸ô‘Fv3„{ÛœE¥“Ý ánÎr’Qv3„{záÙ^d/_¹=½ @ÇÊÓ\pi?€;¿¿@L)¸kð?Ígm˜ÿYócÍh2/.Ô?øZñûŒÜþdû2©eÇl5f||eµxùRV!Ü©P#c*Jí„»¹–·N}îîî¯ šªõ»¼²cîú]jÙ1»~~òð²fZŠ¡¥ö=òìÔd«y7õðÀÒÃYœzI?Æøï†?PRóæãwÃ`ŒÓÀá,,=õ¼WÒ+ì{äÙŠo˜D" X,¹ü-ĈY‰jĶ?™[g{¾¶¯©¾‰ê¨EAíCL5}—oÙN)ó]jÙ)äû‘Ç¿™e§hš «Õ{×f†—axf+æT4_|ìû– æ0˜£‹x¹ìô˜©Ç¿´0‡ÁeƒËÊ_¤üÅ­C՘Εo‚Ì#>*êì²ú^몤¾×Ûcß÷º'T+|—UvJ[?fA”ZvÊÄÄ€¶¶¶¨ßˆ§P5&³ê-ðÒ‚fÖ_ÚëBÛ]÷-Ü·ìÀï¶f;¯ŸšZ¨x0€ƒY fqàÙ¯î[vâ·ÍÙ3c÷ò:{ #>MÍ;¨²ú^+¦ï²o½ñѦïVíLßå–päÈ€yóº£~#^CÕ˜y'\™ûÏ”×Ù=µ^†áe÷ߦLMµNMµž~QóÈ‘_,Óòï0³xg\‹*`Œ:ãìÁ´{·3‹wzûoSÌ·øáÕwPeôÝboŒd¾[n|4ËîâûngK»ßô–ÑRkF<¦”å–ª1'\[ñíWÊëìòö«¾ƒêÞ÷áe^Fµr*¸Xvx`}³ë­‡³ø× ç”âùŠgà‡³8œy¥®y/?<°lþÊmGk÷–¸×Û#—ï w=Jã»-íÝ+6ˆ_¢±X©¡ï pÛ{ŸßµøDyï¯Î©³›Ûf$ô½úªŒ¾×ÛãÒwÂ}ª…÷ÿf|pß²Õý§˜ß§Å{õÊßµüùƒ9¬þ÷Ñ¿ TÏ¡—úéW{,p·Þõ(—ï #ïN+‰ì»Óc¨Ö¾[ánVcþ_Û?âˆm¿RooŒ\¾[ïztãûÓëðôº¡ÁùCƒó§'[©´Rþý<³u0‹ä¢#+÷Õ—Rµ}ÎÅ/kßþ÷™Wœ_w;ûÙ¥ðÝþa%¡}w~ ULß]4€¥ïuq¯¨³›¾ËE|Ýí1Rùn½ëQßíìg÷RÏåRƒû–½æšÑНÏ,Þã…òG®yr0‡Kï·zÅZ¸Û?©$¸ïN¡ ê»Û¢ùîNvJ=ßkbBý½1¦ì58‰–Ækq-6÷³Wœo-ŽN*™ç›|ùÑ©øÄ·®ðÁÉûè+åŸæJ6¹8ƒJ¸›³œ‰ë„»(ç›<·Ž䈓ÙÍTŸoRœ÷ו_d±7FK­‘¥DckK»ðëwû'•D^¿;=ƒêïˆmÆøLÝ à¿[×™—èëæÇvâ®»€€ëw/­cZ¿ûÑL„õ»/²£Öú}¦,sÞ_HØÙõ(¾ï+ ì»Ó3¨búî®»€¾ÓƘë¦îð¥–ó¾Ôr€ë¦î¡¯Û|/}c„òÝ{S0!|÷¯Ýc´¾û%;¥ÂwÀ½ç¼ Àùùýýì"ûîøª¾»ë. šï^úÆøçûÌaÔ릶Ðç×Mm1¿hçùÞ;‚ â»_í#öÝïF¾Qùî¯ì”rßÖÜÏüí'Àùß_ö)û/dÑb,ª¸o0 RýÝcßAêï¾ôzô^oiª¼ÅZž©B§õÓ}ìõmýÝ÷F¾ÑÔßkÑrý=ÙÍî3e™±½÷üᔤ:{៿oÿ%D[¿{j#ÌúÝ{G0Öï¾ÈÖïÜòa»øF¸~¢E{ë÷ ‡o„¹~Tv̮ߔÕÙïyþ[¼ðÏß³ÿ*âøîCS0|÷«×c´¾û%;Å£ïfÈš‹'ÑŸ=߃¾ªïÁU Ç÷ e§´÷ž§èè;߬³o9÷R/ºÿVû¯"‚ï¾µ{ŒÔw»øš¾‡L¼¿²S¼ùîfåÜä}z¬RH¾‡50/hßÑRãÓ–s/°þ¾ïÚ•h}÷¹‘oD¾ÑŸÝ,»‡æ{²S\û®0ÃâQó)AÏT Í÷pæî{¸£Pƒó=LÙQï„êÝ/x+€ ÷Þbÿ…¢ò=í¡ûÜäxfmh%šàd§ø»ÿ½^™–‚ïaŽB Ð÷(†\á{ȲâýÀŸÖ½ À†{o¶ÿZáûàð}a¦R¾-;Å…ï´Ÿ½Þ£ââ0ç ê{øC®ñ= Ù)þú¾ì°nv×y—¸àžïع0}|¬R(¾‡6-/PßÑâØwÆ­‘ÆôÝ_â×â³ïÑÉNñË÷HdGÖ¿ww€ îÙlÿÃñ=¤yìŸñ1ù¦ìÓw;ÄÛ_¹‡¹l7cn{÷Ë÷¨d§øæ{Ô²S¼û•ìhˆ;€;Ï;€oÈ÷PG¡é{øC®}÷=|Ù)æ±&¾ÛÚ-‰ì”öÞu~•h¢•âƒïbÈNñâ{„²Ãîî<ÿJ/Ùòmû¯œï ¹Æ÷ðe§ˆpÄÉ—¤žm§DØՃ¡ìf¼û.‚ìO¾‹$;ÅïÑÊ›¸øã ¯ð’»o²ÿÒAøì¿}JvŠ_¾Gµl/OCß–eDâÅwqd§¸ô]<Ù)N}\vØÇÀ^øþé›öŸâ¯ï‘ÉNñÏ÷he§x÷]Ù)|·*ˈ#;Åï¢ÉNq컨²Sìû.‚ìp„;€?¼è*/ýÓÝu£Í§øå{IJSüð]Ù)^|GvŠ…ï½ Ãÿ€H²Sœú.¦ì¾‹-;ÅŽï‚ȧ¸øýúwÑÝõ ›OñS¼ù.Žìw¾‹&;¥¾ïµ×ì…¡G˜x²Sìû.²ì[¾Ë ;ÅÚwqd‡ ÜünýÕwl¸ÀÅw~ÝæS¼ø.ì·¾‹&;Å©ïbÊN©é;«õ( ?Æ“ÝBtx®;¾‹/;¥ïòÈN©ç»P²Ãî”;6\àâ;¿fózw¾ ';ŹïbÊN±ï»È²Sª}¯Uy„©î~Gcöµï²ÈN1}¯$^6Ù)Õ¾‹&;¼àà·\ àeÜdóz§¾ *;ʼnï"ËN±ã»ø²Sª|Ÿ[~„1žì>Íþ$¦SÏw¹d§˜c=Žú.§ì”rß”qðÛ Þ`|ZvŠ=ßÅ—bí»,²SÊ}/ßøX~àÉÌiõz˘jße”’Y¾þh‰FfÙ)ä{éÀ¯!žìðŽ;€ß¼x#€Wüþl^oÇw d§4ò]Ù)õ|—KvÊQßgÛÈF¶2†d÷iâô–±™rßå•ÝLfùú”2žÝ!µì”E‹8xð@Ôo¤F|ÀG}ÿªÍë­}—FvJ}ßå’Rí»Œ²SÈ÷ÂÐV†·Î®ÙçÜY•%äûÔ®ŸArÙ`b[ºó˜œÑÙˆmŸBÕ˜øâ—#ŠùÚ ãî~uá{¼òw_±y}=ß%“RËwe§”û.¯ì”ô³Á0=ð{†ª5»<+wJssÀôt6ê7â-³Õ˜F°úšò:»YŠxßpð« ßo¾K);e®ïòÊN!ßÇžù1d–¢Ì n«Ÿ»°¡jLËò×"¢Ûþdn]^߫ïi ï'î~ùÒ÷xÕ_¶y}¹ïËN™õ]vÙ)¹\@*•Šúx uhí}1€üÐ6Iq/¯³G2bÛŸÔºƒ*¯ïÕé^±!œÛ6ã3î8êûÛ¼ž|~êZvJ[ÿðÇP8˜ÉtEýV<…ª1ǽ2÷$ٓݧ€ñÔ¼SãùámÒ•eªï Jé{ý½1Òùn½ñQßýÇÀ/.ú€WÿöK6¯'ßípo&Ì ïºM‹2‹O$ñQ^g—·?°Ù̼wšê>•†¶ItCµÞÞÉ|o´ëQ"ßíliÄ÷@pð‹‹>à’ß~ÑÎÅGvÜeÄ,è;5–{4üÛ~åh5¦­_ÞNÕwPeô}n¯Ç£'˜’ݧ‚!?¼­zX‡€±Þõ(ïöö³Ká»ýÃJ"øîþ÷âÿðšß|Áú2³ÔNëw„;bÛ¯Ô¨³Kè{½½1rù^ÑÅ·¢÷@ªû^ÞFŸFúN­bg?»4¾Û‹à¾;=†¹ïâàö‹¯ðš_¾Þ7QµÔš0Glû•ºwP¥òÝz×£,¾W÷g¯¾‰šê> ùá {CÕþI%Ñ}wx UXßÝ5ˆÖ÷`qðó—}Àk}Cõ·êm‘Ë÷{c$ñÝÎ~vñ}¯9yƒ1£ú‘î^£0^ùgDïÔ*NÏ Šë»«úî¥uL„¾Ž;€Ÿ¿üÃ^÷«Ï•Ñzã£,¾ÛÚõ(¼ïöO*‰ì»Ó™JÉÌÉ·1_[‚ˆè»‡Ö1Bùî½)XT¾‡;€Ÿ½üz”ùngK»ø¾;ØÏ.°ïNÏ Šé»…ìTS™“Ñh¾vÈqÝ:F,ß=7Äw¿Ú=Fâ{H¸øé+>àõ¿ü¬ýÃJ"ûîø¤’¾»ë. šï ÖìÕ-Ê çk‡MÁDñݧv‘ûîo#ßð}w?}ÅGrWþõö+‰é»Ë3¨‚ùî¥oŒ8¾7¬ÆX¬Ü醪 ¾ûÒî1zß}mä¡ïA´hÙ÷Pq?²ã®ïœóªdÛ’7üâÓöŸ%šïžº ã»÷Ž`"øn§ÎÞwà»|£ô=€í‘øÜð0}w³sÛ+? àÿû_öŸ+Žï>ôÀw¿z=Fë»Ý;¨5‡¨VQÐwß[´Gã{`Ã7Bö=è±JáøžÖž ÷Š:ûO^õ1oº]2ß}ë©ïþvñÊwû{cì¬Ü)‘øÐð°}x¬Rh¾‡30/hßÓÚSgå^óê_ý oºý“ö_'Zß}îõ‘ïAôg7}x‡»¹åcNBö=бJ¦ïÊÀ¼|sjp¾“ìÙØñãn±7æG—|À›þ û¯&ÂúÝ·„î{p“7Èw„²„wºŸ½¢ý@Å£úzÓ÷ ‰a`ùŽ@—ð!ŽB Ô÷ð‡\á»);‚^¹7Üõø£K> àÍ?ÿ¸ý׌Ä÷ Z´‡è{Ð3•♵!”hœÊÀÑÊB¾#È%|h£PÛ{×X¢ }Èu@¾‡/;Å_ßËeG ¸ÛÜÏþÃ×ü€·üìcö_9d߃¾!ÀýU¨ï®d·y?µ2é…gW¢ Èu ¾‡.;Åwߣ’â—ï²#8ÜUúák>àR!}c¬Rð¾‡9 U„-’åqZ–)O¾‡/;Ågß#’â£ïÑÊNñî{µìwó~ðÚO¸ô§µÿ”|o`^¾‡?ä:ßÝ-Û¸(Ë”Ç_ߣ’â›ï‘ÊNñÅwd§xñ½¦ìw×£P¿ÿÚO¸ì¶ØJ ¾‡= 5ß×â¯ïdw°²^üò=ZÙ)>ø.€ì¾‹#;Åïõd‡ï¸{rýý×}À[o»ÞþSò=š!×~û•ì¿|÷";Р·ŒÍ×ðS<ù.Œì×¾‹&;Å©ï²Ã_Ü=ÊNùÞë?à­?‰Ò÷hd§øç{´²S¼ûîUv·7T«ãÅwqd§¸ô]0Ù).|SvŠ}ß­e‡¸û";åÖ×ßàm?ù°ý§øè{”²Süð]Ù)^|÷.;ü(˘qç»h²Sû.¤ìG¾‹,;ÅŽï eáž*=éñÝø(;åÖ7ÜàòÈþS|ñ=zÙ)Þ|GvŠ;ß}‘ð§,cÆ©ïbÊNqà»À²Slú.¾ìkßíÈsåîÅwße§|÷ Ÿpù¯»âGÿaó)}EvŠ[ßE“âÔwßd÷uåN±ï»È²Slù.¼ì”†¾Ë";¥žï6eáž‹¯‚[ß’rË¿@\ñÃÀ}KvŠsßÅ”bßweàq+dÍØñ]|Ù) |—DvŠé{5ñrÉN©öݾì0Wîî|TvÊ-oüâÍoú€·ÿà6ŸâÂwe§8ñ]dÙ)v|÷[v0fõpkße‘R×w©d§ï˜»„—QvJ¹ïŽdGù U§¾‡ »™›ßü%Wþàý6¯w仸²Sìù.¾ìkß}—@+wJ=ßå’RÃw e§d–¯//ÑÈ+;…|Ç‘ßÃ‰ì¨Ø-cß÷0e§|ç-_ðŽï¿Ïæõ6}]vJ#ße‘RÏ÷`d÷¿æ^žjße”2Çwie7C¾—ü2ËNéë[ `ïÞ=ŽŽ8Un…´ã{ø²S6¿å+ðÕw9d§Ô÷].Ù)Õ¾$;¼õ–±“rßå•B¾ç~ È-;eáÂE:õñªÆ`Þ…ô©}ßkìs·ö=*Ù)ß¾ô«®úÞ{m^oá»L²Sjù.£ì”r߃“ƒañðåGÌø¾÷!³ì”öövãããQ¿¯¡jL|ñËшmßÓ½bƒ£#¬µ1Õó=ZÙ)³¾¿Çæõ5}—OvŠéûÄVÈ,;…|ŸÚõ3&;àßUË$qÅbÉ·WŒ$Û${_‰¨Flû”ò:{$#¶ýJõMTû¾×=¡êedйé²ÿðÎ[7Ú¼¾ÂwYe§ïÀÄŽÛ ³ìM+ˆÅâÁýˆ@kîªÆ¤û^ˆFlû“²:{4#¶}JõTI}¯·=ƦïVí*|aÙnæ¦Ëþ?ïºõÝ6¯7}}æGWvJ[ÿÄÄ$€¶¶Ö¨ßЧP5¦eùkdÿ÷ q/¯³G2bÛŸTÝA•Ô÷z{c¤óÝzã£ßô–1}JvÊ·Þº ÀÕß½ÖæõZjÍ‘}ÿ°`á‚ßVð™©Æóz@âNåuö`ç{øÚ~ "ÕwP¥ô½ÎÞé|·Þõ(‘ïv¶´7ô½qã°\|ÕÁÝ[ãÆP²S¾ùÖ¯¸ú–kì\<¼ën#¶ ó¸7AæÛsêìҎ諾ƒœïÁ­Üëí‘ÌwË]ùng?»¾Û?¬dí{cÜ츫¤t/ZÖ/fýýÆ·}À5·\m}Yy=’Û¾¤ÆT }¯·7F´ù|Ö±Þõ(ï6ö³Kä»î»Óc¨¾7Àݬƈ|õÆË¿àš›ßUï‚ê;¨2ú^woŒT¾[ïz Â÷ ö¹ÛÙÏ.ï¶O*‰ï»£c¨ÂúîTvJ=ß­p¯¨³‹ìû7.¿Àµ7¿³ú[õöÆÈå{ƒ]’øng?{¾ûÜ~ÀþI%¡}wxUdß]4Ðww²Sjú^÷šwP…öýŠox÷wæøn½ëQßmígÞwû'•üõÝß•»Ó3¨‚ú€˜¾»n#”ï^d§Tû^w‹½1¦ïÿõ+¾àÝ›¯¢Oíìgßw'•öÝéT}÷ñ†ª»î‚úî*¢ùî±)˜ ¾{—Rá{ Üîz$ß!äþkoÿ€ß¾ÒþI%‘}w|UHßÝuíþª—¾1bùî­)˜8¾ûÒî1rßý’Rî{%î6÷³çâ«„-Ñ|íÊ›´üèÇîø–ý“Jbúî²»€`¾{éã‹ïA7³Q|÷£Ý£é{„ÄûØÈ7Bßý•bú>w§'•„õýÓ¿ÀÆ›¯´ÿÑ|÷Ô7Fß½wóÃwn¨úÒîÑô=2âýkäK¾#¢%¼ï-Ú#ñ=Ù)äûQÜÝAÐw*ÈÜøîÛ!­ï>tÀw¿z=zôÝ{ÍÝÇF¾ä;"YÂûÝ¢½½w]$%š€†o„ì{p²SºWl˜ÁÝKw¡|//µoºb3€ß•Ìwßz=F껿]|=ùî­ý€ï-ÚÓ ÏŽ DØð}t¬Rh¾-;EÁñ½ú&êŒï·Jã»Ï]|#ò=ˆþì®}÷Òñ7¸á¡úðX¥Ð|a`^¾‡#;÷yÇlðÞ7&rßëmÙô¶Í6~Oß}Nè¾7yÃï®Ë2AU É÷Pæ…à{h£Põ=4Ùa§·ŒýDè»õÆÇMoÝ `ã÷E÷=¨á!úèL%”ùî€xWe™pæî{ˆ£Põ=ä!×ù¦ìðwDä»-í›.Û `ãÄõ=رJ¡ø´ìò¶—ð.VîaŽB Ð÷Ї\ä{ȲS|÷=dÙá;îÝwû‡•6]ºÀÆŠè{óö=Ù)ñÌZ'%g[!Ãrˆï¡ËNñÝ÷Hd§øè{ø²#Ü¢ïNæmzËf,–ïáB`¤±é;cVŠ„/;Ågß#’â£ïÊNñÅ÷HdG@¸#ßÝBÝôf±|{Èu0¾‡¹l/ßí—e¢’â›ï‘ÊNñÅ÷Èe§xô=*ÙîØw/C®7½q3€?‰Þ÷°e§øí{T²Sløn«,­ì|@vŠGß‘âÚ÷eG ¸#0ß½ÈN™ñý§Qúìÿ|VvеFÙ)ž|FvŠkß…’âÂ÷heGи#ß½ËNÙô†(}Rvо‹ ;ÅÂ÷†eqd§¸ô]0Ù).|PvŠ#ß#—!à_}÷Kvʦ×m°ñçaû½ìo¾‹#;¥®ï–GTE“âØw!e§8ò]XÙ)6}Av„ƒ;|òÝ_Ù)3¾ß~åÆÿµK¼GßE‘âÖwÑd§ÔôÝbÙγ» žì¾ ,;Ŧï‚ËNiè» ²#4ÜáÙ÷ d§lzÍfú`ã/÷],Ù)Î}SvJµïõd7²»!ªì[¾ /;¥¡ïRÈN±ð]Ù&îðà{p²S6]²yÓ«7ØøË}QvŠßE–Ré{­®Fv7W›û¢|£6ÒÀwId§Xø.‘ìÓ÷râ…’!ãW¾-»™M¯Ú `ã¯ñ]\Ù)ö|_vJ¹ïµÖì{À 6÷9¡Iêú.•씚¾K';…|Çì^4Ù>îpè{h²S6½r3€¿ñÙwÑe§4ò]Ù)õî¯Ù= \M‹¾f/O ß%”RỤ²S2Ë×Ïä÷LvD‚;lû²ì”M¯ðÙw9d§Ô÷].Ù)仑ÝmNLå¹ÝŒqµ¹/üª3Çwie§˜¾K-»™ÞÞ>{£~#•‰wØð=Ù)›^¶ÀÆ;|ð]&Ù)¦ïeÄË(;%žYËczÏL5PÓ}NÇì ò½tà׀IJSÚ{׵ħòã»e—ª1˜w!"±m‘Èp‡¥ïÊN™ñýwŽ}/'^>Ù)ä;f–ðòÊNQ›{Á¸6þo®¦{Ý È$ét@6›úxM<·=Ù¾lªÔÉ|m¿R^gdĶu¢Äu|\vʦ‹6Øø{g¾cv /«ì”¶~">·÷vÈ,;Æ8c3õÌjŒte`¦_ürD2_Û¿˜Õ˜Hækû•ê;¨¢ù1î¨ò]Ù)›^ºÀÆ?8ðˆŸÚý3È+ûlr¹€T*õñ#»—Ç:NbŒYá £vSVg`¾¶©¨³Kê{½½1Bù=î(ó](Ù)›.Ü `ã´(Еé ê=…’™jLß%€Ä-൑‡ãjs/ý/ÀìùVîUwP%õ½æTé|·Þõ(ŽïBà _uxï?ãÆP²S6½d3€wÚòª1-Ë^ ™GlÏ©³K;âƒúÆ(euvµ¹—1Ù½2ÕÜëì‘Îw‹½1ùng?» ¾‹‚ûð®»KJ÷‚¾S#™¯Ý0›^¼ÀÆ?5ð½¼ÎɈm_R㪄¾›Á*N0©é%§Z»e,w=Jä{Ã]ùn'"ø.îf5&’ùÚ6³é‚¾WßA•Ñ÷º{c¤ò½¼×cõMÔXóÙÑË26ö³Ká»Íýìâûîèjä¾G{E]hß×o°qK ßëí‘Ë÷»%ñÝN_µy q*x_ IDATcÜÈ„õ¦œÇöI%Á}wtRIdß]4ˆÖ÷èq¯Ž¾ß3Çwë]²ønk?»ð¾WË^¯+$ÕgÎ׎&Ï š¾‹F¼‹3¨¦ïBïºuL„¾GŒ{½í1BûþÂÍ6Þ;ã»ýìâûîऒÀ¾×^³×ê Iµy Í׎ ®º ïi ﺻùa–ð›‚Eå{”¸[o|üÿ¹{ïø¸ªkýûY{Ÿ2’m¹w˰i.€1`Ó›I€›KKB€Ä”„ˆRHï~¹7!QM!! $7Ð!ÀÅÓÝÀ¸W\ånk4sÎÞëýcŸ93I£‘4åÌûd>Êx4åÌ1þÎ3Ï^{­Hóýôuóææ¿S)Ê|ïôÔHò½½4&÷˜½<æk—VÝèS=dfÄ#šüÕ»vvD"š‚´{, ßË÷|JÚ#Í÷ÓTbï·–Ý•ÿN¥hò½‹Ý"Æ÷œ9;ç¼t0_»¤*DG°ˆð½ MÁÊÎ÷6ò-=ßË÷ü7+E–ïû7¿rûQ7I·OÝ«å±]u«oLdøž{•(×Å(B|/„ÊÎ÷¶{,#ß Þ¢½Ä|/Ü;» 5ä{_?«@ÝëÉ÷t‹ß;¬ÉË„w+?ß ÚÈ·Œ|/x#ß²ð½HÃ7JÉ÷Rýk  ß Ÿµ×ŸZ‘|/X¯Çð½#uË„*'ߋТ½,|/R‹öó½¨c•JÆ÷’½;­câö„ˆD4­QëOnP÷FÅð½À]|ËÇ÷|JÚ‰tŽKÖËÃ÷¢ ß(1ß‹:|£d|/ÁÀ¼ð½kK÷‚4+;ßÛ+ øþfð½(ýÙËÁ÷|ÈŽ¼c™P¥æ{‘Ç*•Œï%«T¾—ljQù^ƒµ(™s/`»Ç2ò=wácýI êÞŠ4ß‹8y£´|Ï“ì(ç¥M•Žï%˜W¾—l`^Qù^â!×Eâ»!û~Œ+Ü ÞÈ·,|ϧ¤½~F€º·#Ê÷¢ÏT*ßó';kÚï-S ¾—pjQù^âQ¨Eâ{‰ÉnTp¾‡dG œ{‘Z´—˜ïùoVª?±@Ý;‘ã{‰¦åŸï#{çc™P!ß‹‚ø’¹.ßË2äºà|/ Ù È÷L²£Øp/êð’ñ½³óê§7¨{7B|/éÔbò½³dµLk¾£à¾äd7*8ßËBv£ò½Œd7*߳Ȏ¢Â½c•JÀ÷®B­ŸÖ nA„ø^R‡ï]"{-;5‰Éî?­ÀM™ÈnT@¾—‘ìFá{ÙÉnÔM¾·&;Š÷’ Ì+*ß»3äºþ„¨ð½¤¶=T¡ùÞ5²Jã{YÉnT¾—ìFÝä{DÈnÔe¾·Iv î%…Z$¾w‡ìFõÇ5¨[TN¾—‡ìF…ã{wÈÞåÌ=Kà{ÈnÔM¾G„ìF]æ{¤ÈnÔ¾·Gvîer]p¾wŸìFß—‡ïå$»Q!øÞMÏÞÍX&SÝâ{dÈnÔe¾GŠìF]à{ÉnÔ)¾ç ; ÷²Ý¨€|/Ùê§4¨[Rj¾—ŸìFÝã{!Ò˜®/¨¶Vù1²uï$»Q§øY²åÉ÷Üd‡»¹S÷UF²„ï…%»QýäuïÏ­û _Äw“ïQ!»QWù^œ½P±L¨Nó=’d7êß#Kv£<ùq²uÈ÷ɎйwŸïe'»Q7ù^ ²ÕOj0Wê–ïÑ"»QçùÞ}²¿ôðå@—¶¨v¤Nð=Âd7Ê“ï'»Q‡|¯²åà{>d@_º}aþ÷nO!{(÷°—dž*Ù3U·t.êlÈóþîôù(Šdeàn@ŸSÝ!ûÿ<͹±”üâÇ-ÎÞ[ v+_)¥N=iÈ£‹oíÂK¸‡µðm(òdeàÎêËRE=”{8«/T‘=”»½Qþ¬àÞ©Ç´zùh‘ݨ³|/ Ùê–ÍŠÂ÷H“Ý(¾wHö9})ÑÌJ³b­4¿~Õýæö{~0ÕuÝX,fà~Áµÿzëñ«üüÇf><„»RêÝõg*å+¥<îâÌû ]ý:ˆÀÇãÚ=Ÿ¹ø^9d7jï•Ev£Ö|¯D²…æ½ÿ˜³:Eé4ÜÑ%¾G“ìFùó½”d7ª[6õ É÷ »Qδ…øÉ~æ#7ZB0 ´Vš•f_ëë×/Njíƒ]×½øQ– K KÒ=¸ý»-àþæ£[˜yꈞ¯¬hühÇž¯”ò‰¤$šë¡ë-!,A¶Kɸ_÷ÑB9ÉúÞÚI ÖC¾ÿ¿š~z[À÷·þµŒ)Ã{2C3¿»áf0cÉGo ")䤱'á£E8lêµ$¡˜yëø“s¼›l¾W&Ù2ù^¹d72|:t(*™ìF5XßûѶ=-"šÜÊ.…4XÏg}5údGë«å";ØöºU[_E[K¬Fv5S[/±æ“³3ƒ™™†íÐÍë–Ü´n1€ûÇLy`ôTA¤šY34 šaYlYéªAÁõ ‹eˆ0eÜôÉ㦃°tý‚7,°ò½û—¼ò[0wXSÓb}µ’ÉŽŒõÕJ';€Þµ³ûV'š÷­ÿÿÙx}:·Å©:÷|ø^d7ÊÁ÷2’ÝÈØöºÕà{ëšÊ#{¨ ¾çCöÎe°ã†‹¿¼r Ý?nª &H! ?Ì ËÒ–¥Ó—„–¤ƒ w2wDD„IcN°sýË›í]¿ çýú­þ{ðª×r¿Ãwµý) ‚ÉnT=dfµÜçÜXÑdPí¯Œõ½§É-Ë|íB)3MéÔ§¶71åæ{‘ݨM¾—ìFõ‡7¨[ÓÅ-NLv£š©ü­#Ú˜ëÖ/ºþ£Åׯ]tãÚEÌpßÄ)÷7dO_LÌ\À)ç\„±â 0D@PID˜4öøñúÁ'’3¼ÙÛtèÀÇñCìûW¾šûðlÛày^wÏI¹eÇ—Ú=G6©Þe™¯](…9{YækJ­sòüùž¹ç~^T ÙCeæï!{¨º5sA¨—oþÀŠph×mª`²0ž=±ÕêQ›£„æžLµ@‘MÂ"²…¸oüÛ ÊÔAø®³¯Yi-ˆ,dî–÷Üà7N^ðÌf= — Ü™ñΆóBÛ.@Døxùï„ ³ã1åãսе¯ô«‡özÊ_8tãÚù÷’Jc:®Œ¶2Ó˜Üõ‘QVëÔöê#£¬+ ­K$[+WûÖþ½rÉŽ ÿ5²¨ß n]'üûýûôª©)Ö1•DA3ô“@»[œ¼#€Ë=£&ß=zÒÝc&ÿvÌ$ÎÈÁ‘$DkóÎæ§1ïD,ˆ…ÐD1Éüȶ!%³†f>±g¿{ôŸÑ³ÿ­Ç̬îw˜Óãw?žvâƒ_8é-ÿ¦2röòŒØ.²röÏ×.”Ú¬©hÿÞZùø÷\ÎÝ(üô¨h²‡Šoü'€ª‘ÿQîiCîõc:öïaÓÙ-N‘RvÎÞªþ‰† ãMñx<~× #´†dJe,!¤$Ki^2¨Œ4Î]i"mÞÿ2a'€›¯ºè¹-¬ybÿÖÐ f~wÃù‚(pî„kˆpÚÞjÍì)VŠÃç ˆ~Ù¸nƒjþü±3ìžB$DÏŸÿ…¬œ½ý{{+¨•åßsW=Vϧp1·ï¸qXþõ3Ñ×þͯxb@¿¡G•e¾v‡2X¯ÛÐÏÌÙ+wÄG+¨-ëg^xèRsåþaQ¡3‚•R³fj.¶Ý"v‚BÛÎ )YJ&çnS9´í  Á®u÷[6Ÿ¶¯Ú”Ù°fͬ5³fA$@’蛃ÇëùP㦔DRÎz:ûó¸âü{ŽÚ˜Ð¿GßÂwXÏú÷ˆ[øÿÑ«|bî“w¤.D!ÓÃõÒ q*œ¹3.«ò•Ë[ßýÐ"=¡_Mº%$q‹@ÆbK0k0³R¬5´fÍL"˜äG NÖL§‡ r]²(fÅmKVß;¬z m{”ùžçN¥Ð¶G–ïÚl”ƒïyÁ}׺è7lÌq¨X Ÿ•³Gšï£ÔmjƒïíÕÆTß;¨zLÅ2g}æïgýýƒlI$R~Èf;`e™w¢ÌËâ!Ͷ Ö)Û΂8µ6ß+$NÚÑ“Vʳ格’@R$’DW i ±xç–wà½éyZ³ÖmWÂWßó¬g¯23Ê|§ö ö®Ù¾ mÚã{Çp£öýW¡M›+¨‘æûÈu›[ð=wÕc¥ð=¯Ž`5SgžY÷öWý4Ö$eð=e¹S‘‹jYv˜Å÷Ì{Z––R“`L§_ãÇM shË=–¥OÜÖSU4ÄšµF*ŽÈ.Û^5t$õîù—mï[õfÖm9w£ˆó½³;•¢É÷®uˆ ß»Üà«M¾w÷Ö‹¨Ç÷µ1‘æ{m€º¾çSÏ}¾w¢×cÍTÛÖ¶­QŠïAØ’ g±Ç4¥0s‚ˆ(sŸª”,(= #Ø©J "ˆ0 iL@a.‚¤ )ȶIZÂŒ¾}I0˜ç®]¨™g<Øn’Y¾wmjÔøÞ¾1‘â{7[ó¶æ{.¸·WSA|ï°ê1Ò|Ñ nËÜüw*E™ï’Ýä졦ž÷mó/úíDR #yO-«À§Å0æ=«&ÌŒcÖô‘’w®Ù€™ Là5Þ%¥žº±†ƒÖ¬5Sj¿kà܉„ Ë V_ßw’¬ùöš±w™ `îºE×®ÏUuA¾w§»@tøÞýŽ`á{7Én”Å÷váž»ð±‚øÞ¡"Í÷á :¹ïë‰_ç¿S)š|ÏÓ³¿ýÄU žùløÇcÎü‹eéúÁ{C²KѲ†m ÎËlE@jl„€,DzU 2l{ȘÊw ˜LFH!¤$"Ó±˜jvÌRê]£&ì‹ß»ß7&:|ï¾ÊÎ÷‚Ý(“ïmÃ=Ÿ’öèó=ÿÍJ‘å{|ûüÿ®ºY8½ë¶•gÄvA”Ù_zøòææfsýý>½ìå+ÍuÛfÛæpMÕ¸ø°Ã 3+?û©¤„hQ f`!Yf6l<¸8ð)ø˜U}Ø,¢j†Il‚@°,’‚@ eÕÍBbâ!×Ò˜Ë]µÇÞS;éžL½ç¹úÔG„ï…êVv¾°‘où^@²…|oîùoVŠ2ß;» 5‚|Ó˜ú! êvT$ßóôìÍÍÍÍÍÍßOöøn¼—¹eÕkW¬{ë²ñ3ÿjÛºaô®pMµõÔÖÃò¤•^üÀ "–’…ཥ¤ä‰+û2‚=MÆ›q ˆ@–M‚@dV`ÇOa=U+iT5¥ªª*ÜOÛ¦ÊÎ÷Âöz,#ß Þ¢½,|/8Ù ß³áÞÙm¨Ñä{× DŠïY9{À÷Æ÷<ÉþÏ»ÏmnnþEõ`_±¯øë{{ߺ»ùÕ¦÷.um[üÇÃÍšª¡v¸ Ú2Élí›NÞGí'àè7‡ Å«Ö ‚‰l¤e¼<33ÌÍf¿’¹"¥ñ÷0íÅVõjãðý±Ð³»œ)ÉTUUUUU•ûÍ–‘ïÅèâ[¾iøF‰ù^$²õsV ¸w­Á@ÔøÞÖ1á{›+¨õƒÔ5V ßó$ûÿ<-Ñœ¸³¦ÖW¬ûŠ}­}Å_ÞÖ÷ÆÍÌ}W[¶~ø¨¦j%«Ì±u2@d„3–$"f†,¥)ä%«ÖKÉã?èo*dG>dŠiRE“«ûÄ…Äø½1SØ®™MæÎ€eji$]vó‹UÕUO4\˜û-—Ý¿V%æ{QÇ*•ŒïE%»QîÝi¾w¿)XÈ÷r!>GmLýÀu»*€ïùW=š@Æxv_±Ò:óúç× ¸fÍ`d„ï©ÅÕÝÚ|fË ·•23 Ü™ˆ ’…dS(‰ dò)‰8…uóR‚S@W •Ú»$I! ßO{äúªªªþýûwø®KÏ÷¢ß(ßK00¯|/Ù½ûMÁ¢À÷Bµ{ Çò•žïV=ÖhP·;Ò|ÏŸì÷ü`j"‘øíà‰™ž½Õu}å²ÁW,j[ú±ã¶ A’ DØ \]…ñó­*gH˜ýªRÂ’ ²õë,É#ߨ™Á‚ÌX‘ŠèM­¤é>³¾ÿ!ÖBæT… Й‹¨&a—‚žž¾íÑ)ÛÁ÷Þ±þíËÖÌ¿|Å+W,}éÊð©æ?zuV+´|T¾—ìFÝä{DÈnÔe¾—‹ì(*ÜQL¾—eÈuÁùÞMÛª¾g€º¦òð=ÿÂÇ_~·¯u2ÁÊ6‘-Èv`.ŽKŽ#\ǘ÷ÖE ë᥂&ýs¬më%—®‚ÐVìþ­µK¯\þör½›D0‘ƒ‚>b¼b[ÓÒÍM-°Î¤9øFîÙGÄýÖÕ„‹¨RÐü³>~yöÖçNÚjBœÖm|ûPïï7÷ú‘ß3‘H$‰.œÏnò="d7ê2ß#Ev£.ð½ŒdG>²»¯‚¿Ã²=”{X ßeŠì¡êšæ2ÕÇ:±mÔýùÚhÑ8í/7PªkãMkáÞ Áhé–p¦ ò‚­‚Od„ã+>½À„?ÃÌÂõíK7yM_:·¯eóÑ‹ž‰h›iô}8 øØkJ&é’Ù¡Á`&¢½ã÷´qêvß'ß'¥È÷…¹žq…|_\µb0§žÃ7Á`à…KïyöÁKœwõ£=«èêˆíH‘=TgGlGì¡ò±]^²£ØÎݨ°þ½¼dGü{ÁÉ ¾:`z]2_ ßMÿÞY²ðµVZ+Í×­^˜Ôú7c&ûI–9%¦€–€iÓ(!-XYÙ6ÙÙY–¶,v#Ð:ýøª|ÑÙÎÑvß]ïW½Õs#€õ½׺š´äˆ)c«ô‰;ÞçÆ°¤}ïé›vŸº¹ñ”-;fn90~ïÖ·´>øó^9çͽ3ü?»ìý!W.Ô²0?£6_éÓ¹>™H&ÉÎÓ”ºàߣIvtÒ¿G™ìÈÛ¿—ì(s7*È»-;ÙCuÇ¿ƒì™2p¯wŠëß»@v£™ºNn\·˜@ 㦦v‘ewÈôì°ßòWiìcÍÕ}ÿ,I$‚;ÿZ½?á8uFˆE߻”®Üþ=âd•Û¿Gì(s7ê¾ÙÑ ÿ^l²#…õ:¿ˆþ½Ëd ´þÂG‹<æ»GO2.Þ׬´ö’ì{@*õO«ž-oã<ʶõÇ_Zà5ußêž<Ú©’÷Ù9¾¹?€~ªÊwl*ßÙv8ýqÂ\óâð~ó†Wí¨ê½¦æ°ßO8ò¡Ã3mx[×;þUwœ»Qžþ=údGþ½RÈŽœþ="dG)áŽîñ=Rd7êßK@v£€ïªÓ|ÏñÝ!;_s’µÇÖàÆ\ Ÿi׃¸&hÖ2½¶Å¶Å¦•Xð+Fu/Ýgt³?ì€[ÎøhÂëGØ1mR°ãÀY<Àyº6ölmõó#”i)@ÔVÒÒâzÊuˆò6ˆÿ«~ã‰Ä?;±k'ʨC¾WÙrð½‚ÈnÔ&ߣCv”îè*ß#Hv£Nñ½dd7ª·ÔéÎñYøn’Àç7,ò™ï9Ùö ħYïyìûÙ§Tþ–|~÷±–­oý€Á:åëšæKþª^½× Tç¬Uç¬0rÙ‡N[ë'Ùvص#5p– ä¤ÝR¢úÃ~Á >M"EªÉLðÔ©•SfhÍá'ÓÛ]k(ÍÁ×¥“Éd2Ù-óŽœ|¯ ²µÉ÷Š#»Qß#Ev”îè<ß#Kv£<ù^b²ÕËuÜ ¾çŽhºOvŽã8Ž£4+­ýT&£Z^ ¯ü´2ùÊáíßZ»ô–¶Íûn{ßäá/L]PUÍƒ× ©Z>( ÅýË—&/_Ú¿¹Z ˆ8¬¿ë8Ø2 ÑlYò§ì¶%õZ6 Ø"›Q\o:úZ©ÝU2]ŒO–2µ+µ‹ÂòM)…ÒÎ>W›|¯8²eñ½BÉnò=jdGYàŽÎð=âd7êïe!»Q½hP‡”À„ì\×u]×’$ 9+œiñ¡ZTÔ0pû¸£Pm[Ú¶ãÞ£†¡8Hê˜ÞK¹o³îÛ¬`øwº+Ò“ãH‹7÷o|£Ç&5¥QvßïÏ€’,iÊr‚¦7-*îezƒU«O‚륰„øÒÆ%Ñõß»›gÌ(‹ïJv£ïMv£Þµ³õV‰ýë#Ev” îÈïAv£|/#Ùê©@u‹ï…";Çu×±%Y’ÌOK’éÚ˜ñ¾ÏJ¥—U3VV™ÀßxÌï~dÙ6'¸@ÏÞœðH.䟻V8îñÎÃ;r‡:sí¡dÙŒ°ÚÚ-Rÿ2eS‚))U‚">4ì†ã”¶óŸ¦-ÚIìęϭïMv£ê!3{Çš¼ƒ+šìj°Ö­½cŸ,Ë|í*ÜÑß+ˆìFmò½ìd7ªG€:ÑE¾ì\Çu7ìt “éî hnñœF|öŠ+ßâûçÿ±,[{?y·¦×ô1w#hRÃèaÌÄš‚AñCä'Év0ýŠƒãçìÿÕë[¼}i‹gÌê@€ ,H‹l››'Íú¬L&`}ª¡ñ)¾¾ §€Ýš‹=ªéãŠ&;€j¥Ýs便ê¦móË2b» Ó˜0Ÿ‰âË wdð= ñGv£,¾G„ìFõÜ NvšïzûS(Ù‘rîVʳ[R¤.”¾a:úíÝ[#^g/«p]|Ò?î±l‹GO@ÏÞœüêÛ¬‰™ ˜-Öµ¸ö€`Lòßc›ã °sÀc(E_ø‘¿Ékºm퇩zœtÔßšòáÒ†e“íã뤃xÃzG„ÑÚlUÐÙñ¥²zx}+zk˜Æ„eï•È÷¬œ=,{ßË wdœšïJv£ï‘"»Q½nPgu‚ïÍñ8€XUUÃdî?¯iº³ß¡ß >Ðå¥0-!™³]¼ç³RÜ¢2’qÅ–)¦gdï¾fvµ†ÔðEê÷Ð#÷sí~ú¿9†ˆÔ°ƒÖ£ª~4ÀýÒ îFáòmú’º±…²¸/mXÙ9.}eÝû6‰ûÇO1oáÌGnìþ© Ó˜Š±•³W™YâÛQ›+¨½kg—lÄv‡*ÝÕeNÖæ4Ïð IDATÍ›Q±dåmy€=ì“å>6TgÍ¡Þëxÿj˜Æt¿M¦L¯D×U–Å–¥-‹SmYlÉôsÞ¶jÉøÉfvµ2ç(…­i^?å½³?å8tóŒÄ­o±&¶4<Á–&%PRC‘ :¸¾×Á>Gýašù°ûëK,Ä}ˆx‘J?–^ü.€£;!yë[¬I[š|Á–Š@ &<7ÆýpPuý›šnžÁ ¥5mÿÊž'FÞ×êmRk’gݘ.Õ47Þ°|1€»ÆM{%üûò»»|–Ú[A­,¾wXS|ϳê±ì|ÜM3bÄD¬\´SÊLc Õ?²ªsæB ¾¹m¾·¹‚Z<¾ØðÎ¥–Ŷ¦ùÅï zƦöàžÃïWÿø8>žû6€a÷OOÜú6 f4±¥cÿ=ÃxîøwÞêÝ’·ž;¤ Í_úÐóhìïFèÂ[ùÔÿi?ì.ï_!n\»ÀÝc ÜñWÞÓå““»6¦RøžgÕcÄùÞ©zöòò=*pÏÌÙ#¸ OµÎÙ#Í÷Ø\  ¾ç¨)ßW¾zEÈhÛæ«V6sJY³f˜¦Z³f‘LÆ^sõòüü>ïÜ„µ«­^}¸WUÓ}ú éæýµã@s:cÞ5³´þºåž'UÑç{§êÙ#Ë÷. ©Œ|Ü[¯ V"ßÛ[A:ßê›Ò|ï°ê±P|_ðÌg3)|ÓÖþš™ƒ!Ô÷ô3ÃlL)¬›Ÿ”Q `jÞ¯\þvu5ï[uË/™poÜ„5«­š>ܳ7©eÇáwæYŽüçñ¸ý]â»ÓÌ“(ÅJ³KA«¯YéyÂóèè¿™^ðéY -¬zî3Ç…˜›oX·À=£'ÏÿÜý];Kù׳G™ï]Ø©A¾wJåâ{DáŽJã{îÚ˜Hó½z.õ‡w={7ù>ÿÑ«3Ö¯íé£uÊ¡ž=¸bp2]IAÒ¢ŒA)1¾µvi¬š«zp¬W÷äê^ºº_¼fjú.ÿõŽmëyOXN~íx¥Xýt€{¾çÜ,@€ç³b @’‚ýçjÏÇüclKN]Ͱím¡ÿúõ‹\óÃ÷ºv¢:»S)š|ïòÔHñ½›8* ß#P ÙNáciFlDV=Fvþ*Øöºžsóß©T}ßkîõÍý5_ÝÕÛô\T鮊¦#ûJ3ÃÌ,©±Ô–EíUŒß1îè=ú›5ÇÜ,ŸôéíS2ÉžüÑ{¼¯Ÿ¸·Ø¿[03P©ùPú)…€ b\ü”ÿ= ÀÂO®óµöM;0¥ýTKœÔusëëÌ>·þ ÊK]؃ÁúÈîtˆN}d÷fYê#˽‰)gI{Eð=ÏzöHóýPûnöËüw*u‡ï‰DâÐ!ÏW†•œî£«µ¯X)m~%ˆ¤±ê©Ÿ–• öŒBô6ö“AãÆƒß]ßžö×a‹öí¡ó?œjœ¶{<î2Õ$R3+ÅJñ ðÞ…›„ r(lIá‹0á´UÅÁÞZì·uTªËÝ"Å÷î÷‰ß !”žïem?Çf¥ˆó½S;•"Ëw÷;wnù*Ù½êjJ1bûô+I$·Úî+mü¯ [¢kí+ÞqÒ pšyis©ù³)€Ñû¾µ€ýê—Ø+®Ø2©8%pîM™}'ÃÂJ2õ3J듞`þÙ›S¬„ÞuE¯D¸½Ö–d a¶kuöüt³oLDø^¨Ž`Qà{¡Tb¾—¯qXÞÛP#Ë÷.ìA ßÃ4¦þ@€ºÞ¥à»QŒ¶Ð9ŒF¦U—‚$ )„%³gtd1={ ¥Ø÷Ù×z[Ý»¿ö€?;þÎÄ’ƒ{éºø$Pzжï ß·;:] N‚ÿnöÄ*ͳŸàÅY›Ÿ;ië³3¶…·eº°z¸±ö–½Åb±k¿ýf§ÎLA:‚•ï…íõXF¾|寔|/SËßN6ˆ ß»Ü] R|ÏÊÙë÷7¨ë[t¾'’‰D2aÜz‹&#^—BH!l‡¤Ì|(‡ÍÜ[ZxhƒõÔP§Í_\fðë[ìë?~ëÐ~úª¬ššR¾"_È<]ºÑd0¬ƒiÿÎgþ_­yB¥è±©Ûþ1içߎjüË„]:l¯ÕÒÈ_ïÆ\7Ö9Û^À^eä{1ºø–…ïEªé(ßË1¬£K­c"Å÷nö‰ßÛ\A­ßW ¾Î]³YG² »$¤ ÛN›uSÓ3¥ÂeX;f ÞpýJCïž4ðW“››¨_sÏï L×ö0#3so}xy$ˆ¤úSòy¯¸èáæ™÷o¹÷Þáûî¼ÿ×…”Åb±X,ÿsRð.¾eá{ñú³—˜ïE­Ö+ ßK] Ùͦ`Q¨,TG°òÖG殩ë;õ»:î?ª³õ‘f²èo™Y½nzAšUMË c8{U’@«Àƒ3ØTR2›õÕ¶­Ç?xT¥§Òy 5|­µfKŠ 7,0æwOÍÝBl{~ºUQ‹º‡ÞæybȤ˜{®}ãró9áû"¼ÒÜÜ|Æ•Íól¯?{)ë#K0y£4õ‘¥áL±ë#K; »ÛíËîß Øë±Œþ½ÃªÇú= êÑ¿›á¢YÙzhÞm›¨­ÅR@+ø~0¥/õ“,%-ûôã<ªÅ2 ã™ÙÔA¢çžÓ$ï$:Θ%Ö+—°é½KÍ=Çô×#N}ä¨ÓžtöCáóDìˆ@þ^X•À¿—ÌAÛ¿—ι°‘o¹ü{1ºø–Þ¿ç_Ï^7`.õ;‹åßøáqs\P€˜²ð–•}OgZõÔÏ0=7žzñ®ul}Ô_'H™ŽnÒ¶=ÈÙÙWKÒÊÏ­0á¡#)}OæÝÐß3£®µÒ¬˜¥@¸$ =0v—çÑØËÿü´Vif*•À¿—r`^ñü{éÙR$ÿÞßÞX"ç^ØíeñïEêÏú÷ÒXøNÍTªolP7¨Xþ½aôT‡Äëe–ÇX6ЪäQùð¼,«l&AJ’’Þ½h€ Þî\ B°6<ږνÅ*-€ ,2°ðDP-ü;þ£þV¾zÅÒ—>ÿ)*‹ŠíßK< µHþ½,®±þ½¿½¥‰eŠ1|£Ä|/êäж›ï]˜–gl{Ýà¢ð] j;Õò‹ëKA®K–½^ê{ì%S50ªßÃý«RÐüó×8òocÍ£Ú°íR™ŒNáÝ”B*Å-_5âH+Î"€²ø~ý¦~æž‹žûLþ§(T)G¡ïer]p¾—q=¯°|7dßå,:Ü‹7V©d|/ÁL¥¸=!"%4­U¿£@ÝÂóÝXõûÆMq„¸é£Å™¿2V=™Ì­×rp6¥ »4﬎þǨôãÛqï:ÌÜÍqúäûdæöµ¼cz[“AÙŒ ßMg_±éO 4ß´­Ï×öôðΓWå{‚”–ìÁ+ïe!{ðÒ…ã{Ù+5 Å÷ì(¶s/öÀ¼ð½”ÓòŠÊ÷î ¹®ßÞ nXùÖ³7Lœâ’0£‹ü$ÒV=ktªÖ*£áŒ¹ü{öfÇ?ÙŠì”å߃êšÌÖëÜ™C¾·î %Ò‹«¤9åÜUxÑ·ì•ÿÉA9ȼnŠïA|É@!ø^v²uŸï™dGQá^šQ¨Eå{éç ‰ïÝ!»Qý¶ÂóÝÔ³Ûð4{š“‰Tð¢2ùìbÕÌ$(4ìRÐS3>pò¿Gh·Õz&]Ú˜•ÉPŠ”¢°ÆóX«Ô3žÌ²(ÜÖð½eñŒý´ ÿ4ÿÑ«ÍÁÜ*ÙϘ²³k —U»É÷²“=8Œîñ="d7ê߳ȎâÁ½”C®Ë^YXœïÝ'»Qý–u# Æw)Èvá{H&¹~ì¤$ëÖ. K³¢Pˆ3’;~+€³^‘™´d¹õÌkàÌžëa段fm&u(ÍJ¥EHm^mQ)ˆHP ë:¤ü ]ÚÔÔ4ó’sŸ“î“ý´É§OnpæÔgNí âíþÓºÑD„ìF]æ{¤ÈnÔ5¾·&;Š÷R’=xÅ"ð½ô¶=ýÒ…ã{¡Ènð½vnÝÈ|Ÿ‹ï̉f¹§µ—òéd×Ì,R@áþ÷c·PZh†fèŒ@å?V¾‘xj±ã)\>5±Œ0¤f)ð=Ó¼Ûé†b¦=dVñÌ-·Äãñ³>ó÷Üg#²Ÿ9uçSvž>eçi“³~5ëØÆÙ“‚_Z4ðÅ……°Ï>~oîm÷`ºÊ÷H‘ݨ | Ù:Ë÷6É÷~Ö†YéɼnAù^F²P¾–ìFõ›ƒ²÷ºQÝåûó—ßýÂw‡50wžä3_¿n±i†n’jeØÿxø®?OÜ @3ù>¥¶¦¶xæ/íßxGï Â-¬:¼-ÕÏÝ”9f'-^ÆX%Á((d,® Ìw‹xS<ÞÏ}ºàÙO›Ü8{Rã¬c¯8cë•gn57¾¼xÀÿ-`®ÿû½>BÚçN?”ÿs¶8¤Îó=‚d7êß#Kv£üùÞÙ:÷Bñ½\d^½@|/;ÙƒÃèß‹Av£úM õÔî4ß[#~Þgî}å3÷™(ÆcíqÅ08zèÙ7jy¾O/ÂA˜Î‡ýtëÈo5®Ûk§Ú¾?tÔˆyç΀i–Êd2?L, èÌÊjÎ0+3Í»´‘™Ì#ÏŒkÖ/ºaëòx<~ÙÍ/æ8ù“„%¤õ‹¾œ"x¨“ãó–dßHÂ"ÑjßWÞêß#Kv£<ùq²åÃ÷d‡ûn Á÷ò’=8†nó="d7ê2ß‹GöPõÔéßÑNDóÆçÞºúwwÕë3_¿q±­ZºKº{Ø>#ŽûßÁÇþÃpÙÀú¨ûV}×z²ÙŠlß±hï®-?ÿ\_¬{ió¾šÎd2)åÜMÒÂÌY|÷Zòݶ)3|¿iÓû7nZàêÛæ_}[. äOös¦Âzá½¾æ//0oÉ€éÇ4͇'tëeë³òì›®ö3u‡OÞîáåÇ÷ˆ“ݨC¾WÙró=7Ù:÷îó= dޤ|ÙºÀ÷ݨ~}€ºqà{î%ÖŸÿ½™^$…È4ìRЯì0&µ×ßóèûuÛ&>´à˜¿,Ž/l%l%l_8ʵɶˆ_¹àÝÙÏíÔG<ô뙌9Ÿ|Ÿžœ¶ÕŒXD:X# –Iµ6|Oo^ Ë"¿´a‰y’ßœœû]w*imÃo¾ä£à ‡ “úù¢ìÿ¶Ÿž/ ¬ NéúZ‡|¯²åà{‘ݨ=¾wHvdõ–1p7 ï”¢CöP]ø[Œ ÙCåß‚¦ddeà^¿¶`-h¼cfUUUUuÕ/z “‚¤ÀÏkÚ¶>úŒ‡Í®9ú|Y”U Y´z*o¿­âŽŽÛªÙQqGÅíOݶ„Gÿ§VTy?©Mz<üÏ¿ª­­ðÒŒO„]yÿ>itrÿä{ƒLЯ˜eª3¥éh&™zM£ë—/JjdýÛQ“ë·.ù"ÔLmýFÐI²Ÿ’à™7ìÌë.ùÈIǵìéPÉf•hö“qÿÞgÏzø…§:ž|5™Ïkµ©öZÐTÙC…p[ÐTÙCeõŸÉ‡ìȪ–éš Ù‘áßó´ðQ&;òöï¥';RX¯;¬`%’Wñ.så»zô³ª&GŸñð½?žzï§~vô%¬‰54%šÙÓ¾°}r‚X†låØäXÄLÐ4ø§ë˜yý•uZ7kÝœéÝM¶“ª™ z¸0ûù‡ûÃÿô¼ýk{¿üéeŸ:ýÅ“&û{­›—~æ–]_û±;ÿ4摟üÊÒÿ\Ùø³+㿺¾Æ±©O/9 n‡©™yýªûƒ#oÉ÷.}Ω6‘|úµôºè5³?L}>!hF-ùNtÙ -zò<9ï CÌ™]“ÿë8gÚ³Žßþ1ä{E“=Ô!ClÛ¶­ÜÒ];Ö­ýq£Î³¾í˜|ø}²‡ÊÁ÷ "»Q›|ï2Ù ¸ÿᯟ7~ck“Ÿ¸0>q!“Íd_öÁEáÖüöT¿ª@Ý„®ò½%Ù Ÿ#FŒ1bÄ?;µé_Þ˜w¦¾}d¯^´m§Þ¾Û'[‘ã“­\ŽKææÆÞ'3'f½òÛÀ S˨=þï¾OŒÞ#X'˜Rb­´û½µ_Û5ôË{%õ~µÚµéñ;Fʘ7ü»›Q框|ïêT‘õï±å„ "B*– ò™V-Òžš·sf÷Íýbœ"Ο©Ï?É;÷Ä T[[ûåÓ—üÏÛǺ}ê¦ÆÐ¼{IÄãPR…e3Ž×Iå*œ¦¼Ö ­é—ÎèçîyÂóÄñþ‚¹ù o„cþgë?ÙÁJh_LjHsÈuH¸žp|óFý|UöÖLµõ.$¶v–ìΪ&’O½š>¶+§/IÙöT23©Xƨõ³qfC† Í™ÝóÂYÕsNuçœÚbÍöùwz½ð^ßÔW¨öWÊê὞=Â)3g/ˈíB)+gÏ“ï¹j§Úã{Å‘Ý(‹ïJv£ïy’½=šŸºªvÖŠQû£/òG0qÇ_púç›;œóâù³‡ ¦çœÍœáÜ?½ââÌ'¯+·ým€[Fç›×ð=€e·»§¶¶¶¶¶VXŠl M®M®C¡y÷| Hž‡õ›ƒäÝqá8¶Ïšfi€)̘×Í€ûá§<âûÔŒfŠ5MøÕG±¾€/&õ¡¤ð•ßlpúËÏ ×®'{9מqVæqÚñ¥p‡z¢›ë«9Ä$˜²m{ÐÀÒ?¹{PcŸÁ÷6ž­ÜçÌî;gvïÌ[ž~M?3_<ó†ýÜ[ÁìK‹2èô)aSÑ#úZ¯ V(ßÛ\A͇ïƆ|_¡d7 ù^Ñd7ŠÛâ{?²Tc§Ò˜Ð•Ï\3ôäUÃYƒYëÿe­ðÚ„MóøxÐx§=~îiO«XA‚)3–Q>µè“±3˜\¤ø~ù’‹}}ïìC†¸áül_ß¶ö/ û‹vêgÎ8fíˆ#f ùÙJØŠ5ýñG#\‡®ùÚ>Ö‚5ù><ãÆ’"½z}’åºp]¶‚‰ÝƒpÌÍÌÍH%Y“˜øý®ß»ê2V‚}:èáÏÚU°zÅØ‰IÇÜËѽlÝËÖ½’VOÿÚ3κú¤ó‘™³·_?Ó¦æÌî ȧælq+ˆ<ƒ²bkÎÈ.›aÖÜÖ÷¬§æ|ò•¦§^M<õª×æ‘0ƒt9{…ò½½Ú˜Šã{ŽÚ˜ùÞñ®‡°ì½Ÿµ¡¢Én´ãön_îbOE“€¿û_¨ê36w}ä•Ë/`ró0i9ýÉó Ö_›°éÕ‰ÁÇö)Œ& ÁP>†c&Ű1 tîW¼‰òXy rA®yÚªåSw6øIýbÕ÷WëßS=Ž»á‚Žøž‘³·W?cl»!»°I M¿ûÑ ×¡ë¾vš¼8||Š´°ÕÏ¿s\üøþFÍ:\Ymí܃ÌÀuŸ8u’us“¢&Ÿš|b%Ø—®Ý‡{;7ß³ÕuqïíC¥ëIד®ßä'­j¥â‚=©}ùéc.BfÎÞ9¾g§íDª‰BÌ„m/3bóSÞ“CÌêͬŸš·¯Ããxû½] L;®æÇ÷ÜUÄ÷«só=¯-m»ýQñU¢‹ý碣øöù ôí3xbE·Ó˜Žëß™.]üI“›Ÿ÷òù>»ú—Ý7ß:ÿˆÍ].<4zÎÁѽÇà´]CÓ³ö&囈¬ 51…^Ðýå[\ï`óóÌ3˜_=W³Y0fo®P¿´À ¶ÿ6Z­ ¶æû÷®\[[ûà3ްY*4ï¬EÎø>)Ž:Š4aéʤ°•벓2ïæ“Œ9b™=£¹yçÚëØì í [ÖXvor|éx® ×…t=áz;÷%÷JÄ}•L‚}©=ÉI°'/9òöï.o¾·‘[¶È¬“ ë23Ke(Õ©2û M£ÌœÏßZ&Ñp[ÿª‚øžO={Eð=Ïzö|ïÄ~åõë×=ztþ‰šÂ4¦¢[Àgåì¹ùN €Î>8\0Í´ý¼'.ú‡¿~ÞaÁfœ“W;iÍÐС+$˜´Ïf¹ÒÄ2ÄÆ2—¾w‰ïñ¼[„¦·:ãÙs¨$Ÿ³«¶Ê·$¤VŸÿäÅîüh!€[Æ·eÞÛªA+¾ÿä¾íZÇ¿3·†l-l-lE¶Mw~»¯ëЭßK5A(K3“Ž!-yñФc×…°§’0Åw\§uâ¸gnüZ|û7ý=‰„ áÎ:¢'ûZZ²ûBH%]ï{¿ÝìºüËŸô®¿¥±Y8¾Œ±ã"qØ“ìAû–ömíÉÇ.ßó…» dìaŸD¡[—LY9{…ò½ÍÔ|ÿË‘˜<¡=Á!ÖO]>ꔵЊvmkÞµ½iWc“öyæŽ!‚éw3t`޵ ÛÓë=¶P¿Öc«Ð4më cáO{â¼=‰ƒ»Ôq >uU-€×Gn‡¦ÓŸ<ïŒ'Ï¿sÍB·Þ’ïí=x-ùnv–¬“¥…¥…˜÷ŸßÖËuè¶h¥¡Â/Š™T,ÆnŒÉñÉRÌàtYd ó~â'ÿ îòùš›÷mþ>+!E/!z±/ X¸žp}×e· Òñ7l‹ËjH-ù>|½{8Úƒö¤ö«¿µá3ß^Å€mIËʨE±jÀËþÊ{Á¬(o’K;ýo3XL5 ªNØW>}åæË>Êxª´O?çäúÙ×d½úeÇ/ºòÄ%W²ôÚÓ—]wΊ/ÎYý…ÿ´n¸ÒýƧ7ÚŽ¬ŠDóÞÌ^ߎ8ß;»5²|ïÙÚä{^pÏŒÚ ÕB²Äjsµâøž£6¦=¾W-ŸúDló5›Â[f-u‘7ºï8‚Ðd.¬…ö¡9ÿÉW=9ïÀSóö=5o€óOMo5jœHKHK„¶é2ű»D™ÉLXÑxöÌjf~þõìVïWœ¸Ä²¥í´¾så°qdžbö¤Æ¬!‘å{׺ Dï] »Qk¾w ÷Ö‹¨Ç÷µ1Ä÷«³øžUûXí['¯~ÊŠZf°+@«îPMÚgåC0 ¦£¤b­ALÄä{üvÍ6¡iòæÊcí¥˜NJ@”è;ž‚ÂJà÷’ÐâÞÇàäols’=x¿|¿ãRssß#–UmÉÆŒ·®Êqè¡GXg_­á8ìV±©ŒÀlÚÑ)˜!ó~ˆÜô©gY7Üú߬ûB+Á>‰˜/OÄç3X IDAT¼XŒc1¬ÚÐ$]5f¬°aƒJ¢!ý-í í;7ý` ¿ûöÄ?üþ>þûÿ>a¶D¶P«üýéWö8ï”`¬ös¯dæsföï maÌ»©š K#Á-cÓ¯¸܃/­?0>ÖòÏÌü@ ÑÙ¥íHË‘ ò’ªvˆ6Ž?}Jcæ¼Öò½;}c"Å÷.“Ý(‹ïÀ½½ò˜ â{‡UÁ÷<ëÙ[ûwSøxÁ¾Q“× àP¬Ñ{4Rd,¼ò¡}>nË ¡i~-a,ÄîšÞ­ÙNšŽÝ<@yPH“ vBçÞg ´ÂîU|ÊŠÚSVðú„ͯMØ4kÙè{ÿ…F4O¯ÛšÙƒwÁwÖqÖñ`YÕRÂR¡yÿÆMŽ%ð¿ÿL•„3fÇE<ô#[gÍ:ÜÇ”¶ÄššiÛ®HÆ|éx2æýæÁ]âþœ9’l>ì° ¦P ^‚µ/ÙwoýÑhüö¶ÃµgÐC2¸ÜÑßM~ÎÉ=ΞYÔ÷Šð·Ò&iQ˜ÆdldZv˜ *ßÓb€ÏœcæÞH‡š{Þ ?©ˆ`»mcÝê)xIå'•—Tæ-Ðz`"Æ÷îw‹ß»Iv£L¾ç‚{îÂÇŠà{žõìç{§ö †|gr˜sã?N|,U.fìY“6ïЂÃpFQÌhkbeøNB“`ÂÑ›ú”Çd>TʹkQ3 Z#(M4øä‚ÿR_9r=€{ÃÞêDÝ ]éOß;Gëæ^cþdœ;ÙʘqÓúñâ9²p÷}Á¾0¾z³t\¾÷Ïqá(aû¦Î©X&\c½ñ⿲NÄwÜÏ*°íÂÖÆ³ Çß{À·M–ž81 ûªU¾×c†Ç¾ÿ_nûY#3îüú8ö$Y ¤‰´rí!Câ™6ÞL+¾?ûZ†ŸuRU–Ñ––¶ î³MoUÍ,•A2Cßýâó[fŒf1ó‹o6‡ÏvÃ…«ü„f ~qep ÉÞC輤 /ácÿoÑ€— ÌÆ¾ª×cÙù^²…|oîù”´GœïÚ©Y¾w¡oÌ|ô­O,¿íüƒƒlåõúð¸3Ÿ?oöçp–X¡¦H'3"Lf&mLšVÖø~Ô–A¤Hh2‹¨ì9;)Ñ«æ Áž5ÐH}Cˆ%çJž²bä½BZо“­˜ãÌqaët8“2ï3N¶­ÛZ¤Ž ÇaÓPŒˆÍV&}àW̉ÝÏšášZ7§¢vAR ד1O:þOµË‰éŸü¤Ç1ǤHµÒ~Büä¿™åÙÿwË(ö% ®O¤÷$GӲؽF™»ñïÚ‡öYù,4‰ŒXfÕàFRtøÆþÊcí12rvR¢çˆŒç³•1ñ fœòÁè“?ùÚ„îý'dÓ‚[ŽÉ·9¿ê˜{ú½×ôi­›«†× K K“¥ÌUsägA¶Gþ†Ó:”l—ç/L˜†b¬‘Ú­˜÷Mâb։䮿i%X €Í%áªË81=*gôáÒ¤×L?­YÓ÷¿<ä'u#´oqÔEÿeq‡Ûkø´cš¾vù†³NéçĤãZNÌrb–ãJ'fIK\1}Éçf-ýÂ¥U@vý»ÉOF ¡=7Î]Ú"HfÂ&-ÿ ‘¶ðæ™Eî_»jƒŸÔæÒFãHÛ‘#ÝܳgÚv£¬á¡ÊÈ÷bôg/»/ z×Înî݆A¾w¹»@¤øÞ.¾æßþéû‡KÐ;Ãv ¢ŒÀÍšªV¸¡R•3Š ƒdæÈ:ŽX7n7k|4t7)¿©_°1U ÊÈÙ{K“}ï:è _^m÷®¦ýûšüÆÄMBcæÊa'­zï? Ó|øÿдŽÇFÜA¶¶&Á0ˤZ€1ûr<ú¯€ŽÛ mŠÃO¸s‚HkÐÜFí"æ ד®¿`éþyó“cÇZŸ»ÊMux!°zêiï_OøÐ¸í‹ƒ´/Ù“ÐDޝ|E!ȉYvLÚ®eÇ,'&7…øÔå³×_¸´ú‹WõÊâû‹o&À¼øÊeé~ëý¨1ì‚Ip0û3å¥T,cßs(q‹šHV¾IÞ‰´Ø4b¯CrÜæ~Êc?Éì¥> H‹CÒ_´J}„¤Ì;R!©j¸/ ’Äà‰6CÇ'.¼óƒ…n™4õ{×þÎÜÞí¿¿ŒsRu ÙÊ”»¤ÊÞÓæ]+0s,†˜„Ô®M#† xcABØŠÌlª7äGú,Ö µû9­ûBÆ<éx –ï{÷ƒýdñaãm× §v±ºó×M`ªŽY·\ן=iºÇ—®ÿáÒÍZcÜhmõŽ`Ý ÞN[xi,</®’qõÅ«z}åò¾~uzºÓƾæb©RHi !Ej¡8µ¢€Œ3 ª³Ž̼~‹š5ããëI¥’JJ‘IöàJ•` +g÷’ÊKêÖÎ]K´w”œïÅž©TJ¾ìÈ‚{wZÇD„ïéVv¾wÁ³?EŸÆÆË§›5þmêcA tjËzf2s`S@dÖ¬5z ¦Ð¿³ ’wRD ¤ Êc•dCü0“©]k°¢ý²‡æÝ] ñjŸ­â¹ey"L ß¾=À·¿ûÖç;~›¿ùç{HÞªuÜô-Sö|,™JvàüsÉuñÒË Àqá:8e¦¿¹$.e3™Í Íͬˆ}a6+-X±¤"É;v’ãà⋤¬þòH,N?%vÓ5}Ù³´/µ'…ã ÇOôû*‘18™L(o¿bÍVµHÑÜr\ãå[Xxi ¯Y%ã*Ù¬¼fõõ«7~熠ĺÀ³§‰ß5Ô8wišÌ˜ÕVÁÔŽŒXF¤àÎÌ£†1ƒÃ(ÆOh™v+ƒì"FL¤hiØ•—Ô~RýéµýŠÏŸ©A2k²k–JÆ÷ÒLË+ ß‹Jvd½ûMÁÊÎ÷öz,#ß»@vbϳÿ¶7÷ÿpÚ¾XøLìä½C%‹ÅÃw Zä%i󞪜Ü"™áq›û7Wûjµ›ú¨$·¨Q¢j`ʰk°"­‘ ÙƒbK)„B ñFÿmâ¯vëÊfC×3Ÿ;ÀÌÃ>¼ÿ–]jø›×$¾qõþ¯nGŽ7û›½KÉï±n¶%ÕjF5ì€3N#×Åëo°íÀv€ÂÒ©¾`N(ï «D,þ‚VBXJºjáò½$Õ‰'ƦO‹9œ Ã Ô?MîݧN>Ñ4¡§ö¤¹[ W%ú}ƒ„è©Ëñ*i(yHƒaU '&˜e°ž×Ä,í³×ɸòšU²Y¾³æu›€Y'´HÚ$mѺ5Lf,c’™S¦‚™7láÙÓ·ø eœ;-±.lG’K ÒÍÜÚ³ûmî92™L•€ï¥œƒZl¾›ìá^¨veä{Á»ø–…ï%»Ù¦Ÿ¸pö®¡³v5è~9¶yÐÓÝuWÁ”D3 âY´\V ö *°BÕ€tåŒöa’ia`¢J%Ù÷ØìQ2iL¬Æ ª"­qàã4ÖDB )èAÛ%ÄéÍ#¶­N0ÄR )Åù¯\ðþ¿®Ršî\¼ÀÁŸ½ýôûúÚU›¾ú™Õ·|zi›ïú®'ÞÑ×”½ó~Êt À+¯iU1®ŠÁuà:0s†’ßù nZÍ0'X'X'ØD,\áŠ=dééÓª˜÷ªç8tÖ@/¾ÔüÔÓC?ÅV>i_jÏÒž ¡…£}nQOþñÀà °—T^B%¾—𽄯š4ÙÕZxs%ˆk\é¸cáS|ß2ï=Å:¼òΰTìŽÌÕÌÆ¿mçžÌüÚB0`<»J*íkË‘VFÕ£U-á ݬ[çìñ¼=)ëlç?—µ¨|/ý„ëâñ½d‡{aù–…ïEêÏ^b¾wgÕKh/©OÚ1xÆÎ!†Ý}¬¶dÆSµƒ§í"˜>±‹±¬:w¾Tõb÷°&²?»dkå1û-L ë›&»VÄ©ÚvÍáH 6dRH´}M‚M/…”â!;çØN„9ñA—/½h÷‡õ^rÇ…Óö®?gÿæ9·¥¾rå‚›¯hã_—Í¿`ÝŒ~ט2GÖ‚™‚±sŒã¦ëB¸.V®bB@X,†Ÿ< 9á&ßcàƒu»¯ÜCRŸp|•!§ëÂu  ×^O`ˆÚZf%¦ÙS{Rûá¨æ>߉þAÿ±·ƒ°gÇÕ¿ù×8/á{ •L¨dByÿ_wçeW]åùïÞ¿ó¸÷Ö+õJ¥ªRy1„ Ð>…ˆ€(ê¨íhû@glÇvÖ²m»»{zl]öÒ¶ÅG£tÛc÷,Eˆƒ6Bä!F’JR©JêýºsÎoïùãwî­[© yTRavUuî¹·î½§î©ÏÙçûÛ¿ï.ÙxÂjAɇÉR9…7•B—ËÃe¾‹ã»ûpÜ¢›Ïc¯Â÷´s0ÝÀ3µ÷?ZóÐ6p厕ATozyŒÉº'![’¸Œòj]¬Ì$û›7„¿œÿû>—“Ã÷SOv'ƒï§†ìpp¯_xåìZ´Ÿb¾ŸÔΧŒï'Bv¤ù𯑯%]{ mm_Û…ÛTÑ2´}kã!—¼OSÞ“½•´]«Å™ÔŠ †$ÊF8T#-i·6LAˆ`¢ÕŠScØð¶ÖCtæS- %NÉþx{?¸ªÐå67•võ5©ÐÒÚ¼-ú¶èïOþàŠƒq{÷‰wÜÿ_núÙÇß~Ge¿þÓ-"Wö.*aÂTOÑsVQ&Dïõ=@±n­1F·>[kš¶[zâÅI2ÖµæÜomÝ–¯»Äß²¥àõ¯Ïlܨ%’ÄÓØƒñ-‡I­ý¼ïkT{+HGû?ÂA ezÉV$š8²I^¾š,µS)¼[qˆ·‰Fl\´ÖŽUd˜[¾ÕQ1™Ñê¬ͽº©‡-Ù$²It8Ù9t…ñdKâ’ôÃÔvö(¬9‚örôi{%æ¼þ}vcvù~ÊÈŽc²ü=¦8e|?=•NßOì¯Ýô&› ® £EG—duÏüÕ½óEpÁÀ£ô\×ÐáÉ»à°%lH= Ä’$05¡8‘=¬¥ ~ÚjyeŠì¢êFPÙÐö¶~-ªY!Ä0†ŒGOtôaÃXçò]ư=™kÅ=[ôŸß×¢BË'¤èIÑ“¢/E¿pðOJƒÿ£²§{Ûn.·ÌþΦª“Úœ ¡z~¢¶aÙŸü¢5Æx:®‹EK¹dçÓÝã0rÁù¹ ×ä*¿ؾ=pùå!€-[ *´fe½ÆF,sp˜˜À²Ÿdrm¾oƇÿÛwþ‹ïlºÀwî{MY/l6~#›y ú ë¥Vbš µ^µïXEäFYK#.»|M¹à=5™©2KÝA /Èé#OŸûÈ—³CÕ^`ä;²ãH9»UÑ gÂïŸ~±jæ¡5³ÿßÑÄìò}®ÒöJÌßO%Ù¼ò8ÉqÇP²Øõç«ôršõ8eÝòư¼/ÖãÅ“q„7Ùà~鮕›¢PU—À@$D´j_K‰„Á¤ÊÄ¢RžÑn«$øòIYàÅìe u$”Ê¿ %e¸RÁü0À)Þ)­}ä ú xñ“bÄ09²?ÕÞàòÑã“wu–ŒØ¢’‘ʲó¥ù+–õ/oßu`ÞÔvO£þ/¤ëµŸð‘nU-©3ôï“Ñ[¥îÞè}*.—zìZH™2ÙÝV6`*©`ÇÞ<±^¼šó‘ÛØõ‚ ôK{;Šk/ ¡ØòxB®ª·%ObÃ~ÂeOê»>D”a~at,×3|OåW-ƒL‹EFHÆÔƒH"„Œ!õòP˜M["›°5b=± ÛDâ¢`Ø^yÑØC[P®‰LJ¶"¸»wKDÆÏ)™À_|¬¯”—$"ò|ö|ãùlBvÇR”8–i {lÇnÌö›?=sæö‰÷,ؽ¿p4-œf†ß|q<øÛxð·ôÇsNv¹—åûÎ÷=ì@qŠÉŽ“—¹»8©ùû)îƒ:çõ‘/.ïÛræÁg„$ìµ°±NËâ#Õé°*ªj"í– ®ØÑÆêç$È©Ÿ%±‡ ¦“W+õ1ª ÃÌl˜ 8™T0“1l<~¦cDëG;Œáw<²ÿùÂ{¿¸ç·ì•’ç2w·<ûÜU:£sÄ–üt)zåÅ·ÃßÄÄw+ûž·×UÜÞ!dlª<Á­­ÔÚ:•Ì/^Ô¥Z:°ÿb9ïÜÃÔ? Î[]¾K Â’‰ ûÖ„ û¶¶ó½é«O~þû?[Cì]s™]µÜ;{yàÁg»†õŒôŽöM Jb›Ä6IÄ&ÖÆÇ¢Q–4Í@3&Èù5¹4…Mà$šX“Xl$vs/€/|sA¹f¦Ê÷WTK®2‰lR²e÷ã&%;Hq„œ]UƒœÖxa7“ì×lhØxå¼ÝûKÇGv•üý¸SøÓ„ì.N$?õd@ÿ›ckÓ~áà>ëùûœ4¹žõ£íøÒö7Ýu*6Ÿµ Œ«g×´ª]JzösóË8¨<üÖ³xTD½4/‰–I8ÛÀeÐÓaù»Œ«M$øh/ô¶N׿“ØÕßœz›”F¡,j,^@^À~H;šû,Þ=Ïød|òzvွhtAÝûRÔ¾í3ûŸŸ¾°º•=|íÅ¡¯ü÷6À¥¥ºre`ô¹[ªS{2Bf*Ùƒ7©mÈäãLüø³%¯qf¹UF¸CMªè?Ô¿ò &Ci>ªs—);v&q„óV àñ­yµ|Þ™¶`ß²'¹¶›ˆ2¥ü·¿wï/Ý3¿ã*bâ'_ ?óbà=a˜ÊUêìyÓV‚l=&LŽ««µ""b%*–ü°ÆxlÂÒ_}«ýó7÷–&µd<°:%6I,àøooëüüGzKù$.Z—°{>“ï—¡„)=NÓvã³ñÉóÙøüÕ.™vh]QKeÛÉ{œ…¶š²k Z‘½îÇ”¿Ï ÙqjàŽ“À÷9!»‹Y<掃ì7½Õ%ˬ˜ººj¤ãWõ} bMûä9Ð'°`ùóÍN·éY<æZÔ=/.¹éHLÂa=ŽÈ÷dTÔ¢á³=ò¿° ô”ìDiWçҘˆ²8ˆ/šè &@hÎ2€‡Áú¼2Z£ÜêÜ<2Ç«ôív÷&¿o0ð)útË'S“³– º Ëç_lJ)ÍzDÊGÁº$*¨•Ç·í*X{þà~h¨ÀèðØK1ˆâ:ÇýÁ!iiRZ?ܺ5/Öœ»´–É·ìI¦å-l²qñß½ç?ÜcV.U">o…7>©woNª?©÷\ö”ŠÆ÷i¬we0€HËô%±""Ð@¡D‘ðíw5½çêCqÁ†îDÖ(â¸d<ú›ïu|öý=¥|RQc`Ê:»­Àª&à Üÿ×mSĹê²\š€*v•³ñGÏ÷Ó“ì.*ÉûÑ ~®ÈŽSwÌ*ßçì.*ẩ|Ç—³_³é5ž‰ŸÐžÊÆË·/öÛ"8±]  ß5t]­蓼’ ò5qÓD¶ã¥†8fá –fÂïÞîÔ|Ë>C_새PEvbDãp9;y(œ7ª–úIÐ0” ² Ö8°ÊPÒ$hT–Œ?âºÝ *m«!¤Ü,W]Ýðé¿  |…>õág,ã…}iS‹*\§9¾£|Áœ  Vž}jOÀçî 3æ7&D˜£8}f" ¤9;Ûžœ„ås–6KbسìKÐtQöÖ;Òž×,ŽÝõÒ³»éÚ+C›,ý×·¯Íä~E2…Äaßž­jUPxe;2ß©Âzyj’°XOU“¸Èù“j\HŒÏžÏ‰5 OK…ÄøÄªQ>aÞÏ&`·3J`£id7>{>¹Zϧ¿þöÂÊßç/Í:°ÏìÜ4[qô|?Éîâ(ù>‡dÇ©„;f‰ïsNv'È÷)¹úÎë!ôÀk¦2÷ãKF‡ AËÿÖJ„Ô´ü™¶~VbådR 5±ïQ£xM[ÅÛ#Ã=ö„Eßè°ïS‹Ù»¾º§0Æ¥1*Mri’Jcç)ëQ\d[¤¤HóöÖk` 1Å™¬q¶aHÁhÚ’2PY1 ˆß–û‹¿-†‚ -\ ÜòI ¿üÌ€ntÀåÜŸÆ€.[¾Àþ½;¬^Y†{ï-€¶F'C;¸˜Ü ¶¢Æ¸D~û“yZÙÕÊžõ›/"Ê0g¾ýÓOÞ|ý¿Šg/`ö=Ïü¢9ô‚£2@VàNömQ«^è®zÀLì1rU¡IÁVryòÝ{M?@6òDÔ&%‚Œ'‘%(Ø€ÈxøÍC×®¹èÎ0¤(Ÿð|Ã>¥Ÿ<rÕ±é¼S MØ+p¿åíîUÞ°>pWdÕ=NR ßO²Wâ÷K4sKvœb¸£jpõøš½Çw ž`áãïºnó™ûlxfÉæ³÷Ø8¶dàФC¹ÓŒ«³x¤íäÈmܱâ@c)0[{3 ¹Ãe÷ÆÏî=ô™.'ž¬ü×=v¾o)+°û…O·Û˜\IŽÙ놢IŠ (Ø^§„0ï“Q6(ÖÄ H¦ ¤ç;~þ¼ MT!…‚¡¼}ÄZ<ñ`¢Š×¬3ÖÂ*Tpßýâû|x>õ^­©Å¦‡)Åu w%3–,^ à`ïnVy„Ìý3l¿ü]VB~BÊÖ„(®'XJÆeÍ?ùäSV-½¨}ks3gˆ3â0_XaØ3^Íô3gˆBw¢0Ìœ 2ûח’ ôtÏMÎvyÕ’$“É‘+t!DÆ2Ãn Œ—**î KâÄ6Qk%ôY6ü€xäڬ蚵wªÂ˜Ô@ÈMä‘X«î¬É†ÙfÃìO|±Œõ×]â¥2 è—N5 ?©ñûùþ*"»‹—ãûœ“§î8¾Ÿndwq¬‡ã ’ÀU›®ýõ=®Ø±è׫öX¿«­âøX-yLÁ©•y…øÃ29ÉÔJðãŽ*©T kER_ú½=™¹ âEìÿ\{Ü’Á‚Ïôǹ4‰h’eS3b•Æt£D(eÁ r=œ¬Ùp¤Ùz¨ó¹!Ô|$‰‘Xl½OU±ê N’¨(Äç(¶nSßǧÞfÜù¿þµTծܨ08<ÀÐP\Þ\q9ÇŸþ±Æ×nguµåôVå+¤ßé©§>{Y.W³É@SŽsX©õîдã6M[ÉþO¤8¾”žî»1-lìèp67î+kEDÆ#6œ~7lLÅ™kÏ>²ùC,j[æ¿ÀðØ“JŽ»³j6o\²ôæÔŽ  S^§ÖRg2×Ë{vãåøþª#»‹™|?ÈŽ9»‹c•hNO²»8úƒòÄÉŽryûå;;!ôЪý.}¾½BvU‚ ê¦³Ê‚ ªußšñ?ì j%Û É·;+p_qÛnÛ¯_BŒ‹ܽPý¹Æ`h>º„ –ýõâ'xì{óÙ· Š )dª @ý’GF‰Ö(´0 XÁJ %+µê76g~]sÀÿ`¹Š¾:é.ƒû§wÉ»¯V2¸÷CÎ[MUOc`¨ÀÄøÈ².®†ûÍo³‰¤· øîOÒQÓXËuã.©&Þ±3Rá•‹Ã’¿’8—)lƒ¦Br€ݤé‰3½Ëú×È6\â…mI±/M¾U‹£O<3p=jÌKV­jÊ.1Zf=Àœ&òåï̆ˆac {ï·^<øSRmjxc… ŒÙ91 j7ø‚•«?“îÈÕ°ÛD*£¦_úV{åà¹òBZ² DÌÝ=z8ÒgÀ°w?0v"‡ëc&ß_¥dwQÍ÷Ó„ì˜C¸ãXø~:“ÝÅÑš³Bv”á~Ùszxe€Ëvv–iNÐidwó•¦o!‡û@8.Iî}Ùy’k”Ÿ^¬Jþëîƒ#h©GT 8Aïz²!ŒAè÷‡C“ZS‡=ÞVö(X•k¯‰G<$“®¬Hk3†*ëdPc0œ Ö] Gò&àƒß/$6ÔügûÊ)yõ*ºêíè~¹…ƒaˆÀDz¥UÚ‹âР T*.^À |èFàyPEl+ǼzüÝ÷ÍÄ„rXŸñǬ²ž{A èZ²’9Žï‚­²œT‚Rž¯(ß[IÛ©þúôB?Oò`>IDATE¶é©Χ ¥3÷<ÿ;LÝëΛHO„å©gî™»11°aö bìíþnwßÃ>J„™‹˜jGJÛK:Ò® ­F“[7\vÏ\[cqþº;]l§°é§µê¾âBe"=¸å0—É—7ÿA=ATíÝβ"_Í÷W5Ù]8¾wuuáô ;æî8:¾ŸþdwñûÐÙ%ûúÚ ä“ïÄ÷Ëwt9jWçé/wªÞbbH‚¦¿Ü_Ó(µÍšIX3ň$ ¢V Â3 ŠãTSƒîO,©ñ..%=!1È€ @`£dfAi•üe¸—A䃌'õð”žtöU$‚õ—LUpÜÐ5ÏŽObó6îéÑ\55X´Z[©’°¾ø1Ä1*Ï`<Hhl#£øÚ휫-?9Âó»­¨×ÑÑ%’}þég­… ‰¥õçfÕ² «å[dG•ÇïÕwÝ ‚Wt¸âéÅ:UZ8-mŸ¾¥Šìî¢I$ư*^óÏ{&'àûH,¢q‚8Áà!pˆÀG~5†jjQ;„Â@G&‘É¡û‹l@ÄHW2 S¾éîb- MÃzêYÂJÛ"Æ%£­¿jî•rÑ]“𠌇•+ÈP.¡AÐ5W3ÙÎA€¡a]´Z[¦%øŽ_z«Å”K.|qMó02’øÛ?á)]žðâ^¨mþ¢†L÷o~'åy¹$â…Óåk2jiRÞ ¥yõ :U•äôvú;)©ÂÍ'@A=Åe ó;Ïqoé@ŸžÀúó'álÀÜÇä~Ué…¾ZD’'›§Bže_aw µYß&YÓP‡šzSSÙ|éú»¨¢ü•^¤<¯Ü®8Uþ+ÿ¼ä÷x×nD“{~}x¾ŒÚÂfÙ ³û´§>œ³oß>ã§“sw¼<ß_]dw1“ï'ƒì×lº¡é,R›V»_[X’öÌ×9œ—¯Tzé¥ÛSw©Ú‰&DªXü=…"òyp€Ø¦|ÏçQGM j<ªÉñ¼(“A6‡L:èï½¥“Œº<Ê ;;AÛ¹ý:KÇzVÇúü¡)ÑfÛ²ƒÖô´Q.¹­¶?±0°¨‚»ïÁœ{•áŽK/¡®faß@¹xñ0™AàÃ7B@¢Éa÷SyP[ _½Ýèî"ZÚ>ý¡€ÍKZ“/¡†–ë,¸¸kþžÊ0µJ:výÐïJVI¬ZRU¬U«X³æü¶Îs*pu_úcXÐ’¨biGidxûÁþ_NÈd‘l¥ÈØQ|B(h±ó¨®FQǵu”«õj-ú0¬ˆTÌ Žôß¼ó&3.6^Á䞇gͳÄý³ âØç¯žVQ­³ÇÖ“'Ñ8ìèãˆc¯F²c†ÅØ,’½:’Lqà¹BX¤U~ Jÿy]AäÔöi+åßò8©b÷–¬þa÷ž¼NŒ"fÄ b‹Á^dLˆ³QV €z¿Ü®@Ûgz´ÄÿØîT ¥TŸ`2T±2äÊð!hM;ÈPù1RXJbk€÷},GFÈ2òÐcV«|U.½„ìä®éjµûú™”¦±]+?‰¾€(© abæc?õGö«·ßWfÕiÎ’il¸Œ¥Í@/n>4ˆÑñ%““/*Q6C6µ$Î 4!U«V@ ½îòz(©ÐÎüª}}iþÛ9?MÎ;ZRÀöp{‹uµƒæà@®«yw©t(63,Ȍė `á‰øiµj-¢K—Ý fDDU\)¤Š¤v"*¢jË+ª˜ú³Nþý¯ßÕÛî?{æQwϯeãe¸z}I%ùùc5¿çø<š¨¤A~óòY±›«8lõÄ-Æf+N ¸cß_¥dwQáûÐÐ fì.mW®©fícíX·»cóŠý®Ø±hXBõ‹)¦Né¿/PvJ¹W…x7Õ§b)µäg´¯…<úxˆìÁ2D`G½‰ï·='H éÜWqéê åÝ註,`…º³³¬tT˜ jXB¥(óçœ÷¿t€-›KÏòä×Å`°ùaqÖ¾—­ç®Véš/û¦8VL¯’ÔD"SçµÊwÞSô¿v{Ú}u`ÜfB´ÔWj(Sùü©§µ!Šň"DEÆ'4“A¡UщÈ5‡^w¹!AœDW1iÝÉUmìzzÝOêhMG V,².™›à%M?´ (´,bÁ(ꬉÉíK¨…M§Iö³ââ+î`(Œ*"*rè§(? ôÕiÿÓºí"¢ŠÛr~õAxÏÃ|õz¸jíÈ}¿›w<Ç1€¸³e!yê㈵1§ ßO¸£Šï==ûñª%»‹1,7Cÿ7lÓgë9 +·¨Ûq¡ÃÎo—¦`PW¼>Ò6É«ítwT'éåvJe)wêÞ<Šuù÷4×a¼ñ ² °´1C `†ýðÞ–$„ñ¡ŠÉÝ!D_oϾ¿Ï»î½{¾"-àvŸ¢<ÀÎgFh²Ï¬¦]âÀ°Ë„‘½’p" “Ç韴ԙÊüÿþÝ»O›š(:Ò¢Ý{×\0ØÔÐèž®·D ú‡b·s õnŽw§@I|X“R°÷tÌ—$–± ÷æeBß ¤sŸ0+ Žo™ #k²¡h½©ó“´71é6áÄÂó,rs¢4©úèåe@_ÙÈjõÝë·ÿðÑÕÕo÷ÞGC ¼jíñ{ŠqhêÕÈ÷ßSõx:ðý´ÐܧEÿÏ õš¹~'Nijšå2€w>u½XH¢bñ@Í‚±.ÝÙ!HÚdC«ÖsmeÙ½¬¿WÉîT­¿¯øÞîBój0žÇx“Œ0žÇD?ö#ð0VÂÚ_tz>Œ‡ €ç¡Ðí;…=XÛ‹‡µÈÞæV§°Ç(˜¥&”«Z€“Ú›=¹ô €ÕÝ-`íü“àÿܽÀMoYXåç~˜+¤u+>f\µÄxð7©ë*ª7J*Űg%Àì«Ä•‚HA€øQê'S9!ôèÞý’Ï#ŸGG;EÖ^8íl1^¸@}î¢?šz. ÁA¥ÔZ\öWÌ\±\xúÐéC§Ÿ:æ €‡H›£.h±Ì´¤+z졃‚— ‘×V‚xÉ!O©­ærVŒ>èÃóë®Ød“têSùU¦®e4Š ô33z©ÞøƒÏ™y4¾aM?€_nm=ú¯TTv¬þbsGSÏ>·ú»¹ø ™“>b>œh¦­ãŒ,äø/úæ6*:{ M!†C —Ð4+Ï|Þð¿;-‰êv…£Ö ªl@%/ÆOÀÏ¥ÓšÒJ;)?Zà¤v(îh¾«qÁ[G¢¥ÅÅQDyÌkÅÁ,oF[‚gêÙLùAʨ' Ä"6ôKuQÔž§—²6Öò%MuR°*+X@J¾²¯ªJ.«ékÈœ—o­ýùà~ï¼iù–’›ú¤€²ZÖĨeµF­QË Û¼Î6ÓÙÆ?.Ë—`é"<þ”騄6Õ“Ýûaª *†=7¡ËUÉƵo£¾!™ÌkM.-n©ÉQG;‹P]Y‹ÄbÏ^¼ôžß…gw⬳(ŠV‚Š'ÅžëÄ·d2Z(( “y‰cI¬ˆHb…X‰¬ªô¬ÒÃ`*ÚÑ"ô0â²mäOhÜxå¨\ªiX¥ÃϹóchš QgRÜÛR¿z^ö¬³ÏùJg×»TÁ¬ £ÊlÜ5U2 Øg0“s>H'L¹™±ìL*^åv‰,íßöÒ4ˆ¿þ‚~ÿ±m6ÉÀä:¥Ð#…“ë<¦g>Åq”3•üÚEñľxbŸ_ûÿµ+ä+FµÎ~’,àOAÌA=I4œ¿qdÉ`^mš­W§í•t^Ë7ÃySE2ÓJhÊéüª[»GcœÄdõ9*B‹%J@ [1úåN2 ¨a°‡bwr£-*®CÉäžl N]½j/®¶DU‰|ú•&öŸ8£Àù{ç?´¢À»ß×\KÒP6Ô¢ªíî1¤ãf©jtöÝ ÝþljI5RÚš €›o„¹Ì]& " ßùqJó=½’ÍÖhm*3’jÂ#¿Ñ8B)Ò8BSëI‚ —þä‘GÕí,3:æ¿›œã •ín±¢¡GL Å“Ýï"11“ûÞÕõ01˜ùÖøóõÅEI" 2¦gï?-^öA(žÙö§ÃËt^¤É—<ÅÅëî«L¢†“ȪÂ]50‡DD1y3Dªêt>𑹋Е™ý­?[Qù­×?àþ'ZŽþˆ=ú„Ó<?Ö9¨s•¿ŸFp?,Nj¾S'£?_¥,òµw¿QmÚÑtЧ¾By¿¶ªPrzÅäÂoìi©§B…%ÕB ¥5™Œzƒ]ÿyifEÉÁÝ5¿Ö–h¢= á…ú©¹©TVØY¹,ËLI4M2Ý3•é/ù»1¨ t µLG~Ë@O· ‰ÏRTKçœÕ;îNˆOõ ònýq¹ŠHÓ±æç÷€3–•0QÕÏÃÀHxô·oµW¬ÿq5ÜAKûÜY­yÆu1‘¦4ß¾çÝä°nˆ d¨«"2tßCõîñŸûho\”¸(®ƒ6¥eªIlw>÷ÙÁ‰§"ã]µn©ŠÕ¨”ø¡¡€¤$} ÂŒ"ž6Ö6µ1œJórÒÍ×ï8uóŒ^]âû+Æœˆïÿ0L9—ÕbIEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/html/images/logo.jpg0000640000175000017500000006650610115335023022274 0ustar pabspabsÿØÿàJFIFÿÛCÿÛCÿÀNï"ÿÄ  ÿÄU  !1 A"Qa2q$‘#%&BR'456FVX¡§Á(378EGSbervx‚¥±·ÖáðÿÄ  ÿÄB !1A"Q #aq2‘¡ð$4Bb±%356DRV‚³ÁÑñÿÚ ?oµÕ[§Î ȶ¬K–·9K¥dZKäcmUi(‹«‡ÐÏ—bÒIb¡ÎÅóG-,@"Å(˜)y×ølzYÿËûñþàäO~9ÿ‰Ÿ¯öézÕ—»ª^ñCž2'"#À€~ï©Þ¼p%ã‘÷ä"êvtÈø2&ôq¬&à3–@yƒ°Õ¨Š¼ ECA6›ÉŠ"YZ§$å¼=B®ýAsôYY6ÓR“DfgëéA¿Šœz!NsÊ/fïá±8|}É*[· ³  Ö5‰ZÜqF ³µ ìq q³6ô og[D ~Tx;'οÃVÿ†Ç¥Ÿü±q÷·>`r xàæ™çÀÿgߨuø7[^•åþ-ããÐû¸Yßž8ÿyŸŸUvmøhv+0–[Ȱù_uïì8û_n‘-äîø€ðÏ&j•)iÖ(È3kƒ½Œ–¬¤3K>꣣:É †ä°È­­¬ˆ$†À$ƒ¬ ·ç_Ü€uøüïÆÉÐÞüøùù{=4hÑë×£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—£F^—ªÝSË.ÏŒ†b%Õ§»éܦ®oHÞ`ÖZ]~ÒŠ¤­3®6;D¢ÜÍž9ÁBÆðd‘•"‘çXRC5êMžmenÃUÒ8¤0R/ž¡N_QiÉ©Fʨ ¢s‘›r 8‚%TQ;®¼Ðª9"¹)Tº@ÆOÃK0yἋoL‚oRôŽá‘ž à=DÀ›†®Ò(*ÙÒ Ü&`UÌT¯¯eæÍNŠ@¸DÅ;À9þÀûñÈþZåÖÖoê?¦ù|uŽ7Ö<âñ¾ic’XÄcxþ>Ÿ¹Ç+ã,PtÄËÆ¡·}kÕËÕ†¾MíEjÑ­4³ÃŸ­Š¼xÅäx¯b+ýÕu€<¶dk)`º°2¥=ˆYÑ‹Fª÷(RAнêè²Âª¤öü³£óë<}äm¦[wÝònÝc ädÀÓØZµàWˆ DãÚ‹!ΠQ:@g0”h›“DŒ»ptí«f€º&\ŠAúíz£_‚ªV"˜ÁV«Ñ•êô$bk  Éؘ¨ö©€&ÙŒsÍÚ4A0"‚)¦@”\jjÛq®f¹­ÇÁál{ž,hºBs,4¯3Nï&“æ-"ÞÌß`º7ÌÆ±hÅcÅ2+sRÝû]ã¼wù-ŒÝ¹dŽk9lœöƒÆª3¼@{”õyfi þ–,ƒÏ`>†’Ïøÿ_€?>}ù$Ÿ>£þìG¬nX-¿æAÿ£«²‡wùq0ä¥Ïä}¿@Ö®Ûµ6«¹‘ü¶û™‡û±Å“Y; ì·¸Lݼ ¿>À#Ïâûp>ÁÈ“­}Âæ—~+^øÞüÍ[ðüµl©-#k¢GÈ’|x_¹>¤Ô›‡ô©Û8” è:±kuwERÃ)‘“¸…ÔÒlì/­’7£ûôÿvŸ÷Ÿ]×F–ß}Ý}åö§Ÿò†Ýéh޶Ìã)fq/6Œ–éŒd‹§pM¥ømO‰¦ÙGê‘Ãó [Š¢ÀƒÖß(ˆ¨ƒÔé×)üI]B¬†p…¿€qsQ«ÁÐgls)*Ýa3—wk…†%¦*+6%_f5v¨6›w‰ëÞêO£fÅFµfÅš³K^x ¥8ìš1É{ ^'*êW¹”‘µb<úÒ9*aٽ̮Ѱ Þ™I Æ÷­øß‘·ÒѤÜèuÔóz»Ÿß£Œwº\ï1“ªÖÌ'J¹Z^«G©A°¸ÀÈÔ,lçFcÚen8²(V`¬±à«Ò¢ÜèI¾1•VAfé¯>ºžuÈ„Ûeš{o»Wk]¿æˆE¾yÈsœŒäŠeS[bÁ›¦ßµ·¸Ð E3»%r­ bGËr]¬Í~32óþ<0RrKR’Z’šE2!·=”Tq 0C$¡äuu`¨›y]3 ÿs´fïOv|úbÏ&¬‡Q¸p]–Üåš]ñœ&áä$Õvòœñ2ªáUÙ =:ºö ÊÈ(xF1o[¢(|ƒ¶k1U«·lvL·±µ|k¸$áP¬ÍY™†¸Öš,»†p—¤ËØ Äc—r©âd•b”ü)YÓ–Ðòì¿t´‹w‚üG¨¸NcjÕ*0_©j´M`Ez8TO]dHÚH^ çBÑ´‘û‘±V@ÑûŠ®Éâ qØ%P2;‡pkãÈÑ:#c`èþÛÑÔÈѪæß¿T-°ôõ„`\³7!eÉ–’¨áÚBm¤.Ó1àªÍI7$.WmV­â 5NjuÛaY»ä`YM9ŽnÙVóÇį½œ‰$ù QÆ;}«˜–-B“(_Q*àr ¬‚äìsÅZY²m1ËÇ­óƒ Øé –öqÎ:íëm-´×}:H,XM€@“õ$0±“M•!‚ö}lUÞÈñò??ÿ¿ÛçÓÞèÒ㌽ñn×+•1‹p–<}%óSL,ð 1~$c>ƒ7%dºÔ´ÊΆîÈÀQ¹Ó[Ȳ]fSEºƒøÄÇ=`:²à›Œ2׸Œl£¿q\²Óòu‹9/þ1ÁRbºÒ_É–DGÑ]Û… ,BÇAòfSÕÌXÆÀ°MgrZÔì€k[ž•xã°Þá2ZHå¤01ÊÛÛϯPt{XñÝ OÇàkÏÎÏ­ôi>v'ñ,X$n5Ìk¾ª…U­naÛX’gÌuújâÎTI$²-ÎdØÈÅuTVZv”x3±I3!L—?®¹[ù£¶’ ¿`é»Ö/[¢í“Ö‹&壶ŽS*ÍÝ5p‰Ž‹†î9EdŽtÕLåQ3¦Æ’â9-g³Š°döJ­ˆ%Cšìà”Dw áX¤ˆÏö°G%._^ÆS·VÞ¬¨t»‹ÂFO­š¦sˆdòD¦|‚J]k÷læȬZuÁÜ·Ô¿²2,›!Âq®ŠwétŽšÞd_Їz¢£liƒöëš* ÌÍ2¿_§Û¦C²U$Ú¼…ZÔ?¤õJ:­<*­^¦VɱtЬߙçwbzw­K÷•«CVy}È’hÿ¨È•Ïtr#x›JNT‚;Pƒ£ð@'~@:×Ή>Ÿ3F’Ïk=i·¹—ݱq“3MWúÝ2¥^f±Hs˜è –ÐjËzk¤s$¡‘Y ¦ %ë¹É{®Ã¸Î%‹·ö§&e£JÅÕ«ë4šQ¤“$ßG¹8‘ Ü ² w΢ȩê°nø %Л õ;ÒüŒœýó7çáXήò¹Ž_6#G ¹¿æ‹_ù7Ùå2Ü‘$ÅK ¨bŽôÒÏZ:õ%yB‰TÜ;2µðÓÖˆd6¶µzQÙyáû`¸´²ÁˆvØF÷4!C–¿ROF©?"o¿1ÙݪZ‚Ñ´^ñô[F4i/.tÂ$+Ùyf«ÖöåHÆ!ÁJA!Š* œAÖò3ÍTŽ&M—eÐ!UÒÁ.ÞM˜å82´—ŒvÕ4;L=ˆ"šh  @‰(™ZÙ–þ'¥œ8¼Gê?%Ç¥`ç1¸\EZ¶Gwh›G/žÇe&VýLdÇÈ1Ì)ôo”Y€K%œMYvµ¥±bYøý½j³Ä§ÏÌo2ÿâôÃú5IcëŒï™î‹µüÔâ µxÊR?ä·i6©]-ÈøšÃ7.zýÂy 0ÕŽŠªÄY¤ÒVºQSNà#fîßW—§ÜÿŽu7‰â¹—žÌ¸Œ´&H£½VJ7êÊŒcš¥êríೊÈêHŸBH&šIX{À䏿F\^V ˜‚°(âH¥ÇrK ƒÃÆàìl+)Úº« Ñ¯ÂŠ&Šj,²„I$ˆuUC”‰¦™ &:ŠÂ!Pæ)J" #¤¯êñoÒð=òǃzwÑiÒÏU‘’€µî#¯.ó¡4ÁC3tÓVªÒ°’¹›W±Ixwg¯ÕÜ;dU #.•çͦ”™³*ü7ð?'ü¼lü ùôÔˆÒ('÷?¿ÿŸÀù>OF²ØÃ½W¾$^¦öÛU[kù3Ýä#Xó:ÃãŒYŽj48É,,R•¼¡X€i\týD]ý¦ðâÙÒ1œ™ÓCèóÞ÷þ'ž™ˆV¤w9—7 þqÔtu—"ÓðÎvÇrÏ»|'„u“Õ­e <¬Ë‘#•c£ÝZ–šoTB‘ôY,ž²ªFÒB íP°'[òGé_ÿ&||úØJŽþ=ÈU€î*ò £þö¶P:^ã¯:>µѤéÁñpn:å›pöÞ ¢å<«y¥c(ì­ˆqŽï3W+ =m•ŠËT”’—£[™¦ñù•û·PPÎZ(äÍ ñþ5•\ll|l¢7ø?õï둆}WtWêß©ÓþK"ò:u~ö÷ä“òŠ´Ã"=¥¤ÒØ«’« ÉZµ†½’¯NIaKrÂó²G³¼S7Ç{#X}¼Ø–븚³>‰\Ñ9•I’6p `¬GÖhѪËÞ'SlK¶Ò°I´È¶%QZ#$›zäSÒ”À(>m븑vÙ^î:8‰•%V®dÙCËŽ þYS#ŽÆÎñ[9›2#$ùJ~-ÃW¸IÚŸJ^ïpœå u-¼+9»m)ÃVªÏ5¯uâ’e °£Eb}ö3è:󩛣U/‘÷ùj˜UV¸Ò5µF0½Àœ¬º ¦,+‡1ùEÌyxï¶2Ræ)LW€)ê5)»|ÚÝà»ýéÎÞ™Rí0ǨРQ*‚OQ‰™¢ +ú±ˆ"C°Æ(„ùwñ5èÍMˆÀâ¹ï9¯ZcŒæާ‡“´…c9ü¾'%sµû—Ü|mj²€$¯fh\ȱý%ä÷`Y¦zÀ+™¥yÁ:ÐV‚hãÞÇqOédR5êÿtj§0ŸP@—‚Ëæa% å&ß¶ Y¡#eÖI w-ĈÇ<‹ne]=“G­ÓõU"o„©µÕ¯"².QIÃu’pÝÂI¬‚è¨EQYH $²J¦&"‰(Cé¨C‡!€ÅÕ©èÔM¾ pó½?É\i1’Ão™¨¸îAƒšÊÈõW#I'µ]¡´±Jk]¡rõ ÞáŽÓO^ÄQD9Ëñ‹1ÖÊÀ¨&Vjö!c%k*„1HU¹ /|r$r(eb]¼º4kà2vR aºt÷%Y£êµh (8‘|*¨¢î)Ì„|k©8–”t ©ò±‘­]>p ¨d1PÅ1ܹSVÅëö«Ò¥R,Z¹nhëU­J^Y¬O3$PÅ‚Ï$ލª bôÀªÎÁY™ˆ ª 3àÉ$ü6}}þ,Þæºää0{#¶üo Uˆ"‹7Bõ’RÁfv€¢GñµX÷kõ÷%8”Ãë’+7:­Z¬MCEÝÏW=ÛKÎFáÛ¾Z¸ŠFKê«cÚÕFŸØr9fÑÕ¢2¿ ºé¤R “ɶ²¨•rªgÀ.Äâ :ëÃ'²µ8ü9žS4ŒRÂÐ÷!ÔVbX'œž ÒÆÊ £:h–û7’¬¾ÃC;Ëäv"yØÿ»¦ ïü)>wBÖmq–‡‡žn*¡€„Àþ.ǰñç“r^8öÕ¯e=Ôu¡ÙJ‘2Ù’{'@×EÏÒ‘”»×1öK¤I¸r.¬£lñŸRx UÀ••œúÑé¹4[d ŸT•2DdÓts‡"%Gø@ 0ñÇȇáxä@ÈqªÝõ#›£Ô<^*šàsÌž Ù–¦r”U§òK‰0˜PM$Ä0¥ réðžÛ³Ì5{õ»DµÁ‘=Ñ"•ØîB£Æüùqä ƒ¿W×±I#9Í8<n{ò®8ã‘óÏít8qÇ#÷û¸ýý´àH½‚ßÐWp›{ŽõJwš1K@/€˜$@žDE@÷ð?`ã‡tÓWðûÅÏ‹Áõj9¢1{¼¿èÖÂâ$Bu¡ñ üSîku.ÿ%tnîÊR)ó²7(#óÿô 4k¡Þ Þ£ÖîG¨îp-½f‘þìoeÖJ)=!DƒÏãG€óíÏ%nyþ!ä³ZÕnø@6™º`Û¶kþAlÂ:È}9äÅL}Íþ¨ñÇþ ãŸ>xóÈ€pêå~ÖÆûk\nZÿ·àëGãüïÒ0™A<¦:Øo‘­†?ïC_¿£3vŠìGu;g}Àaü<˜#VýÌ{y?@çεœÖG:w[¶2ñÈÿH\.QyÞEkÏØD " &Gòð­qõ³ÑÊí^®x@{TJ“¯Ô=«|xø#ý}xXDE€$ìƒç[Øñ¿ ?Ççãüú4hÑ£?¯~¡¦ýp®-Ì[PÜ\~D£Ö¬«4ÁÙ-ü4ÄœKéêÜÄ 6Ã/^œ®Ê¾bíÌTµ~c‰H—Hkw‚¡Œ’¨¸rŠÙ‘ªÌŠˆ‰¹ä|Ž@ÒàCÏwžyò!ÇÛZ—n¤xÛãÇòÀ¹|èöŬµP9D½¼ñÝÇ#íÈ}ý¾ÃÜ›À‡9Ð ¬Pƹ ,É ­¡+…PÒ*O@í X!wíìw6¾O¨§"MY‚€Z)vA ’>ÐB‚[äþ·û‘ë³íÒ3Ò³)ÖÝ¥'¡ó5™Ô–4¢8«(’G2™fG+åa!XÌL2·»‹‰“If¯aäܳ–ŒxÂE‹7Íì›>ôCê ·úA²#ê ,ĤÌÒ¶føjÀöïg¬&%ÜýZ²î~XÍÄÊ몃+K6¤Egnž$Ì‚è܃¤Kr}H¶˜Ñút‚Yyȧx^ÄÕ,rÑË€å3im]DD§:*…:bbŽºkáËpÙ ²V®¡‚óAI ™}ªÒšÕ¥žs ˆé#OÿWGò¬ÑÀ£¸¬¾ðÕþ꼯$’€²ÑASØŒX£'ûG¸)ø:]oàŒ”]½•w$„${'Ï%Ÿ>F5Œ[VŽÉ»r°5oÑŠ (éwÎÐj’gpªç")Ê :7ttÛMãjÛÄ÷(B)ZÉSî-9&í]r‰P‘‘¼Nº‘ˆ†š NI躚uÖSm—)FÊ ê%Rw0î4öK aä.JddqF5G!,ìïÕ½¥E«§rUò‰‚Gx¥œ‘a6wgH3¸3áXÉ€N% t­¸WN«ñ¶oµÑvy j°öWûtŽxÝÝ·,¬Ò9‰‚ƽãr_ରï¹ôAr'dlèxÐØù׬šwò6ä3ÞV͹iÃÓ^òÎfbj=÷Ì÷׿ŽÖ:žÑDEÃ(Ê„cv•¨ÆJ¤’Œãã¶Q2¨™¹›#vy¾=êãÜ[clw˜Ê ÑÎUËè‘eÑ”{¸ôÍ^Zª$K}–R»Qxªn™¼m4þA‹¢{ˆí??‚öHßï³lq11p1q°pq±ð°±ìâaáâY¶Ž‹‰‹Žl›8øØØöi¢Ñ„{ˆ¢Õ›6¨¤Ù«d“AÈ‘ PEo‰öX©ïc]"Y¢Æg#íúհȦR´­Våm¯ÇM:0Ê+ jòqJL,u~)2 š‡; îÏ®?N½¤¥#3šYfL‚ÈŠ•üù°ûi£ÑS%Îå®—;@·XÖUy&”K‹,¸9Pñ8—#Ý1MxL¨"‡?ä \`3çD ¨»£¦g+fSŽè–<‘6ÚªŠzª¦U\ dÒLÆ÷îàyG‘ÿÑð­5º,ÐÅý36ËFr&3ˆVÙdë ¹%³¶O›7òÌ—€ûöÔ¤¹¬L|êÿ†ìreO¹–šš8fZµr˜j¾ôŠ í+.B4@|êVø®b¤¯‹Žð½ƒm+‰¾U¤’¤íÿ´u þÚýüY”äš5x{$,M‚!×oÌÅNF³–qØ=ÄõØ¿EÃU» ø‹ê$nÑò³ïßGLúäfû÷'û ΧŽÔÉÏgkUXHæQpP(XGØÄÁÅÆ4gÎNQëhx¦-Qi›8ô é·(ëBm-ôÑl]ÎeõBÏ`d"~GèÁȈ #Ç }{óÜçNúaÅòÜvD¯%Í ÃMcÛW‘iMÎ\’4,6KJ?Ý©¯I°8¬ÿ ¹_-[î ­Œ{QDY•}õ¹N0ÍÚA $ããÈ'ãÕ;c|hÑ›¿Ð)@9/â|ö€óãØ9çÝ‘¶²ˆ >;‰Íµ£™Åí'Yå2˜«Ç â[À¤¨¢ÞÃ>V¦AÜŒ„»„œ/w?KF ÍÝ=Aû©#Q.f=ÝÀ^GßÈØx1÷ù¸; 5nËb†M*-cZ#V¨”DJ“võx¤‘H¢""%M2 ""<r">uO¾‡xoëORyg ê."‡&N#‡¥v–3/R+¸éòÙ[²C Ût¬$•®-*Zö«ÙŽHDöb°PÉeK=]ËXã\{CÒc¿˜Ú–'®Í©R´(í R!WŒÊòEÝ"ý‘´a‚;\þÙµ l†V øîÿ(-™ËUš'*ÅB‚ž‹²¯T“|á#¨'ª¢ý'¤MÒK¦B©ÍÕ­„æ: eìiwJ»w¦=•‹IÂ.ŒÌÒ 5ïU›¤!ý)F*6xÂ…MtÊ`€€=³ø¤².M‚êeœ«ñíæã)MÙbµz®Xi5^à¬héø&Ÿ¬W«¹IQä;”)Ô0÷#uºïôÃÁ9G"ég+â¸N-ù&™A»Xü6/[7ŠËùG%R¼UáÉMŒT H˳A^ŵB!, mÓ~_v£r ùK—îQ\4·IJË„·R¯t#“r\wrT´HH-­®3Ìû™ØX«–wë+U ÍfªÏ5”tŒ´’¿&Œ¬4ÌSâjþ2Q«w윔J¢MDÄ Bˆm…µì¾MÂmŸnÙñ$ÐE<á‚ñ_MÅrVÉ%Ð+÷2&ܯ±xT,Ð"ºdÍÈ&Ú·W¹"aìDL阨 sªr)ÉŒ#σyà~í¢úW5:aôâQ"ö$¦Ãv‚tÈÅLû|dž!ùJ Ù«[ÆêAB»Ô­Œ‹ îÈ"ŽžOãã).Xühï{> \Žwµ4v%¿%ù¹CÊò;¤~ .ß~Ð AØ*=Uçż«VÒ:[Úà±ì˨îé2{nle㙬Ä56zÍqÉoÚ7* eéôÇØúEdeFøC&D•2n›å±9UE;PáÉÜ™4Ìb&N@}C"#Ü&ä<ñ­>¾/­½ÛòçLš¦Q¨4y ]¸n£|º5jÜ­Ù^³ã©ÅAW/Ò-6Jh.¿rmD¼–zðÀFÄ:y˜FÛžÅÀÊÁ’5©Õ—M2¬ü蜊÷jG㸠"q1„ àCž8ä$³CZ´vš4a$¢‘_v@ÅIa`ƒlÌ;|¿MôCîÈѨ.Å‚ýaBö÷úˆPY»B«1Ùõ¨ÂW1Ýô¨Bbµ" “ ³_>åmè'6¾I–¶¼uL—˜n ÙÛ:Â âø˜—Ê$‹Eþ€ý‹_QÌ\‰óÆÿ]ðÆg¤@d|[‘àVn´«;0{ ;ô 'Eb“]«¶®AüT«ÚKBË4c1õŒ£oÅ?f›æÝoOŒ¶Ç5íc(Øqe½d°d›te*êéWõ”­^iòè:¯Û ×9Œ¢’f£Ø‡‚Izûè‰Æ¬äÛ;FÈ~3\gb5~›Ô o2xÚUÁÑe%›¶ög–ª,áÑH“¸’Èù[½b"=™…i%«Ÿ.ñbÑ•”ʱ#¹ÈÈ»#ã[×é:ÿ¿ŸÇàÿZ’ÀáÙÐ6ƒåIòÉÿMGäkÉ[íÅl>o¦ç\Mµ…"ú… »M¼^0¥ŽLè(þÓ…î¹Z·)JzõÂH®¦ JY =™áZG¤öÕTœvÉŠë4(룪i´m¥/Y+žß·û ;¸iü jëLg“ñFP‰NÅVÒ†MƒªßkЯ˜è ò>1³A¬Ø‹È4d¬Ü ÄÕBj)%µMÀö|é ´bÃÀ€¶z°pí1~·1N/Pãôý(êe]Š~œÉzm Ø·'¦eDU2}©‡`¨"q/qîç_g£¿è¯Ä}:à:>2ºqÉúoÍWHcÿ¬ÚÌâÏ坿r×nåšÀí‘/ËïÆÈê…D¹ ͧä–sqHË:dÚÕsÜBA7ý^ w¿m!D‹·à í;í~‘)HTÉÁ@ýò<ñä<ˆýƒ‘öž8ž; s-õÛÛRÉ Æ*-âö˜Z%,ÕI÷%IÊÌÆ@¢Rt›©Ü Òí‘r›ZÃò³+Ìžû1tºŽFGÊ.Ö)E–9ÔXHÜ eš‘U&ô9lÝÒ•²H&^ÍvzÅJ·L‹$5ZŒ$i*‹vi L²ÂRθPNå룉¦gNÖ]Á“M2A"dó‡éKøvõ{£xÀõ3™ó%_­f$£¿—½–åö+!ˆ†)b³‹Æ×Åãg†ù±xX³jˈڂÓ+7ßC>å=@Åæ0¶1õj[iî,!šÒ±Öì–9˜í]̲Qö*(Ørÿ§°ÖOUÍì¼Úv‡¬Ò•7ïG-žZ>!Ëc\Öj±)6-‚‘Nf¯]/# ¢ÞŽg2Ò Pg%…êçkÈ2ŽÏ=r¹ÖPMé(sö;„@ íÈr_o<€ý´â»à¤Ôr¶dxÞÑÖX*ÐÕÆ†p’jnkÄL@LýnqbªsUôøOŠÐºl[XJu#£Ë¸€ˆ°v€ðÀðo!È}ùñ¨?^þ²x ŸP¼ÛȰ¹Y—§Ù¼§ÄeH­×¢¸+­C7%*LcHöjµ¹lZ«0ÇN&h*VŽ?xÞ’ŸŽÔ²µ«­ø#½f¤±ËžIÑ^-ˆÃ—B訮$,ä(gviuÐ#mUú†¹îRR5%ï>Í)L®I¬D<^<¨*ÕhÆœVj¤ýIJßZ(˜Ê5¨Ò¨ÊÞÝòWÉ”»V=»D3Ÿ¨Ý e+V(wè"嬄L»EY¼ADœ&ª]Þ’¢tT2f2+•5“áDÊ!\[TÌxWj{fÆx‚YŘ‹RXñ#ks“¶ë¡â¤Y_ÔõÜÍ,¸‘3zI ‚ŠMÈ™=‹GX ”Ó”Y ›!°Y?)©‹mÿŒÄÂ(,J€˜ÃøJ&T¤äC¸å/&œt{­] ËðN-Áu7§÷¬Yãô.d11rl,™®e*Gs!Zþ<[k1YŠÅ™kÙ¯4BHY *”윎5yM¼e¸¢I?¸ûi¾ÕÂ1Nø¦)í¼m­£†Ó)óé rSe‡Œ¥¡å1õòÙF‘9]±Îú¥?!ìÆlå$ s¸TÆIÂ(®˜U4ÎS,·¥n}±m›r”k¢¥YŸ4¦äèUB°’§N9M²Ïœ¶)ÊšÏ*ΕBÇa+óq¢ÌU+7ïRZn&ß—÷›òÌ3qñG2d¼‹üH/˜FÝn³vf,ß =苶ͤÒAȤc&+&q ‰{uÖ0³dY°J_÷óíÇ >Þ@}¸à>ÁªEÔ>[‘àöjç¸Í†I°ù¿ÆXBú•(Ú÷k¬‡® ¨IãqÛ4.ñH¥]—ÖÇã°[ºâedR£zîý¥»Nþ@;[ߟzÑQDÓY5Y2*’¤:j¤¡ tÔLå5`œ‡(‰NC”ÅÕ!õ ÄÆ™z·1]Š…^z”Ýy‚EG³lúEœÌ³rI¸E£tŠ´ŠÌÌÝ›‡J˜çU³Iðˆ‰îÉ<™ÆØöbCý¿+G©É>ð¨\}éυΪáþ9SøYEÿÎçäÃ[»üì‹G)¸äiªŸËë’ßÛÿÛùh÷üC¬×³ô©—ÏÅ]”Ìð›ØùäMš_•«†)ï‰Þ­©`—´¯rHèÀ‚G§þœwÅËa‡¸‘ì]ŽEhþÜlFÆôÀ2ÞˆyõT° š½4@¢ˆð@ðqî!Ȉ€˜|{pjwìãk1™ì­¦êgÅ¡U%“N”dµ‚pɦýxãÅ¡IkZIkM42™9þfæ‹É=0Z³n½$³ $ *K,’D|”Ç \äï.¤8V^ŒçlxÜI¡VÄôðd`ЍG Y^¡âyª¨Î{ÜY7ª'_Ÿ]E2½u1]¾Éó ÒI$±é¶Úã;ÅÌ‚åUâ1/^¿‹ñǦ‰u3ýƒ,¤J‘.](g7Þ‘ ã¾ÉS2.˛ƮvǕǙ|Ë!Pid¦$På1Š˜¹XSU söc®ŸL=.ê¥?…ñŽ?šÅdh5 ž ÄXû7“í¬c¬>:µcb‰Žo~:ów¤6!á÷KÞ%à\Ë%‹Ì·ß_¹n…ŠÖMˆlؖ‰cŒÍѬÎÁf/¶XÞ’0ní.¹…g{°¯Þ¸K c À8õÊ>ü€øäžxóîǰéÇzmfAÍÛHǶEžûÚû‰ÊKÇ&Qe %®Èœ",™Úµ×pÍKé¨á1*%7ªC˜í›ã™žîÒh;$““5Šsª~ƒrÇ"ì ?¨:ÔCá“¿ºÉ=1b¬¯Ne8dƈ‡Ê0¨hóÀøGûõúúrn‰u>\Ö*ÀþYâ™,NBØŽG[¸Ëõã/» •\E.‹$sXH82¾ òœg(âê![¥“«*‰ $ŠÄOÚÊX{l{”¶±EôÁꪒ (²Ê&Š(¦uUUS•4’I2‰ÔQE B&B˜ç0JP@GK ¿ ó+Ÿo/–+×$ ÖÖuH…1Γb³/qrۼɚ^pHW .$õ[±ù8Ð1ŠÏ½[ÜÞuÙÍm¹2U‰ÎGòqŒêÍ ˜˜†µI³ƒ9U)L)(‡² &Â"ªd!MCåVL… ºÔàGð“‘Â>€ûˆýÇô¾ÿÄ;«ʼ‡ô{ zZ8ÛTc漬C#'ó1&JÆ?Žãçd*Z­9ñy,…Š’Žkbì²+Ó…™Ÿ§X*òc2¹û1,¬’6>Ÿpß¶Ræµ"þ§Y¡‰CµDÈ ÃÔSc‹frÖP¤bê· Yüp§Ä(ë»å¼Ÿ”m“×§L¦TŒ™výR”ÇMª+(7gxl‚qÖÛ±eccFÐÕºã4ʺÄH…‘°ÍªŠ%–´Oº(z’³n]*c" ‘´k&,Û«‡KjØ[wlwHS`oö〹QMTjrP,\¬E€T_å¤l Dxp›Ò¶r#òÈ9(·–¬·Ñæ 4àv¹5•Ioݽü²¼Ì½Íu*Í'²ì;•lØ´DݧúŸkqý †^8FVܱ§i-ÚI#^HÖþ~vF÷ë˜æŒORθŸ áûËÒ5\‰V•¬Ê"é¸:+Ÿnb²•lŸ¨‰Ó’„‘+I˜—H8l錣oZ9léº+§˜ø¬p1œ€÷sü_o?Ú?ŸŸ"yR7L^í±n4¿ë`|¼_ïÇÖÿ¯YIL®ËŸîïÐxð>|ÿÏ ?`ЬS¨Êà*¶·-Ò.ÀÙ)=uð~G–×úëÓ6b¡±. íY—{ý?«´èƒàï_Ÿôóêݺ:¨êO´àäÃz˜ûˆÿÀKoqÏ&7#öh߬׺+Ê÷R­¥žJ7Ù²ˆ‡ü4;a€DÀóìïÇ·(u#èöÆ *¤‚W5 :;óö~| Üë÷>¾áah+Ë)]NHH1EäëÁ#DoÇÇÇF‰[ôÉ– 7²½Óäê’‡BÕNÁ9*R²ñ5ź‘–UäZCLB¦©Œxi-¥J€É™ƒ`]·«ó),ΕkX³ &:ðK;䔆6‘€¿jNÎÁÜü"³|éA'þÕuAøˆ£öÿsµà—×+y#T%ÖïY‚æÝãü{U°Ç:;)ˆ u}‹ÈÇWI˜‡(¹fòyìƒJ³6ýŒÙÛZú¦"¥å-Ûõê«8Û!f¼¯šlYÓ X©b¦(×i–yù$C1ŒÇuÂ×1û+½pÝ^!°7L¤]óôÛ uÉœ´2êELcª±Ìªªªcʪq™CyœâcCœDL""a÷¸ž€•êËþ©»<ú,×s ˜%kMÞbSØ™â;™š¨Š)“pñŒi¥dÚ Š'j³äP1±EDë Ü“9Ì3th\½-Jy…j¢¥y uà‚͈âß`!g’5r§Ò>¼…`¡ªŽYfuò€»ª4H%€dˆ;{>«×ØWÃ9·ìS]È;Õ–6{ÉbÙ¬‹œS éì.«HŠc¤œ´Q¥›%.ÁQ@âåãºÝ]Ê…rÁíVq‡¦íz¥øŸq¥+nShXçRªô:lÜНSép1•šä30Év²‘´d$3fqÌÑò"%nÙ>OÉÇ“œÂ/Ï¤ïø†ðÓÜ¥½Í±»D½Íað*åX8åCdkJÀ<%ð>=¿²kÕú=ÓÞ™fò‘ÁÔE6–ËŽéåsf5OzÃîW/'h [ÛBB*€¾¥x¨g½‘­Z%2I3„U4ýLÇé]“¿ÉõUÀq°,eݲ¼pšK™C{Î`àxò9 <z€¢FûGĈ¦P)%à P“q8ûùñä|ò>⬸–¨•R“K´é"Åð@?/~újM‡p;QÅB òœoñúˆëÿCü¶ï/ú ç¹;Vt“¥¼ŒÂY‹(þ™ð ½ “¡£ãZô_ê¡Ã1PÆ¡V<½5ñù?ËòE‰ÙÙ;OÇŸRóKW½„[s9pDÀ4û>Òˆûˆ@ÄyòóÏ¿þþÚeM-†ó•ng.hˆy—·>È1&ðÞG‘!üƒEÿâj\tw‚”:?úM§ûö·'ÿ†þ}jô;ÿ‰²¿òI5þ˜PõœÅ”C»žD|ßðþ_˜{{‡Ç6N"/n(ÆÿWR‹ýÕ¸ÀÒž:x`¢cr!÷òD<€<ñá±1ˆâŒb#î8ö–#Ï¿#[çAïá„Ò·#êǸv?‘qm|oÿxeÿàk_>}Jºñ¿°ã›;Õ¼‡ÿ¢¯ãû|?ß×BÒ õËÌ{^¸uÜ~Ûó;FèɰcˆÎ¤³‚¦@EYœæ™7ÚdΛYþÆ;CǶŸ£YëüBÛ"¢åýn+)6|þ7 LÇâ‚휩8<>Çì€É(bŸ”`Ý3 SpPóí«íõ0¼Mø‡N]—Îà n‹l×fK¸ÌÿòLi[”«)ûHë ¾è=À¹‹k¯ wÒ—¿s,Ô+R¸ßôvгRûûpY©üÇf„1VãC Ý¿J…»ŒKÃÖvCA¶1³T%ŒušªW),á‘ ÜraLDLA(ñÞ<{@DZøt«)KÒÿ¦ùH=Ä.¶~Ró(mïGûCÖ697d\"¼}ê>\X·QDš?r (ÜHS HtÎa:|î oã‘Ù?¥`:`tàl/gàO<þè÷;|ýüqçDî}Âq¼t6y2òé"¬«üõ7“`Ç$Âhýð„,ºíÛ)b ±õæUŸ',Ôñ-…‰ä;¢îd18P®#%Sú%”²(R+ãÔκS*y¡g _+°öúMÖZ­mªØX7”‚±×'˜¯3 /èŠ6{$ÁÊí¶X†Md9 ³iêùð²n3lö;Nlé÷hÜvÜ<„ÙñqÜØ3ö!ls¨åx(ÈD[šK/TØà”x$r XX«Ò«G+nœ¿ïŠ·t;¬Ú^ÙþHÛFtÉ89Œ¦VÈõ‰!Žm¯kN,${P…š«Çʶf±EÃaY«ƒ¤sG*ª„MD¾ r«y}2·±Wê²<ºJúì 1w¨´ÉPŒLĪeªÑ ‘«†D¦[6mdjíü‘«20’FE2>LºšŠiä¬Ñ’Ñ¢9r ¡ò ùý@v–ÒàŸQÕY`†;*ëÛ#ºvúD1]xÿ°Ãá”ü7‹µ¶F惴à.Œ$c¥«*!Xi¸Å"äb—hs$³÷H í«¶Ç"È9E%’P¢S”§ {IÊ6i_I´Ä$c¢¹|ªåäŽÐ?y¹XD‚?w<°<ï7¥öÃ÷ÿí–é¶ß²‰hñÉÍce²ôi¶U»†ÊƒE\“me é¬éWõ•œ €IB?nC76m=qz!^ºYdªl½RfC(m7&«#Œ²L«f¨Û --~¤óäô¢Û3‡-”#}iJý†%œd]Î%œ³¶Pðï æ"سÚÅA@‰ÃZŒÄÀͶN¢’6_rBCÃod2èúu©‘–Y$=ï%‰"XLDÅÇ€WúˆêÁPx S@x>õYÛ ê¹.›vÛww±$"¿Ð®£ª]¥³~W2,…aQ´„\›U¶c.‰²Tݽ4íFZy»Y¶*ÙÆèèÖÚöÝN0MÛZfl¢FÛØÄÈ*Šòu¹3(¼e¢¡,»_êŽ%鶨骬²ìÄÌ×’‡t«C¨ØéØtœS8:D„L °y1…1 ˆˆÏ<ý‡Ÿ~?.Zž|$–™µFö#?4t6às„]7ç|²%¬šJQÉcVùt~ È-òv±QبìS’0Pb [;×o!CR¤ï»»ô€|è®öIbIh|¶ÙPW¼‚0Æ¿Nˆ øÑÑSãÁ#´kÆg9ÖVמ3Y)ۭݱýå—Ÿ˜…×" €Ê*¡EMCjGhåÓWç=8à¼Ãb+4y'Àåá’ª×1µåžÑýU°e«bÔX†XeU’6P%ÌÒ“–ÉД%KÖ ;îÌê®?uuцÃ+‚£F¿Á( Œ DDGÀ‘ð®ŽsÝk%Ü¬ÔøHÉBŒÎ8g9ý]xy˜¶NZ±;ô•MB¬Ð\¼qË&ê"¨8dt+¤©Ôh†×!ê âyÎ!ƹ!ÇâsÜ÷#oÃñVZO»Ï_¡Wï.AN8ãZ»Fežs u’zÕÌ¿qj¼R᯹n –k×y`¡MrUÐH#‘ûœ±,ÛÒ¯s¬Ý½¨ÄWÎáèæ‹âjÉŸêLÎr‰$Z"9TŽ1HäQ3{†)ËÉLQF*÷qÀ€yo¸ùäxý|‡#ä|xã¾o&¶ú-–y$N,-pAÇ鋸¤ÆÛp<ªƒFq‹(ä Gˆ<ˆ€Eô¬N9ø‡‘÷°ùG¸{xüŸýSñ[Ücê;®«ÈReê‡3Ȳ¬øüærÞsa”ÿjÆd©Ù¥fK)RlïXíñì%ˆX]Da倖(cŠd#ôK‹¯Ï«(Ä»~ÅwŒkX±Mļ}!(Õ៪IWè$£†²o˜¨@Š”„ìÞ˜ "A0€ò>ϧNÓr*!iÇKH@9‹ašl óÏ" 6x‘€|ûóÏ÷¾gW6Ó49:©×/Ô+2ª¸I¹Á†"d~e%’)€Å$‘dJ¿`© ˆ Ä¢á04¾×èéK£ßN¼Û ç”:GÓ;«ÜAšÌÃÄð‡#7)ÂÑ‹ ɦ³qj›/pçñÙžI¥iÞ]¼ŽÎň'‘åy?3–ǶW"­Éû û©–#ZV2×Qwgg²ñ€½½ xÕL¹è—ÓéÒçplimHç7x• ™tI""oŸÕ¥DD@<ß_mHé±ZÔ|Ün/™”V5Á!b¾[å¡Ö]1$/£–M¼Š%8ŽÍð.ÉÉ@QvÙÃs#Y†¢ÖgÝ®9Á—ºu*ÞÂÈí:‰6’ž®Æ)8Þ¤òIÓV° ÌE1õf]§*uW2¥†g#"İÆ8m#ó l_/Áô?‡ã±×ù¾€a±–3Ì&6|ö3•gÌä§ö±´` t’ÄÒ#È´¬PC=©Ìu«Ï,qêM•žYƒÛy„RM ®òF½Ò9ì €£öòI  ³eI&Ši¢ŠdIˆD’I"4ÒM2Hšd(HB¤!@ R€jœ:O–3-c¶f8”V ª·äÃ(˜ýÑü<ý‡\–¨£ªYV6tžˆîÍNxûíTÈð?§<ñ÷íý4 þ Tâ¹ôÇË`—B1™ân< v¡]íøÇ©‡Kôy… twZðò7óZO??·ã÷ñê53”Ò1€ÁÛÀùçžyüþÜò#ù‘ÕÜl—þðñüæìãÿÖj‡`TDOÈòRò¨ùöö#à ˾=“¯þ¹³~žó\ÕþÇÔ–u#;¥œœxð‡‡~?ò?ßûz*usµx¬*ºñš§äì}¥Ó¿íò?ßêYé ~3ZÛÙŒ…²WÍR:„es*J &íl”c€€îðéö´­?nC6Ø6ê 2+±‰©d6åN)üä½iAíàÈúñǸ»aÖgO§ü#ʯ°Z˜ûøHæb@ÒÝÌR¤Ï™ÇÏ ×Æ&c’UÇ9*¶+dGpÉBÄŠ@?³(ÿO÷zÌUœ+ŤShVêCªDÀ†L&p<óÈ}¾ÀùkT…–¸ò±Ò²=ògIu³¾S~$9D¢rÆŸÛà@ýø÷çJQéÁÖÈ“õ¢ý@"ÅP¥:%í(óøJ$y~|ò!ÇwËè»@Kl­Y¹Éw' ‘KØ+³¯D úŠ_ÿ½´é¯ÔêOR±|[0ÓŽåòv¹N¾ØÒ´ð±çÀü xÙšòî'âÖ­Ï2Ë$Ù:q/b°P„NFËhoô‚4>=wÞ¡Ì”·Ò· @ou“¨‰ Þ_€PÀ—Õøîñêvqø»t¿öÊ(®Í_ñ|þ㻞>ãà9äGÇ#Ç"?Ù¦hÝ%`ö¬|d‰Lw¬Ø‘”ÇüÙ²Ïy!D;û£Z½ sÈÆ*½¦À5E’ìªÕOŸÈ}¹íöyä8°ùñι…üM$ÊqϨ~5“ì“ì39ÂKNp¶gÇfóôîÕ{2À[Gvl–Ôƒ¥ö¡—ŠÙ¦ÁY£ÊÙ)KWµñÚǹGÁÜmälzù~–pW7¦ÙGUÆ·– Ê%0 Î;¢d‰x(€5béqˆJ€€wiEŸ4­˜ó†÷Kˆ¯RŠÒXWç]QE«hKƒ'•IÏ„ ;8„æ‚e`P}8ª¦AY4xi=_¿áçÍcå}¿FY•ò\•Û†Ä[Ü‚ŽC±BÃì“Û4Ñä C¡³QüxÙu˜üé1 XlÀ²®¾=ÅwYãν¶>‡fp›îã <~÷òn|óÀˆ<ëK÷ŽÛG´týêé¶fɺîݹXÀD›¶l‘–]uN> šIêÃà¥(ˆûk6"†7È<€û‡ñx¸˜DÃȇ>ß—:2õýQ›‰ âÿWê8P<~Ä‚7ñ±ðÎN…ãªü{ä€7½˜ßŸçÏúþ=I ’·÷µ!ञ ß÷ŸX÷@CÏaôxñÆ´Ö~Û2íOxR(¾å0_‘ãÈO«—‘ûóÏŸ·ŸÌ8Ö:uè9žÐ×þ±­ø×ÿ*|üåãöñëÏAp¿ë§ïãúz×>ÖõøôkÒ’“‡bâN^AŒTkBŽä$ Å‹TÌr¦píÒ‰ ‰ç!Ê(R‰ÌR€ò`÷u wó³˜}øm²Ï·ÛÌ–:޲ÎÔ§´Äµ°=hz¤ëYÄ›§õüj •áÚƒu3¢ %8¨RœÅ‰ÆËÏyä­³a"v‚¹•aJªJDe`V0í¥.À…ÞÈÐõ*bÁXª÷0ªì/q׳àlþ¹ŸPmáíŸm'qan͘É)Ùì!’«õJs;Íaå¾ßa´Ó,•ÈZíu¼š²².%¥}FÄ]¦hÕ&Ïž½]QïG0¢E¥ÜpàÂÏ‘ç·çž<ó÷ñí§Oÿ±YŃï¼+ø‡‰«€Ï>ßóï÷óíçÆ¼„øWqaGú`ß„GŸ?ºjè}ùóþy~/æ?oãBO‡æ|–z³>½4­‘¬I“§1&GVvy ±†ðª,c·Dì’e³FË«{Æ© Ì…ŽÈ$–aøý»¸;ô¢8~#æÌ=æÓ]HZRÇ×r3KÔvxºÅ¶&qù%É}Eūʑ{ƒ½S&^CÀëSŠ&ç6å“àYÙñîwÄW'íZ¬\ÈnúÿÏ>9Å5Ñà9óÇ"<þ¿¦¾ñ|w9ãâ'­ŠÙ…ʾZwG…]Kû’0÷+!ÿdÞH9jŒ„=ÞäûÈÞæMŽÐÑïxÙòHòÑôÕi¨EHER9IBDÔLÀr(C€‡!Ê"SÅ1LQ0€ë÷®U‚qk\„0Þc0âÂËb¬y‹YÏ»h›SqõF¤ÞaËVp‹'hÄêÍpºmÔ\È‘eJ@9º®Œ13´Q´±ˆ¥hѤˆ8q•ã‰^ðml§a½  ïGò7ù×ïêõÏ8‹í#qS™O!Té„w„rlltäüTlÍšrn›=ZŠzí»É©©ÙuŒ‹bÅ%Tpìâ$M%ÔK+Ù{Y•DZBþ#€h€€‘Ð~ßÅ÷þ\iÔ÷ð®ãÝÃn;ç÷»ÓȵG¹Ç2dì¾î®ËW$ÙW䫼åÑÄ Iî­i¬Ùãz»Vë:E¹QLoHœÊ3áÆQªAß>Kp%†«$<ù á‡Ï<¿ÜpçU@ÞÿÉm||TSÒ‡"Ñ0ŽúvÇ“òDâªEs!³ö‚ ´ôÅm IE„@ıy2Ýijå )°ŽIÓÕSn :[Ô² þ̲4K½BëttGÕ+,5‘™Ú¬c‘%s õê&AS¦¡X)¨dÎR˜D¦X_…»â—xw÷ >'®¦!ÇKÒc£PN鹈¢Ì쫽}n´Fà`)"„±€dL£s^f`ÚN(ÊX†EÃFc¨J@¢¦:5F‹&µPEFâÝŒ_‘d¼CÄU9(°Q©ÄxnÝâP®·t·¬ýbé†__±^šä1x&ûEŸ“dë2´/ä`š:Xøå®Óý¬ÒNÕÍç€Ü†´(Öâ)ðÜ\.V¥Ì yU[Û’XÇýÓ˜Õ;¥*ÁK">Ù~Ð_µ ÑF³(> ùx(xàØ9ý9ãôÕÌìcuÖ› Šr=‘«êô¼›ŠÛù$];Øy§&•Y±Ÿ ’è0vÎ]ÔšËñš·U»¤‚ª¨W$O•§Ó. édRÈ8ÜñåOú»£þÓ&ñE;Ä;V`X5@žŸ É$à Š}¥³Sú`@¶Q%¯yV^Q>L+0©À3ƒ7sYiw–TªÓ2œÄ"$Q ¸*ãί§“ýXtã©'—tûá-]¯Jö7#äÜoù4Ø«¶+ýÔ7V‡$‡&V+´©Û‚LjÏ*Y©´6 ï†S§&Ìðœ®QÍelG’Ef³R§pØY¢Fìh½Úf¸äte˜¨)#Êú"Ê!/´«q;„ªJ¬¡œ¶òm>’j±Û¹!ÞR"‘‘Y3åPÄã´Å]]ÑÙköü÷”'«Ï[KD<²6R-Mf¯JÁ‹8åœ4Y3'-TpÕQlá3™5Ñ,™…3”uj™§¾=¹4§ÅW-“t¸Z|;ȶÌA˜X—|´„»É‡’_?‘lr,»‡‡ ‚Dl™JŠi{—ÿ‚æ²í._žÿÝ8áüüùd9äG‘ä~Üði~ªú{õW×Þ?ÆøD}#âÔ©ñÌž;]äxŽ¡álUÍæÿIJêc1ù©0ù ^&­¼žJc¾–®YZõìbV(Ñn˜à¼NíÌ¡äž[ÍR*–1•ªÖû´–6žZ¢ÄSØt‚clHY×±üMNA>‚··ØD8x8ñïíì_n|ò#ÝÀûjÑýARëï)Îà0Ý>é¶%Æ8ÆwÉâäy^M‚îÌÞL>B„¸é0 d§R·ókqË$e­´PË @ÊÑs‰b#µ{-È-Ó½~Œt´«ãî1¯]ìÁ0•.AÁ¤“í¢eíTöÃ2°säeÛ™±fëb« “%0˜}Ôs‰_ QÅÜPï({‡p€":Õ[¢–ä°Nl韱*þ0Ê”kuŸí+b»íB&ÑîáN½b|WQ¢Ý lÕfïÕœƒw9 º‰'(Å©ÝÄ»Š˜@¦”bºÕ‹š~9‰ü‚‡ß¦G¬Ä?)ˆxFx^®ýÀ !ë­xlsxä}2ä!í¨Tø/ðÕbõX½O ¡"îµiƒ´’¸Rª‚O„–k*VÇX—Õ‰°"eŠE™N&)L Ñ£¥¸þo˜ù– ŒánÊVo³ãN¢À2G0V’"7‚ðË"7ÈcãÓo4¹Æ/Ka2¹œƒÆ¾Ü²e#vNÕ)å쟴ùý/èþ>$/ÆUQRg¦6µ´dš®i;ÐÇKH>ý5ÙÖ¬8ÿE„àWöÄz—ªB¤¢é‘·¬A" ¹îUÎýl&ºVdùJ~Sú½Ïfùªiš¹:«ýzÁ‹®("Ú5Žf§B©Ú.‘‰nÞù^j«G–ªÓ‡ •{1P¯E¼Ô§6`Ü?¹ ifÙãTò¾/¸µ#K&éÞfDˆªG z —Ôi#í$žÅʰU¬¤SôP}ñ«ÄXŠC¹ïƒGjw[ Å«h»“É[qN@¯]£ò i¶w¡Ç»ò1É¥¬”<c‘.òÓbɳHGÃÔÊÞ52Mˆ%ïÁÛî)©õv•$ë[]ïÀ×çÔ:´Ðö{;½¶Øî}› †Q¢Ig·À'Éóé°¶÷¹|ºüq —6á—h¹—εné’:ÒY&§r˜Ÿé³±å1%ëíLUÊV¬±ñ6w¨9a+Éóg ÓQïŒ'{Ø)ªã}–Ô®µ;–àå3Ís$Zê3ÓRøš¡J§\*úÞ„yÝ­U±ZÝ"cëÐò¿N’”€=’A$Å‹büÝfNüA«Ó ¾Åû½ÚÃ’®îJ1I Y\Û$”«="í2©Wq­ØêI‰Á8?4y“Yf§™xŠ`uû^%ø+3[ɳ¦û±År¨ÍáUYž&Å[¤ì‹"Š&;t$n3Ô&ŽÜœ&U°¤ÐHŠâÅïªvéz‘å‘,HKkÜߤ$€4AÙò| ëg^¾D±E'ºfuT$ÆÊ„9q­~|?;óùØô•økåœë“ixk Ql)äËJÍ*•Yfg³3“oÌa*)&"“vÍ[¶"òÒÒ+´‹…Šhö^]ó¦Þ!³‡KÝ•Gôõ؆Ývœ‹¨ÙK7¦ÖFŸŠ‹*Üå¤.¹.Y‹· š?{7¼}ei$|…Q„RnFI6Gtáèͱ¾˜QJ;ÀXýÝ‹-IEŒM?eãíf]‚À™ŸEFʶŒŠ‡¥À>]2(ö• ÞXÇþЩ6â5‹”mg^«Ãí‚¶Ã=À/ ö¯’'ÁÑð gřĄ*3˜×Dw€ my:€$$ è*7ĽÑÂ÷½š}y›Vˆ3ýÑ`H‚Uíµ&[êyS&þFaÂ"Ù»ƒ?»ãyéI(¸á#u'ªóÖFÅxæR¯ ývºyí/|u̵W½dÄKZf‘í7 9rr ‘[ Ÿ1‡žÿTä1CÜí¦ö¢ÆJÚ#ÈRoìM/K³É(wå+lÝ”£Ó‰Ìw’Њ¢f.*¢Š,éë¦H¾p`Yóç(sV¾«:gÕÁoÔé=^’ÌÚ¥n†CËj˜®[¥n>Çn?›iE:7‡×ÉE9¿ößZH½»~žsLw–XrÇ&+»+Ã-+-öÈËÿvåÒ&]ìš2ÒǾÁ+ g>“,Œp$õ‹öíÛÏÁ¼QB1”M¸Ò?¬™N£gÍ ªÆdø¥PÈúʦ¢K¶YfêØ|Fù±Kæ »è[”{⦘¨É&PïRQC‰»ˆÍçÖ[R¦P)Œ£´÷pBJn8¤žÂm)™REÝ«2‰yIIFpÊ(nñÕRhYÂ$NÎ ÜU–0‰;@¥›ü‹Ø-”ê'õkÔrBõF)„Œ©ÓàC¼¨•ØCàR÷vÆLL<ˆP×*:1cø€ô-§éoÀUzÍjÌõøŸ"Ìôó9ŒÄÜ»;­™±æå0¾=&¸ÒXšœ!–ÛKqé¼Ö,K4÷­^Ž”lÊÑȵ’q=0ó鋹l¢ÉAÆAw-„Àvät€)‡1 eûµÛ^ë±Ì&;•¶>¦¡ wŒº¥)Þ]uœFÁØá ÄÍœ¼d™U;«™pTÇ)›0LAS°ò¥Un2¦Žg°.(“T#Sä9çÈl|>|°~Z3ýdq.»uc‡äz]Óžœcrx …Œ.FÏ-µÌ°t-K% Å×ÇÖÂd&¡%c˜áW¹-« 4AÖ(¸‘686C…È&_%‘–$ÑGU)Ï"jUì2<Ñ,ÛBÚ@ŠCh–:#Õx°IˆNLà ãôý=¿/ËŽWE²;mmέ§3YةٴÝÄ*ñºRWÏ~u¢é3: áfˤä„MtÓ̺k QïDà‰>›ð„ÿe©±„j±üßøßþ@:þ’;àQ㜣2~8÷­1ð<õQàGŸÿœjŽ}6ý6ý^ý8u-ú‰C£˜N^,ñü—»†³Ôކ2RÈÙÆÝikdS%ûk1XÅ×ÓIFÔMJ† β$ï“r~ÉñË¦ÎØ¨VÄVc™1—fÔ‘¤‰ÚÑ¢ …e`¨¬R€U¬hç"d2Ф! cœç0„!@LcÆ” &0ˆˆð§>£AY¾Ü¨±òòΫp2Á'òk$õ6G—|ÑFÍ–Q¹Ô*nŽ“®¢¨š ·TÅì]1XÈô¤2-"ÁJrýXÄ'Ú$ÕGè G*¶·v"Q"("-ÀœB€„y8^}WŽ<þñ¥ÃÇþo2ÿžKŸùÿ¿W{ëc}@õ/‡IÒ®‘ôÓÈp‚ .O9Í-ó\Þ6î+:o®ž +b„²³Œ~:Ì™o»– ©ª%A4fa éþG‚ÉGœÊdæ­n£Xн$¥<É$s×öŒïb {²¨‡Û¹Ëèöš]c‹ãST V©óì xû{qÇåäyö÷ñ«´ØD½j':¦„¬sYÖvùgf‡]Ófï•m"Þ ­^5hs‘W –\ ÔI3:L蛃 }ÿÏO`° ûd‰qðyçÀŸŽCÇ‘·ä¯:› ¯¨Q(äI`çŽGö}Ÿ÷qõ ýÜ>ê1ôóôñõ©Ð¦Õê=ÃrøÓ’Â\Ã\êg Å{ôò_lÍ%|„y{ßkfªBé$”mÆÈ%‰¢Û¬¨Aå¼Ï‰r¼SbæÍÏU}øl$É»7kÂÑ£ïR®à"èïÆš|.‚.Y³„ˆ³w (‚è¨P2j¢± š©(Qðb(C†(øˆ€ûéu÷sK–Û­íìÔ\þÈÎüÄ¥"m@9Û¿Šð¬i܈£+uœ‹uùƒ$,äE"6“hcßý ¦™Z§7x¤‚5È–±I>U·QÑ“°:$:…HÇ÷C€¬:þ6UÄxï6SßÑ2m^>Ó\þ0¼)Ów𩨒2²mŽŒ„4«r*©‘rÙÑRUvæPÍœ.нú“úl£õ;ÓÞ2™ªÕ¸—Pøô1å°ÓKar•±²•*6‹_¿R%ûì\óׂÈÓ€:ÚÇSÈW‚H ô¬ŠøÏ'“‹d-šäÝÇÙ&T©„Ì‘;}½¸Ñ‰1ʪÌ}·b;%x؆ è™Y6øÕêk(ò<ýÀžCŸ¸ ‡«(Ú[:ö;§ÄcmÍV­“ÁZf„TM¦&ÂfZB-²g#6·(9I(“ªö9ÐfK[çneM#WÈ<–’ûlÛÐÖZ^Qw˜G<·g åc:æP‚r»È´„yÿk«d¨˜Jš+Tã”D‰ÔzðêéðZG@ Ã),Ør®{Çè0r'|‹=ÆQV‰,c_´LhÍ8z€ éN5UE@g(šŠõÿ  êÇB2ÓÖÅEW¯ ©vºäqq¹8cdhXžÚŽÔ.$¯#-{p¬’(1{³£5sSk9f?·­²¤˜‹˜Ôí fîן kz쎉»n³Ð¹²‰;·ýžãl1wË1®¨ƒm™ŠjÊU£K#r0‘gCª×_ا&,Q®ßÅ5’r¬ˆ†ú¤syjá5£nè=@ò!ÏæÀx?Q!>ÞWiÝ>¶Õ³¦esŒª&–¾,ÕF²™Næv³—Ǩ¸/k¦¬¤ Ñ£Üc€íMxÊÌ|Kg©$€ÊýEÊ äjœŸ? ˆ†çnb>?òoÃÏÚÒùûêÍó>Ÿu#‘œVJôÔ²ù–cžI*R£ˆ® g¯/fh ™¬;Úk2…©†(ÄÒF"oP´2Dnå%sÝ´Rˆ¨ìÒ¯q^æ}¿shè¨Ñ×n—ïl÷8äv÷}´¼ÚÅ+8â‹}–@R:ÂÆ·}¯ÍKºQ`kÉÊþše2‡À„0€kAjͺ§u‹Br›g¯[a\¦ŠÍ¦+3Q³Ñnp‘WAT$"œ»hªk r,‰ÓXÅQ#•B ˆ`_`øzè;š¸ˆøóû¸„C‘üé@yãùjTìû¤eGh¹Â6Df«ÕìEŠ%8 *|d;EÉa‰q¢Æ|Ömê©™± ¤![˜1†1@yþš`¹çšz7xÝiñÙ;µ^ÍÁ™ “PDÔ³ˆc’Á´‰wˆcvhʉ?Xíõ‹­~‹4mU)ž2ò{ñƒµ˜(îïyíÒ’AóäkÿÙtranscend-0.3+dfsg2.orig/Transcend/doc/html/images/screen1.png0000640000175000017500000014527010115436474022710 0ustar pabspabs‰PNG  IHDRôôD´HÝgAMA± üa IDATxœìy|Õ½ÿ?çœYž%ûFBXdG67 U—‚K«Ö¶—Û[¥÷VÛÞ–Zmµ×nnUk÷ÖÛÛÛÚþ.Õ¶v³µ•ÅTDYDAv{ ÙŸm–sÎ÷÷Ç$1@€°'0ï×¼`žyfΜy’¼óÍwÎ|ûÂã«p*IT-]~Å)=˱’©] ÚgÊ™îÈÈÆ•Œ‚‹ÏtG`ßšgÐç‚O¬×Ï™mÃf=ÙÍýS5KÝÈem”_âe—ßG&&·H#NEãÇM.Û …Ÿ`;[gÞ`سO„> ÍJ þ€“ÕàI¡4V`_ºìLwäúçÖØÓRÔýCŒSÖ™³d•Ë Ÿ|â­Í›3{0 à0Ö>N‘²CØ™î@È1Ê=ä(”Ó¸c}V·÷?в&âÀÂÿý·ÉùlwìRÙ¦³€Óí~…„œ[„r9:Að¾kùSÅGž`Sà€ d‡vo#ŒÜ{¡ÜÏuŽžq÷¥Z«YÆéfð~de[‘?[€t·ƒ!=…PPîg''7-ŒiN ðÄ2ï¼}É>Y 9]„rï]„rïeœ›‰ÕC ‡6Ö8 Èj*–•€ß ä¯Í™}e·ÇÌ„c:ëMg}J»§1¹%&·ô¨3-48—íÈe;NpÀÌ gŸÚ9óŽ3ïtòÌ„t“=-EýsëûçÖwÀÌ)—{vù‰ªÅ‰ªÅ=j4d´Ï”LíÒLíÒ2²CÙ:Sæ<Ú÷<™éìì H¬–™m*:4 ðN¬± l@v¼÷BÎ…à}_º¬4VS«éi£!•0r?…œô(gî&¤jOÎÄúŠeEǼwŽÜsNnCN='Wî þ€B³²Ð¬ìiCÝÏB¹§.7Ò3Ÿc Ú— x¯ªXV~\~ïˆÜC¹÷FÎ…Èý¬!”ûû„ÏÔ~ƒÇ4Ô8²“ߎÈ=È^3{Úñ¦ÝCBBŽÀÙ/÷Ã)ÛOî µ:Ž6Ï&kwŸÎÁ» ì¯XV|ìÁ{Gänµû=¤F^,÷0Ð>ÍÈC’3{+–õ;¼ß¯qÏ¢-?:hcGäffz#¡Ü{=Nî§MÙ=³vXOFuº­ÈÝ=âþLÓ¡;"÷àžj8`¦wʽqúäÚ½þÃ'×W,;(9SW±¬ä0Á;Wú -sçÌÔ)rÓî!!§Ž•{w”d·íœÝi0TvOF8&Ò<`OŲþ]ù](=mÀ½ZùÝŽ-1;ï42ÌÌô"ÂȽѵÜϦÚ!'‘³K@-9ãËgï1{°ÄC¹÷6B¹÷"Œôxw”­õÁž‡ôR:ßSíœy¯­XÖçà}aÕ×–ÝÛyË¡‘{ÆMgÛ±Óx!'D(÷^Dב{e‡(ÏY^Õ:©ó–Ã'×W,ë¼ÇV7eǺœÍƒ+=­ä[¯Ö=Òö²Ó Õ`´L”´ŸI„i÷“Žz<äpTµNê›õvuò’Îe§äL¼ÇH;™„ .þ>ãJ3¼?fFt ÉßÌz²a×{ñù?Bªù”_LÈÉ ŒÜ{üLw ¤GÃH—Åßé¼¥sZF4šÈM(4!Õ\ÓÚXÕyg®ˆ+}EÞ£m/;Eî/Ïz²jÛ H—]ýùó¯ºsçÆ×µoA=È`J¦žCP2˜LµçÔƒ &S=ú<ûêfÞq2:rlõ ƒÉT»Ãé{P2˜)»çŒpF»÷‚ª2A…™ž€&““*+ÑX  |øäÎÁû¶á“‰H Ò\kAºeÿîŽÃ…ÒBo¿éÒ9réqß5¤+¤+ü#šé)°s ~êA3e÷^ÂÈ=äHÔ¤/dDŒx©sð@kEZfçZ R-µÛj*hep­;¼wDî›f= €ù.“®é»¦ïÒݶòï§ùêBÎ8A=ÈB³òLwä,$”{ÈQPAð{?Ïж7 Ÿ €´i¦•ÐÚ eZ¤«6nØér¥¹¢Ë£ßC{Øžžõ$€í«ç1ß¾kKÏ’®å»¦ïl|ówgê2CºÉ¹¹Ÿ„r9 5Î…Œˆ“.‹n ¶tÛ@«Àïév³“IÚÐÚ®ØÚĵæŠp ¨cTŒdcÓwlßµ¤gIÏò½÷ýê´_bÈ1ʽ·Ê=äèH²8']j¬P>|r¼ih­iC+“È$ehm’†´L¥+*ê7n©pQ»Ù×-ý£ð]ỦïšÒJ/â»ß³¥cKwÅüŸœ™ëì/Ι}¦»p† åÞ[årtjü‰Œˆ‘æ¤KÅZ°ÚGµ“ÖD¤ÐnvS“EÚÔz“ü¸-É’ÊVt_ÙLº\º†tMéY¾õݨt£Ò‰ø^Ä÷"Ò}ãùÇÎÌ¥‘·æÌžŽÇé%ô¸ª!=I–ŸH3ð>|}çaDŠˆ(°?Hh¤”Öœ´$mK¥˜8¬Øíô 2÷]ƒÈ™D6ÈК´"%•VJK_IO«Wþôk>ñèé¿Ò.Y0gv1ðÐìaØÞ{#÷nQí ãZsÒœ#]ÖéaTÒZ´†Ö)‹È"m“¶IG7¨0H»OÑ¿°â¥' ß1¤;¦¹zÈþ]ßÍ’^–ïÆ¥÷˜ïƤ•nÔ?Á¹¸O‹æÌ.öénô´Lo!ŒÜCº‹‹ˆEšÀ9¨ë÷aL°H‘&€@šq"ƒ‚P\ ­ó—1¥9gšs‡ð]ƒÈ1­©¨ïf“fZ1­HI­•RÒ×Ò×òÈý9=¼5gv9P܆íB³÷B¹‡t—9ô<íph0E`}°¡–FûPH1"b¤m‚&­´n6ÉK¿Q).4çÄ¡Ò3ˆ,Ò\ûBËâD-‹ä2­˜’Ð2HÎäÏúÙ™¾\ÌŸ3»è ´—‡fém„r9<³´CàšÀKhS?Ÿ´uDî`¤ÈÒzǰI|ŠÆT‚C3ÒºôÝ5ü¡‘ ZZ)é狈ВkÅ”¬žõ3óL_&ÚS1Å€ \š½aäÞ[år T³ Ó2"E`œi€ëŠz­Ô¹“æ×zG{RÞCT(ÍAœ4Ò·|× åZhijeH?_%-ÅÞþÓ£và?ž¹ç7Ÿ:x^Öcåêáw/¬øñvxsÎìr `À`Ð žïì"”{O£¹òÕC7æ ˜Ê=äØpXÜVbùwÕ‡W¶ÈrU!µ®=°ð¯(—4bÏš¤OF$g§”Óûm_¿³Ü0`i¿bÆ—|Ï]+xCùØîœ=ÁNÔ-×¾ëåŠÃþ™?gv˜=¬® Ãö å~zèRÙÚNhu»5B(÷c£ZŒ$Wp("Æ™"b\K†ÖþðÉþ!û{ˆ ©Œ³–TæÚÜüh~ñŽËŸH$kš¤§=OqžVz@Ó¶Êü¡G>õ­ žHè?2ÆtÖ›Îú BäQ™>àK¾ŽîÝEí{¬.<.³§1¹%&·"{-48—íÈe;‚ ‘ÇMÖ³O%gÞ‘œyGÖ³O¬¾;t©ìîÓêÆrìtŽîì÷¼ÓºÜù4É=»üŠDÕâDÕâ Bd!ÚgJ¦vi¦viP!²‡`\,WÊÆ•A…ÈžBö$V#±Ù\‹¨41]ºw3‘üdmýÈ ö›Ãºˆ[–ý& –áⸯ`zß»•¶U~¯Ëw—Ì™Ý(n—û`Ðõ%óœ Áû¾tYi¬¦4VTˆ<2'¨ì.9œ²ƒ’¿Aùß#Fî!ÇLµ9v\ÁI !å#«´dÿÖ«*™5̈FD4Z‘>àÏF1ÎŒk&%’7²oþCä”?W_¤9×\d¤˜Þ³;Ö¿Ë3~dÓóIÆ3`žqœß±×ßOJH²}«s*¦(v»€k„Ìa8¹roðš•…feP!²‡Ð\ùª4“’~ÖIlöpÊ>„r9\Ê”!„`àÇÕ’˜ÚÒ,‡ _Á8@î>‹2&ã€`žëJÇMTWøN£Ž—h.4iE)©wº1Hiq<‘û•ù™ÚuY䵚ïôÖÂö€½°=l¯v—†f?<½7r?!6N¯²»O(÷ã¡Ú3X¾-Œ‹§•—v„ÚÖ"!ï€]càŒ ¦†‘ö¼:•² ;Ϩ«5 ´à¾”i©ªºÝ¢¤ãÀY+îŸséãWî}#ÍK1Æ…ñ¹õOÿï˜ÛºßÕËs¾×Í‹(}pØþF{’½CîõÀN`pþñ6g?=Mî§(+LÖÑ´LÏ$”û¹Îgþø_ éÿéß~x¬:"^•Õ·8U'38šqŒëYj§›wÀèAŸEDlLŠ)žX›gEc¨í»J¦“fÙ~K=GúŸŸÞ¿5%ÚCóϾý唑syÓ{ÀË€¥ÁLadŽ1xÏSû<‘Ì~½ñŽóLÅyö$° ØÌÃö#rzä~:Ùg%¡ÜÏu~ý/ßù£3þúíÏÍÏËÉÏÏûÝä/tç@—Ç#H·gf˜&78P|ð` ÅÁËœ­ ƒEËÇgš8òûæq+  xX:µç¼xË.™ @¦âæùÛÿ²¼ôCX, "#Žá›öfûK.EYR[_é4*¦Cî>° ØVëÇ-÷ÃùÚå-2zÛñ5{N)»û„rÁ³7~ã#‹~j¹nµëlÚSsAóyy¹ùùyùyÿW2ýpG5XçÙ2-„ c‚3¡™‘;P“.p*#ïßóX”ƒgvg˲L|DÒ«j°˜ ˜\µ©è€öoÿä÷¼ý…¿yëAÒ0ó)“V¶gK3a8ÝŽÜoæ_–dKmKy£õëÁÆÎ©˜¿ÀZ`064û±sN%²{¡ÜC@䘞gy®í¹õž»½º6ÚšÊmN\Ôø§üü¼ü‚üüÂü_&>Êq˜Cú‚1¡™Ž•r nÒùΞ¦HÛЗǔoô Š{v_F#€q!ª1X”» ¹À[Ãe—#êÞ\—ÒŒ\tSîÆ×4RÛŠÙ’,sçÌ.nOÅt–{XÛžjïAÃ5Î4GP¶í6y‡Ãq8_s¨ö¨Ñ2g¡ÜÏ0¯Ì™8@¸îÔ‡/>}÷ôÛºxòþo}jÆúçLϳ|Ïö¼ˆïµxnemƒÝšÌiIæ4µæ66OÎB^aA~IÃ* ƒ£ì2=”ׯ2"‘æZcœiâ<ß©jŠ”ðyt›“ÝbQqµÈoð}Âbq"b @ÚÊ t2mýùò¦»3FÌ#Ó…ép1(±q%ÊÒÄl!œn uŸ¡0à»"â“-É^’þê+]ìÅ@A'³Ÿ ÕÁöí^íb× 6rPZ& ±{&¡ÜÏ0Á`êWæÌ.6Ï™m¢÷Z2€kå:Àõ'Ã8oýó æ9B:E‡|Á˜NݶÀô}Óólß‹xžï{­ž[SßR~éTH¬Æ~À)ì|”#âZD›Sœ3 h0NL×\+â,שi‰”m(¼zø¾wã2éîÍQ¥Q¾ŸÕD9i¦ôðÆ77^ÙÑZÆiëž/L¦G¦KÂá"ÍYFó c1!Ü£åܯõ‹S«+"¶"{±wï’ìÉ}S»ÙÇ÷Z³w?‚Π©ûÍAÙÊ~@$tzÏ&”{ Cñ¹@P¡¬TÍ™œöèÞ9–‡kÞ[<‡ùóÝ<0Î`€ùëͳr…gù¾í{žïG}OzGš4£12`G"­›‰i‚æLgDšÀ™&ÎtŽSÛéãñ¨)—¸$Nm #ꈯ¿èžL†Õ6q|az0]2˜ wàÅCÌܽy#·ž:rä>ÍùQž¬óŒˆ„-É–°p`HÞ4[€êïšÂ(]ðãÚHöNà„É?œ¢DvéÀ 'X~ ¤·ʽÑ¡øl Èr2 ç@¹ïŸ3;¸í/ƒíêä¯mï½ßïä0ÆŒC‡338ÒáÕÒ)“V é_´ç•3ÍÌÈœ?Õi©¨Î~¸£#áiCùM<£‰iâŠÀ8)žˆþjÊ?¹ç³Êƒ!‰ibZQ~G;®ËLâ†Íß̈x ËMñÜ´ÈɹRXRX"¸Ã©[xgáî_5 <´'—gþ§³Ë5£A6F’½DùÐÝv­_d Ó¦Ú«upÕé ÛÏìð¾\¶ã¤œ±§sé’Pî=Žkf=)W.úë“  hi}>p2À¡r¯3»&ÝZrõç Ýc`Œ 0Θ€Á˜f1vhm¯€;—Ýíš±<æg¸6-ÉH)pR)0*o­¨:ÌÏtc´ÄOWŽV·’ oŽ|Ÿ8û³w?פyn¦ÖåQ¿Ì¤ 7VßR:XÖñ¦A\kN4´aùª²¸Ï8¬5Óˆ¦x–ä–䦖ä¦âæ¾ì¶xsKÉÕ—ò=‡vcJê7ýÓ›]Ö°$ì rï¼Ã†e¶ ÓfT&Èù¤qÍ^4lÒñ~­Ú8TÙ)ÝÀçUÇÝfONd‡rï„rï¡\uël£àâWæÌn=Pñ¹íá|GÞ&û.'YÔ3Æ€ÀíLF{Àn2f3fƒn.Ð_Mþñgßþr³Ëæ¾\q¡iO'¡ˆ1ÝÏݳ7ÞE¼ À1âƒëWo-¹@vãnâ`¤i®q ­˜RRM•)€$)" "¦uGðîºp2Ìõ˜†bfàô6¿ ë€ÓuJ»…!'5ÿ ¿[+™%™í£-'ó–øÏ`Ÿ5‹žŠF\˜6È"²ˆL‚O$™ô“ä9ÁÛç…¦n0uƒÏ¸Eqf•}² C²gŸÂÌ;0ó„…!O;{ZŠúçÖ÷Ï­?jí°Ó'÷°0d÷é( Ù‘¨éRñÁRäã#Y?½âÓ†ï ÀcFÛ‚ `·À"@„¡|ΗÞ=Ì$v¿¼ä'f­»?Ê|͹朸pt’1âJ³`hcb5²'t`c¬Ÿí§›k–x¸ìµÏËéoüŸÌ_ç\kâ\r+;SŸŽjnÞ¹ Ýre4­9#NÚaÙCö¿½½øÏcŽƒ¾-른\2KrSrK Sq³*û€¤ÐŸ÷üDxyj7gVÿýÛOmy…„%a½õǣŒ 3G˜6`ºi«à‚ Ãå<Åxšñ çÍYG)HyTºTvLnУªþ†ÇT²Fî½€£*¾ÈJM¬Þò–A¤‰lÒ‘ÄѰG‹QÆbG›ïbÎØÇïÚtæÐ‚kÆ#:FŒ4 ËToÍÙåQŽçhL4lXkŽÑ7ªãÃËš¶g\+Éìº7¿­7J+J®é{ňªå܉3M\k® €ãò gQC˜ía;·väŽ?r‡¯ÚýCS§ú9™´/3žÜ¼f¡„xnkyŒ‹ˆ.laØ\XBXÜ0xš)Æç¾æ]¶Ù“³"=à{ˆŽ²Wwé™…!{;¡Ü{ GUü´9³_õdåú…Š´"Ò¤‰4‘f¤# (ÏX,~Ä”éçošqùOñÓÙ_Ñà PDL1Ê Omª8$rÐ//ªZÎ Gù„wöúË#ˆ£&€â–Ý’Û¦gоVS„u}/›°­ŠqELhžÇ2Œ9>ÜJ´T¦(–D4…XskæÐÓupKæn²\ûÍÓŠû̼>bÏK8…JFˆ6`(;g„ÙÊ¹Ï…Ï…ÏøÄ)ÿrB_•s•“+÷SA(÷^Æ¡ŠïìwÆ\½mÕ\IZ‘Ö¤5iµeÀ‰éì£ýHö/Þð•[¯ÙQ=¦ü÷™(™ŒˆqÒŒˆ!Ã@Ã+*²/=ô@iD9€jo@©Û0&ñ³õÙ_°?wàþÜfÅ|i*y©[ÿòæ7þ\zþÄ×rÅø½5Ö6"Cò~’PYý„“!‰½ŠÅ$„„a䫱ز.ÕurãÃÉùPµ72Ä]&&É2ÂâwÖ©2ÛŒ 3"̈0-ô„a S(¯™”fBq.¹áó®Ãö³€Pî½’ƒßÂÏ™½Ö“C'Þ°yù_$i¥µ&MZZqÒœèÅ#ÎCý‹çŸ¼}ú]EYKó×WbL^ýRílÅ €‘ÃŒR#R+¶ÄÛüþþ½G#"ëÞ°.2*ÿ< ÿÅæ¦÷g¿“^ÜŒ(X¾˜ÖLƒ-/?¬f7×äyðGpIÌ䆑7Úâ9ì·ë"Þ¢Ûó î'€+«žº :ÚÏh¤´H)2̈/b}f= ï™{"€ ²ÐvµIkâ\s¡¸Lʘ×é'0Óó åÞ‹éRñQÀÈI[·äIZj­ˆ´VD­ýæáo_ü)€ã>Uœ¿ªµˆUV—–}É÷¸ë Ú0œ`i'©`7‹Ô¡Ç渉„•S8,U)6ÕÉεZ bf¬˜ú~0Ó´"ËŠÛùç+˜‘ÈøÍn½û¾>¦ É㦑?Âc¦bænc$Е?XõÌ“ï9d:@štšÒ"Ev­=5اùS?÷ôÝ6ÈL E+C3®9WÜð÷Žkê„rï „rïõ¬ø9³Ëf= `ìeŸZµèW~[~†H+/µÏ—~7ù=[ù雯ž“q˜ï±¼?¶G0d†mô]Ý7…1º„Ö¼Í.À÷}‘•˜å—°äk™† ñäJsÔ×x¯| ±>Ö5Oè̪¾#?¼ó½ÅÍ«_é7þº&wŸˆõÝ0ðò>ûÿX&*ÓUëØ»»Ä+˜Y¨—íóoŒÌ<´o“ªþ<°a³/´ÃŒ´¦´æ)M)-ЏˆˆÈûÃÛ×Þö㟾Û$Jj .HKbB‰¶°ÝסܓPî=ŸP@ÖO»ÀÒy¿Î¢íúï(-Ò}7:ž™öŒ+£cVG¯é|TÇÏ|Ê9±Üýd·ì}§¨ßE‚ÚÞaщ:Y¡EBëü 3øµÞ,*tw¨ˆáº\3Tî4‡%D"]XýÏ [¢nz¯ïš'­)MI%Æ÷ðKFØÑÎû¿pÛ?ùÛ»’L‚m9-$ã>^˜– 9{ åÞCiÝ¿O&|äª_“äPŒ¤xaù‘f˜›rýg,÷ë`À“oxgÉß$i‘œOý0ødfieeÓ¾´3Äq†ôÈ=y{×÷Ç•} ÀâjêîÁ"[rws0ƒaoë²þœè÷€Í¯Ï0|X[÷Œñî[ùPVbƒ$«¬ íƒ+´fRs€i IÔ\³L7ï/Ï‹ÚNÊ“BBhÞäð’zsìAWóÒ€úÍyÉFÇŽøÌö¹-™íä ô +±{·³#n$ò™Qί7D:Žúýí?0~ÎlÍ„æÁÝTásá±ð†êqFî=ŸPî§•ã{îñ‹>Ó±þÑK~ÅH1Rœ‡bF¤œ‡æ¤)->òÅ Ðïq­H\ÿ±1D¢ò½WL¿¹$õôغµ-®²\çiáé¬ê…yã®'âN$4¸† "®!¶O°¶æ c÷¿Xˆò̺‘ÞT‡ÇÜHlµ¿D‘wV«~\mÏ4ë½ÎÿõûïAWÓ¾ iH2 0ˆþLЄþ£nÚµnñŽ5/ç\p{ýößgêØÙy—Nn¡\·~+9éù£¼È¨ƒ>‡Qû^X¿åïãîûôò¯IfùÜnó;,ŸYÖù×n(¼ @=aé†.>Æ÷Ú ÈÈ9_ †BNû·Ç—#¡Ü{¡ÜO”ST½/§x£à⃶/ÚזྲϷ㪕iJ‘М4o_Ôö¯@Bóa£?8lÓ+rÛ¢VO'=rc̈rðLK¦ˆ8AhjÓ:!xÉGW¿NàB3A¸ÙÌýYWZ©k ýí£ò®™˜ûA‡ÇWT½ €3£¢^Õ§–<8êƒR¿‹Ò>×Ò,í3ªÁcõÉæ>Yr×ÄðtíF€ñhA‰_!ùP©-§5cöbc2öÐÎÞøâí+¼–¿åôÇë³ýXþpkå»ìjŸ[’Ù¦d¦dt¼Y?Ë;ú^!G"”{Ï'”{לñ™ÃdãÊ#ïðZíÃëWg=Ó-\ëöø½CîZqRbض·S’I•Y†a¦Ï˜îs¦ý´&®!ˆ!¨Å+4xGüN¬í¥®¼Ú¯ï¿µ_œj|ã’âËÀhRÿ+Ç#ö×Êf‰-¹%ãeêu‹sK'Eˆgз˜Eòlr‰Õ'š²´ÉPé&CÃ*Ú½RAHfîoÞ—öD&v^ýÛ÷»ü7ÁÕÕ/úŠÏ(ÏMnð“qâ_™"F­_èðÉmŸG|n+˜>³ÖvFÀssKî]*ÛOîàºÎñµÙžS_˜|0Xù ù“˜n‰S«¡d›Ù5×Z@s“©,Û`\2îƒIpŸ1Ÿ±´Ÿr2D\“8\Šæíá7"±â·!Žîƒ~õ’´k^1ìJý±i“Ÿ_ÄÐÚP:åëÞx¢µ¾ÂÏn6,ÿóÅHel}KzÏê’þqM4øò%+evnY0:•Hø1!¹åh«º¶V›IÖöÔRËÂ{ÁPÔZeÞô\ôÕ»óý }aÇ»4Ýç¶ä–Ï, s}A* t.Fî=ÀsJ_ IDATŸ³Aîg¶FvâMÿË ËåÏâ~K\¶FeŠ´¨ê;‚4?¯~§“@°ø€Ï ýT`v"1ÁXhˆ7õmm·|nɶå€9>~ÅÝìkLt1@>$äXéÕUO‚ÜìH/bEn[‚e\óÜ]^KÜi¹ÈªWµNV:A$~9å'ªS³Sõ› ÙÀœ4ïÛþ÷+\³¯kG`ä5‰B#âFÄ&_ÿ-ÆÖË—WÕnÄúAïŠîY•é?±¨ï˜ÖÆÊ”çæÛñà¼}¹ÜG¬A›Eà{V$U cæ'?ò‘æÕM’Èˤ6 ˜|Ó3W<œeé|^é›eŽ90(CÀ¶Ï#¾ˆø"²©äýé>|éÝeë¶ß¹üî_Múñ©þlÏ5NnäVý=éVî§HÙ‰ªÅzÔ|ç&kónVðyYnk<ÓrËšïýí‚ûFN}rÇkw™Ä%XÙ´õß}ÿë«–òLHÖêØ%X×ñZ:ïQ€¡)?åïÎÞ¬¦Çú\úà­qËŸN[Ù;§~ÑöhíÅ}œªÿa­ù«¹n3ìØ`yë64£·¥h Þƒ-SÆþ,¡’Ü]Õ cþò÷÷=þ™U_•d¦œC?{ù‡"ÙÂ-ÿwÄJj!‡#LËôpŒ—xe÷vÖ”\ß±>aÓ2½pÌ‹ãîØ÷ê= ‹¾ÚJlÈÍ‹íß™½IßHddöàKUl´Y|aÇQKç= €ƒeeƒYF«79ëŸ 1€ –mØñœ{ï*€¹ý&WýênÖO‚þuà…›ç¶0¶Göo¹æëÂÊ’ÊL4ìÞ¼èñ˜…ÂA—ÈxMot®RüÍϹfĶ/슒).9ï^9Û÷5Ÿ»òGÁnÿ¾þkÙ¤´$iÕÐ[.ß·œ»i‘IŠt’§“<ÕÚœhÉ•ÞïC³/¡Ü{8‡ÜCeŸ›¬î7#X¾oÙÄA×¾²c!€Ì+÷ášïH®|(ÛR›¥è½ÙÄm;’;`ÊõmqzÖ‹ß‚»ä£6ÿ㌂_.hülðljtïj€eúMè7ÿa B´NdȼGϳ߭‹ÇÖö`_û-{+î¯Þ‰æè;âj ³tõ›¿ðCvôÐç¶/lÉmÉíáùÿ‰P޵Îñìoºaç_ÜqiCk¡ 3­lMŒ2™4wÒÌIñt’§,ÙÊR­} þû[Ÿ8½ŸnHÈéÃ%~®qkIÕ_ëʺ[Eéd…ƒ¦0þùçåÿ2Ár®}<'±zÇògüüþ‰¾ðøª3Ýٓé%Ûš[ßiMU&Rú’OG nÝ*+šòÌòŠ>å#«÷ÄâE×JÏÚÅ'£ýG>ø/š¨¯|)ïâ{*‰ïÛ÷º ÁoΉîËó3íçv˸aZñHNÛ+<°Kg<бƒ¿ïAˆfüF-O®³lVÐ<5Í,É-Í•$Ã9–SÂ’ùŲz½ïëV?©ÓIl-‹X#‹JN˜uŸ@HH¯ã¬•ûžôrf ´è|Æ$c’sŹ\žÇÇjá*îiá)üë*Öª I6”á’é×ߪÎt÷OÝñÜÛ­É]ÉôÞ”Ó$©ÿ±·ÐopQß&½<æ¥Æ^7‰Õˆ¿ù|îüM°ªëè¼³Ñ1)XNí6ƒXðË^ĽÁÆ•7Þ `ÜÜ'ö×€QóÞÝžÝ=¼÷ºo{nÊ´b±Â÷Ç8“~?4lØú°m“má“1D’‘WÌòÄ´4³$7%YÄ]Ÿ,WäJfJX–d¦æ~þ/'í‰$ú }óâ[íØÚ¾¬Èy@éü‡$ÓA-„äôG,›÷€O¾þ›j6|Dz0„V_QxiZ[K½µÜ°ËÔ‡Ò̔ܒ°|2¡2†cdKØ’™’Y>³öePxÕðèŠß /.]Z¼ìæç¡Ü{8ì4'NÛCLÿØò9Ó¤ò~$Í™‘4¬”a§Üd’¸§…GÜÕÂ'ájÃ#á)ž$Ã'‹Èp“šš]4}8Ó™™ êïñ•øè’ŸmO¤*SŽŒÆU$ª"1eE†ª}dlÀE»ÞW9C tÅ81¦ëØsžÇ|¾‡±}f0®#Æõ›‹þ¹­¥FùÐÒÈí×_JŒÙ^5nP“Qdæd&ÈýãRÆ»ý¢çvÝ{äÎ\dï—SP¹£žA7ïÜÝ·tà ¹»;s¡ø¿ÔÆîÇÔ»FÏ}ÿ-qÃQú¹­u"@Ìýž‚VÌÚt]Ûð•ÈKßX²}õMÌ.®ºñã)mIXK÷¼fYÖÀœ›Ód¶%^ÈÚo«d:ë», 9¬ßQ¥ÅO˃gì; œÝ…!CÎ Ý) yöÊ]yfŠi—n”‘"ÒD9yˆÆU,¦ªª$˜ÒL)@¶z ïL÷þxùèîÝeXçfEýHÌ·#ÒŽúVDZ‘ÑÍï’!ÈÄ5RÛ2‘BÆ4×:Õ'§ÿKZŠË¹­r2;_õ6Iž1š9iàF>ˆì Ëþç¦ÆmÞ5ƒÇ¼|Í#FÍ}|ÜÜ'˜êÞøZɨóËáóSŒé]×?êþêJÓ‚†Ÿ[ä‚Í[øÆNo¿icÐyCG–ܺÌ:¶I9Î?¯ÿ“ÃŽénޤîÉœn¹g—_‘¨Zœ¨Z|ª33Zùy©´%­I;q鯴&­@ ¤ (/MƳ¼ê½DL;Âk[zÇTòs¿½ËƼ¬\‰z‘˜gG=;êÙߌø–-M;'á“Pĸæ `\63NœiFZOXì i¾tGöŠJ±2^†,ØÊ/ôr‡·Ý8ýƒ°zéßïÁÀK®4Ò .x aÆCo¸ÜÜ'ôß±ºfðÄ£vxõ ÷û¯¿á¿ Ÿ÷€Aöš]?˜4U ׌]zÛ,©-_[sþö ‰+Fo¯ª\S9oäÔcû…{þÖšýTq.¤Ý{oaȳ6r×ÒHsÏ´5)‚¯bÿúÞo7æ¶à%$ D…¾e,;»uý&¯Cî9¡õüüþ‰½e@äÿvU#åC•¯y–íöØ]Ï‘Á5 8¥Óʰ®Záyð<Ҁű3rÁÎҤ뱜_QƒY¨ ŽÑ›]¶è·Œaòõ_O ¯lòî?vJF ûוùºjÔ4_¥€7õcy¡á8Æ…œÂÂ'—³VîJrw­ˆÓ–QðsÌ›Ÿóã†ÔBs¦LlH „b¤ •y>V®ôŒÀï½9íÞÁK®ðÉwJp“A3¢|JšÌ®k²F×fÎÏÎ[WÓ8Î3˜ãrx¯ý¸é»¦ôë~àÐ6—Ϙ1š4m7Ó,ŒHÃŒ‡Jç?`ÐüïÄWÞøõnvoã ÷Ïxýc^ý„ù/—¥eî‚Æ;àÎÿ ~Úüæ–Ýĵ/)ßñ¦„ŒO0§†Õ`zçBäÞ{9k宥‘æ~¢Qfå)$?Ûð£–g[ÒR0Í18²Å,Uß÷ ³7^sÞœv?”¸lfœ~yÉO‚—³fÜU”µ!a¹™­ÎØÆÔ¸`ûæ†ïT·Ê”Kºëv‘7[±à!Æpé´Y™Ö} µÌ°]'¡¤¿ïº šÿ×/þļ+þtÔ^Ý2èRZRãl¯ûbç·j+ßQ^j"fá ~C.<á¶èK÷K YxŸ$’Œò¯þÁ }(!'ƒPî=™³Wî¾´®†ŠF|’ MsKg\)ßðMӶϙӚ´ÔU•yV¤^Š“¯ÄË/¸=$í~RètãÙ)÷^ú¢æràuÃ4ó ²¥Qæäi„²˜/¸Ç ¶ÒL*Ú¶µÜЦ=?­-, \u#žÖ=ÓqªØ³ôœ]$è‡Ö­0•gJ×”]Èý¿ÅÝÚñøŒ6®œŸBÎe·ÌhÜ·@AéÈ`ŸJ~n±Ì¦ëï;÷‰’ï^üÂ÷ƒGR»¤`×;ÁŠ “jòÜ”Ÿiæ†ÕgÀE‘5¯ååõ]·ð»QCGM5tÁ¥ß½ú{ÁQu‹îÐZ³rݧ”gÿÛàk¹®cŒ´¹hL‘Ç•·6'p”1£öp`Ehp‚ p ѾE bBSûD$ èö•EëÃ*ð!§œ.•ÝIhqb‡Ûáì”»ò|âR®†Ò€ŠF)˜Z1¦8“`¾6ÓÚ^½ilŽÕ ß)@ ò Ü0Ï&ÜÖgzÓ=Õîp¸i£‡í_j)Ïž%=CzËFðdéšÌà¦ÌÚ³æ­(ÕF6$jw¯Ðg`ÛHžë þ×ÿãâí™ cè»|ÿyŠN-L˜û½Îü¸¹OÔ š¬ìãmc"cY…Í"åešv\<åFNî®w~…ç2Τ³ê¡˜©b½»é]7k4cŒ¢ô’t㺺¬óG}ÁtÖzÕO´™ØŸb¥vêR` ¤ wæö×ÐÓ xBD €Àˆ NA kÛ Œ€×¶?~²¾ g aä~YÙÇGñkÝx¶ÊÝÓB“ð ’ 5d,Ë4#F¤4¤ÄêŠQR¤÷‹˜âY>e+e›£¸q%þ.[ïÄÿ{¦×Äï/ýö®ï¤B#ö¿iJ×P¾uHØ^ÚðÌ·^DºfÌyC÷m{Ã7Kàã¢+ÿuá?~²nÙÓWÿË“úÍø »A’iÅHV »˜´` âLs€AsG`y{}ÿ·ïó¤z–ÊÝ÷H+%\ÍTðê¾*YZÊH‚¸&¥µÒ[vP&“†R†§ ¦„£,SÛBYÆ‹æušþe?Å™®@Ð}↵þé»ÇÜvÌÓ@¬ÝT¾©\Sú¦tMé¿6úSÎoý¼òú3B³‘çOMû >_ñuï.¸wJ¢2/o`î‚ båæ¶-;j ¦kîü:€’ù`ç?ðŽâ_«o¸oâÜïøë®û®}ý_x–zqê_Œœ÷ØÈyU‹÷ÇDFrú"“”»1F,Ò×dŠ˜ÒLMýÀÕeɳm•m©l¶ù‹jz¿5­N,o£?ŠSVŒZ½fqÒh{üŒÀ4Œm9•8H€8À¡3 ¢x°%©¯ž„/Æ9ÃÙ-÷egÌ€¤Ÿ}Rš=²²O"g©Ü]O ­…¯™$Hb’ ³³$$ªù±ÔîÅf4¥Üme*iºÒt•m’Å”-Ü[Ûf–è-fí¯æVŒñ¦gî)TRÏúY÷µ”c*ÏT®DîÊ¿Â}4+ùv«küneÙãÏ¿Ðõ`ýòýâ¥û ð•+8cYy”‰m…7Oùös|ŠÜöâ· â?òÛ¯ÿÆù¥?ß¸ï ÆÍ}B¶— Û–Ó?ºñ#%?5á¾Ð:ý‘ØKß\ñ‹›Ô€K€ ÐÍì<ÍÞn`wþg‚õ»V~<ߨVS¦pX„8Sfœ82ö°HãfF`*HµS:7[ऊX­S[´Î–êÿ|åÿÍvfu³ 9›8mY‘3ÎY*wσ¡µéjÈ xTõÞLYÓÄê’Yv<¥®L¦ ®,¥ŒŒ²}m“÷‰‚Fz‹ÙD +ÊX”ñ(ãÆ ûå=·wk ÉuóÚÂvå™Ò›¨çǼڭ~^ Ä3«S’ÇGÞ8¿þ•{êû b8€DCg´sÄÅ‘©g-šY ùÇŸÿ¦]jmÖ]÷`GûZ‹[û~ï¯Õ÷©öbæ=,ÅÓþ€:\Wð‹1Ÿr—}÷yüà•…±<Ö© Á¤ëŽž™¾æ©ÍÑk(È¥pp³Ž8”#Í™ãƒéàkL1ÛÎËÍÅ#¨Ú»mÝiµþ¾­€kf=¹aÎìUsf»€ xÀµ¡è»AωÜO©²{fí°£r¶ÊÝ'EÊð‚œ;˜"H_Q<7!Ô€ ÂÚÍb¸²¸²„2IFÒ:"Ñ«´3¬@ë6ãcç—>}÷Š£¥hÆïÁÔ^¶[Ê¿”½U {/È©qF~oÑNè_4¶iáW °ìtc&ZÈ®}|Ó²ïªdÆDyã:sÑLÅ¡,“Ì‚>ª`bëôû8©&‘x>€ùuŸ»¥ÏGÎ{L›¯ÿ:Ÿ¿oðùŸ¿6çÿùà‚§U´%ÏÌ1S+F~üݹ䩟±cm8‡æL¨qH#NÄñÁ,.ò²âÑìå\ý³>Ók–Ü1#Ži{{÷ û’ëïéhpô¬'›æÌÌîëæÌöÚEï ]ß•Ï>5pægÞ±û$†ìÌIdŸMœ¥rw\D†DîŠTÒƒãáíwåÄ [àªmÊ2”-”iª¨G7¿~¦û~̬zå² +0{°XŠŒßôÌWе|ê0wY'4<˜ÝÒž¡ò; ˜Àæ×g{ Ä–iÇ9ÔeÓ§>¿ôu'6#«‘Úb`ÀI7£]î”4$œ`½xÁƒØ{åWü暬¼2/·þ{Yÿ=ŒŠ:‹5íÝc1â«oÙÅ/´,²L]0´‹ø}òæç‰Ã±âšÄ€ Íb ªJ£YåÄÐìa»¸°<'yùE¿‹LP¤pØðòÿø¦=þÊÿZΟõd´SðîvZÖµoÞšÞmןõ…!5x?²²S¢@Zm>Önœ;Êîਅ!ÏR¹{>qR¦§!~{ÅÃÃâÅêW´J@2H@I¡lCÙBELºizaØ5ìv³3‹1“1“1K1¬Z°ç-ÙÛ6oÛ¹u÷žéÖè©ABÆRž©›sšxË¢i~[“?dSßvn!«`d´¤~ýÔ‘åÑXž…DÄ”ßìnr4šå¸é–†Ý¹…4ÿyùÀ¨·hÚshÒ®|GJ§©n«ï&KúO¨™ñ€Zðà4Ú¾³´„C7jcˆ±:ÂÔnLHV>d™dY„‡‚ö'lÙ±bAº¼sÞ Ä!XdSþ%mU¯ü° ”¶.)+*ü³7*8¼ˆ±42L]ñæï¤ašô1™YOiÙvÅ{‡¬¯iß'X®;çãús'‘Ý‹8r? …!•ëÁǾÉâ?ÀðÞ¯õøýÅyêêë[Û‚w·ÍìúÆþýËÿ}Šºt|µ0䆥ÏF ËfÜfÂbÜâÂdÜà"$ºèé»ßiÏϨÆ&ÙÜ|Z C[;߈†(Ö®xß’îÓ—>6¸yÕ«w>ÿêèÛ:×È©O¨¯x¤Ôh±X±åíZ°yón~áàñ_¯÷H°)@ð”iÀºíuM­©7Óßn¤$Crú#"Ý奓õª·/é;ä2 ½G wÝØ•/ý%Þ¬®ºfšIŽIîâÝ+ö`<«õ[–¿Ö±n£ÿü“¯®þ™’Ñu™£ëäýzHOìÍÙJÍžæ`ÎÀÚpܳl}eüØóÙóxMîÛoàqõ|B È~›øLTŠÔíÛ¼„uêÙ…‹ÞU_W8EÇ»ø¶ëkÚûúw¾÷c¯ß² E‹d7S€o=ËÆa™s岓ÂçËSÜ[B+ÿþÙv…ÅÛŽJùiƾóCMoÁwá¿ÿB¶í²uÝ£¾ò| MB“(y0;ç¤rÌÌÖ1[.Î Û³é‰cAs•v)iS’R¾àžöÀ ›ölx¬¢ßþª‘/~GW¬Üó!©Íìþs=³ý©›<=»úªÏ RX†\`ýó?pËé<|p¿i)Þ¹íÅøi“Ž=vªGÿBÃŽç~ý¹Çöì] 6ö=Û<íÖ†Ã/£Tê IDAT|úûÆó•ñµñµ« Üà´(fü8ýwýõ_{ûOêñ—¾çR><ŸŸÚxºEÄz>àƒ<À²€W’à뻎ì\³¢œªh Å„íü¤-½-vÚ-oà?÷Œ¸ˆQ/õ¿¯¿SÂÅàò÷N  ·ÕúãßÅ~× @q°RþÐ º}g‡§|¯¨ì¢fÀ±sÌN:ÇÎ g…r’ [û£î~Û4*4©0ë{ºL’/'؇ð@=ÊòË{Œ?PyÕ†Þ·uq¹é…Gv*#Éx\ð8Ø#Fyº¤éä„GÖàõžZS=`÷kK ßºÍ ŸU{÷íêéG«üc“^÷b„e}‰È]{çà Óoü£M+þàšÛ?³mõú¬­ï½öÑ=ôª ´)–mA•³´õ†YüfŸí~=I+îU±>•â²§ÊøÎk>åûœòù—|¸ë'VW(}1˜˜ÁŒxš©8йÇÃߨ$ĺ ×ÃOŸŽ¦Ÿ§ùó*Ùçp&Õ„îÉe.î'ÑVë[„þ’ëAyº¾nÜìwyDŠ„°sÎIe™ [Çΰ3ì,s$ìmWí<ºz¥Ó•Š}é|I¾0 œ«èµXOy¡ÿ{º¾ÜÔÜò)mR6ëqþÇǬçA *NÏ;wÉ!€MOþ¿2žýòñ†Ø:5´÷èÕʪ9³?»ì¡/xéá/μó‹á‘5•8dt/WMû}µùèfU3MöÕÝ1ÿ_ÆjWð8Ð.8ÄI bH°)ålªû«&å {鳡¬ŒS)þìG>vôÄðoýþÝÅ vMl›mv.ëlÎÙ<»€]àlèœqÖ8ëØmÊR•'‰ø9‰¤_~é}Ý'Õ=á$®,qoË%ªé1/Ô×õÏ5yÊDÀ±³ìŒt–Îe¤3ÎEÒEìî¶Ó9v–)ÌVxÊWÎðˆ÷«êýåW(¿j}ÍëÌf}uöiy?wi}—÷=`K;Ä1w‰€2íÌ5Ù‚pË*ʯv*`=Ì~Óç¼ôðW<òy"ž:íí‡6ülç3_vÃ_ó­ÿ˜~ü¯FåÝ«^±Á*ÿöÉ…§ Amðµ ´+hx6e Ú…ßò•·_ûÀ‰<|ŸG\Ûõ<øÇÿrõ?q6ãLŽUι‚³s¡³‘3†•c眽æ£?:£ÿŽ®%[»£"±ëŒÎ‰K$]$÷nË•+î—.+ëë&U‚ŽI€+ÊzÄΰ‹ØM¾¡µáZómkµ ¦l}>ŠBO:Oؽ•ò¢â@ùUÏ÷\øº—›yÖã¼Ïùƒú)Ï\›óûÌQÏG^´Æ@¦ßþ¥UýÍêe?]ë¥õ ~sþ6øé;Uy/­Ýþ÷?ÆUõüØ[Þþغ%[×ÿà«ÿÀÌ;¿¸â‘ÏX»ì{£jtA?&"b”ÒrbÖ¥˜œ]¬É×h*hò<† Š|Áûv|®j‡xhÜ?ì(¤ŠÙ2©®n§ÑÙ »¬s±m/8:²2ÎYgGOº¥eÏ$½ïtHĽےˆû%Ɔúº‰@U˜›“ª¹ä{ßé2V0Û}[s¨8ô8€Éî>U›È ƒ#©ö>­ Yãš_ˆ•=ŽÉH˜ïöüÚ ¦?õ4åÛ'Ñ«`/.Õ? K6B´Ä\ß+äü²àœTJxm&MqÇßXõØßl?±oJŸÝ¹•—žþ…TáX¦¬Æ?¥ëÊoŸy¾¨ì*hò4…ñŠÇtdž/h}k軾#Û÷3‡2Re•ÎI•W::T:”zøäñF½kÉN›-ºÕ ¦„+DÜ/¬¯LF£¼t5°¸¡¾.NiÏÛ ýú–B¨j5Â]WMö£|DþŽªIÏõ¸ÿt®8ºé%Ï•Ýã|™¶’lüï£zûŒÆAív–d¤u®át¯1l³ùm/ê*}Ê­UwŒùmÁ‰m'ØÄ\sÛ?¬~ü³w¿H´uÂô/ðÝßõúð‰ÅQ}Ó?µo 6W\7*³B#0\ÐÂÓjQˆ3&qb_ÊE¾³÷oøjs|½ÿ‡º¸©½·-Äÿs.'¸à8.d7nrC..?—}>Hœ{·%÷Kƒ'êë&ƲŒrÀ. 4·Y2õu-[æ,\¤QЊâΡÇaƒß÷ÑÌ^ýGÃŽÿtgíI¯jZíÙ¼ÇÅÅGÞ÷Ì.o’oœçˆ§ï\¶rÒÔ–ý%*,÷†7í -Á¿å+xæ¯BOÓºéé­ÏÞqÕ´qúùmtÍIšvë?n~ðÝ*8°ù7wŽËéªtØ€ŽLôÀ„h«çŒÇ‘v‘ÇÆsQoÖ–É9ÃN—Kñ±}¿Ð6úÆ÷žz§±^ï|îá¼ö ÚtêÚ{Ow:ï„IĽےˆû%Àó¿\4Á«Še½ Ø[²êÍmô½íÊ] Ý(ÿÉs¡Bèq¨ ðÞôègý?ëðü…¦f ¸kZïÙ¼ïò¾ËûÈûœÏXRFVø¢’´±%·=F '%—k>N€°Ëz½ÆŒ˜÷/3¢ßlÚ÷«¥{–ß6èš×vîÍl½?Oí²é{ûÀ^²jý’-¿™_{õg±ú›;ÿðÞòñ?ؤGŽ wyP鈣ªº¬§âÈØÈEÇØ™ÞžŒÊ»ðÝÃþø›/üø/çæ'Ê~Ö$âÞmIĽ»³â—‹&´1ì;Ì©žÈ·”Â2ó¼¯hèRL&«kI¡‹Kä›3mßnÚìÙ¼oóž+x\ð8ïq<»+5Ñ7@&bnØü‹gÆßwdÛãSÄ»Ã=yN½¼ywMØÔ8í£¹m÷|7€#Ûoð_k"2Ù†ƒqšM>:¹ [hÞ;§¬þí¦{7ÿÈÚÌÈÕ«ó `Pó+ÚÊ” š¯·N8'`%;^5ì­izÍ™óÞ“Ã> oŒ—öqï¾Ü»ïù<†L8qïÖ¬©¯‹ƒì5í ûI+ojmŸŸþ²¶¡B yò¯zŸìú*ùæfÛ/‚ ÷Ðôªgò¾ÍWH<ʳ(ì”={W¼tàÈÐ%+ß3¡ieŒLhùp_$…!Î]†LĽû²©¤ìÙ’aonïÙo9%Uf~å—´ Ba Ê'O—ßa6H£8Üx`Gsêñ…C9W .Η#JQ˜âСϡ © âxð–=O=7ïÿõÌ­õ´8ÜP(ëÛkòÄOnïÓ.°žÎ£Œ¯æÁ;Í#¾ý«PèmWí|Ú3g ËÆÜﶦüS÷¹§?u÷‹Êo¾²¤îO+úoÝûàêA]åàï©×·y{”‹^Iõ/”njPî„‚´¯…¸}óoï™=ô„3'÷îI"îÝ‘GêëF€Àn dÛaÚŽ–lÑë{FÿÒesÎìjÚ{pZ‰}…L¦ÍeW?‘*·•eº*%zhô¬éòggÊмÿžüˆ˜¾êÀ?÷¾ U‡ËSM›R¦½RaÀH^ª%YNRvŽ•cåXzIxÈ~LõX”ï÷3z7Ï~í7Ú<[ðL!° úLzü#áÿ5ï‘ÿÞºî«Nɵýîè¢ñ‡*GìÏ«òÂ1ÄBHw‚„€ÖRâîå?øÃ¬œÙÓO8C·¸Ï’ÅäËí%<ØðârqÄýTý}”õ“?ôBþÐ e}ç\Äf,®¯› Í͇b5·…æ 9#SÓo+êÔ©üÍãÿG³ÑÎ0ËîXØÙÉOífü“ÂóK^«”ª¶OʦªÚѼ¯Ò£ríÊ\X†fŸ‚‡Çn²ÿ×é2¤ò¥,u¥‡¢ù ö¨ùóÕoË_mwp,øC½'=qÊE-¤eeY çªx"ªø yÕ…†Pû Á †$ØðÐWšÈð{ì|Søþì¯Üóò×T{ßrÊYKmYùÕi‚ ‚Lt$à‘B)!Þúø×}ë§:;<¡»1Ò¿l9Ö§³ü1p¾?.Ū¿‰s¿ ¼î™ ý`4PY2ì±OofÏ¿G” ìðÞWݺ ßßjW£mØäú>Óð×½¯:ƒ&åš2…LV9W®TZª cÊigË(ò9ð©°·º§ÿ¬þz|‘C– Á=ÕÁ¦T¹î=S¡çz Ëʲ´NY§†6ÎI÷~f]õ¨©ð˜ßù~³`¶`Á,ø9ûžÞÃ#ÍŠ×Âé Wü¥ç Fu,îsx- ŒŠB€BA€Br‚‚  ù(RBÜ÷¿ó‹wüégH8'œCçÞbØ·ŠßŽv÷FœîlÏ…Õ³êO,Ÿ!fŸ»‹_n$â~8WãÔ·<öƒ @Øé^ÍÀM 0ÇWèl¾Žƒ>«m(È=x¬ãõº¦ÐÜd³=”N+å+YÙ4»2gR} þuÜפSí²·ôº~òÁGA b TMz…kD;:(Ûö–Åóáû¬5G~õ–~×9ô<,ƒPtñ‚_rwz¢ày¶€ãM[ÿ²ñë_©:Ù}¿@SP”µÛW (NÆ”pAxÃâ>KNkë¸[E>yv>÷þ›|éGO¶;Œ¨QYçæ•¹ì—}æû³¿ªœû«³&7=‡Ý­S½vÌ<8%@)ž6ø3«ñÕ1‡Ÿƒ[fÁÌ,™-³d8îCÍ©½›d¾ýÔÀÿó†o3áüq6νEÐOŠ®Ì+¼ÝOë—#=UܺÆüÇå± €UnéY\ü2çÊ÷וl“Ý “Ét½ÛIœý8õWêë"`0ÿLêÊÞ4òÓÚî<«R´ùLVY›V*¥¤–Ò“2åBa†ð½)_p´i\‡CD r±ã~ßÊ¿]7hn;–¥eerNZ§§¯âs¸gņÆÂ$[úÌ0ñГpˆÅŽ!Ž«‚ƒ† \ß´Ü(õLß®J $\*ÌÝ< „`‚ᔸù̃o·åd ¸#ç~Ÿµ‹"½š*/`£/5.q¿¤«÷=V_W 4·ÌÚsÚÜ4öů¼qÃÞB!“í¡TJÊ”TZJ%eƒ(/s®þšŒwøßvаÖlH«<³@ày‘:µêX(ËÒBÙ’iç—oóýkÖÏŸðé§7ý3€}ožxä 8†;fÁì8ŸwNÛ!Í/_§êO§’eÂ…¤­so š£“NÎë_žÅ£®‡ ñZ>âSŽM½&m9Ù6"V' Ô; ³ö¦"0j«Ã½Í,L[nWÇÆ?‰ÌœD7÷‹5§A<ÓË–YR_w¦šsÓ¤/Þð¯ç¤ …L¦GYeJi­”VjqŸ™è3ó#ËxÊwfb‘p‘ö_îÕñä«*Vö¢·@*åÒiÎÙÖR‘{ß2ñØãpÌ`ˆ}kóÂJá$;<48`2r–üñòêêÆ$\,: Ë´úØjvp\b€Áb­à¦…Q¶è Œé7ܲßzøîòâàæ#½MWIÀ÷¶’6ïP(gÚK·zß¼7:¹Ï¹RvÒØ´V¾RZ©‡‡¥ù¿f½Îw*…bH0 †DßT®wŸN÷w¬”ã8,£,«þ©ðñu_»gÖCún¸wö¿]V¼âÆž·˜rì÷™——ôF '¥• é¤pfpîe£~¾ªâ]çæþΚ¶âç/®pËщìF“#¯ä0‚u,î=öª°œóM„UJy0“ €h¢`Bíl§òío–ÖÂL°ŽŠá„¶œq¿¤£"W8|èK{´ò•ò”úõ˜»OÿÀãë­Õ¹´éTºJrrve¶=;Þß’*†Ý¡Kîm›?ûo#¾öûåßxàm7Oo8iÿµ=﹊Ë…FI'­Ê ¶Bº»÷õ<8¿yUžé„sL[q_á–Os§‹¹+Ýsq´$•\·y&ØÁN-:nࢋ—ŠÁÄrµÃ^c¼’‰0¥ÝµdŽÊvKka G˜faámdsõ…ºÛK‡NÅý|Kvó¾¥ºÕ ¦+8&ãIõ³itúGU¿úë OJ)%É,È\ÔQªLL1,iY¥lLìDü£ãÙ ƒûlZxÇ'êi—Êy¤çG3™)´" sùÐHé¤rBºÁǶïî;ü®ÿ`õç;ºTÂ…¦EÜ?ØsV˜Ç¶B ºå§ÓÅcÄ–bq/iX ¢(î6[€À$^ÔïÅ6Dh•&LYXƒž£(UI{aáÀj­2SLvo‹:{O\ö¥ EæW÷ýír[ÅÖ=•åUå#éœpFJH8A.ì¤7€ƒ´¤,”Ÿ;ìÌ\÷>ú/zo]™ïN:dôˆ¡”É2=ûÏÈ0«¢¬1ùBd$»P§*lÃí™/?Z‘Ôì½ø¬üÙwf¼ûþï¾ÿû?[þ¾²YCXíÆ‰Ù˜V±_5 †¤‚¹^±,Ø6Â?"FÅFpØQQÜ` ½&Äáí\ \ìâKé^6l€âoænœ <;nùE}*ˆ.j‡uêÜɾøÉ{¿v¦‡ ­îÁU•ꄟËd#9ŠÇíVU]ˆ»%å Ä‘Wd`ˆû7|ê;“¾ŽNrÆ%ššE¦Y4õ.o|lyù°Ck7bò|Ňƒ«ì‘›ƒ¯>é¦íQQj¢.lÔ…ÝjÓe_²LD`ÄÿÏ ìDCkTlÞaá"@\Ѷ»m”À°!XœGCsË«Ô ûA«ÀÅïl1êF¼öÌ×®p/žË[»¤P‰ˆ'œÿµ·Ï‡kï:2§*µAF²Lµ¿²öm÷¼«bëúí;w÷¹€%Uxõ¹ YYÿ%ZîªËçG;k‹Sˆ¤€Ôœ=?zàºQú•Wž3á:€Ì̵fÿ|ùõ§UR:æ"Ó™ùïüòwñ,"€[7vz¯Ù« V©Ø¹»ð‹+.ŽÉ¸V}x5L(‰{»_¢Àp/jwmd¦EÎjžͲÓ¼ Sy¦{"^—„„ö|7˜8´ª²ªL+8ÁFÂH˜è”ª2fFŽíUÛÛo—|ký²?lC‹"gŒƒ Å ‘§ÿè_éGv2 ¥_\”*¿—Ýu=ÞHÝ…„sµÑ÷ŸÓò~Y àÄ@¯Q… ÷+h•O1ìÀ6ÎmqEÛΆ؂-œ%gaC؈â×xKË+3à6€Å)r hƶlÄ W²mÇE÷¸+5îVí>Äîq¶{÷!®*W˜¹ðÜýÃOüñxË?yÃ?1¡þãñÆïV/¨®¬ê‘ÒÒf›5CCÝALæq1mLmõUU•:,p>ûª¸©lìºÚÙ0 û:ν…ÿý)­<-µ¯¼?ìžðœÿÑH¦"™ U*–ø¾vÛ¹¼á„3‡Úç̃¸uCEE¯ŒöÒDSMœçŽXÍ#¸Î6è76Šõ6„³í–XÙAØûà ˜€“pÀ3û„Ñ6þN‡¾YyŽSàãzýÒÎá9Ï+ÝtSÂEä@}]O’=‰jHÔÈh_‘h`>ndNÿøÓǘ?|Ï-*Gì[–ÜaUKjæÍiú}˜ÉiÌ’$¥r}ßeeåÃvþáþåŸúά¯wÖ’7×ü€Ô‡¤ÖR{R{JûRxN}ô:þw&ŽÁ0ñÛP÷+>«2 ç_–/¿çè¬C}#Ä2°ªB}DF4͸F…RðÝE`Óm7˜ˆEéÕ 03ņœ@ 9=Ò)ÔUÇzؚ㊠\ñɲ‰¸'´òÄè)d­NHäH¤ˆ|‘h71737ƒ3ÌYp¤åÖQÓ&ï\#A¸Î ß«&ljŽš›)ŸR %¥RZÊŸ^ÓÕÌ®¨ÿx£ò¤TJj-UQߥzñ±»ö¶=O6Gü3&ÜKŸümøÍóðT^ŸS©¶ô¦ZÍ&‘Š`#Ôzú˜ŽûXbË\L•i ¸Û°mos¥KˆœÍ–p¢ÆÀ€*öK&TüŒ±X«â!¯¸P5ß»IÌ=¡•[ÞÿHû¡NÚ´Ÿ×~Nû9jÔ~£N5k¿YûYåç´ŸS¾Ñ‚_6öµáS!:uî¾[~ýøžU£ª*)›‘ù¬,äe!§Â|×9,d$µ”ZJ­”§cÿ®<¿t¡ÜÇ–ÚOGÒ¥IßuwúÏÏåãHè’YrÚ 1+^§Sôýw½‹™ˆlÉY s2d¸AUÖƒƒM9˜R’»bƒšA­ÑöScîÅ~ÔÞ&Ûׂ!›ÈEð¶Iï5äädä8Ìqæ¨àÒ§K‹¦Çõg.Ä£é$Î=¡‘òC!B¢ Ÿ(G2$ʰ‹ {–‘cW˜´€ ;†t`e§ÎÀÜõ®ÆŸ†ÍÍÙ\F*¥¤Tº«ß½éõ?¢})$I-[ý{œi=ð™ú:,\tCÙ?11 Vnïñ—‹ ï;g#áõ˜.æ:èJÊe¸uèÜ—g²CîÆ×ôð‚œÌgU>«ó¹.v>"Õ1¡ŽJu\*’Z)O)­•ÖJy²µ l ðp}Ý3ù¿x*÷¹Pø‘ô…4 úþǹy ¯G\ÑŧF*hÃÕâÞë_ž5wÓ,gÉ\m`¥v6vßÄ–LA†ƒ, YPF²)ŽMm{¾ml{T|uz°ÕC —F¹¹uÒ­—±C²rs0…¸“–`‰-±%Yò vg¹YnW¾‚"ñ‰¸'œŒÑ~¤ýÈKÚ/èT ý¬ö³*•ש‚òíO½ó f°äÉãK;ä׳þdlu¥È5«|F:wÿ‡Ÿ8&äQ©Ž Ù Ô ©„ÔJzJj-=-½Í+~ïYÔ”Žzºéó¡ô#á+Ü0ð¿Îî$¼>±ù•”Ì: £}=œÛâ>ñ:[8WLw122°!â° ›â>&‚ Û½Ú*«v`Fa«¢Õ"Z&‚,9só0!œ)~38u‰Ñ>ÝÝ4ó­fæl9Í£ŒcïJ°íHÄ=áT¦ßþq«S‘JEÚ´_ЩœN´+{ SñnËÄŸ´ûU§5—1€‡nùLO¶2ŸÕ¹NgD9*ÔQ©„lò„T'„lJ*­”§ã¥dÞ«jàñúºøí’㟅 ÏW]}-H8·f ÏÒˆì-K0jFÌ*†DÚÚö⺃dÙqª ±!g®Aå[£o mH6‚«´®ÊÆÂÛªÄá7¸ ‡0ǦÓÛ»Xì@oP:EǪÌAß°Ì®¾r’ßqOè§}«}£R¡ò#åTQÙC•š2ï-»±$Xp…N剷ÿ_•iVùŽÅ}Ë?yLÊ¡¤Š•½Iª&©²BI©•*fÎQ_WSÒ÷–ùÂÓ{?‘¾mð땤O8Gü.ýRl½îdf¼’¶Faü£Á“ã¡Ip¥hI,ñ6¢¨.%¹³)¥´;ô¸;°!œ!ª²”âü6íµa&ˆÇ²»ÎÛºÄ `K'ö9îû•aØ[HÄ=¡&\ÿ>«}£}£S¡— ´êT¤Sc¯{wË>Ö×Q\VFrõ ;äá?úŠÎ6wø£ãB6ÄÑ!…j’ªIÈf!³R-™3ÊC)&/O•Ì{Ì’ ÿÖቾ_ ÿY°Eõ—Œ™R¬«>,Ôƒr NÄ€ñÔ2 )þÐà ˜ 2®=à—ÃD$ØÂP}gî—æ„0‡\ï©Aõ½Fùðʪ@YüYTÙ U}QݵƒÐkzGß‘è?ÆcÐ$ ™Œ¡¥¤˜‘s0úzˆMz­{ä¢=‹D"î ãtÊißêT¤¼P¥"í›6ö|e}Ýí ±dH†ä¨ËT™éLÜg¿ïk B5Ê¢²7 ™*+UFÈœ¦¸Çž½¦}ä@\2ìÙ-ï}|Ç7nõÉ3mØy".–6[.vCÚ— «¢íoìð–,”ÜUÖ•ǤŠŠ–éËÃl‹#QûFß‘m¢4†œ¡&Oƶ½â.SýÎ;ˆ2˜<ÕܶêæT<â´úí¦æ}‘eªzOÀ2YGŽÉ2Yîx%n¡cÚ²ô2¯׃\uô¤íI*dBÇŒœv×¾‹-‰È0[ð¨ P}ÝN ÒÌ` ¶«³Ê÷v:™Ô‚w~©e}íSßõ¤ò„ô¤ò¤RB*¡”Tw¬®2@hšgêën8ef«'^ûæÍã?ñ俤øÌy‡Ê’\-[ÌÙ†Ù Ã[ôÁ&¢ÃÛà Àqž jÞjâ¡Æ#kñêQ0ãÐoü^w„5AWˆlá|è5€žÃo>í}O¸#qî ]@€r¸ŸÎ¶WöVq—Ìà.Îq^¸aᢧëëâDgÔ”Ä=Ó/J8Ï,·«gˆk•˸IJ“ rÄ 0˜K;Ú„eâÈ [v‡dx@²Aï÷ÐÔöx#šr"˜&TãL¥àÑàaïo¼¾·G…€\ñÌ ÁÓñí-n@QÓÛ¼a*Å®ëÄ´øCÀ²Îó#;”ì@7ÈD•oøÑ‰dŸ‰¸'t =ý4µÑô ZâÚ,ÒY¾9Wó.z¢¾Î T·÷Åõu·¿ëÿ\ø&]iøyÈT½#ŠˆÐÜ×ô$@ڣȶ|Þ3À8²5Á¶–aCUÓ É…Æ†dLŸe›š‘m¢Æ k"ôÂù¤À¡HyÈ<áëlÂ2A8jÜÇR2§jzIÙ‰Ó›Tn‚1š{TÒÃÑjï:º˜!É>¼õ«gó(.˜^Ÿ‰¸'t h+î¹¶¶Xzôó·Ö~$:½97Î9·,\ôÈ)úžDÞ/ÌPÃ­Ž Ø¯"ç1Zä•æÍˆü±¨ê¶¨º7t6ÛZ,lêXn:J9d³¨fÚÑÀ“Æ"WÀÎi™?øÚG¯™‘ éÈÓ¾ƒ‘Ï0yCPLÊ©­¦—fâƒPQ©Mª0Áígj¨ÚJyøÞÁ/¸säg:»Á%;ž¬ã¤°L÷$÷„NiùŽ{’so‡äÈÙ¦sÈ Ø\_gQmâ3‹òµ›Þ“”ÿ=¿ÄeÖ…†v׌„üPKêU ‹È¢µsõÄ^Tô*Zz‘y,Æ÷ål™#”Í"›Cº?O 46㹕¨H£2‘Ãq°¿£LHÌp‚BAW€àˆ‰  뻚”ÇRCy$5”ÇÒ#U‹cÕ8ÞßTPV½Ôsxk…ÈžÃ/擇íÏcE»Ó‘Ï䂈`FZåA{ª%w†™Ñt'~¢ÿ·@yÙš ˆÊؤÙëÇñÁâeˆ,ÁÆmXú˜Aå Ï™,{i‚,gHF@$ ”$/M©j¤*(U‰TÑXcGšhx”d2ýLC/#bëà±>/á ¨ý›ˆ{B§Äa™°½s?qÁ»S;bÂÂE›Í¥ÑªÕíǬ&œ¨˜—R|¡g³®mÖ‚`GZkµ­!¨Ô¹ °,ù¾e1´vS.@.@0cÇ.X‡\ˆæŽ6"[ÀŽýhÞ«t5›,GejK÷a¿œSåð+ȯ ²1¦l¤ñ‡Z=&cCiìˆHH DD ÂÈ´_«åFÙ¶¶ûe¬ìHÂ2 ¯K¦¸ßtj–¡ìâ`òÂE«êë"`p)8“¤Ež ¯ŸÞG±nÐ2ÜnÀ f8ôhÐMµF8vcœÞ,áÇ‘3²Çà—6‡W¶â@½w˃ãÀÖØ¿ù+×ãD3>z~ð,¼B ùP>åC;Ttð°“‚…‚Ë¥IÀ(C¨p L=¨ÃÛA¤˜$„ÂVɤ@ª5CÿòVv$âžÐqojîÔ È¶\L§É5 ½T_gÞ%}O8‰S%;K |Æåe~—úrÜ)V(¦brû¾  é‘ö öÁ•Ž&Bæ€ qžTh„òÙæˆlù‘Ïú7~.Ô•„c‡éãÚû@AJäV°$Ù)0TŠ¡!Ò³[Ë ‘‚ð 4Ë4KMÎãþãèЫ Ä™4E b®.ŽU½ì•‰¸'tApÏt)î¬NkšŽ ÆÌ…‹ž¯¯ TMÀâúº¾m\vœA4gÞG°ëw<ô½þ­hç÷À5‡”M‹|•á4hªSMŒ 2r0Â,lìââìû”>|²ZŽ«Ó̇°'Žsªei@@Xl~ЗšC%ËFX)aX$ƒÀ‚`ÁCU0 6·4‘í4ƒ+CÖcqO芶ÎýæU²;9÷˜ë.z¦¾n¬ÍÔÚL³ìFÓªåÔ˜´Ù’6[â ‘¯Ë“ì¸*d\!òô‰ãr­²S àŠá÷Ò‹"D˜¢\ƒ2p?cÙ .ÿª*³ÎXÿ•ª¼ÍTÝb7úO|SÞø‰|à!¥a$L©0ýj4¿F•,ȆúÔâÕ½¡ü:ƒ, ‡<©¡úZîå¸Y´ê»Y°b«iÐTìÛ0ÜL@­S˜xÖ±û±§±×ણƒ«Ž¶­@ˆ{B§œäÜ;äÉ•Ÿ¸ m:=nX¸èéï}`4P,©¯›×mÌûÁ]kDbϹ=í…DÓ¹¶W0¤Æ¡}2ÌhÎ!* * 8^q[¤³¤‡šÔpk Û”3Tž±T-¤ ø>ü=ú0²h, ] çл?‚¡ËYh°'©YT%¡°ÂæªeY9;‹ ‚` È4À`£hÈt(í€\¥¡ºKÑ ÷„N¡öÎýÒâÖ÷üùÃ?ùZœüþ`}Ý]çSßOßbŸQñÙî9îñdFú:* ̳`‡ ËQ€¨ *ÀY€ãaE¶+=Ĩl]Dä !C`Ãf3–#pH9h½ûq³%])à$Øï¡öN#Ç«¤I&å¡q7H@@ß1€€#Rƒ#ÝÊ øûL]ßÍåD"î B€+9÷[ºù=}î|ÏŸÿîçßýFÓ"ÏGT¤ßЩ(MÜqéÒ’G8oûŒü(»5ˆ@è•Såìæ8 `B8ÛZ¾+îØ$ Ü¡ª€É‚˜|æÆ¢s|…¾‡ªF”Á2ÈCí8×|œ<…ÈAHôÿãÈpâ×JHD\)ŒD+;IÝ’ÅŸÒ€©,–0òzl{á"?º ÉE÷¤0äésá CRG%N¦r*š× y *§^˜Vqiâ ¾îÑúºÛ.ºøÕûºÙLLo€¶â:Er»ÊŒ0Žö45±`‚]ë8"ÓÜ õÁ—ˆÀ†ù ‚{½-cûq (GÃŽ¯ú[#¦~Rj®½1@D¤pâ Ýï¾já±³€DEäœTØÆæI@Ä.]7’˜ì\&\ÿò¬³™˜©³ÂÝĹ't uVr ›Ñ¡dKs€Uô»ñÍÇùþ™*û¥¹HLó}nÌ "DÅÕt•áJ¦–ßj­ÐXZGßQpC ^\ƒ,Úv] …ïiBxŒú¼=4ŠšL~×ô‚G!UÏ{Þ:.1•6*út¡‚ìHH@›úbmJç³°tyO¹×–DÜ:¥Å¹ßz1b2çÖe¿ãƒ/=òý;>úÓsxÎ+ض[Nûhl ` d€”·³Jíüû€±p·è/ `' á$œÅ¡ÿôjÞÅ Bz”áˆRóóºj~ R¨y[täÇJú Á$ Ǥ`åköHg@²x ÑR÷NÀÆ9]äVûåÅxl‡DÜ:…ÎCWêKïÓ…(M–sÇG'~¶Äј÷¤g¹H³3pœÁ¡òŽ„cp“ zI—Ê3bÐ$¸.* z«¸K9HG Á ÿ£\Ž`ôx—åî©4gŸõÀ\ý¦ˆ’pHA—û•!̃ˆZ/!™ˆØ+~-X=ä Rv$âžÐ§)îÇöoïØ¹½z¹0a¹áãŽ2°fÃô×=ÄóádQÙ"KQ‰Þt?À*43Â2À©íB1m5\Që¦]„°lÁ®²—³áŽI² F!4ÙÇ4“,žÍXx'œmeVR^ a­ñ™ ˜Dq|jÃÎÏ#ì¦$âžÐ1Çv<厦ßñÁsh·ÉîVa'Ž]§(hݦ©Ó&mš>yýÊuWw}Ô:[ÇñµN; 7-’³Ã¡¯dØ‹šîõ }ãÁ¢š³I o’ •` W°Ð ¸¨°1HÄ ÁÖP¤ûRµ­Ê4±c¬;æ¢Äó²‘+gaZ‡ ¾¼IÄýÊâŒdšN/&ÓsÀDÝ3[&¡3„°&Ž]kò¶M½‰°fÄéS·ÎºfËòUo$Y“Úþ³ÂÃŒP¶„GŠ>‡_kUs¶ Ù•g&(Ez¢-"‹`‡*X ›3A;ò B4@ѹâ„ÈÆÂ„ ZwdȘj³Q’ÒÎ<;rå¹zb—‰¸_òœ¿ô¾Ãéß…§ãµ›×œ$œ?Þóœ5aÌz€Öož—°NJáV®5óš³gl_¶â̪ µ' ŒºŽ_ ±Ë ‡9½gUQÊÙ²”ñN €©˜/‘š`¢p‹ƒ$¤.ÖTQè)-¢bVÌÀÍÏîŸ0»¢°¾¹lXù€.O w%G=•DÜ»)J¶Ëï ßðiÏ(*’-Mu”p‰2uÒJfÀ{cH çxû!¾~æ‹ìðÌK×µÝß:!¥{iõ°Ù3öϽvïs/z;¯<…Ë€k`ך’ #ž½©UТûr‚#†QmVB f4ìCïa-9/ÅÐ,TiÆÔ8Ô#™™¼~ ÿ¡‘éo¤ÄöLmÔ+ÔÎâÎã3®}élžá¥K"\½ï éæéí ]G]ZJÝ7ƒ©ª"/ˆ 4°—»ïæ%&°O>×:ËœµBJ·lÅ€ë®=|ÃuŸy¾ßé_ôW?ûÎÛß}¿‹¡¥­ž™dIÐÑâÙo‚ 0΃{;^å“`€‰™Á$=Å;ˆË æBñ¥ÄAFÀØœûgÆpþ8R{èW¸r%îʽósÈ“ls|€ 6Bõ’÷(5Q6êÂÆ¶Ù3- y–¡¬Ù0)~«•¹çºež/¤Ôv?ˆ@RÌ»vû’GTÑö¸0¤µBJ~þž×Ï>>oî±%Ïõ<ý+ö« Ìv|$7µ¥5ªט)‰»Ò<΃Ámõd!Š“]ÇsîI;w ³‚P`]œ½/޹“äÚè¥F\ˆšìÐuøÝqGš,33øÚsúP»)§†LĽcšŽlËåÎíi/­\‘;“˜Ì%N,îÎIZç$Åš»^!H0È’ÀõÓ·F¡}l]1În-IÉÏ.«½ñºæ74=õLÓ¹Ü{ËgÌTSa%¢xkKÅ^S©HH3Ê 18Å`ˆ¸ò—dbbfbŠëM–Â2 Æ¡WˆÒ±y'H2I@y`OæÝЖƬu˜-¯Ä<™˜+KÜ/~i‘„„ 3¦nô‹«F·l¼kÖfÉÌă$‘ !ˆ Ý<á¡(´K_»%}_ú|å¼ëó7ÍË-^’îúŠ\5ëÕL4åÃl´×DÅÀ IæR=F¡  B4ÒX&Á¨Ø«àQ¦_U\*d<6ů‡^ÅÀ Å>ÕxøEDŒ ‘€„2fçXVš1õÊ™££…ËAÜχd÷è=@·*–pšá$dka% f033,“ë:@$ˆ IÖ Öú–£b}_òlÙ‚£[„O<åuqÑýc?Ÿ²@ÅOó‘[¾ ý‹?5“¬™dÕF)$„WTöüPÇЫ4*˜-‡< ! $·ô¦‚ÁÌ ’·¦Ê±3>HÇ5ÜA=”è¹~‚|ï&÷ãSÛv*;º­¸_t‹?t%ÕM¸¼ˆåYˆvÝ·NYÌ,˜ˬÀ 0Dó^ZÄ-Szbí›ÚˮӜ¯;ìØ”ó—ôB³;½ÍÁ¸/šw~/€ DºÚÅÊž`SíQ%|bÅ ìb@ ’[zS™*…Ýc}— D$Ê™µCž¸ Ð ‹R((XÜ;“ì°ygæØk^å°7vÚóIªþžÝ»êï•HœšRw%£ãŸ(*{ìÜ9¶ñí4]H&ÃÔÆ¹·À8YÜoº1Ãl™M\ã%Óž[>À¨ÑŸk=¨eíšÈ; {›8öÞ»Q‰ËLØ»T¬à!XH´qîÌL –°;ïYUw‚Í’ªf[Æ$(.ÉBxŽŸdG\*Uϸ_Zé} —?Œ¹³jéó}”ˆÐqqdÅ)¨\ qX`L‚H’°qŸëI'lÝrË‚mÙ°ôù>mwlæk,·ßÁFLž°:5ösxåKûšxÜRÿ‚Ä–w¯Ç…˜!˜‰âjB"îMe13ÑžµsB$u„‰Åž$ ‚I0PI»ÎÛ3½ôèTÜ/¤d7ï[  [Íבp‰"¥Cû˜ÌÜ«a'ÛØöâ+1(jz›È oºþñ‡ž½µå §:÷ÅK;žy¼­íg¦x*, 0ÈjAØQ°zÇ*ŒÍ=3C—ƒÇw![{SÁÀ¬V3Îs" È5 .K¢ ¤±y§SZye£Î^Ä—Ð T¤HÑIÊwU êIâP©[U“Ô6Â÷©.ézÓg±xiYW×.±~óÔYã·aÌ_có—‡k-<@`”Õ¯!ÂЋšZ>5J'•œ;À°“[è#=O0¿·Ø©¦åGÏÛÕÞœ›™(ûItêÜÉNH¸t!jÍ |vËí7Œ~¬UÙK*Žk»œlÞÙ˜Þrçâß<|Sñ\Ta>5bÌý¡„”$•èÕ£À[ç.–Фû›úƒ0âÍ‹ˆÀK?ů½P,K@²šbè[P ›!­¿ºU»åZ †œËÛž…šƒMjÝ!ž?ä`%–ÛÕ×Êi³å´eWdnÌ©¨DÄ.3Üg–O.iõ×$ˆÛÆd˜ã@-ÀÅ H’DB`¢Ö‰­câÎù×ç˜ñô³íÞçþƒTB*! )âiíâ·R‰a½ùµƒ´e/êg·?'×C­UfŠq3Œ\­@€›YiЦš»ÞÄS_vß›«fÂѶŠ• ŸÿØÖÌ* Sš%§1`Y#@·M…LHHxÈØ“*ÍZZThé03Zl{œnÕ$X&"Å¥ïºoéÏq#Ptî'ÙöÛg>5[BÊ¢¸K)D-â.•ðÓà×l¯¤w|{ýæ)s_ž †^¯¢«fä:%œ¦ÛYW!áeñ=/_ýKÇ¢!ÝrK{¬ò+Ü•2Eêë".vÎ1O<å9Ë7ÝEu>¡8,¨µC\JƒigÞI´ÛÀÌ}zǼäùòxË×=f ,„(Ùv!•ZÄ J ¥„ ¸ ©æ5E§}õø5ÍoÿÏÌ;þó¹ ÅJ$Ù¹Ö±¥5{Uïƒ ‚O¨‘gz×Ëíê+s°Rg$âžp só¼ü- ZóI¬%R2ãë‚ò„ôE»°»+é;ŠâÞ2T•D±Š#½ÿÃÏèÝ3bvϼPà­w-¾{Þã®À°J(%b)õ}ÿkDF.¢þDn¿3ÆÙÈmxyÊúÍSZÚ9eâªXˆÍD‹På.U³_) ½ 6º_\çw9“ˆ{ÂeK\2ž)»û׃L›-gs’ù×[pCãM7fâ·7Í nšWh»ÃSϤ˜yþõyc5é áÜ¡Z,!ƈªÚhjëÜ ˆg­cïxËÓ.`W`8©ÛÙv¥„*šw&&rØFÎFηjýæ©ë6M[»i€©“V†ãÿ+޾ܮV>â…©}ÎMÂi׃\u4~›ÄÜ.æ^»›„¤Zq¯éMó nÌ3»§Ÿ)—’¥dv®å@é 1G\ŠÉ ²Ô&2#„%ÏÏ`öôÌ|ø¸~×;—†¶¶ÇV]È–Ø:µDÞI|ˆ­qƸøõ¤Y»ñšk&¯k»EùP‡Ë €ç]]9$âžÐÝ™1u“ZHÕòûô³íj¬/^âXpcÀüë3 ÷Ìs•Kž/¿aNæ†9™^JIO8Ùs®P²íqX¦¨*žDÈâPUbÇÅ,ÉBOv\1p«_e£ŒsÿŸ½w³«,Ïÿïç}×Ú{ÎI&gŽ!ç „(§„@D¡UQ@ES­Ø´¶ßZ­¶µýZý¶ÕŸ–ëÕx*  VDÅpH ‡„CH “!™&Éì™Ù‡µÖûüþx×Zû03É$3û03ϧsíkÍš=k¯½©÷ÜyÞ罟Œ1Y&¦x½TkRâÎ@ç+»ýêýŽç¹õgëé…‹ôÂ[Öý¹€I\û!â.µË¹o{^)7þßéSÏôÛ4`Õêz—]ÒÅL—^ÔÉÌA P¡s÷À&_v· 3aÀ{È'¥ˆ(\}E¸S”M–£j µvµk'> 5Q1ðæÀçÀ7Ǿg¯´;^«|ÃâÚ Ô÷75X#‹¢C„ˆ» Ô:Ï­?sàO~ìÉ&K.îXiÒ„Ü;Î÷u’ØSœc“0œ+)»s˜¶Îȇ䘱Ì<ûÜ×âjŒÉÄŠÛÛµV4ØF Á ß3×oYFé>”'`W“Ä:5± jSelÂLí`ó k-û׿AÚy{5„̓L­¯ö}Œ4˜)üc?¯?Õòäš1Ö€OžÜ“ÔcuBé¤ÒI¡hAÕD_6$2Úª €Ù0› Ë&cLÖ˜Œ‰5ÝvÈ8iGÑøPÙ™(Ø6Ò¥,sñ;v*å¬yvz|ÆvÎ(òž&¶ÝæANiØSí9âÜ¡†á¾KÐKfýZ¹¤J%H'”Õ/X?'0ôÔÿŽðkÞê<0®y»M¦Ë;Ä9æ ^V ãÀ€R~˜œpZ˜¡:½Y² C… §Z+íÆ#ì›48ðý‹˺ßKÜIõ!;ýµ¨ôNïîïî<Ê>&¡ODÜ¡véäò®¿eX3Ãä8ȲI HN@'pí•Z•·r¯ê'?xç[oÖ™öýSfÌØÓØ26¨7©·Ç=‘Tü¤iN: ÌltW\1YŽ÷ÆM2h1îàФ3ƒ9,Ëx†¿þ¾Â—P$²S äS„Ú… œûû.øf0;q©<îk ²09$ C•gPöà†ר„z}ÝfæD7 ÊPÓ¤1ã¥2ÌL=@CôW„ÂÝLq€»­Æ9£]¥UÔø¨•KLBhÛ#Vóعû¾!n²¨ö{Ù%]Òí#”wA¨]^Þxî‚ù/.˜ÿ⬠Ìš‹ò g¢†Ý~šMÎÄF>ÔúfžõÚ–WæÜ}ç¢ø/8ºÑp[ꔲ>Ø0@\í×™€=?Ýä­É(Ò6C†´£T 1Â4xÐI¾>û¾ |{[|£“ÊIÒo×\YøŽHl{è|3\°|ó@€ÎLDÜ¡ÆÓÔ ÀIè0í %úÞKèì³ç™ —÷ï:§¦ÏÝxÝÿz©n?eêÆ5€zG)Æ„Cs.€€3Ìd«1ì!¿Uš‹”‰‚‚…Ó¨æÎn£ÒIzpõ»JÞNa±ptbÉ>1DÜ¡FùøûŸfÆövp“ºoMG~,jo¡7YYûw ?¸þY¸®NæLŽ2‡ÒnB%Æ$VŠl¥¨¡‰½ndLaW;mÆê‰ æPÑ©ïûën£~àÑÒ8ñ›>œÞ³·(‹x2HÉî“–SÂ1v§Œ9€L€ˆ» Ô.¶ñ<Ω {£˜Þþ »=ެ= NrA´Öè„«“{†A·§¹ ¡gUoL ²¾É°í±hÈkº]D5Gò §öQ'Hk€h**µ_¾$ÐÞ‡Ó IDAT}ú‘ÇUø$ËL9ô’}bˆ¸ Bòã.ºé½OrN =àÉQË!&À„Ž£ }\Ž·gx=&È‘NpÉdög™±qÝÿ™·ð[Lb'é“b·Ž½.† 2ªØv¾#ZD%˜Nö½¼g7Ƹ J'iãÎ[ß6ý»î¾̥‹Ó¶æóðªá¤ìG—lã¦tyÍÇ{ÙAJöÀqF2^ÝYnfƒ›Ù`"k„gNƒ¿¹Áßl"‹ۚÇ5wŸÚx©ÝÑÞщvù“æ±¶Jbµ Rý»{;±ÃC3Ú%=6É 2DÏ¿ü—gÏþªÎ0“ÊAŽWi­T}ÔÉž/µwqQà6j]GN’t’öïk»|.»¤ D€zôñºò~¦¦¬U‘ª³ëÈ„SÇ8uÌ]G&ˆ¸ BíBfö=CD¤`gaÐ;Rƒ1>Tù0勘¹ ¶ù<Åaû)ÓˆìÅ¥cw'«Ú̺{¶ß?ù”kr=0&Çòˆœl#Pa›$! 6 ‚ë"›>¤\ÒÉPÖu’tQ7ÆŒ –\’²†}Uÿµ‡ŠÊK¶Ýžj·ªÖ&"î‚P£,˜¿À“Ï]|ý%’";O‘‚ýVí³'™ÆQ¬ìz N†År€”-°Ðöì_‡Œ¢§v~añìNçRon½kÒ¤ë  Z7Ø:;…—6Þ=ü7AÓØVåÒJ¹JiRJmyeò² <€&O Ú÷¹µôÒ )MÇU–‰õÚ×GôÛ†âC­!—]nDÜ¡F¡hó¨ïRDiz(ôÈï'"(E4&¯òÚ [¯¬¢ÃØ€a&bB&ÐCoüÕÉÞ©IJìßÿÈøÉËup\fb±2L Š‚.»|0³®£:w¬rHiRmÙ_ò¦Þwý´«¯;íÁ_îÈ'ëõxw'Ž:#[èºõµÃfS!mBdí`S!mBdí`S!mBd aS!mBdÍ`ÇìÕÔ‚*¢1{GYP=笼¸á¼øÌ{Îyˆ"‹ï%ß Žó5z®»kl_<Ù.;é•u’së32šº(ËjÜ4o®æå¶$ü¬¯Ý¹çþ X)“µýŽNSGõMˆªúÑ…Á¾ûæK+þÀ7nû\ï÷uíGÎ5ì=~<ñ5+îµYv·cödAUj‘sßö|ÂõÖ®{gáIÏ TÞ°CõcÞK¬½"#  WqF)Ÿr% (Pð)ûJÃKg¤zŒÊ5(jdÇ |X߯˜“@ ƒ\§t N¢!fàSÂSOú)•¬zK.9xéE{Mà?õÌ)ù4$òĨ‰Üùá¦"Bü{¢ä dÀaB(ú²¶ÉÝ(j :Ÿ[u£ýi/|扺k¼,hå¿0Û}ͳÓ·ùíg¿üüK †ü£DÜa°”/Zd틸øo*í\sÃÄ’EÈßFó?ðÞG{ z±ÄÛSùóù'ø&ã’A’@ZÍòyx¥ÙÌÒÔ¬øuÅÌŠ÷ÝÔ‘"Vg8òì‘y/–{;bÛIèÈìç]ÿÒ¹®zíêø]¬}aÎÛÏ~¹Ÿž â.}SÖMÇì–‰±«ŽK.9¸ôÒÎUO´ô~¯~{9€ß°š y ÇgRÙøp\­J¹cës~öyÿñÚ šHèfŸñ@¼¥A/T‰óÒé5 rêÙϪ\n}þÉh ë ¬šzv{@ »®2½«7¦tºÔó/-8÷mÏXÿÊÛñÑ ¥ˆ¸ £ˆÚLï?9~饗/éyôñ†>ŸðÖγí@ë™g¿öãï]àæO>¤šŒoŒÏÆ7ì¸ ˜ Øégþ~šgÎûÎÁöGUâ”ÃéM{{þ§‰“ŠÑ˜hR …ú-/üÅÌó¿¥ë9È*/ë¸uA3Ch€- én”˜w"€Équg ÞsîC¿[ÿîøÎÏ9ëf*lú†waØstÉÖþ>sðx/[k›W=Ñrù’žeKsVF­¶“'f­CÞ³7ñúóóÞy^ÇÌY{ƒÎ&Ûµb|ÛöHJ:J‰ŽçÀøI—Ü·*Ûõd¨ X3͘~M¦›¡ÞçŒu9ú'V*{üËšmö ¡34ï›v­˜ê¿3û»¯´Ýj¬‚P÷­Ê½±~†€ ƒDÄ]¨Q†ÊeÎ$íïÓþ¾À™T;z=ð`ÈB}¼aÙÒ\á™É“=0ƒ½û6lÖœvXMçžîCA†µ«l5†•Ml'ÃÄ@ôÈ9ž4añ‘C¿ƒn˜¿ð›ÄœË€d|íÁO&Võ†³NUªÞÂd[õaDB?–”Ýu¸À¼dâê͵>rßš+œ}æ:^zuáP}°B )â.T”ª®Öæ&Õ£0{ë²K»Û÷;`~ü©fûôÓ~†³ö³dØÏ²ñà¸vB9Ž@à0%’™Y|£4ëíß&ëóA†‚ŒqÝ\6ë ¢y«VÍÃG&;›‰8,Ê·"Ž« <; K4×_òè/ž¼œÅ¶— wa°äõ:·ŽtÉekÇe×&K÷0ãñ'›ì·Öô©ö®›ƒPÖd˜ WkG9ŽÒ®B‡yqºX6Ï ÖuÊdÖËh"v]ãçàg %Pߤ:@*­U=uºLY"( ;1Ñ܇܃ LT ¬¢·ygæó_èåçTø£%ˆ¸ }3|×G<—/ÉØ}L=ÑàO>Cì½ê&¿Û‘m'"íjív”vI5ØY¡¦ÙxÚ»M*g\Š‘"0(òˆ€ ÃÊ5 ò³Æ§îd¢”ôýž0Ù†@á²Ý9þ¡`[¬Ÿ.½ªýEæ}lS÷¡Tcµ>ƈû袼U‘š a,[š3V­nølÝçˆ €½ïþˆßÄÊ®mÇãYÛ®š(Ñ„06,ƒxˆ’J(•TžÇŠ  £@p“>¤@š÷¦±º§G9à 0¼\@"ØÐšh[l$÷ûÀ“€ Dà “aõ_íåm{A„W6‰m/"îñأ ;²î3Éñ™\ìÙ³ì$´ãv”}Tc­þÇCW=ø&ð T’t’8Ãð. 1ƒÂnyRߣ\j£2id»4'ë[zŽÊ›÷XÖ­…W°-1^ÎÏ¿=-Î5KÀ'Þÿô*㈧†Ä½ùäũݫS»W×Têoýä ÓíkÒík*œú{LÉ6éÝ8¸EÕŸ|\—-¯d7Ÿ‹Ôz¤Ö‹y/l‚åKŸ¡§~ŸÙqáõAWàg™}r*öìÚQÚU…üZÛÍ> ÅÝmÒ:IG­a"“ ‚®Ëmh [½²†\" -:›¡ ›ÒAKSëøžÔA?À¨v¤ï0/Ħ^¨?ìàï^[šú¦6ƒ!÷öLÒ°gJÞZKýµÔ¸Föp^¡ÜÜâ~t]ÀÿïvÇ¢kƒnãg˜˜´K…Õí(jŠ ;æT¾ÎF'H'I% 6´(7šÝI¶Í° kTäÒ=÷\üÑ›×PZ›€Ý&¥r²äL5…Þ&À˪·µW ‚au8Õ¸z­”eÊ…ˆûPÉ®ÑyByø˜þ2‘ .ºŒÄšŸÇç½î È0)¥]e»m5F;DcAÆö>‚À¦³@Ù}•T*IÙ û ®C@Ô˜ ç7á""YC.È¥›?öÔÿuñÍŸú_êÓàLÌ&ö.99cUž¡s'ò­s'pÑ/Ÿº|ÁüIÚ Ë‰ˆ{ßêµß½ €çyƒ¿¬¸lÁ²ö¾¢m;‹®=Ú@´›õߨƒ€‹ª[æ½7è2Ú › 2ª•P¨ìGBMÅÝmÖ*I:©ü,pNk;=À vÐHçLe•0äÒÇ–?ýƆyÆ£YsÚݤR VY¨”Ë*Ry6`p´îJqYæ7/\i/þòÆsÌ_¿`þú—7J¯,Œ.q—µG¡*”HyOè-ñ¡¿Wdâoý±òðÓÆv²kWé‚ET=!Tv¨³¨ÈnU’ì:ªJrlk0Ø0ŠÝt¾œ¢(6ïÊÅÌs_{ãÅy]­-­ÊoÚ¸rˆ,<—mˆMààø xxÃUÅXÅ]8!F‚¸—»*R›cT…aA²¾èÚu þæÇ¼¡ðä³÷_pÁûŸ]úøGW-ùɇðuEAÉoœúÍÓ––×Ûeô2Ã/(²[q·«ì†!H&Æ9Φ;ݤæ.¦¦ü+Æ£?@0Y£d%ÀþŽ„B+¨õçL(¹Õïý9€U›®FÿHU¦¬Ô¨¸‹Å†;…²¾<5OA1`` ˜aÌd2h*úfí} ×°À¿~àýûV·MY2 À‘ó¯†áÖçï·¢ :Ŷ=TöVÀ0qˆ‹J1¡¸s¢9Tv•$»i5Œˆ1lÃâé›¶a&¿·Í(—ž\3À¾ÿ K.Iè±' ÞϱLùyg¿äèL6—̇,…ŠŠû1%;—ÚŽü´Ý"’-Ô…²þ‰®YŠ0&œ &ò*ï‰|Må‰5MK.ê@Ó趇V®¸rùŠ+—oH“Ù7&0ìT•ðÂKg—í¿ÃhÇCö+îU‘ìÔn¨©ML‚p9/üê¼ 'Íö|öÁ6`ƒñ6ú`Ï©vO¸[anú„Sˆ ˜qKj.Ã2ÿÕ´µðúŸüÙjülõÿûÖ¿Ø®Gí(=%Ìàý‘¦Eí1A`Ü&Û!Cwßsé%"¥Ç“âLnÆ0Ìc”Õiû^TID?Ç”e嚢÷«ÀcO4 w,[š7Ö€éüÓžz¦†¶ xœÁ‹¸¸lA@m_tíºç~µDßíÙrkÃì §èƒ»˜¬Ä3Øà‡A›}8{ZÎb„[|Â-"0ƒÁaÓùM©éLF9|GÝŽø¿øùíï~ÇšwÄlö¸õbq×u¡gÿé/@ÀÄVD¾ÚgózÙ0c q@|„iLø*Å›ÂÊ»r‹ÞûcO6,½4½ôÒôª'êm:²¥9ûï•UOŒP úuî"Ù‚0p¬²k&Óï|ÿÜÄ4×QwêíßëÙ¢‰n=unÇNÛú 6P {`󞉀֓ÈX§Ïaá:Ìrdø>$uÚ½M;ÜØ=ýަ¶ÏöϾû£§I€aÓnúZA5ÏPÐI¥ëÔ?°4¼c"Rjëæ)§ÎØK›À˜€â×á¸%»¦Z°›‰ˆü\@ÙÒO€Š·$=¼*1o™*}’PAqA ±aÿ¬šÙÙé3˜‰}ßèfœq—ÞÎŒï¥6gÛ‚ûü6Åô¡Üi9c(*ÈDžÄv`YÃÎ.~t]¥@ ŒŸä~¼}æ›·~öŸpû÷oËW`JÄ=àD½VIúÕï.o{ò„ §Ö¶Ü06i ã +3¬; -„Ân™Â•ÕŒ¹êâßÿæ©+ã >ººnÙÒܲ¥¹ü8éb¯*5Ú )ÂXÙ?Þ5³›™ ¬á6ÌžÏ߈鮣~Âm¿hݪ³°Ñ §{Æ0ƒÂiH‘²ƒÆM±5[¨§¸,&©0šŠhꩉOîšóƒæÍ^úÐ÷&M£±sKI=ðY×Ñïž}wÉ+Úê{~réõ×ßH3l º)KÙ/2‘ý)gì£"›Âf§ïÛ«åó?Ýyæ6oüÚÁW_ûÔÊ‚»e‰ÿgý{Kî|ÉEÝ Õ÷SØ?,`LÜ0cLü­ äkî±y/¼æå‹»˜ƒG¯ÏŸâ¡4ï6r¼»sÈ®8Ø<È) {ª}#}P[â.eâ;'Ýl†Vß¿<îL€bA$ÞŠµò}«ïìù|M'" ºÁLƒUv«éÄD ½a†10ùA§áÎf("Eê4C¾×³E±RLO¡ñ§by百ßôšï›ø+øÑWûØ#J*Ìj´ßþâþË@\Ü0ÃlQ°{qÍÝ~d‹ÄÝþfÑË ©¸ Ç‹”eáD 2Œ‰§Ðþ]Äv €Š¾ ‹36 @'™ì…K©ÌPQAÆ®¦¶Lëàq¨=^Gµ¦Ú:h¥ˆ‘±b5þä(]™O¦æþ ùµ¿ýÊ—ŽC¶ôÒ @íûuáö‚í¶A¸ .®…±¿%»™Pð§!„Ù/}%Qöª"Î]ùX·žÛˆÜÆü·ƒÁ*ûòÔ¼ý;Œ 'S´.š·ð ÙÊŒï³ÒPJl;8_œ K1 6„^5 ý6)R¤@ã¦ÂÌ8°;üÕO¤f£ÿ@›+–¤—-ÍQñXµ lÃ&,Θ€×ÙK†.©Òöc#5÷ZBÄ]- £qkÇ: Nß­tÞ’šm˜lõuüI”×tVQ±E” ÈŠ»Öì8Úv®¦Æ•™æÖ°Ún l+$âG&p\pWÄÐÝÆïü7÷ø=äõÀë!¯›¼›»f @ß_´ï²K.½ôÈå‹»ì™GW']Œú/óÁSØ0ƱŸAÀÄ‚ÕTºùU¯å >—\ø–aõÓ“Š>))ËTwa„cuüÏÞº#p ]2Œ`>ø&íSÑ:%Z %>”û `Ïgå°vBÛΡaG¼šWÛ9^q îY ì‚jœ€Ø›°‹ `÷À»¾ûkï[xÉ…oÞó#×çûã‚PÄCϾ›9Ôô¨ønŒ1ù5Õ‚šLÉœ 6>›¾Ê2"îÕCÄ]-Xç ʼ[G|sjz”Ã îØ p(ñc'ç5Xó‹«Úã0›°†8ªÌ€ÇhºÉ¯ ÆæÝºvOEÔÌN\‡¨N¶ÝI°N]ǺÎè:£êX3ðG_Z à›_¸zõÓ“{rüª'Æ<ºº(‚²·s J¶!2,ÎŒˆ; ㆙‡^|OÑgk|#â^cˆ¸ #«àVÍ9÷Ów«ì7vM ír¾S¸n 2”Ü®~_0™·‡·Ê:‡ê¡gÞwËÚûÎ'rúëœyxUbÏ^,]œ*9ÏšnûwÃDÎ=l˜¹ÿ™e…¿rþ¹¯Æ{æ…Ù%—ºí‰•`¬¸dù‰}àÂ`Øud‚´B #–Û^­ëno]x{ëBû„cBä˜uϵ¶õ0Ò^0G"lâăͭÔyÐŽ°3ˆ•v¬¦"¬¶‡— c lô´ Î8 Vrؤ©³ 6Ìq&™í¾àhU§àècì}¼~éâÔe—zìÉqñÉÇ6¿À{Î}(Ü­ H·3»:³:ÜÕ¸`þz²qÅÄåü ¡ïײLõç.ŒL •QM¦°2s¼\ðþgµ î­Û¥5xvÄþð>&ô0¢yœµç¡…w]8.çÛ™ê›K«í… ªžã3P8DŠ@ONlÓ./=2Ã0˜ îœd»À ®VZë_¨] ÆùDrÆ¥~ä(ojÕêf‹/Þ_²ôºæÅÙO<;#¿[5àm‘ë§î’óÜ~ÃÙ¥ì^=Ĺ ££ ¾‰Dê¸Ìûù¿ùàz"0¥ Œ±}*ùµOŽö*å½71˜¹i,u"{Öq9ZM-L÷-ú=®O@ÂsÈ!ž=u›¾ð­™Lœ:"·nÿÆ€Ž£´&W+M¤AŠ(C¥…õ{rÜâ‹÷¸hÑΧ×å­ß÷ô>¼lõö} fŸÄÿõë‹ü‘ "Ýþtï“õ“/qF %¶ýËu w×|ÙYø™ðGÔ÷Kùˆ¯ÕñŒxm÷$wÞÀ§ùÌ& |œÎ{d?7' åÖÆ}ÌcÈ0uN p×R]cØeST“!0#HøëœcÈhàå9Ûà‚m3 ¡û0ÁþãÂ×gbŽ£\MZ«_Ò º©azVÏ,üú1ÿy¾ú©‰.Z´óç¿ñ¿ÏÍ(ùéÆÝcÆ6uŸÑ½ãBlû‰Ò§d"îÂȧȶ?Ëž¼É×V0¬ˆÀìhfŸ™ƒPÛãÆ–®ÔØyïP³ÃŸ54“ërØ'ÛÍJ‘¦Çåz†Ÿðc•u˜ 1‘ë‚lˆa"e·þDp´r4i­\Mš‰kßÙ¤ÚÐ<ý˜oóéµ§½óü7-Ü´vݼøä‚ùë<ñìEOœØg'â^Ìà%»7õ“/êó|͉{óÉ‹S»W§v¯®©a{õ“/L·¯I·¯± ‘5‚Óz¾ßñœßñœMˆ¬šÏEj=RëÄȊSbÛÑ×jj!G7ï'e·l¼ào3ŸþGÒ¬A Xq'6¾aì`+ðáÎ&;ñ.*—¸xÇ`WS‘¬§¨2 pg HúÖ“«Œff(Mmls€³^šÅ„î#ùRŒõï¤XkõÔ˜ÝÆÀ2d¥¦¿ûÁüÛº¶ö¿ÏÍX´pÓÛÏyyËKMÎ>s€—^DZÃ(÷½=SuÇï5^îòšŽýìÓŸdœšwArŽéÜûÔ÷IÙí ù‚õ}iæSßøtÝÌ•™­÷èí7ð42 šýhq5¾ë5Œ‰+ﱋn",£òC9âj{PçfPÖ1l0°gñ6—0ûÙÙ†ÐÓi ù6è‹_™µÓ>q@*@'å< Cc ‘lÍe;r™‰º ù¬Ö®›÷ös^žyÖ>f2p^ÜpÞñ|Òåå wÚxwçxw§ÿ-+Çe±›Üc?Ç2xÉ8"·mDZœ{ŸLÌõ‘þÆÅ¥`œß†®R¤Ã~wŸMXzGÜøNùšL¸¸ÊÌä†Î‰$Sð,†ið8¥];N•8tU[‚è”Çg³€7¿îûäû<øÈ 7𦝑c8y½3í§ÁvfÞòÛ« øcõg€¸ ¾Òôü?à{þÅç½í™7^x„]Ì95æÜËQ0ñ”‚ž©å¸ø ã¸ê.ÏÜXíÛ„rñù© {÷‹|~êÂoï)ú›÷¯ì}°Ð°—ð›'¾¨Ç'»:îQÛoÐgD Œ*Z[ [ {£¾…Jj2`LüñÜCŸ~­óÖÍãWÎ+ª¶7z0@[0Qº®ßš&ünÖþwoö|òs€E°õÇÐâ·Î°Ñ“6¤ ™kùT"ÜËÛøQO[þƒä)ªWÒÀÑÙºaÒ±Ÿ4*"î•,d÷C-ëpˆU½¾7|¸Úw"ƒe¶ý¯ÛúúäwmÊÕ»÷øäì§¿ÊLÿÞµY4È)"u³÷5n¿¶kXÙ ¦¾1¡Ò[³ÞkO˜ÙÀuÙæ³‡"n€¦âPÙã`¸ Rv™UÍ[3”ŽÄ xÊ„ÇVâØ€‰¡pF„{±Àϲ»ºõ¿å½ IDAT¨ŽH›]'vÍJVEªŽ£XS³óߎ£e®«öý  U¦É?DÜ·[ÿVçk6<ݺðõ [+ÿ¥ncàÁqÛ´ÃïÚ=´+ßÏonêIq}c´g4*«³A" ʘ.·°ø&só&HÞ3W{¹I÷O§XÍM8T 0‘mWávVStÄ×Ó)D|/í°þ¡÷,º¶ÊCËTU’=pb­XFM¨{à@æšjß’ œ}Úv8÷Ýàä^æµÁ?yïoÿqÊ{ÿaÊ5_Ý{߯w‘cÿ4|œ¦[MÏz²t•9=ôðømhÕ´6Çå 7Nû#M¸³)ÓE‰†â•U׆ jò>q½Ç=n8nÁ·lLþ÷çKd»Ù‰Øî‰‚µíÌáPm63c ~«Â!y×Ñ)öïuöÄñ8CûÉE²MöÈÑŸpú”l;Cµ ª#ÇnŒV¬”!"šRÿÛ½éÒqº‚0LùÌœ|Á=Í@AçÌgæ,üÑÆ'©ÿòºåãÁ4fÊr`'f„.ÑÁ%{NãÙÛ]Ðsg·ÿâ ۅά©²mP‡›dA½ùÕ­i½[þvžuîÎg7Ð}Û\í )|VÑö&bfŠf4³ ëùl˜ã=«a­ÆúwŠ£ÎZö“ GõõÐÞÙÁ—’‹Ë2b±+‰sÒ¡ýPê@ëT0)VŠÕ) ÿófÏ»ª}c‚pÚö¯}féW¾·Êž/,¸§M陘/~îúú·Ÿ÷>Ïìg=à ˜x›Ô† €Açl:L[/ضþü7œÏl†ï[uçl%êò{šˆ)YÝ 6tö·^ód»µo˜  û@‘b°“d;ÿÈ˄ɠ²µ&Rþ ±›TáÞ¦‚LI†A7Ù~ðÑi $q(‹qÇ|òQ$[¹?>ú„òá¸=áð!Òš”îתXMk\µ½{iµïMN„îîÆø¸°Ã½Ä¹÷÷+_Ùûàצ\ýÕ)×¾øÕÍ L€] ŽÃIÐìµ3¶^ø¯_ù†›àS9#ÜÕd¢ùÖ ©ÏŸ•ý«×mÉq07£|ƒl¾!Õä+Åc>ÿ:o}õ,7r`ìP vÂ)wÙn˜°2ú÷E¯ÄÍÓ¶/G ¤ÉWPÁ‘ÍÇõ1Ž›2À »!o[·rŹËWœ»ü¶õ+sápCi‚&R]ÝJk¯©eVó“[R—Tûöá¸ùÆ¿Ž‹œ;—ž‰ù·ÝÑ_}† lºm¸Fi3?Æ(:0kÍl0½õî×ìýÐSï p.'g¸Ž˜ÉùÎYÎòW2)üìQsÕ…:“¥À`Âg¶Ò¸ÀÞš)»† €p$™áDÓt3E©a€Š‹6ÌáMõ(ŸHÕ›üÕûè¶=íXÔX«ûèA¸L®kÈaJ0†\7ãéœfÓg[[í;„ãàSï¾¥ð[; Ã~¥i.:SȇÞ÷—}^P±Rˆn@)({ÆPô#Š4ëéùÓ=„7o9üG›¹°FbÆÛ¾±yÉy¸â"0áÞß䲨Ñ´mÔ{¾>'wÄeÆÔ¯¼Š4Û1«¶\ceÑØËê[¨iŒ}]RPŠìm@i¢œBŽŒfÕd\­Ô¹§|ûÑÔß*ár­H3)&G“Þá_Ì6ž2J£çøsࡲÜ{zòS#nzgQd(š‘ýÁ‹/ùùS«ÃŸF¿x(qRáÅ)ïÓ)ŠE$îU“£qlÁ„<à”ßœ¹ïè^ñZ"úoÏ㬯oñ gsP>:»pÑ"üöaüìÌÔ±œpiþé”êt9L€þâæàŸgs£(g/DJIá­ a,+E¤ ƒÁò5ˆ¡ê ™ Fœ £u8yYGbÉ>wI»^ò-ÞKåÊ1ÚA¯I‰‚Pã”Üã¯_¯G†‘aüz}ßæ½»»ñˆ;épb ¢¿Ïß…V=œ‰jm{ä‘ÇŒ§–V4·ZígÛ‚h;'ýò¬I? ƒ/ntþ~ë_œ•ÍPº>w(…¬‡¬‡«ÞfÌœ‰Y³ÔûgýójôÎüÂëÝßšÉààO_7Èúu¾Ÿô½¤ç%|/ØLÉð5âÜ ¦VŒ™@c&PNÕÀn'…@©jMæç^%”QÚ('(ø2ÚeUÍ M62µ{uï¡762ݾ¦Ú7R„̓ô;ª¼9¥›™Z_á—½bî?‰{Áô¥ ò_Eç÷­Íì_»ÿàÆîîÆ+æþCºýiû_¤¿šÌ؉jìD3!*º*”uŽTžØOª¦ž•üÖºé”{çåê4ßU Älµž(ŒG &Ý a³ O8D„ÃüP…‘€(Ç(-åu¡”)Z@ýä‹fÖÝÑÝÝ«s‰=/÷~¢Äº»gÖݱ5s3¢ÈçoÁ_ÿ"l,ìK²ÃCèeՋ΄g™®Ç)Á†2o%wuf›@‡Ú£q ÊÍ RQi½åŽé>þzÛ^;ù¡3í¥ z¤x‰†¨³=šBÑš¯í¢$ ùx>Ó¡b¤×djçÀ®Ç´N»¢Úw" Wú–ìì^èé9±kžÀžÆ°®•K:³ÇýÍî(¹‚åÀ®È—[?Ìcìd¹uK‘y5¶;¦¶¿›ÕüÍ ->©o•ø§Í ¨ûÞœæonÀ·_íúü™ö6ˆ`{ÚÇÿ׬=Þºí²W§üz~¤Úv?UÔOŒÂnø¨Jn ¢êi¬ˆ{•aBßÔ@Föñ.¥Fæ4èß¹ý;÷Â+Xþ³eã§:çÈ—´ í‘açH5¹ô@]S(¬}ÚùM=¹Ä×6p¾q&’ØðW³‰1ÿk[¶ÚmRá¤> QlMýÙÌïߺë]›2šùÔl˜8ÜÁD6Ŧà8Töüs~Ô¼eH ;6"îUBÄ}QÑÀUÛ*Sæ1ªq‡»Û¾º @^š íù·(ú­ù þ¢¯ðSëÜÝžÕÞäÅ…ç  ÑðÞQ(؈Rº8Ü»ÚÓeŸCyÓΣëO_K$8ÁÐýçgRt 6ÿÝLó×0Í‘`m;ˆˆ øŒg¾¾ì MØtþësŸŸ™wè€M#ˆô=My/Œ6D܇=ÙÎí°ÃS”²×Iì.¢¸×<LÀ³ç´¹.\ÓVE̹º(=2“ŒŽsYfbßÞ¦Š›a¢#º4À@À†ŒR†—ížñ»ImOLÚyÅ¡iVÙ†‹„žüÆ÷—iÓ@¸íõ•+f/_1{ùm¯KêoEqï›>%Ûë²cyOÜŠŒàÂHuèKÜ÷½9 €¿õÛÎe Ëú­óq7¥â¾¯áóëþuþ{òÜÏÿ¨äúÀê»›v~´û´pz]^ÁC‡® Ž ôØ´6'—`§­šCPª¹¨üRrÍ€‰ 8çE{"]Éþ] °òNÌÊ2êª3~6ŽEבh—(0F±R†6·tÍûž»éWÎvBAãçãêØö÷j0œ{%{ ¢1¡&(uî]í»¦h­Ï¬Û´`aû‚…þKSãß öÜÁWÝL¿ÐÞv]ëÄ÷oZ÷Rë¬w½hÿëwÙë[Y|áWçEö÷¸;Hƒp#¦èçÎ6í‚ßð¹›N·ÕyE FÓØp'k¨ìvq”mL4¶¬4| "»‹*¿S)ü;VôA6Ì1Ê€ŒRlþØÌû¶nSA®™\[ùùTb&‡³øøù«VÆÿAĽÔ¨¸=»QÕ—=Ò(pî×~nßêŽÓ_3&þ¹5ïEeö0X=k!ÒX¼%oimI'ú§@W¡sÿ´;#vë?ôÛâJø=Øn™˜/oŸ–óŒ±vÞzöph‡úŒÙpàž.6dìLV¥•ç… êTàÙ UÞÖh¢¶HVÌdŒ2Š˜'t™=û²{?rs¾àÎüÔ»îÒO\6TTÜ.ÖÉ–Ó³;²;’-§óÉ“ìúɦÛפÛ×Ô”ywZÏ÷;žó;ž«­†™æs‘ZÔúòîcz} €Ýá7ûl]8ÿ|ñ–u??yaó”ŽC¡»'Òaµ½Å׌¹ýKá ßþ¥“1~{|Áç¯þÙE¿¿Á ëgÜY¶ÎþØ•w¾pßy`|27×ë69cŒ1v2_øEP*êd‡}€ èNqÞ¶+¼ºd+åpÚª¹=@Ó˜¸2ê¶´ßÙÖF˜90†X)Ãʘ€¸¡s;€ÕWÜaïÿòÇoâñÌ£ roÏÔ) {¦4ì±ñ¿µÀˆ{™ª"¾ïC¼¶0`¬(ßúÒykÞ•›w«4þíõ ‘çrôKèÜÿ®kçØÑ£Kó”c` 0&0&LXKíÔÒ«1P°VÚÓi•ÝD5å{×Maº;UóØpSÜðÞ<yçÎD¶ç]1”Q6ÄtÙÃ7=¶,¼·G—µ{V“Úø3ÚèWÜeíQ¨}zKyÏéXw{ëBãøVÇÝÌß9)Œ~<®k>¾ô'%g6ßÿ2gäãÆ0+«¾Œè±È­Ûƒî#ùR»µíJÃ÷È0l¡¢ÅÕð‘ˆØÜ9 '0@`XVl01—¯º©ðoOM â^ œtûÓÈîÁ Ô\$[¨q¬¾ß޺ЪùQ”ý¸¸ôá713Æ:wBA{ ‡Cù+;˜»‚a 8|$VZiMÀ€ÉÃtR-ãóñ2ö*a-?rîÌÊeÔæŒ;Õ¶ÿHo¹UÏäûFý:w‘la$ë{üí /¸dÕMžálkß1à°=&”ÞXå)Ÿyvk ­¡4Ÿ|cŸê{ªC™¾\>‡ Šù¥° >Ôwb`óÒÇn\uÙ]ƒ|ƒC‰8÷jàÔO¾(NCÔ\éX}ðgoÝáÕ êR—¯¾)¬Ã€Ã‚{`ˆTA5dcß9 Óc2ÛöÛÛVZ)MJ“ï‘ RvÛ Îhìĸ8Cc&óáýT°¡),Å(Vd8Ðö[VlŽ~ÿ•FĽÔh+¤ ”ƒÈ°op3¼º³Nø:q&`à&^AÍ?†Uòü®¥#Ø3ìÌ>6`m‡m+(…lŽ™b‡£–ìcT|KðñäÔ¸ÓÒ®¬fÖ\yÏ ?§ †Öˆ¸ ÂqcWV_¸ï<7dNó™©d…wpxa{ŒµíFimmûãÚŒO—¶Ÿž£Àjz\œ9²_›6DF¥ŠÛ"ížUkÞ}b"ù_´¢ŽýAú" (µ6~Ñø©¡¾+â#ûÁÄ%_J“Ò°_A@~­¡5˜É>ÞÇ6– ­S…äÛ"‰èŽº.xÿ³UýHúżWwA8A˜}fÿîÆÑòf~ãRìÜIñáö0=†É0å­g·_»2ã“Ö¤5l-¾äñÐ^;IçxØ?$á®æíw7íDuÓcŽÊmo®cÅÉË«}#£ wA8Ab1ýIÓŽ»š·å·/ˆH)bE`bCls£‚»)´í?¡6 ºÎLW¡¾ƒL¯G ŽìÑÍm%7S£ˆy¯8 €ÍƒìØþHµoF†‹®]«êÍ[cÏ>á±"ÜSjØÃšŒ‚R¤4Ý¥¶HŒ™õS~Ã*»Ö(QvƒLÇ›F†8ˆàÇÍ[JîAbĹ Â`‰åõGͯÿ°å5)b"|«¤Î^RÁ]j·$g¾í²{ ñO©MiÒš”`¶qqæàn&„S±²eÓš7£ö {Œ8÷Š#â.CC¬³ßoyõ¯Üm«1¦à1¬Ï(»ë¶ßålá–ÄŒÇßs'€[êgà{õ¶‚eÕ(F²ÀÈ£óÕ¯w¾ZòŠÃ÷ŠS»âÞ|òb©Ý««}#EØ<ÈtûšjßH6²ææíÙ<ÈÔ±ã_F ‹®]·¼k¾=þÏ–ßoÙôƒ–ͽ×Qï®ß„OºÓU4ª[+²£6”&m£â‹‹3?lyí-í“ÿ(5y×¼*¼Ãfˆ»ÍƒœÒ°§Ú7"]±‚0”ðéÔ™feó&{æÇÍ[íÁG»NgðÝ ;5è4Ýu½©ö§Ï.ùò?öoïÞ|§jû¨ž¦ ü€ †¡îlÚ_ÿS]sm‚ÍK7ÖRÀ@e©ÍÔßZCÄ]†’XsEgìpj?iÚ@1>3âV™XÜh…ÏÔÏü÷ô–;ô¶šÓ_¿áWñï"*¼R‰7QFºm¯ADÜ¡¼Ä•q«Ôç`ÝKÀ¿ýµí^–çUšé“3þÍÛþ_z7GÊ>œjëý!â^qDÜ¡Bjô³ïý)€¥ßXø­iÕ’»\ý% ²nq/3~GéædwA¨«–ÕÍŸ~çWâã‘#ë è-ÙGÄ]uxug¹™ÁC9=Μsƒ¿¹Ç™Sí{É#Áå`0’ÝNë¥g†ü5AJâîw<{8ÕÀ÷v Õ5{KöqüîPÝ„ B¿ [q¯ŒË."î‚ Œ:Ê$ÙcöÈôLò‹Ÿ"î‚ ”Û­\1vùбËo;¼²|¯2|]v9q¡"œhe¦OÉîTGøfï‰]søJöÀ ŽuÚÛéØþˆÿAbŠÅ]\v¹ç.ÂPÒŸjs}ê(?= }Jv‹»€'Ù2ýSÓâÞ|òâÔîÕ©Ý«mBdP?ùÂtûštû›Y#8­çûÏùÏÙ„ÈZ¡ù\¤Ö#µ>Lˆ†-C`´‹ûˆtÙ{{¦NiØ3¥aÏÞXS­iq¡¬ D²»p0n€×ìOµÉi>ÊO †<&"î‚0Ò¨Ñrö°mu¦ˆ¸ Âð Z’ÝDmŽ ^šEÜ+‹ˆ» T“BÉN›ƒ|ud׬Ñr¶ˆ{eq„¡§F #ÕEĽ²ˆ¸ £‘ †,·d3Ðào®Ff0¤PYDÜ…ÑŽ¸ì !ν²ˆ¸ #–£«¶ñ÷0==¿ Hö q¯,"îÂ0cȶHv…q¯,"îBMPùÚˆ›Ù ¦†1 Â"â.”¿ãYd÷€ç Õ5ÅhSnóV®p–¯p–ßæ—1õWˆÉ‹»C DV …D*3Dœ»R.ÉN­ Áa â^QDÜG8â²atRëâ.©¿}ÒŸd›ô[¹Ý»UýI'pÍrI¶¤þ 1CêÜk3²vRk]ÜGâ²…Ž”e*ˆˆ{%¨Xk¶ßñ€Úš×!B5qÙ=C«Úâ²…ŽuîTå»%ˆ¸—2P½Îîø5Eµq¯(£EÜËQÎFrª¨öðåÄ‚!ËM3§ÁßÜào™ÁRs¯ Ã[Ü«¸éyþ¿´ ÂPQ‹â^"ÙÊÛ×ûäñ"[ª8÷ R9q—>?A툸WÁŠ{$;N÷>)ÂðCĽ‚ô-îâ²ArnK¬\‘[¾"·ü¶„C–§PÇsÇ©ì"Ù‚ bÞ+E‘so;½çp[Ïá¶–é®Ö ‚0’q¯N‰õö;;«u+‚ Œ|DÜ+…ªö ›™Ú½ºÚ7R„̓L·¯©öaSelÂL aó m°» 6r¼»³Ú7R„̓œÒp›ØËA-ö¹ ‚0bç^)DÜA¨ "î•BÄ]„ "â^)†AÍ]A8^D܅ы̓t3ª}#EØ<Èsµo¤›9†Ú{!ó^!¤,#Be¯"î‚ T÷J!eA„ˆ8wA*ˆ8÷J!â.Bq¯R–¡rÜ6n%+:–WûFF©õ%_¥Î½uÚÛéØþHë´+ªr‡‚ BÈ ™¤,#Be‘ÊL9Bôl<_ÃCÜ›O^œÚ½:µ{µMˆ¬ê'_˜n_“n_c"k§õ|¿ã9¿ã9›Y+4Ÿþk±×ÿ £Ž‘*î’½7…f·«[S^Ó ®9ˆÿ½ qaä0¤â~Ð;m¼»s¼»ÓÆÿ=qÙå@Ä]„ÑGù%»±a€TÏÔ¡¡!â.Be)_Yfغìr â.Be9^q?–d§Õ0ãšÃV²Žˆ»0ªñêÎr3ÜÌ›Y#ô8süÍ þf›Y#áéc¨m µÙ„ÈÇŠ»¸ì2#â.ÂrLÕö»Žï‚Ç’ìzw'€ž2-¨[DÜA©õ²882—]fDÜaS•ÚHjp­ßÂÀq„GÙ$;Im2CRsÊŒˆ» dR8DÜ¡ªô#Ù¾mìS='rÍ—lS툸 B—Ý?·ºrÅÎå+v.¿í´•Õ¾—‘Lâ.©¿‚Ð7”lÇß WK}îÂðbØ8w †8 y\˜ôn𼡼h½Çšc¬©îí™:¥aÏ”†={«/3lÄ]Ž›!7Ú"ÙCÅ𠆞ˆ¸ á–lU2€ –âF>£À¹Ww¡6¨â dfÃп´ Tw¡œ”Hvv/`p:.µ‘€8÷ò#â.?#«ÏO‚!ÎC åDÄ]ˆ¨„d¯ïë¤0úq/?"î#‘å²A "îÓcI¶ò÷ äiEˆd Cœ{ùq¯%Äe £÷ò#â^*¾›Æx>%Ê.Ô&"îåGÄ}¤ÖpÍøx°ˆ ‚0Dˆ¸÷Bj#‚PnĹ—Ÿ¾Å}C–M²½ž4Gä[ÌmsW®Ø´|Ŧå·Í“Ôßr1Ì»¸lA¦ˆy/35)îýHvsKK®sGn÷}‰–Óûše“lIý=j5õWîÔf0duS+(îâ²Aˆç^f-î••ìlg'€„hº wDÜËL?âžZ_¯ŽØƒ!{)QdAbDÜËŒ3XùɆ? 9p†,R(3ý8÷æsÓ¨í¡ˆs/3ŽXoAª€ˆ{™©ÉVHAF<"îeFUûA„¡Gœ» Õ@œ{™q¡ˆ¸—wAªˆ{™é·ænó ;¶?RÁ›a´pÛy+¬xayµodÄ"Î]„*!潜 ³n™æ“Hí^]í)ÂæA¦Û×TûFаy~ÇsÕ¾‘b쾊rD ÃJ}·yãÝCvÅ¡ÀæANiØSù—ç.B•ç^N†™sA‚ˆ» € s3ª}#EØÈ°sµo¤6†Ú{¡!-Ë%HYF„*!Ê>t¸éR_"â.B•q?½%{àˆ¸ ‚ T”ÁHp`‡ 'IDATvxõ¥£DÜA¨#˹÷'ÙY?ÀõžÀ5{KöÀq¡J q¯ŒË."î‚ T‰ê‰{%%;Ù°À¡ž©CþŠGGÄ]„‚ß½ €tá5+ã²Ëˆ» Ub`Îý8\¶è‡¯dwAª€›Þ ‚n i…Äi<€ç6TÖMÜ[§]ѱý‘ŽíØø_A„c2p±¾}Ñ_ßúÌ×o}æÿoïŽaÛ¸î8ŽÿŽåU"I‰d$23xÑ’Á@‘ÅK§véP KQA½Ä€§NºñÒÉ€³8Š¢KÛ¥K6/Z´°-**IMU¤K2å™ìð’Ô–(ùÈ{ïîÇïždæñ öÏÿ<¾ûñó‡?ùì’—M2eÛj +Vù&÷…µÛƒÎÁ†kˆ4¢ñÞG½ãÍÞñ¦kˆ4¢¾|+im%­-×iŇêl«³ý]C$ÊàLd£Iñ¨;õ‚.²‡µ+ªú!ÉQ÷Újópµyx”ïgªå w¾ÏÏë…™çƒë+ñþJ¼ÿœ“¨ž"»íIŒnd]· WÝKŠp¾3˜_û;qÇ5DÑ­ßl&»Ídw0x_|êóÓÑ¥ho)Ú;͘ï„{0„;P¤TSöð™$Õ®¦\³ÚGØH‰pü+þ,»,†Eo ºw ­¢";6öe>q, áŽY÷jd×’o$Å£¬!^Í)¥B¸£²¼ÚD¶LîÁî(™ ‘í¾CÕÔm™Š#܃!ÜaŸ@Î(Â=Â]ÙînßTNdiî˜S6¼aræ áN1äìÈ5²“$è ܃ar¯¸9µ$Õ¼7S6|yð³G÷¾¾sïë;~þ¨è½TM)ÃÖß ¦ìÆÚ°w0ìÔk—¿0×ȦõaØ,†,¤õ·”á^UF’^ORq6q2ážž¦) ãÅÝúÍ¢÷òCG¸OoLd×NÇÿ<"³ˆpƒp?‹{~*`VÂ=ŸÈîµÛ7ü€I0¹‡QîpgÊJpÃb¸§‰ì+õŽ$i%åšD6`áF~áΔ ¹ÉîEEöZÏ%-xo9crc|¸¿Ù‹s]ùq¦lcîaÔ3¦6‘ Â=Œñ“û«‘Ý>>¯@©Ôõó¿^}…+ûm=}\ÐTÙƒ_>ÒH÷þ~§èTM­è LÉõAv6ŠÞÈk\dïx³è¼¦¾|KRÒÚ*z#¯s}í¢÷F>g\äJ¼ïmE\äjó0·w´xÏÀláØ=€²Nî@ ®2îû¿ã›…ëƒl&»Eoä5®r)Ú+z#ƒÉ@јÜ Üp€pP4Â=ÎÜ ‚˜ÜÉ=Â@Ñ÷wE#ÜàÌ*ˆÉ@јÜ ÜpÏfì£Ë©Â}ùýŸ¶ž>n=}ì"À'Â}œŒmÑÝûOÒ¼ÎUþZ w× é"íp­®!Ò× é" q­®!Ò W,ãJfìpÕ]ÉŒ®XÆ•ÌdqïOw$=øÍ#{’ô}+¤kˆ´cµy8ì¼xþ†Ð±*8–`@™‡÷ô#ö°×I¿lÆÈ wà¬ÁüzÜ߉û;¦†÷nýf3Ùm&»¦†÷ÓÑ¥ho)ÚË:¼Û ÷@œµÆZwt-ÄÊgî È+ÜCDvún|Y¤6&²G§’š/{S¯iêÿ¦@¸0àÜäèT¤ì‘á ?Eö†ÝK~÷ÎGv#Þ—Ô5v[&O„;€¬jÉ7ÍÑ Óç&÷Ù±!ÜŒ‘~ˆ®E'é—½(²_Ö®\ò»˜áÌ@ÙÃú»ÝlW!GŠ|má”^±×ûâhÏÃûE„»g„;`ÔùÈŽ‡Ï$5“é×4{î1"Û}#Üüp½¹!ܬr‹l7³W2Ê9s÷.m¸Ûlý]X»Ý9Øèl˜*†l¼÷Qïx³w¼iª²¾|+im%­-[Å ª³­Î¶µbHIs:‰}§v%sÙÙ8s?ê^[m®6ºÁëe˜Ü1C&¸Þ'×ûÞÃO¾øô«»Ÿ~u÷‹OzYðùàúJ¼¿ï[kýÍ áŽÒ q*2¬¿;HJC¾‘·bÈÙÞsC¸Ã¨b¯÷IŠ“ïrC¸#Wc"»v*I¢ÜÔ‹©Ùkt/7ÂYq½~p,ãáŽñò‹ì^W’ˆò™ÇmH¿÷ÙòVÜ©q½&˜Ü½"ÜKo‚/çóûr^Å"Ü t*òb°P_ µa“»O„{®Š½Þ— ÚÞßð…c¿wÆ´÷Íu%eyxSYîãq½@©ÍV¸çÙ½oŸKj¼M”iq,ã×án³RÒÛþ×û€’ãž»_ÑÝûOÒ¿ºõô±¤Â}¢ûÛöSIs‹ï¿ñ•yFvïxS’©Ö_IIkK’­Ö_ImIÖZãþŽ$SÅaúþ¯†µác)Ú“”µ8Lúí_~'éË_ýÑÞ$I+ñ¾$kÅ«ÍCI¡[s=– t*òïÞ¼¤…e[ÜQvƒùõ¸¿÷wLå{µ‹!Þ=òî…·÷IGÞ7¥va¸ì譤ƴinjÖ`ƒ»?õìs7‘ À .Ìxtáä>6²[/þ%iù*i ÂÝ›:s7#˜Ü=ª½€³õ„*˸ éáÀ ²Ý€LîîÌàUø@*ˆÉ€Ëx4Ùäîú ]7¤ k·%u6ŠÞÈk\¤ë†´ÃõAºnHC\¤ë†Ä ûóÇPýúo¿÷µ ëƒtÝv¸>H× Ç2À…\¤ëþµÃ=x¨cuj®Òuÿf1’F>öÂ*ˆ3w†Ð@à áÀÂÝ€!Lî“ZÔ?Ïÿ°­w¦îãózR„;w&¯kÉ I‹êf_¹­Æþœp`Ȩ<ƒ»—ùz¬‹òz"„;C H5PdŸÉëfýPRû¿×B¼—C¸°$ÀªñðÙ¢Þ—õ2_‡C¸0$åm™ôóu<:‘Ô½3ÝÊ?äõÇÿø\Ò_ñÙtëœçZ!]C¤®Ò5D†Àä\f0¿÷wâþŽ©|ïÖo6“Ýf²ë%ß}ôGïÌG'óÑÉù^™óë2"ÜjÊçJß‘ö$µu#Ë[Ð@àá”Æ«y×N%Õ}$8óu%î@‘Œ?åXˆÂŸcªÂð,Ÿ¼nIÝZYü2Ëø@¸Sòâå¯Ãar÷bš«tq¸“×°àåy M1IEND®B`‚transcend-0.3+dfsg2.orig/Transcend/doc/html/images/screen3.png0000640000175000017500000020464110115436474022710 0ustar pabspabs‰PNG  IHDRôôD´HÝgAMA± üa IDATxœì½yx×y§ûûÎ9UÕ v!®"%Q\$QûîU–mÅŽãØ‰ØÉ\{掯xbg²Œ“±37žÜܛ͙8Û™Œ³'Nb'ÖØ–G¶$KÑNJ"-‰¤(‰W$±6z«ªsÎwÿ¨‚ €t8O³QݨnQoøÎ¯¾C?÷ÛÏaÞØ±>ðâ1oþ~Ä,ØÒ]p°'×è™È¦Ã÷µ6úDÎa}[?€cC ýƒGöLýýîv蚯]ˆó™1+üãú¢u>‘sh¥Ã†yS£Od"9s@Yniô‰,hd/nÞ9í‘jþOÆá˜Ó©¼Æx½Ð¼z^ÎDZ¼ñ«ûD™í>‘ÙãäîXpfáq‡ÃQ'4¯m¸ÎL,µÎÌ =ŽYª¼;ß  §”ºâÝufê"…™4Wî3ì̸ÊÝ1G̳ÇŽ…$Êl÷«ûüê¾tú}&8¹;êÇyÜáH=NîŽ)I<žõ`äØTG:;iÂÉÝ1·Ôép,œÜ—+uy<è€æº;ŽÙâä¾ p-r‡cù1ïQH¸4d\TrÞ<Þ°ëT§Ã¥!ëÂ¥!gÎbOCºÊ}Ñâêq‡c>YŒiH=°+¹¡:nrr_ 8;Žs±•“&ަ8ÀÉ=}D=0rxšÃœÇŽåÁX=>†Ôg&=RuÜtöö<ž‘cZ&-É[²ïqw8– ç«|RDv ¹ž{*˜ykÅï€æt—q8sÎ =>¾OHT§yÔlÎÈ15Û"ž»Sq8©`†Çd*Ÿ …ÄNCÎÏRçR› 9ϸ4d]¸4äÌ™]ra<>mÒUîõà®Îw8–Ó¦!¾Ÿ9NîÀyÜápŒ#ÍŸ'w—"w89{5 ÐåÒ¤‡¥Ä㓲Ìä>Áã-<ÒyÜáXÌ:²’r–®Ü]=îp8Îe­QÝ@-ž c,PZó˜¹/Íñaó† ÌÔE:Ó2Hk`fnÓ2sÕ"Oíø°¥˜–qKŽEB_´n…|…<…~O!e¹%gæÌÁzý>¯K©ÆÍ;id/ì½ßÓ-wçq‡Ã1ŽEYi ©‘»k‘;ŽsYªK C#äî<îp8Î%ñ¸õŠt|Á Îã3gžå>Þãa'ŒôO~¤ó¸Ã±ÎãÇ2ÄÉ}Iá<îp8œÜ+Îã‡cqéx\.ÈvÝæä¾8°•“ôÀÁ©sw8– •SOL¸ÇãþñurO“—äù¦ w8;Ë„ó=~!¼ü:5š–iX. YgkeÙΆœéLC"­Èt¦!‘Ö@äü¥!gîñl×mçß9> é*÷µÈÇx.ÒãÓâä>÷8;ŽñÌ·Ç'ÅÉý¢pw8ãiˆÇ'ÅɽÜ”‡Ã1žª|¾=>)Nî“ã<îp8Æ3Þã$ *ÃÃçÖOJ#Ó2HG`æ|oÝÔàÀáÁ ÷7\å.0Sé ̤3-ƒ´f’–™¶ÏÉ€²iIÊ–oZfæ-r‘]£:.Ÿ×“q,OÜø°º˜ïña³k‘3àW÷µÑ|œÓEÀÍ;id/ìåæKVî³Ô)²å¹>‡ÃÑ`Ò³Ô¹0,¹»ÈŠÃáÏróø¤,>¹»¥N‡Ã1†óø…HµÜÇÇÎãu‘¹»ÖŠÃác¼ÇýL@¥Ú?é‘Îã¢QÈ ¿zËJÏÅ‘ÎãŽYÓà ²ÇpiȺpiȺh`rŠz|u;ôŽnÞ»²ë"µHW¹;sC½}•a`…|]Sgÿ¦t¦!ÓŒ“»ÃQ7®?îH?NîÇT8;)NîG çqÇRÂÉݱqw,yœÜKŸ¸x @åôá©sw,%Ò…„KCÖ‰KCNÊ…Jòu«€ã§uò×”xÜí”]. Y®rw,Vêj­xù^YJ×àße»SöìpiȺprw,\‹Üá¨'wGºpw8æ'wGÃpw8æe*÷¿þï?bÂÈTÃûkÿÚèsY8; ÌÒ—û÷ûŒ‰B…&ŠL5ÒQhªaò-MÕ/î&}ì7ŸnìI.%ââ±Ê©ƒ39ÒyÜá˜?R…ÄE§!Ÿ?ù÷F‡Z‡&ŽLé84qhâèuýœ ÇšCáåªmùõV«"ö‘ŘNî. y!&­Ç7­É8|²2þÎ4xÜí”]. Y)LC.úÊýµÂÃÆDÆ„ZG¯ëgµ G–C‹ØPÜýæ+ÖŠ-V†¬"–¡•¡•!S•)f2ôWþYû•ÏÝìŠ÷©™y_ÅkZ Û•ºÁ¿éÄ¥!ëÂ¥!g΢—;€^ó¼ËÚT×u_m)²ZªZZY2…LÆ’ah&ÍdXhc…±l£˜©ÑçŸ6.º?Þ?‡'ãp8fÇ¢—»1(~C÷çêo*IÏD=fsKšIƒ “i ¡A&ù*iöâF¿€†âÖ9Ž¥Ê{¨ãêËöŸŒöØd¶Ò³Ò‹}Ÿ«e®™½&t"ÍBK6ŠáéF¿€…ÂyÜáXV,z¹kÅ&´º,<©#/гB³Œ­Ñìùì&„™¬Ô CB ‘Е~ ?FûÿßYRmwçq‡Ã±èå¾}õûŽ}üXïs›7mex6ô¯Ê|dOñ«Z³ŽÙÄl"6š[›Íꦜ9ÝoÕ1< /f‘ï3T¹ó¸Ã±(œx$¹‘.¹¿xÌÛ±>Þ±>®+ iLÈÐa5ô3!³¿·ô??ùèW+mA¹UU›Õw×Üh4tÁDèh¡æœÉ5‡Ïî#/f/Æ´m÷ƒ=¹-Ýå-Ý冧!'x|ß)lÙØ²ecËÁ#…ñ÷7Öãdž:×·õ¯oëOÛNÙ=¥ÕÝùÞî|oÚÒéd˜7µÒáV:œ¶4d:‰2Ûýê>¿ºo!ÓcŸ”tÉ}vh X#B?>³ÿÅ;?ôáC÷V¿â«[Ë/Æ -aecDLƒCÍU-·o)=ýŒñ4¼mŸæÁ/¤«33ó֊ʯËv¥kð¯£.\².–grj§eí“KAî«Í5=ü´q±77…@xa“³ªâeB!"Pl`bf²™L¾\5²¢åÕ;‹O>û^Ì„†E"/¦E®òÃs}:‡£ÁÌPåcŸ”¥ wG±¦XRVâL6‚8Oq†b*T­Œ4ð‰ž¶ S‰¬Êh±óÖR¾¥òµ¿^ SuK‡cÇ·Þ\fáûâÆÒOoæŒYø3w¤¿õÕr¨”.WãN­¥ÑjìÏö––ñGJi…pr_,sOJ£MCîÞ½{†Çg»nûÊ—oÀúÿÏk‘ÒŒ‚BFŠ€·¼Ù+Œä ¥Ü÷Ôû+?ó%Á¢Þ²Ým¦ZéLCæÛ¶yCŠ‹ÅŠ7P\]5mZË|fò“,V¤ÒBê@\2ß'æÒ3$ñx{.0X¦8²!OÏfª)ªÜÇ—äqG×…»`¼Ê h GL!FåŽDîâ»÷FïxŸfOÛxªŽ¥JsÇcTyD•Èx½#WH] (d.L a¥´à…öOÕñÔŽTòÑOÞà«_|`Ú#›Z_Ñ‘¯µÔáz^0bÇ>ò½&èðš7þjËæ•}±F ÿÅk|àåì@a¸©í²?,û9é2•Íóøb–Ë'²Ò@æRî±’žQ쎅¡¯÷’|ÓéŸüÈÿãŸÿó‡åš‡/eѽãÏVùql„ÖBkYî?µª÷äê\ûÀF*æ#à”È„¡Ynú/úI·?;œÇÅ,åîFe9Nÿ™î(´*#¦>¬øõæÖŠ—­x™ ªóþ&Ló¸c œÜiäë÷½ôþ{nŸâ€°Ú„DîÂY–È’0qì“0$BF¹îÿ^ªp÷·þçõdŽôž‰M®£}3ÑQ‹a!^µæÖx~f$®vv¬Þ¸oWþÑû6l¿©àLéï›Û*IÙî/¬ÜÇõ’^¹¿xÌÛ±>Þ±>NUò`OnKwyKw9miÈÃ}­›V oZ1œª4䱡ÎõmýëÛúëMC¾ÿž+§> Šr¹ü‘Q§k"MB™¶vÓ×çêi]³¡ŒQãßòþ÷=õõo`\Oæ‡GãœÇ—w‹3Õa‹‘dhÁ‚R ¯Z^Vº:»Ëd’”ÐÜ6âe*^¶ìg*Õc¿Yï»1C‘Bßᙹ`æM­t¸•§g6dš‰2Ûýê>¿º¯±iÈôÊÝá˜eLàÕœ. ‘ÅhýÞuÉðÉãmGwoÚú 2O}ýÅ1gNñúU-çZŽ@h“e±ö²¯YBÆ:O¤‰ôæ«Åë‡_?„K®ù»æLÅË&=™rõؼŠóëñæ“gÉ]=î¨`Ï!\»×nžê:U‡£QüÓ}/ýÄy…¼’¡qÍédI$ý™äO]8ñ U~æÔÑ7fè†äøj‡‘ÉÞÁ£'¥0—o¸$P¡Œ™˜¼qëŸ1ºÉèL­‰/LçªÇÛË…ïTN­í¼æY•©ø™²ÊVúê!3i­œì³›6mÚÔ’ºK™Ò‰KCN«ÜigEWß·|áÝo½jìž—w©ÖÖW®¿ûy"#„!2G|t£â8ñGÿÎPµ™Î6Ñ?< £!¥¢ ½'ç]ÎÀ櫾b´ou¾ö9‘|Y“äd6\ûÜÈɵ•žõk®Ýø}ês¾¸èáñ™<Öá˜'wGÚYÑu&ÄãïÉeÖí§O¾mŠG5ûW –^Œ_iÏ_ ¨L Z›2Рw`ô’lâ÷K©ÏœÉG•VOÅùœG¤‰TòÉÁÖ¬ßö9/Sñ£>¦¹µ0!é–:éÄÉÝ‘vVtõe½ç•û¯½üíú^îËe±âŠÓ>°=ùé¡£§‡Ž®jÛ0á[«;Övç€b„æe/{²Zj“Ò\ ž òÚS±ïǧ…Ò,²«O…§VžÙsþ]I«g*§/C_½i·ei¬pàèÎFŸŽÃÉÝ‘zVtõe‚8“‰ÿÎÖu?Ò”NîžÉcWµmèéïééïéîìžô"Þ~ã÷ŒÎØ8câŒÕ™âƒØê²¥ØÏ AäQ\ò2RãÞ}Ó{¾ý¦÷|ûá{ß5ö ËÐãç³ó²§¬•ÓçX@jdO;ø·Q¸Ùu±$gC¶nýÅâP[q¸µ8ÔÖYèÊx+¢Ö¦}Ôé¾ãÆ*Ëj°Z–W_qVîG_/ØpI˦ ?X½óO‹k"ÏÄž }{&öLìë¨v{ÓŽ£¾A¨”Í5#PÃ$øþâ¾Ù½–™ÎÙ¸ðNÙ×_ñ„±b﫵1j[7ìÅïn6ä…p•»#í¬èêËdt&ˆ3A¼2PÖ àݹoFÚµ/Nþ4ž¶ž6ž±jìßvksÎÚÚí‰GN—s˜Å+qûû~ׯ¾Š¥°Z’´djyÊÞ×?Ÿû'×jw iÑX`´Së¿“ hòýF¦f놽nÜØÜâäî8Ë"õø¤$e»§ [Yظõ9ðo'ì«@9Fçúß–A´°gšJÆuÌ“†Œ]M`Á„xè5Jêw&IB1Ñä¿!]ˆd |(Í’gÈÝfêb†™öø¬7S5Vg‚Ü«ª¥ËÏýÎ9êé)­îÎ÷fý–ŸîòîEyþî{=oŒ©KXpÒd˜“íf™ ± DÖzî5¹‹«®xà…—ïÚäÚí÷l߸gß:3Û6ìû 9ptçö{fž©¤±™E wÇE²”êñ™³ò’­–W õ];®¥Pã’Kÿùõ×~|Âñ¾ Iž1A¸ôî‰èǦÍ0~~ç¦Ý< c«©¶,A€H‚ ÕÚî‚ Ibšâ}ÛdŸ=ûŽ\»ãÒgw\úìÔÓÊ3ÄÉ}©Q8ñHè·(œšâ°¥äñóY½î;¾*y²¿çÖóÖQ'÷΋Ñk"5Ø®.=2ÿ'8ï$ö¹çÀ2 IDATVñ Û6ìU$àüúÚ*À#Ï^¸Ä8Û¦lbõZÚ„8±ü·>¾ûÉÛ'þܤ6çÚ‚ó?c„˜JuÌžsäîÒ‹‹†D¯¸?¢á‚ŸtÞ$NßõÀ­}IÆ“½:Z™ J¹l¡{‡ üjƤ2^ŠÊý"ÓBرÛç·e¶§{ãº"\ûEÇ<@F[íâÄÍLµ¶{èa,íFme•ìíÌLˆG^èW1Ût¼KCŽ1pä䆫ÜuµV‚–a-kSt‘êBrÇÝ¿¨ª ªQ{Ðú艗~ãô±Ó•RsXmŠ¢œŽ2RF¾ªø^mÔ¾c½ðŽ]ÖéqäuäªÃ»ÞÜØóŸ^8|Ã5›w]³y×ÇMþcR·Žoy7g ÅJKMîl JÌÎ 3Ø”ïHr2Bs-)j™™¦ì0€‘J+€ic‘É/Ž2æñIqrO˳E>‡d³E#Ö ]7Üß–ïnM¾µãæ`dô@ ´-èï#b–VêgŽMÌJ.RÆï3a|[ææ­4e ðrðDµâp6ÉIó]œ2“4dÁDbôÓ‚p®Öï\±‹KAükñœÏžç^¹õ†-ß°åñgNlé,s¦öøx:6Þ•Üpro$ÎãsÎ~ðÃqG_g©`ÙUëW8oÊ@ç‹'»ÖôéÖS|É)yIö/äùÎ#“VÁkã¿ %3JRf’Jf”ÌH•Q2PßC±ÒÒ”-À#V€Œš KµÊÝ€j!w‚`&YŠR´5e …JÛ#׸ãÊÝ(eæRò‘&¼¡u7 <2xv¶-ûâ}Ÿ”Å!÷%†\H/ŠñaóD>×7\mâö+^Rƒmj }d ¥O\è!äÅʃLÕóC™Ž†ûœ0aqò ùÏÆ2{>ƒ­!]46„Ȇ°Ø6„ ¡2|ëwŽªb¥¥m¥!Y<ÀŒ®¦B3¬…0ÄDºpzlB$%ú§}G®ÝºñùÎ5Ñím»Q%¦HŽ!¤ÝóÆÎÝBâ§oÄ’ëÌL†œ+OÊâû¢Ã-u6„~ò®°,îûæß¸å}ï- ¶ARÌ_µÈŸ=ÿ!O<ÿ¯]kB„Ê•_}æ÷.øYÏã-yUî;Öú’ÁÌ–™–™™MÙØm²É° ÙT¡2ÔÚ^l…xD>³Gl’ù2T›DÀ£Å;ƆÌ$ ªBÀÒÀ™¶Ûïz%p‘Q—e "&â¤~O,Ï„7¯ÞM•:»I‹…yõø¤8¹_,aáHáÄ™is_¾úÅqÿÿH i 4¤†Ð˜ltû·|¡kMU‘ ¦žO~uIíÚñô7Þ²í·lûî1Ö‰ÍÇ™–Ù&ºØDF†d3°l›É67l(ÛL„o9„…¤qW¨&Ewm¥ci‘ "fê¸\£”˜Ë„PY 6þD`ADೊ'0![hŽÛoßñàã/¾µÁïàl™àñÖL²tŸ=ÿȹòø¤L”»KCNÁùõx˜_9é‘NåGP¢u©!ÌñÁZ×öãñüPц¦ŸéŽ{ušSpñ³!…°‚,ùk³ƒ™™a&=袵¡±lÄ*Ç$Éke«HHfË,jCÄ’¯¤íŽÚlwAV°5ÌÄ#D‹¸ ”‰ÄGû0»AD‚˜˜¨Öˆ·@œ7o{xÒ=Z/DCÒ _ÏW¹OÎÌ[äAËÆ–µÛæõd³à©¯Ý`í-ƒŒG-¯÷ýßÛ6¼#9àoÿåЇÞ{CCÏq~¹ëÒgBax¾Ïc¥º…e–5³³eâ³·G{5U6‘±¡Ê·è¨§èµ4±OV ©-IËÉTH j™HÛ  "‘kŠ™…ÊçmTÊ@%¹5Y€e&ˆšÇ϶݉ÑffIþAkt:Ên¼òÑÝ/ÝÙØ·qŒY{ܯîÃØöí ˆ“ûE-u-å¹>Ç\r⩯@×ç!5I}ëCïÝܰÓZÊùaɰ«Û± l‰™ü×0æq¶l˜Ù‚Á£uýxÑ3G9ãåÛc;P 2M20&’RZA¶6ò—jmwÉ«‚„`&åƒK ’å lUиÆ:ˆˆÀ£Q™ÄìÑF@‚ˆ šXk£õS/¾ÀW>zýO+ö¾zË¿{3TùÂ×ã3gyÉÝE—)§>ÛèÏ!ôS/>|ËŽ75ú„k…¼L\Ý„h‰™À”̇ñOž]eå³]ø±?µ¶šФ2ÆH!XNôLÉuL  ߯øy6oJ$*L)¹¤élíÉÚ(ÿ¬Ö‰Ù©-ì~éÎë¯xÀÕ›v[–/¾vÝ|¼QKÀã“B?÷ÛÏM¸ëÚÍÒØsß±>0Ã4ä‚y|Kw@ÚfCØ´b@ªÒÖ·õX°ÙØßûõÂ`Ûð@ûÛoŸ8šª;ß  §”®5Õþq³è¹ß¼õùჽ&ëžá€ › ÿª¹,,dY0óX9ÏR•˜‘Ífy˜a 1‘Íð`RL`A ¢*¬ô+ã,—ˆ,,’ñîœt](Ѻ€î®•ê òŽ±Ñ±Õ±ÑZkmŒ~¾øŽñ/'‘»±bê —9sÀ=÷FµÈk™… ¹D*w=tÌ„m«ßÕ¸ÿɧ¿~ßKï¿çÊFŸÎ…ùåú+ž°V>ûòm`’PJ2÷+±-üã8ºÄÄÌăd˜™AL£7À90·ÆÑËŠD– “°Sr°g5Ç„ “€MÆŒÆil ÀB€˜œ,®˜ im´Ö:1»Ñ±1ZŸ'÷©÷ñó85ÅŠÇ/ôÎ,UOÊ$rOOr‚ÇöK.t¤S¹cI2‹4äµ—?eYìyet’kÉtNO(¹í &Ž»’þ ׄnGwÑ«5t˜™ˆGë la<Ë( äÉ2$[&/B e!ÊY0,l h\">ùÑ‘êa£µ¶Úèx¢ÜÆèV}cz$fŸºï/zMqgSÜ_ô–•Ç'%-•»[êt8ækÏÙ”“±I¥ŒÑ*žjêÃëëTëÒ0`‰ÁI˜=iÎ03˦8*²((›¬Bs™M³ ‘¡(Žbp2| ‚›a@5As2(c»} {l¬µ±cN×&¹ab“´e yà¦9*À®Ý\Pëã=ž5t¬XîƒÑ¹ÏÚãAK SmAápL‚C¶¾ûžUß»¯¸ò·šÎüz£Ïh¾¸jÓ3–Å ‡Ïæ÷¼ñ ­IÛDÉ6ygŸôk˜ Ï0ÓŽ¤ Oµ‚ÙÆ3HÂ˱-Ǥ%[ ÍW-‘å¦ò/C’Áfó,jS €Ñ“³?’A$zM¬µ6±5£N-Þ­ÕäÑ ÇWÜtƒ­ŸÅÕã3g~åîêqGc1™S²ÚâÁ·½³p´»©¨*ùl5ÿ…æþO™úöq^ä‚R±Ò<ñ^ËŒD¸à±ÉøF­uNälZQk»[f&AëŠe{Àø…H>ËÄ,šÒOzë€Á‚¬QÉÇØ` ¡£02Úhmu¬­±£ÙGf#2$=ÿ™}-nº‰ýìÊýGwvl\°7o©1gr_/Ù Érž ™`³§`)öίÿ¥‘—DEßÊ!ˆÊž¯}4®V,¨É_yÇ=$, ÛSiêjêíÎ÷¦- 9s¶nØË€e±uÃÞñkl“Q½Œd1£]ª ðeâZ~…ˆ bl€À6iÂ[U1tòtÛŠŽÛcÿõ0ûZ÷ÀÇ þ“%ïáfùV+Æ®DM>Eˆ9×’—Äýcµ6F[­­­£‚Xf•ð¥ðèà™wwlĶ{À4Ã^ Ù){–rw‘Gú‰JÂZñÚþ#•Ç)Ž<Ãöõèu^¬hÈB`Åçž[›i-äZ/Ý´¼‚Êz$¬&Yd–O„žK¶nØK£;.=ZºéŽü.X ²<êßqc¿P »`")-•9cEÈÆB± ÑÓzvS6zG)÷:éM‘±¤;ÂüƒˆäÅ'7³€µÖ$C jK© š81»…1l äš|áAø,<ÚwôÝgu)™½QL/wçqGš¹í“wxâ‹ç¬¶Å¡:±·Z͆T²F‘ÔR!•µd3…«~ò/z+¯ a…ÔÃÿ'Jlº{O+q˪‰{¼ÜSíhÐ ºXÆ+>Ù+uÿÑ•npG×n0`Q‹ÄX°åÚÀBØÚ’§Ì\A‰%É$¹?w@0bïðkk¾ž [ƒpåHî%–ƒ"ÞÂFV£=z»̵ù@ìµ@€‡Œ¶&qº6ÚjMTV&Õºð!<üðÀ[1föévãsÌ„s®Pïñ[®Ýà©=G'}X£T^×Eª †»Hµ.æö"Õ?õS~¾(xö|€‰bfbÐÐño¤ú¹÷ýéÈ`P,‰°‡ƒ¶£P¨/<,¤•ÊHÉBóÊ{rmå|Fv^ÒÓâg³ÍÅc~aNÎðâ™õuªg7Â&ƨ4o¿l7,`ÁLµ–l­{c!Àlh´OSfrj(·¯l^ŽEÙH ÛÌüWÞXÜþ÷—ú0[ PÅ{H_yo«]²$‚I3À Xƒ¬ Å’'Œšž}öαM´/ÞìÓ^§Úþ"U5maîJrGš)ö­DßÊæuû¯þ7ï{þ/¿‘Üéµõ«b/4u¼û/ …l8’©Q„°¼^yá’ÜF°X)6ëÃB$Áö–žæŽºR‡Y4·àC­«NMø`q‘47ÆŸÔÅ¿z#€Ûwî&æDîTû «I bS+íÉ’¶‚QòöÙ8ô¨UØŒŠWEr°wÅyÝKùJkOÛß* ùuD‚|( @ÅäP±„!À2˜ˆ…)!$H¢¥Ý„^Vú=ýè-c{s»š}9§-3ÞãAKrÏÆ…=‡£>Šƒ^xþ ׿í©+?øá¢ŽŽ}ó¿µÞößê“_ÿ@ùûï‹¶>d«S îhù9äPhý [MV*_tªMÖrE¾"¾ð<ââð)ùtë³®ÿÈ‹Z×yÑ3Ö¿Nä~ùªïƒð­oÒþ›6¶5¿Ã¤e0Ûb™°kOJQ¥—LdD¥œ3#âea¹md`Ê—7Äò d(Í€"p¥Da²^yÀàc¸Ç Ÿ„‚ðIx,|òãb–ÂÇŸz€ÄìûœÖçå sÇâÅ¿æWF"œñŸëhïAÚqEœó½®·þZ±„±g¥ Ý¿YXô¨åÕß.\þ«L DRÈÛ-ì”Ò AQ,®Û¡¼íïrR©'¾x£_âE1ázÎÇŽÀÍ7¡½ o¿›ÃžÁAìÞMïøévQ±6†‰˜ )„Pe¹ÊŠ˜õí…Þ¦’°$,ˆ! ,“ÇÔD?ÏDÈ0UÁ¶„JUFò Z;%ùLàÌá<Á-ÙÁ›·>R¬4'»­6äý Ÿ`Û—àdÿ´\¡:C\².–|rd ºS A’(ŠüªñÏ<ü»q¤…nïj úã]ýgvu®¼ €ˆWX¯¤1¬e² ©ž±\ʵ­˜’ËÆ”­©°çûRJ%¥”JvÆ`l,˜“qcLU †ñTkÔä­Ö•^iAÊ#XfMÒg0%/Ù6–î]‹ä9ª *¿ßqéîÇ^«ùÀ¶ {gêw¶T<,¬¶³Ü^•Ø`,ͲNîŽÅËsOVu°¦óÝ¿dhµÛ!5“…¨}*˜Ž¼ßî{}·ý‰|æãÝ›e‰çl“ñɧî€&UY×Ñy3€¾¡Œo Œˆü>”ÖÌÉy¦gK ¤K²óª‡âcÊ6.± ­çBJ%•”ÂÏj X¡[6œ `X„cCÞth}q•ç ë¸6=˜X$#„™@œlʲí,džCÖæþÖö©‚« €ïèÞýXÏI[fÛ†½3õ;[Îo„Å™GíÊ;ëzÊ_ÿ¥Üû¬€S͘[œÜ‹•btqÞZa@ð‚!,(ÉÀXÛ[dK›¾dè¶/첕{¬,ê®ý¨ÊôYO¥¯ÒÁè ÅÂP&0P1UCü^ýcPß¼oæ§”ŸÏØ•ýÛ7ܯËÖ” áy¾”"i³{Yk›È²k­±` ˆêè¦x`(‡¡RKSP @Æ ±Ï$’yíIåNœ´jˆÌ –ý@²Eˆ8Û‡¯ÝSeTùŽö] Þ`ÿÑ3ô»í¼Eô=°¡8ý]õ–¾ö;ŸRñc¿‚Ko3k~Lœú~ýïå,YàÀÌâ“»KCÖÅNCVKy[-ºrW‚ 'i bˆÑ?%dήjª¬/Þù‡Ã/ I+V•¡•ÆXè,ˆW•>>þ9‡sYþáJè i CÙà]÷ä¿=‰ßÓìñó3û¶u÷Å#F—-G¤<_J©”J2ÜfÀLÒØÐÂZ0 bQüÑšð! \*e‚öíˆÃXY+„r’Î %ƒÈˆÌ ³’Õ™Úäxæ±eÉîËqgn×£å³~Ÿ0ØrrØ OsØ(MþnéHzB2QlO>5?FK´áŽÅ(w‡#!,ç6F ¶‹§ëÉ@œ­ßéÔ ÄV¾ò¶(Ó“ã¶J)'ªWšìᮞϜÿœ×á7ŸúõÀpþ'gàïþÓþßøÂæÏ~ªr÷[ÄŸþÚLN) Ÿš¨™2s åùRÉÄëRJ/£ `à“5l˜- Ê­Çû _ë]ßéõ0\lnÍ`˜ÙsŸé¾gnÞâÔ0•Ü]`Æ‘ZFV}^Ò/BðÚæ-š……`eaA¶í™¿pyý߈ŽC(lÌ÷üD´ò_:zöBO;päáfÏ×Z…q¥*IÒð­ú›I̾ÀŸÅfªclÛ°w,ˆcá)O)©j­vée­i#K¾ÆT-Œ›Lg‡1×¶ºö^m<€þ¡né®ÆÒ”5KŽÔ BrG2Ò¢VÅDº›½ÞdOWJöÕÆh ÏLµ^G90¿ݦû]—½øç¦ÿÏ6©k5E1¢˜B(¦}1BJ%$KÅÂB(fAd$ˆ¤÷Ê+ë}K®rw,NÈÞùÉÿ÷É/ÿÒS_üÌ-Ÿþ¿Ad[÷}eÒcsÇ>¬»ÿ!Îôh?óÞB÷?ÐONÞ* eÍ2²ÁP77ŸZq¼Ã»ù‰æSŸ›¿—²`¬Ç×8–žªEc”’R /cm‡`á ÏÚ*ÃZ*X¶œü>T›Ý †ò‹ˆÉÛ•IB@XOqIaãØ³RÊ~)¥°DÄœ '(¾„½×kÞ‘4hFOšOûá}RÜö¢yø ÞµQí´˰ÄÉæÞ#ÛEæy%”VI aÙ&ñ} H¯´'Î]3Oïyqrw,>FV}>ñv¸L<Ý—2?¾á—/tü)õд¢ÚßúÇZ{$ ~Õ&ÊÝÞõÏ[ùnFù§gƒüZ2rž_Ð<26Òkö”H´^k´K)D²‡6ÃX6F Z£“½NÇv´)x|øØ:ÁI„úWyA9ßDZ0ÒUm#°´, K)O )¥]Õ‚4ñZö_€$0™„gjQœóÂNØï{<¶ëm²÷~Ù{¿«‰Ôåê¶æáWõ®õÞN¦d%,KBJ2JHNÃ"n¥@’ ÁâÊøÑ¹}玓»cñÑ|ú³ö®ÿhLQ6K¹öjä×¾5aS¯;Ãë_…ŠJ»Wõ àíÛo9·gßwoœí…V¤Œ‚–ÒtÂÀ’°òÖÿΣôkþ÷_¾ç}éZ$Ÿ9ã;.úÛ±>{"ö¤z7+bXˆÀÚªƒl ³±Dc~ç$$Ãð„U­¯3ãôð%É  \T*/, ŽÂØjcYZkÙ[){¥”R¯¦¤óB„x=üc£ùH>›ÏÎËž6VZÓ´ÝÙ\R9«•‚ 3.S·¾ÿëáx÷Zï&6`ØRi‹ö³¶LЉ@‚„R  @&-+å_Ÿ1?3ÿÆ?ŽÔrêî{NÜþãÍ­…leý¨¹e¨¹eø±ÒžhöîAî:K­]ý-ÙŽ¶þOuÿ<>þ{è>aƒ¢’6v‘•°VŒ¿A,N_ c%Unw…9g|A|Üÿ”£k¨IDF)³AP²Íž14Àl˜ 'EoÒt!®Õìðˆ|UYg&ƒƒM´U”Ϩ¬È¶’eG‘Ž£8Žã8Š£(Ž"yÒ°HÆM‚¢ ÑÛ$Aã¯3»îò'“û^»nº—h-Œ! cÁ00–­¥Úk¹ °–„RRªª<‚»I*" ÈÚn Z&!ÈdãŽùfQÊ= A&»2¥‡$™ìÊ”*’d²+SzHBÉ®LÓ2päñ_ÞŸþbSóPk[å²_ùT&Ðá·~µµ£ÒÒQбñ®ŽwaUÀkVžZ½íåÎKƒ)xí#ÉSu|÷{›ö JÇ6+i` –Ȱ±¬êÜÑÒkBXXäKA~dþÞ„y%Ùio|å«FGÇ(Y›"“ì¯GÄþQÉÆ²1¬ ˜kñfÀù€Ï‡^Y ›©Œ5ȹŠNKT¼¼RYRy 2:Žté8Ö:Ö:Ò:¢c‘:i!ˆ (¼´ætJ¤?ª¢¶<`ï«7¿pxúÑú!K£¥¶€elòn·0GÃÝIÙK°,*—)’*Ù3V%¥A‚e- Ä‹R†SàÚ2Žt1Ãü¸ýò¯­ØxWùîëËh¶¢­ÓD›$Xú‡Ÿ &hÍuÏi#ÁTñÎŒÓñн§;¾`˜Îì½_P;|l|ï—cíi-+U Vƒ·} ý‰{àªOŸÎ}éÔs×­¹ñ™•mÔwFÀ ²V áE!!‰³ù’ô ÀB ÛºãAjÿóSíœ3mrÇ5ƒðSä IDAT+w<¦+@×(_(ÿÈ íI%•Ræ2‚åà¬L6Ëf ËÚ²e£+¦Qôpèå k²ÇÀèXw¶Q^+à™ª¼üÒe—]õšGšÉ2DÌ6#k%[a­´VZk¬•l¥<$¥”z3â+¼qûŒðÌÁ;fþ¾ia,,ÁļÚ"*c­ó±è‰žð…&»áP[@ R Ë‚™˜Ç~e8?«3CÄÀ.ÛqÓì;߸Êݱüÿì½i”×y%x¿ïEDfVÖ¾¡°oľH‚XHp‘DI”µ´,Ë’ìnw[î#[’ûøÌqÚÖ±¦mYÖœ>=Ým·Üî·Ý¶lY’%Y¢D‹ÚHA$Aˆ…Ø—B¡ …Ús‰ˆ÷}óãEfe {a!”—yŠ‘‘‰ÂÍ›÷}ï~êñ¶9õî¼Úúñ ØÆÙ®ü·~A‘ý‚…ÿÓ(ö èéØ@˜Üʦ±hÏýf·ùëîmë§¯ß MAÊ#þlR%dÓÍHÜrwXqÏV6žaoÇ+—õšÇãîU۬ž=/^þl=[ÃñÐHÓÝó·3 “0[&yyßFã%¶»E¢š]«X…0@J‰ÕŽ‘žÚzÃ8Ó;ÊìÊÓ oín›3ÿÜÌöc^Ö@E•I Tbµ6°–$<Œ…1êTüAcŒ).Bêl{óÑ«½ê˜¬À(fkØŠª )’éHÝßY|iðÈžº9«¹‹²`ã±QeVÀ¸ U°Xç’HÂÈܶ½œªä^Å$ãÊ×ågçhžs]ý˜æ<ØzfG¡Ð•­™sxƯüÏhćš“ÇëÁB¤Ê.ÏÄb¢Š:¯Aœ9éyñÌY§·B˜}@¤¹W>•©ïŸ;órà“ÅËŸUоÝWܳÕíYýÀnfÙ{yëÂ+y‡U÷ï°{v=`ÿžÇ,ZöÒÃ& µåÃ\úœgb¢0œÒ”>Uת QA,¤DReŸÈÇá³³;šO³(e€QùII¿ØúãûÖ¿k»ª¨Š*«…D¹PÄ1*Fýžhyãí3QóàµÝ:K‰rÙ2 ,„HJ5>í©•]ÑÎÁ#¯ÕÏ»ððv-µ„QÌFY”E‰˜È”Öh])¸ûyGë •kN¦¼¡¨’{׎ëÎW¹¢ÙÔË" ðàïþç¡8Ž‚Ìá½óA* !%H‰â/U݆žá:ñƒÌÿàw.?úæáa#¬™†b:蟔á]%fycç:·±úÝncí†#D†È¼¸yÆ„§®YwÀJ$»vŒá—{]´ôù>ëÃ+´dêÐ÷ž}òÁ¥Ï¹?)u®/¶JIª Ô%A:û‚GzgwÌ9 Å™Þ<4v¬èíÎÔÕœ<;ojËÉ5Kžï?ذ÷Øî]ù¬BVåh$‰Õ'áÅ16±ã=£/f¿4:ñÑ:üßDdÉÆlÙr¹¼]’ÌQZRÅ6^Âìâ 0“j™ËÂt=£®Ä‡ i]Ï=/¸ÚLÊ›†·+¹Wãî “6é­$&«™jËò»–|üã¡°,^qtÿ®yPÕ±?ñÉÅ´-¼!0Œ'@Ôf¦Ô6÷׿{nBÖ÷¢eÏ+èÀÞG*w¾¶}¥ÛX»áˆÛxhc× ›:Æ»vÃ˜ÐÆ9ðæ#O¬ùnOìát‘Vß÷ôù‘ZØ`æH6lèàk€Râ²(Ô–*'”Æ‘þYÓÚO)¨ëü Œ&öN€W‡7j¨kï±U.2Þý|}w’Ô¸¨í)U‰G"[2ߪÙ}´j¥uýƒÍÏ]åmEíôù#'Žœ~mz°–ÀJ°€(´Sîë?ùÊà‘- ªŸûl ‰²¨µ*NtþÅa:¿?*Õi$yA¯¥`ï¦eC¾]ɽŠŠ›Ðh²°ú7ÞE©¢ÍíÞª°ˆ¾qpDR{S…DTIñK?Õðæ_”O<Òüïë «òÃÏ|ôð_}få§ÿëòE©=sÄHÕêšúÓ©:™œ¯—Ä% —¶ÌsmìzøÑž€uç Éñë>!‚—·N`m\ñ-ã‘ñ˜™§ÕˆñøT8úê©ì°kAr¦e8€Ø½çîIý˜” 8bç˜a3­í4ÏœŸ‚)Év”ÿ¯®ÊE;Æ|äì=v/Jä^Ž¢<Ðó~0˜a¿¦‘U1‡Ïͧ¡C¤}š»fÑOÔâ• ëOHjË^­ô·ÌÜmå·èLð‹µø2K–Ksªª$€P?sýà‰ îD­Äg ±’Q÷%ebÔv~ÙÍ Ð¶ãÝ•¯š®°O\bl·UrÿyÇÛ+ºvÖ}ö…<kwlY¯³äÖ߈ÀZ ›=µÑJ¨Œ¡xàGç¾TßT¨o,Ô×Rµù ¶?>7„¾0žÔL­¯=¶P›ûZÎÿ›37xü —nRàà›—*’qp²ýñGݽ?{6žQ¥þ2Ö-ü†ãtff&b"&̧G³´zý?Úö€–l®Š=fÌbÏ0cÐÑ|Š`ºÎÏ('Ý»O!-‰{ÊëJ›ëz eˇ:Š_:–âœ2‡“¿M`‰¡1$ÿ­ÀœùAD95ø€ì¬ù=üÑ oŽBˆÙ2[U(Ⱥ"JPSý¬` %2¥‚«n‘ªŽîæÄ×jèkRʼ©Æ·žßý’û›§ÝçFà9”·.OîÕjÈ;ùþ#ç]‘½ yübGZá¶9³ìpqÝPOÝð@½õ”¸”Ý+!µxKËÿÝ3_jh*µ…¡ïývœ s©¨/ˆ“"£‡ÿî§‹~õ=÷-ö÷õéºûþ£GmE=)øàãÿ¬ª§¡Šw>ð]KòˆÄÆjcyíä'.<«(™çÞñø‰B쿸yVyÿêÙÏD%ZO8K€DTZáMmß÷ð}‹¶dQÌSÊíö7à-Â(³³)e@–â×iŒzGg~¼ST‰7+)¾"ÁÁMBr÷sb@e·`ßÿ%ÌpÑð.W’høÈ¡ þØq+T 6¾ë3ŠÜž²=ŽÜaIUA4ÅIBAºT I DÍÄç ©’(Ô¨ÂrbWzÇþ7Èd´“Õ´·.ócß}?ï±çÇûïÙ¿ÿ­xÛѦ;ýC—ÿk¾¥¨*÷;êñ©ÞľöÛˆÇ/ÄÖ?K.³õ¾ÿ¨P5FÙvÿjn nd°.?X;å=_:˜šY’mÿí÷$‡B:¢t”IÅÄJ¬š›2µÔ(õÀW¸ò×>¼ò×>îÄ-›¦<öÈ‘Æfùé?Nó‚‚ Ò:¹m&&Ì!U¨ªŠŠè#wûù]v'‰˜¨˜æ¾{õö]¯=`JóiÆ`ºúfvQ½àþä¸6”ûçaBŠoTäÇ€šÎïÙiÀk_$6ÖdÝ'<Ÿ™‰=2ÆæN—ïg»“Y€·þj)<§^`ãs6òˆ§JÉs‡¸äá„Ö““‘ñŒÀ²q¦eõHÏVÏç5;/%] ÓÒº<°¾—÷r·žû~ì3 HÙ}ÝÇ&§ ã D•Üßö¸šÒÃyÍsƒ¹U8÷ê@3å_Ð(Tqä_Ê×ìÎB}šØX6ÂFfþÎï74æ³Ï}kÜûø~QµXÛô>õ_nÄ8-}^~ÁŒ@±”ôpÂìšô‘W}mcy`Ñ׃zÞòÊ/}Ñ »·ljwÏ\úu›#ÕËüÎÌ Âqˆ¨T¡¢ëãÅý°ã­µ«nIÙ‚z$‹Zid¨«oºa$I½¥å=en§¤0ñ¥dû88Bwä>nõ¬˜FÀzùsÄdű’ñÙDd<6 Vâid<2†ÙÀýdƒ¾]€‚ƒ(*öÚ}íf‰W8D@¨5­†[ýÊå’˜„륞©OÙh”{=.º¿MÅ]M­+}ëûyãÙÀ½Àz~ì{Ö4¦ˆTÚ- ,ñÖ»ò‹¿‰è9üc·Ñ6ÿ]Ur;ázüñLãí•-sã0¥aÍí°yÓ P–MÖ°]û›’Cèih,Ù¼_[¸ðÜ×þ¿¬ûì;Ò™ž1“ºhÙsnúôà¾ï]ÿ”›Ã¤s‰²‚ *—Y@6[ÀÃ뿵ùÅ_,¿°{k°áɰ¹¹â{… ÌéTÁïÄ„™€B*PUuåìe9³hÉÜ7Òá`%á¶Ç“Ì…å ŠÎp”Ö/4Ü/GñK*(Þ3±(oýòטMM“ †Ã,b‰ñÈ$Ébl@Žå2ÃeV/³’Øs8ô:`ÅOK1tWŒÜi2„L *ÝÔD¿t½Hâ ø -ÓsQ—¶Êã?‡0<~NH¬–zB—Ì™L3¦’9Õ%¼6V[j ªi}ˆ$?E0Î!&ê Õ¤’ªª QðÒ£•—}Cm}Cm3ºìUt÷MÐÖx@KýYlŸ¤;” g†·Ó¼¸/Ú`(ÞÝš~ 0•KÍ7RÊ H¶(l1nÞeÎÏQ4ÄA”Š#o2PßøðÐàÖ¡Þ~ã×x-júfq†…¥J€%°"tuýqŸDÒUÜ`zjU„ˆàš†»JËñ9£e\›E>騒ûÕ¡ªÇ«¸Z¼±sÝÝ«_¹{õ+»^»ßí±¢° ­;s† æ4©ëKÚs–µ´¾Ò€±¡H6€xÚ2³oÍ]£~8[†™GÝ"椇’*ÐU¡Ü¢Ph%¹;0])ôôOÐRÀÌöCÃù†ëñdÆ!• ˆÜLéè”iÝÂ{Thðà«=#/ë[R?oT›“´´ºT@ÁdÙÆÚ_zƒ``ØÔO MWí´–áSgãh„z$Z#‚* $¥™i@ì>+ R"é _kýü{MXÁB¬0Z·Wp<žL¨NÖ½›Ur¿(®™Ç39ÈOúˆª¸^|¥y€OŸ¿|…Øä°©|*ŽÙ­8Z'rOKæ ‘¶Áô8 & ™_µå’R“"Gë.Í‘ U.L¥6RMfË>»Sí"jÒl2ãÓlhìð.‹ÞÁ)Ti™À©¿1ÿ­¯/3R)is”êçÝ?xèeƒ‡7×ÏÙXj›$ÑÖ⢑xØëDα’5STU•";èÕy2à 뱚Ì,IupMÕ+HÁV"+`Âqq7Pµ!’d÷Ø0ާïþ{²s.¼ªÇ¯oorŸ¬lÈÉÕãwv6ä¤c²²!/ ÇìnãJøýÌHÇÔl×Ôl×õWCR…4^=ëïÅ2‘x”ÖI‰„;SK^n+™sÆ­iÒ²9ST±å™’'CãçQ£Ê]G=wU1y2é1äþÐÆn¼°éª=–qÌ> óèH¹òjÈ aàÕíÕ¡Å@¹é†kZéoJõ’õ­€ ¦aÚHß±üpHˆXS5Ü! ÍÛændYH{Ó z6WìL×ÍMxÜyWI<»o¿àþ´ºy«ÄIRÔBÇaÇq,6^xn_ßšw Wz²!ßÞä~ ¨ú*UÜ|p…íþÚ‰OÜ7ë‚ÖVœ<4]ä–_T›aÀèvç ¶(+HYJ²Ê†;Z(énM¤ I”»@TMšLš½±ÊýšÝ ¬|ë;¾UÔïWQºKKñ“4xäUwLýœ $5è2šO …ŠMÖÏ2¨ÖŽÐqFRÄ«BšNÕg…¹³Å‘=©ºe@ˆ ýÄcôgòM†@4|l7ˆÀDlÚÍòÈF±U‰ÕÆÇÖFqúù/ùÜ­ºi—ÀLîU¯¢ 'ÛÃ7ñ;;¾Ò¼ê Åûdá•mKÖ¬;¸fÝA×Y‰Ó,Ų#®eÄs†J²””Û=4*ác"¨…Z0V¸s¥lGÏh…Œª’!“!“¡ŸþóhâÕ>|‚ˆÎõÜËΤrËAë©@»Š;Å€ú9kT8éÁá´¼›×,¥±“·8pж ‡³#Ä6¥0 æ>ßxäeû Âðë;Š}ƒùþÝ^f&±‚KŸv4šxY?ça°‚uèè6¸³P{°'ù À ¨ÑƱ"üóà½xkîÚÅq¥ä~›WCVy¼Š+\·<™`ý·¶ýàÇ\úuÉ9€QiÎ@L7SÙðp/-l΄T¦^E ¹ÚLbYTV¸£©dV8ý¢BF¦†Lš'³v]'µ¶:ß›ìú‡O‰ÄªvÛ‹óoÁÝxD• ¢B¤gН»þ¨diJê{…öQ-_é¹—YÞî‚‚b¨cÏ ¬a›Š”B¨‡B}&5°š`&ó¡8Ú jÌ4®ib»E©ÎȪ~þƒ©NLj£}.³UÕt¯ˆªY•M¿KÿÓ­ºuâm©Ü+©<_?qû1Ty¼Šœl9 ëÀ§ÏßñN4æƒeÛ›¿¼nå7$§"eZW"!b²jΑ6£<}Gnbô¹ŽIE”ÌèÒRW$“¸1%ÃÝѺBD9 /M^†j‚©k< PKkÊßûvRÛ¾þá¤òµûEì«/ߨ&AÂ×ÀBIÕBO·D ÁZK*ª å³®êÅfõÜ(ßçj ‚CÑ ÖfÛûZ:½BútA‹3¹~N®u;ÔÏOî6@€éU±q[ÇGŽÐcëžÆ A@~Ê ¡uÀN}’»ŸwÝViä(.·zàFÌ\Q*¤Ã$fCN®E~gCÞü\eC–eû‹£²ýó©ã^½Ä;\g6äÃö¸RsZÛ¢T ùœôœó…jmÓk6§’W›S-¨ñ˜ »ö§.6RëKN Hˆ4( 6JùŨß"†Ë€¤zR&%RNH•[âq`b¯Ï¯#¿–Ÿú_ïypÝ Û¶ÎºÌÐËîÞ,6°oÏãWrüÕfC–1Ï_ň¬Úùf…Xµ"«â6b÷mÅÏ I´pR ±’*j£t¿*zfŸX™œ cåZÒ’‚ ‰R\w¶Ã#d‰jM7BÞ´È.GÞ4Õ0¬ 5ýVâ8Ž#‰#kcÇb£8ŠUãÒ:&)ýTæam_¯d¤’±Ë&––Ë鹪lȫŠ3Õ©Î*nÜÙ¾aã™Êb=Ý^kk ¥¹Ø{>0t~UCÛNë µžº)Ö1挎¶˜ Uˆ³„B2›JL”%ÕÄp·9ŸˆÁIÜJnx€|øÕÏ`ª-ÄïùÈS© å±w¾*¡H¤ê–Wéb—°w×Ë—?;¹·eܪκð0AY†Yl6ݬÂ0¤¢Æ3$VUHMz¦q’î¢\èñà5‹ö+T© EOHj÷ËÈI®Ý†ˆ¦({¯úÞk¿A7WAî•ÕU¯â6Ç8ËeLdEïëÄïk7¼-›¦NøêK/N}pmçôyg>8·Sêz5föÔzÄF­'’+O±2Y5äÙdµ½Š*”e´Â½¨ŒS©R›2±2ÉD ªÍóYBTB±‘–¥ºDBU›|)0~ét`í]_éÐ/ïø¸‚ áxÜo!=½‡.q¤[Ͻ۠1d¡´'1÷?š¿ì¡®íVÅBmBèb‘PyÂï£P*ŠZ˜b”n‡cK“+ ¥‚ÞÈv¤˜ˆ¬M ~ ì`†)ó!…ú½[A XÍHšÒÃ6¨ xNQ Ç@S¥€<ölq_y&ƒ<""0ˆ´NƒQ2R¨ù…+¹9·—'÷1u‡-sœ?vtÂ#«<^Åí‰ßO¯²•O½U\¸QE2÷=¸Ç°ø/n¾¨©ýî<PÞ"°íÚÛ¤¢^[OÉ#ñTr*aÙlWóHýŒˆ‘²µ &¼ŒM w·š^¤®C$Ô¸ŒB—Hm˜=L¨’·KÛ–|s˾Q£F/ ÷ëO=ä¾Wiä4Öì\îyHØ{+°íÅc¢"£¡g¥#®ôª*¶8€¸–Hux¿ËûEQ€X5‚/i²d(+Ä^Ž3…öpDòMs]Ÿýi4‹ [¦Ðh7Ô<ÏX#ìibÁ Ȫ)¨Š ÅÌ¥¿wdßkR5¶lHþ€ÒBÍ ã\ÙXU+àð+ø¯’Þ'Rî®ÙÞ;VÀ±ÁÒMâ²Õß½ÀŒàëVõLô± _mêûa.F©ôjÏ÷µ–¡JLL†¨Id\î:‘ïe|¿&š¬à  EÀÉ7&€"íȇmË:ÒºÀ=Ô'7´rÌoýÛTOV6ä䢚 yU˜ÄlÈ /›:p ñ~…ÙËï~ðw¼²jÜþ5‹ÿ¡’Óý DbFújê ”ÎÔ œÌ{um2Ö‚wõ”trÏ BÏcä»{~lOoŸ³ä Ç÷iÞÂ/‘F¡hìœtg)”@ãÌ•‹½4v}jÛæ_ézËIɆt8þòÅ^ÊwmÈx+]Í{éÔ­à%"í·Kt–½v²0KDÉß3€|ÃÜ ÿ […Ö'È‹áÅiÏŽäïU3Þ۔Ы¨bBLXº~%ÊýzÌ™%Ë»vˆˆÉ([uä‚t<¨±,¡¶¦n87½ۤåÁKä6Çøž,UTq‡áw¦®ƒqß™:^b_'öìzhÜÏ7žo<Ÿ½À=Œ˜ÒNSÚÉcvú¦HÈÄjÑúqxu†Ó|æô—sÅCäߣfÁ¼†_(‚µý^ìÍói!lØ‚==Ÿ=Ÿ=ÏÏßßÏx^i0{ŸÏÆ«<~âýD »Ù-Å/ܙס`o \U¤Š[y”jœB±xR!°â2‘máLD Ä#”¾1æ«* kŒ5Æzuµ5 æóÛoõµ^®ÎŒ¼Í³!«ø9Ç5Ëv‡kïûö<îVúìßó˜Û³ná7ˆÝš—$’·âÁ˜`'iJ½‡Š¤ÐOGXEMF"õBäÓ°}É3+#àó? ¹ÇÀÿÎP Šb¸3È>†(M(Œ“*>04Ù ¸NhNdžŠã“ÃÞT q]µî“Šæ¡Ê)AU"Ëd’6Ø$àROS 4¶“ú-1;|˜8Ðîˆ[“Œ_‚­QÎ @Õe/Š'&ÖºÚt8|°xž¤ùþ[zÅ׎Û([¦Š*nì͉ùÕJ/žÏÄÎXk¿•ö'/¡¥Ž å®r€‚ÓQœóÄ#ðÓ EűG§Ûº=¡‘ánƒ¸† ¬Jª¨ùiÄ!‰ØËØ‹q·–÷”vŽ;ŒN–T§zz¦HûÎÔÝ”›x´†ß‰#IòÑ¢Øø”Äï ŒTãÜâÀÑbáDàM!ɨ!"Fò©ä»lሥu’¬ hêÞ]aˆì¡ÇsóŸ‹fl :×Ê—6Çsx»’{Õ–©âÁÅ‚bÄK'ûq²ôé„p§—ò]9öï}TA‹–=àÑ»¿íÆ8&(ù‰“<ÌTæ6P³–¢Æ>DIÕOEA:& Ê!QVE<<ƦG©7ËÍ)¯Å­.2Q™L†BzIå;‘”6*÷ŒÛ‰NÈ)•bHJÚ8²qhãЪ n~k¡Q\èÞѨ$-ÄU£é'1÷lé²@D0)'\T·‡’Ž©˜@¥B"hóšÃhï@¡ ec=›tÝ£^vàÛ·úº¯Uå^ÅŒO-ZU rE^ŠÊ™O-Zõ&s)Ó½,ZºiÑÒM^ÀN™“v ¢‘PÒüsô§Vì/=R)[È©¶Ïþü¶¡¿ÎXé÷ŽMÁtŸà‹úžÂóÚÎû«Žù¿‚©H5d‰|JÇãm–’f§^Ñ’xu‹„JOËK…T¡Ø±ãIw]Þ¼•åmñ7£È&ãŠbR@,©¡ÄŽA°h ðSÔê{þ©Ÿô¼׃Ê+R–$›¡4›j{^Yü¾»Š…b¡°s“BµîÈ#Ãs7¦¿˜é\÷Ã?5>ÆûÖâé~³<˜Úú¿ ` üëë¹®W y‡(wWéâ!o¸"Hy[ÁAºxÈÛ®ÒÅC^-*eû?õXyY§‹‡¼ /cö”QyÊ…pE.òÒ8ðæÆlz¸W‚1S¦¾1³™g:0±NìSÊkšlš%Ž5£™”u`z ð¾U_Ôýl®}Œã¸,Æ‘4 Ê{„Iú ½j»ÅvÙ8”8§ÍK:]F·“§òÓW?‰. Wéâ!'6Š%ŠÝžD1T©Ô @…$N Óç½oá‚'§,üÄôÅ¿:VLN‰ 2ê¢Úž——¿o¦ÆyÕÂù—g—š9  +f ¤j†eÁ()W%̇*žxàÏd뿞­O¢x?ø©É½ÌIDU¹Wq§ad$[Þ®¬p§Ü/vÊu.X%/0 œò©Ë¨íåæÎç’Ø-7ç,YÁªp¯@L]@…>ßÂxB øV=¥Ö©ï7¢(Jœƒ3±2‚aF䫆ãõ¸ $ÌfOô{Y«ª/ìýˆ»¨{Y¼ì¹ÅËžÛ¿÷Ñk¸'×6ýZÅáùãPU;Ebk<„TIÅMxôþd€ºÙkm[ò‘ ŠXò¯gü«Sœi1ÐÕ^FáÈ ûÞâ çŒã±[Šd!%õ–ª˜ÒþçÃ…fÿâ6eê·§LýöÙ3¾%·åÒ¨’{wþä«ß/oWÊó„Ü'ú•¯<åª0.e哟Œˆéd§ ˜±¤4;zÀ8¾.±ùÛ¥#á¶¡ª¦Öx>ÒžM!ðà)ôÔÎ@Óè@ˆŸ¼kÙoÌëî¿Û—K×Þ÷‘Â|ß?8æµß @Oí^´¶'Ñéb,ªûÏÒž“¸ç.¢¹#Ø}"°zÚ¹Íû?S›ç·äž\ ®šÜ«ÕUÜÎøä»ÿÍ_>óWå§—Vî¾—âÌ„'NˆËfAEQ9¤ã•x²í˜¾¼]úoì¶:ÿ Íé&Ãâ õcõžÂ:æü[2œ+žRQ@)í–ij<¢jÕϧi¸‡bÍA†ˆý ›ç§•ó Ñ;®¯Ìþ]¹ê%ký8º5ÓªÅ6²6¶ªª¢E‚ŠSîPѨEýüUp·BH ÊP1>›áŽ=ÿóˆ1fù§æ.ÿ•ÛÿW×úOÞ]^ûî[Ix/tù;ûm®Tûήç8VÌÄ›Ø}|’K.|ɵ!ÖÎý³—Ž~Öí ‚ª-SE7 •†{.7öð«kÇ„Aº¡¶âWþ—ï_ÿW·0 ‘VžèPIåõ3&Η²òÝgñäCßWe6–Q%>ªÊ‘PûX×¥¼í^bŸ8Í&E&Åœ&«b€Xõ¾¢uþo"uéi#ƒiõ)”™U[%Vhmq††NåT5®«ihÈ õ‘$Tž|’HIÀÓ(Ñ¿zì勺÷¾×¬WÜŒiÒ!q,‘ú» j£,ƒ Ré/îõ´5]çÍŸÚ)1¬/éØûƒž¢ö§©‰©©­Ku‚8i+~ ¢ì#b¿´0'&WÞç§cnWkU¬ˆ…@ }g¨Ð›á µÌVä\c µüݸh0ÖÚ G}½¶ýîË_ï˜ÄlH‡"Í'{Î3Y(ÄÂP‰Í‰Aªœ(÷$3.€ È£¹Ÿ>õÖÿh‡¨¨ˆ¸[“óý½8rOd»Ë†pæL²ê˜p!³sÇx·k5äCîUü|Âî¿ôÊ—{ðã‘–ìO·Ïwm%~ë·>'‡Qîvͯÿú¾ÿÜ£vžýµ_Ã%ÑÚpÁ0‘tž_x‰Ã¨"‰eóž¬[ø Tøé‰OdRdÒÌ%B/ivÎõ :mkj‰”­ cQäd‹Â†ˆ!± Ï”%a ¾U¿&UüŽ¡ÔUï¬Ú¼((Î3S¡ÏìZ@Dõõ¿ç}ǃ7O«ü†gTcl{qÁ¥ïÆM•(–Ð2+ø.5 ³ª’ñ!Ÿ,Ô²ˆ‹ d°ð³=~›þ`西ì~×矨kQUløÌ@4HP%­pÛ˲}š[ÔErº´¶¬Äìª0)÷Þu+÷„*¹WñvÂõ*ÍÉÿ×.뺘'ƒ‹Û2•Évàgé½~?üáïù#ÿ}?uá‘Ó[0 AˆäxÏŠ ^SU²õàG˜ûµ²!Ù óÖËžL®6š”¨"ŠF<ß° 4L&Á>ØG* œgª#0Á’2…±_(¤ˆTs"VãD¿«i][hÀZZÌ98ÔØ76Æë>Cî݉‰Èü½´eîÄ÷èV€ÏþX‰åž?²Ï~ÁF6ÝVÛR&ž«fÑPÉsw 9n9®Äj Cä ùR½—EßžBCGjÍÇf@ߤ1ž Uü,:×ìª §F£„^ÞºxÃCG6S\6™¢¬ßÕê`Ïi2æÌ‘í[F"Ý}ïùÚº1C½XSï[Šä/LÂ8Ý=ò©Ù»ƒLÊGˆj”3À~ÒìÊH‚ݧßÉ©ø¦y;ÌÙ¹û¢” wª\²DLÜÕ¤/ö(³—œwNÁŒ­n7·¥x¯*÷*n ”y¼en€ž£¯MxØDõ* ‰'²½_h/®ÜíÅ•;pÕä^‰¯‰oŠVܳ•oØT¶gJ¼  ]SÞ³vý €$6<ÔjETt¸¯Ê^`¥¤FEÒ¦ª òÀ⢨ÀZªMåFš¯ê‰JªÄªª¤±Ú¼À-ú©À®×›ï]Ýàõך®ý²o0²wV¨3ûþ)ÝÛIÄ u²Z)OTf&áTEþe)HG,ldÀÈ@ØöuêFÓ{ʵíå‚È ÙîÔ:•ÖóB& k³ûÞí¸”©JîUÜl\•¿ôå wÿì¦j€Qr¯”çÿy¬³òŸžÆÿñþ Þ-QîG÷ÖÌ[r…#œQw‘ÔL+y†W?°›Ùg6/o]µ~xª…Úd2³¥%VÕ ÛmCͺÃA‘PMÒŸ™¨ ¤pm㈀©*yÌ>àAb%OÅ*F€ (»¡DL¤ÊBJD2$V㜥U÷þlÇë£ëª2áUÜ&àžÍ¨8€-ŸQS«è[Ҧƃñˆ Œž W¥ÏÓ p î MZt°ÚhT¿kJZæãQÏ¥Âmwú½Ýét*YíÌp f¬rüªøÙO§]ü"n î(r¯Ì\nBÁÌ5ðx/0«±wu3®ª`¦ì¶»tïýتJÆ*^p|¥íþ؇þçØ ,½‰û{ú|øôƒ'ÿÐ{ÿìwôä[ÛkzÚjÎË7_ð~ ß™r!´s† cè¿vüõÞü¹Ü¹œØðpwSóˆ zÏešëߌ"µE±E%I[²]* ‘Ú¨hòêÓêGdˆ lQØ’Xˆ-õ%JÜ™Fâ>¨qå$ªJL$VmNtlô¼ª½pð×I+˜!ãû ýçÉ”Çܻ࿋u“ Hq‚x†ûv£ÜABçx oK“«â ™Rú¦¸OÍJ·½4NIµÌØR˜q œV‘Éøt¼Õw¹Wq 1‰züàȽiE¢¸­TúGW¤ñdzݟø—«üèowô½±@Û¤ŽmàXèf_Û6ù<LÉ6{ÅøGÀ½êí ¶½4íÝÓ_uÌ.E1žIcªÐìÎVR :R¤i¾ñ¡‹Så:j*)w@`()‘+ìSUk¡y]±ð'o|§Þ®×ÛWÞÓµòž®Ý;¯ Êóæƒx`·=è·Ý;p¶Īs¨"*mŸ"ž®¥J%5„³î&–ëgÜä*$-ª L)ÇMAfL…{Ò’ÕñïC†L"Û9H~¥}ü€ç~6ëï<EÅMÏϺuwj<ªä^ÅUãÖòøœ› àô+ÇÚ.ÛÆgOÎFËq;¿µ@gß§Æ÷ÌßýEåÓ{~qzgßó臞~ù¿xóÙüô5³@f£ò}¯s°o97L@Ý”†´lª9‰‚ê©SÀt[)ª- ´dÈqâ-hÂïçµD€ªOÉ'²j‹ÂBbE„¨Ÿ‘"8'@¸—TÁΜV"%VØX’œ7D¼_?øüv9¦u üt_y§Ä€ûJãŒ(@J„ŽQÕÍí„scëg,l[R˜rb>!qÛ Ì„&¨B‰Ü-”±²â”:gÆAÅ66ÚÇït³¾ë6Š¢â+/Or2ûµ¡JîU\WHå7œÇ'DïŒ0í®ßé>95ÇË{bs¡1Sqü“«ºóÀÉ Þʽÿ9NäX{MÏõXTIƒt¶ïÄé㯜³z!€us¿c‹äøœEN%ŠG²$Ç­g-žòjÓ­õâåÙƒñ 1¬µ¢–‚~ý(""«BI”HÁJ*ÊD6’%3žÙwêÝî@•Û”ÜA¬u ¤é¾Àÿ™Û±zÊŸZ[þ:S> ®ý5wÚQš€7+Ð3Z?cc•X0R2܉*e{²pÉÕ­9N/52—‚ ˆ¸ Õ_xÿ›L¦¿?ËŒŸþd €u¸gÕŽ(*†añ­ÜÄ›5×HîÕjÈ;·5Oˆ‰È½ûÔñ¡ÿâ=ºÊ{tUüÜŽØ»¹wçŸþoKßû÷¯üã¿÷þe›æÜÀ,gF:¦·¸¶ñ–£tkZjUѽÿ”ã()Š-ŠDð‚JrOJ`ˆ@ç`“u•€<¯dÁ–lQD yb•û MäJùÐ:GP°H•ˆ )ÀJ6uŠåÆØî×iJb€üRøâkg{uÇŸŽìåm(ˆXóɽRn!ô.^UÇ—Ê$n{èl× ÜöJÙîÿÂûÞ¸¿?³iÓŒò€·n¹ À=«’N³ç>F…3§>xSïZ Uåþs Çã¦@Ïás—8ò6¢ò Ñ;cRô|Ï÷€9ÍmЀfÀ^œÜÏ?¸ y4oÛ ûä軽ø ¬ÿ¨&ä~N÷.*oÏmßuåãUN0÷þ¥G¶ïí9v¦}n€ÒTj (O¨:¯W$©¼Ü™Ï.KÝuN"PÝ ¹íÎ÷|ì&Ý© TÉýÎÇmd‘O:Æ+÷á³'çhn€æm;ö­\uvåªx×ø…9«OþKzÀÙPZåÚÜö};v5/Øyrçz÷n#÷JíM_YÐñÊ%ŽÜõ·/e¦RóÜ–iËf4wn°üR2•ê›QC†€)£u6avéîË´6ÕNkéÚut°s$ÛVc|HNEÀP…ÓyB‹@;¨›HÀDJJn‚Õñ%¨Æ—½Ì[Žr-ùˇ?»vñŸIÑ™ïèwRê)]ûèÊM„@Ä…ü¸G©¶¨ª%NO&TGgSÉCMÝLØ}?4/l^4ñXK8~ôÝšÛþ¡¶á¯q¹n«“^0s§‘ûÏy5äÕòx—żÖ5kæ¿]ãÃ*”û?Ó½é©`ÎÆ÷^‹ïÛ2›¬Bßm§ç,ÒWñä~f¤cj¶kj¶ëbñaouÝï6–LÛ:î¥CßÞéû>é?Ù÷Ì—ÿù]ÿçms§w9uöHgǼ[`¬ãH]•èJ˜]\6/йçT1ÕMÍä‡SxP++Xfr™cÄð脪[ÙôƉ'Êc;ðæâ…‹ß\¸øÍƒû—^æ†_ &7>̯ÈçziÿgׯøïRT)¶ÄÞ@É‹WÓKÔ ÷奼rP®#`œ-3®H†·}(ÑìéìöKx7í‡%D]p@B¼ëÉç$‚„°¡nÚú\ç{>VÛð7×®w¹ÿüàNÖãWŽƒ8<év¾ 0ZY±ñ­ÿ8}U]Çù¾óÙñçæ·½ßßð•ÿp¯ÛþÊ˜Ž–cãÞ𠱯s]y{ÅŒç;¸Ïó}%0‡ªŠXŸûÓgM­ïûÅú¦ )*%ò@cYI躖þ§§vTEë¼æÞ½)o¨¥¶ë|ؤVA¢V„@– ¢ÝLS ¦]¥ UUVÂØRw"·»x×ÐΫƒø$>¤P’ðnÉ€FtœrOV $»¥DîDÈ–ÝöÒI U05qÀ®Ø&öOÕ4I<¨ŽÐ%t*!l„µ‹þÌíW«@xµó·ÝPkë¿ åáÁ_½ w©ÿijn£qÖcUr{àí7ÕyÓáHùÓòú¸ýN¼³?^¼%³ y\¦ v¢Ü¯šÜ+Ñ¿é¨ñSsA•ógI 1$U;4˜~ãäìem£«RÇM¨ºTBAÒåT4?0œªËÖOiÚ{ûM͵=aœÕˆýsñt@L6áw$uîªJ,IZMåçÌ5Hõ®lq֭‹/ÌßøèÉžÜôÜL›¶þÿì½i”]çu¸Ïùî½o¬… €˜‰£H‘¢HIEÒ²,ÉÖ²³’8éØù•ÕÈV²ÚNâÄîn'8î¸Ý«[S§»×êÄÝvÚö’c[²­Á¢DŠ’(Š3 €1Ϩ 5¾éÞï;»Üûª A3É·W­â}÷ x÷¸ßyûÛgŸ_úØG¿d1-Û°…ø^tè¢K¨zစ,#\ˆ ›ß‘ŠÝT?[sù3Òl|Éê`)ü C—Ð fïVî yî2 |pí~t(ŸÆ§º +Å<¿zä~Ó¡Çã—Žó©ü9óâ—†îµÈ/æñÅ“ù.ã5/[Nüa'€@4S1¹²ùÏ}CF:Šr|ZW-ŸoLínª‚Ú¢š†{NѰú®u¼Ï6oŽäøÑÙ¥+ð³ÀÁø˜sâ"qN&ý-nL¹2 9&ª€+˜ Ön(&T5›÷Ýû¦—qs Ž’ÁÁÎ#;úäwWøîSŸ}ìñ/1†Å°6­ g™gdJd [¹³[¹[`þAˆHe> t¦Ë¢š±ù0R.YüLAèaQÁnrÚw4 IDAT§¼äCB€Â”™hÓ__JhcæŠ6Tß’Çç1¸æÑùãË'÷žòÊÑ“V®r~ÿÒн9›_„Ù/aj{¹³¿]èâ»çÔ×<2‹cB ¡„L!”(N(YÊ >Õ¸¾6Õ>2&#+ˆHÞ¾d‹Ôöã»Æh^·tâÀ1ÍRT€€þ‰g†o՘옉(ÄDE0q‘è´N ®V—OLyî­]ÿbÞÉ àà¾û.z)7 ¾ý×#>v°Ï|f—Y˜žÑ¸.yñÞ•h|7“ ß§}gUî4ÐqÄtAmhÍÄ¢šó²«7Ã,û—zKég°˜ÐCZd'«ºÈp?1 ÔJ“öÛ›¹X[]ºôXçTLDT`– <¢[6}D6ÇhÁµ†×¬/.ÿÐþ›Öÿ‰1ÒŒÒÌ8ŸtÖ_O­ó ÖŸ±¼xi±XÖ¼øNËc#ç+wŠT0Q‘HÌMI·Z—¤¿C)ÕJ6_°çÌž»$JB¼¨HÇÙ´žßµó¤¨â §¿ó&vÙsx¼¿”ïk»xK¿ zä~õqÁzü–Û/“Ýãñ뉜ßqÅÌîïÀØÁ(=âjÓß¼0Ì ~ÀÃ'~Iæ‹@qE°æ­RÝ“@|Î EåîC[B–h12ÌûÜ»i(]MÆÈïž qtúþ䉤âŧJñš"ÉX6=‚_Û|a|«y ,ˆˆW–S|îù;Ù©•£æ†¾—·e/ ùeÜøù¨—?ÿó­>ôÐ^ÒìÌ£ÃËYß¾¢xÏY¾Ó XÜÙ”“{Ö¡cž¡Œ™âTÊ}ž)e™eô)B ËêºlŽ éçË2ùó(–ßÿå?þ“­oy]3j©¹|ùÈUqCÊgÿÝåÿC¿g#€›Q–¹cMà:¸!ß–®²e´ àfˆ°axÀM冰fpx{Ù×£µ“aò•Ù´Þª‹ÈôÃí C0„Q¹Í# â½d[‘çW5w¿"îÜ’e! Ò6’JTãzÌö‰j6?¸6J\”8Ù ²ŸÁ[ð¶ý¯O¨w«âÛâv)iǤAl¯>ð¿°.‹ªûK`P;Ü^᜺HÔITüVÄ9‹ªí9½¯ñ©˜˜€_Øõ\£åý.Ï ùð#‡|ïɵçßõé_üßa ‰461H°$9bXÖ†uÈΉFp‘HµØC›U@¢rØû³«7Ü/ #ªu‰+J‘˜ÞRXVÔé¤ìso¾ ã¿vܹvá1ÿùÏå÷óKò¤½ÀU!÷^åþ6ÐÛêì!‡[r7ÆöTæžnÕ?Òø€O}}·>ºÁ$ǸÝ$¶T6Ã,/é(Qœ¢ïCð^˜Á2Ùlè"Ÿ IîÞš‡Ž«éŠò×V—*áM †ðøÿ[5 +?úãî´"¬­SP`ÇlA&“¾.)7VçиÀ}në?ÈŸ%°ÿe׺^ y1<ø¡7² Ïüàãj?ñs_Îf…i{aˆl-M¬½ª´ôø¼DÜè tMA™`–vÐlXPØ÷´ù–†öº;>Æ Õé³<·êÎ.ÌÏ¥uyó»Šc§àâ…ÇüÂ/îþÚ“ÿðê®ÒEÐ#÷ £Çã=\­úG*³OUfŸjõ})¼dA¼GvP Ê)ÛÅýF²ºþvTDJ ‘¬-–‰eŒëk­upû÷­xp“t€—qæ`»}ø¤š.¯nÖ–Z»ä½š„^<á=}÷ê¿Ùì?uVìjÝ/FIM@›‰· 08÷ê[^Ú¯lùûÒ­ëÿýî·~í¦íóOþÄO}‰&ÙlNë9¿ƒ†Ž¦¡TZÑ<1J µÑq_áÔŽHŸ Ç_yß÷ÿð•?Ј¢ªò•/ʪ{‡ä™EjE0Ùüh§bõì3ènÍÏüð Ôp×!Ev3lßõßì‘{ϲÒÃeBýÏš•2í8¡u«y¯‡’ÇËasÿöü‘~mì«A‚¯Eq£¢Å;Æ 3Cðù@lt©“a³Äv ^ƒ“@xŠ  GFð¾¢kî¾,èÀ™ Ð÷Þä6!7¥Û„…KïmùÛ*ìßîþÊ,ÕÛÆ]w?—¥xõ•ûŸ|ä#_Jg 6G—Ö»?'­t¬R[NAãä0- nÔêia”ì.¢ öïùVбï¿üj©DðôkP¶5‡÷?=ôÀÃÍ«5áùá{¾˜ÍÌK1Eͮ⠋8QN‰ƒb1h•¹™RÔ×FŒ$άë`ÿî§U2/˜M”èתñPKgf%ÒlI¥v1¿8¿ó¶­Àöý¤á¥=¼ãºyd×ïÚʽqfï©}G.å‘=ïá²Ñì¬:ó­êÌ·²R¿\+‚) „ œÀ "P+«ï*ÀHXéÜ ñ¾/˜7†¬i&Q¤L8¡ïÏþÃ'—O}^M—”îL4sˆ[†àƒ0qšðð”ÐØ H^ ͯ½t*M«?óÐn±à?ìþd² @Îì¯,;¥n¾åBºû%ã77Z»›±¿±ç›We%ç±~ã·Òì[ø¿òƒ[¾@“t02ip'.¸â¦:Ñ‚âQ›á§«²é˜fÒGDxýéß“d3£Ô,T<‚¢@2€±œòßa¹"í–ŠjR놓å2,Hêó±ógx0Ò@†­+AÃÎ#|õ HŒ_ײïr?¿oÔo¹à#{<ÞÃU‡ÐøRf píH¨¥(à@B,9ÊöÊb§®p[C¸<ŠÆ•¹dëƒebâmÙôá%c¿ƒ¸&$jýÿߥô©^ÅlÈ+²BâF¸!/QZùØÇ>†ëâ†|[è¹!ßnf7$€ùlÈÚô_ØZësÍØµcI]€÷â2/ÞKðíQcAëóêJ@O[æƒ÷>ó×"W‹üÌ+Q÷ÕîŒÛIÒ.9¯”Ü4n!3Ç+j·äfz8ÆÛÓ,ë4ÜÉÜoÁÂÐÖZßÔÜÜrHD8Šƒ$„þâ}|¾R`ón!ç7T!×ÜLŠ˜.¾ š•Ž¿ºïû‹—ëÒÝÃ#ÿ‘¬LŒÿ|~ó¾•_í–ê•«äóÁS¼8È`‘È☈æ”Ð,m›wýhå„- IÀÊêÊ„œ--¹Ê†;îêÐ%%·b¨Dµ4§rY´ƒÊ3]BïJêäü™Ew±àú—Ç— æ¦ñâ‹pµÜ7uå~e–•ìê¾™zx34>Y›þÚxãõeåmfpô (%¯Ü xÖnfñeÞ†¢èL>È9XÌíÒ¨\¯­ŽÚmͪâÅÃfˆÀ RhÚÜ¿¢âcИNj‘ĸޱÁß@÷óº2û=íúƒÿç®çßÄg·^­àw7<4ÿðüøŸUøN›×úÿscæïˆâEÜ­Ý"=göEŒ/C]6G7%,™”JÔúbÖÐ×'‡„µ7¬¬FFgˆ YßàßB¹j^"\͇VZQ•j“§Îáñó™}QñÎâÌþ2€zÿ\‹Õ~3Ü,äÞÛêìá !ƒ³¬”º|£±XA«B\<ÎÎ0°Hµí²8èÜ´PÔÛPêýkµEצúÈ眞ÿˆÁÒ•Æc¢q`DÔ£;3ˆ˜îÏÒ,MÓ,í˜]xeöI@ó®«ÅøÒ®/çŸÛú®ÑÊ|~Ý}ÿÝ¡?¾”Gæ³,êý¿ Öÿ‡µäŒK$—\ IÝÉY¯e‹‹t¢ûå ¿¹èýýc6šÄÜԷчFdˆ 1°ü^G–c$´áà%—Xk™Í–0­›Íž£½œCâç)¿¸À™ÿ À¥ î¸*¸äÞãñÞ}˜ü™zãK§gÞXRß8ƒ¶cPVøœ¡ÅŒ=.ð{÷ ÏÙ™×D@Þ¦¶¦, 9³K ,wÈtF J²¢ºáöxŸ÷Yš¦©ÏÚYH}0oF…«F¹U³:óŵú¾Èûÿü®ÿkþøW¶üýk±DÇ|ÊÊVX±å€”°oZE°ugíšæ›¨+´uœõQyVÁ,â÷N1|CLúk} úbŸD !4†ÖßÌ”„‹sí%´¡.É4AËH£J]Зe3]Iý­˜='à Ý`wÃ# ÔNý­ë°¤WJîwCöx¼‡÷NÖ>;2÷ÅÉÙ݃Õ-Œ Ò‰BI°;‘SŠQ (Ž©„¡¯ÞÐ5-yÌuvC€˜!¤Ë˜g±ƒ¬¬~Ÿ8@™¤7ófÁÌ„`! ¢j¢ :ó×€¶ú¹ô ùÝÝ¿7ük[þöUXš³q)©‡'w¯ðS¯ï¼1.*¸m]Wa_¹ˆÖó9àg•êèNÇç@[øZ#UøQ‡)™qÜtç'#•‰ÙÉV•teafôMh y”ª¾qÆy›“~ ªh{îtñ)ЉãÀ‚Û±ûßg^ÿåŵzÍ×€ÊÑÃ?}ñpÕ*÷÷ÐÃXý—Ff¾09·k º•JH&BQRãIKp–™NÙ싪K‡t.h'FH‚Ë;•$¤Cùð !!ùPç|ôÕ‡Ì{ Þ|°`!¯Ù£RìÊâ*Mƒâšý]öýöî?Ê~có߸Œ§“&`{ú€©©ƒyäù)+.ÞuLj“G Š]GyÇGt^[_Tª/T댬{µ ¿¿÷ì¹S÷6nþPÛ÷Æåä;^.“ÜSycÉ›n‚÷x¼‡÷š~YÅg‰7g§M¢0 ÚB©^¸(‘5^P!½¦*¾b^LÌPæÈç{*Èè¨eÁrÆóÞ Œª‘”+‹Õ×höÿÄU¹®óÆŸÌÿææOŸÿ†¬K±(9Å^—`ûñû¿<¿kz÷­"k°ýYC’° ´p¬PZŠýÌ…c,:¦¨hZÒ(BÇG¡5›¬»ç’¿Ž Ž&n€Æ™W“êm3“K— ¤:À£º4hi¢3«ŽËªå‘Œcg3»|â[Ÿ=ÿrn¿ó ²ãµÏ¿ë|\-7ä[[!ß²$ÿÐ}¼1¹éJÞǵÀuˆ|[è¹!ßÞ)nÈÅX2ö;q×ëk£–smÕ–3„4îði=t¿ÃK®¹gÍçÁLµT­ÎÉܦ8"h >ªúz(œŽq…!Jˆˆ#z4˲4KÓ,M³, !se¸´¢ÎÍu’mO^‹k_¬«|ᑥ̧²RQ¨¹9"´Ï|þífC~ò¡/»¦ëƒ¯<wŽž¥´œMâóE:çù-‹+Õ¤“$.‹AÛ÷Â_Ürç'ÔA# ö0D I¥ñØ&'–Z ´Mddý!-QKbAµ³\Ô¢£yýþõ¯\ØztÏû_ðÒ ÷\ú _7äY•ûåI+µ|òâ™ì¡‡w6ú'~?óš¹%Y:95±g`pƒ—¥QÜšu~*Dýn޾FpÞêÅzãÂf§e€0Â(ÆÒQ“®ŒKJáíË&)îxÈ«õÌ32ªÆ®LWQÅ4"½ZÌ~q‰ü—Ÿüòƒÿðèÿ(œ÷È Ëoºw‰ÈFˆÑöƒÄËûâε Ùù…y®QÃï e¸’¸2\YšX²º”RR7}àã™=éEâ\çèÄw,)Ì–¡-,š° 4˜AKÒ™ê×D´,š€¥4Ln®4,›8µô©'o½à…|ð¡]@åG?|ë0÷«Žè- ½'­ôÐÃ9X»ì+ò™||5¨]匷eSMM¬FÓŒâxÍ$öMAÏÌÕâÛc"Qmé-^ëœhðT}¡D(¼«#/=»;„,æª"ñid–ëìæƒ誱+KŸ†¨8MF–áí7¢^É€·_Û»pòw7¼Å · öƼífP0û…jöÅ" írzþ»59¢b”`q'F4² ÄAĘžYÖ_;#ÀìÜûã )þVÚ$ó NùÆ8[¤sª\­Xµ>úÈ>…ˆˆNEE4M[@ù{O­»ÂÕ¸<œU¹÷x¼‡ÞËfÿ·æL±Ç¢Z_ ‚’’ÍyèF¦{üÌŽiÙÖWmXˆ# b© ¦…(4šþŒ ‚Dš,Ëf÷yAÃ-hÈÉj®º(Tù|> P³úBÎìfŒ*‘«ˆºSP•HK£#®¢8s±«¸¼™Ë—ˆ_ÙÿÃùãϯ»œI~ñ½à'ørNî4ÈÃÖÏ#tžµkZêê0eÉÛ§Fàúf…Q±4kÄ-1ºs™]ÈjŸž^j``F-‰– %HI^~®Mn¿}|íºiU™kœ•ºy£h=Gtå„þŽË†ì¡‡Ë@múë€Uø†u  ’Qˆ ˆŸó\Ô¨Ï "›4Ú#ØÙîÜ].M Ìu×’†ƒVDÉhH¶úÛ›Xâ–¤Ù4¥dñ¸<éŽýD×"Б|5³`³Ë 8I†WEUhE^0·\SK|îàó—ýÜo>û»çK0˜ €àÏas½+a¡Z/‰–áÊÒ9µY:%Ò]Ë| $2†TÄ TAÁÈÊCã'×Ô§úû§gf¤N™…°hIÂ:ùŒ¨ñ}ïÿË—_ø€;–çs _}uèj­Û•ãféPí¡‡›ÕÙï€Lº]†–ÝMÅJèdE( © ªNÕEQäÔÅEÿRÝuliÇ¡Ê#ÚéÌâU’R•ê©Ûöºõc®´¿:·Á"‹ÊPómøÎDT&éÛ¯C$®mÉ߉‚ŒÞÌÕbWáqRZ¾VÊ6yzÀÔ©· ôkÁã×O¼ôÙZßÕÊ[—ZÈÎRÕ‹äBqU¸®ÂžWîšnÈ&i­¹ñ(PLT5ëDZvQ%°Í¾EŒÐØ/E‡™JDˆB¥€¤ CîÌu¡…:}ûkÃî~߀W^¼A«uzäÞCg¡<÷CAwÄ"4~ª~ô‹i(ƒ)ƒAÌHSP]©“(vª‘‹"uNãÀ=éäKÙÌ qýý fí¤jâsi /3$hÇsòöe3wx%”. Ù©¤rºÓ΋wß|¨o³Î0¯ÙÓö¬«¨Æ*<•ddÍŽýV/;÷ŠÞ)<þ¦kt–„lâ|ëºÆÐ²¸RQ°ë¼ÓÙ¼hÎÏ_bñ¼ü:P BZÄÿ° €ùlsJ®‡i9Sü‰J€0"t¨€wÞö—¯íüÔü; Á_•+¾*nÈw3¹o?ß±&»cMvS¹!wŸ¨nmnmÞlnÈý§6 Oož¾©Ü‡§–®œX38qÜ寳qç`>Y“Ÿiö-Ðby×ïfA áÅ ¦NÅE€F.ŽÕ9EŪ‘‹œºÈ÷+ȤÿžtæÅlöyPWšªÚZóf §wX‹bE‹§·G^—„­8IˆRù¤o;f)D²™×™YH—û,‹ª‘+;áQQ)®ß±¯ÀàšGËɃ• §^ß(LsÀìý—1);ÿÚôƒ}¿ôÀ-_è’;5.„—\ŠÑr.ËHÂÛÒ™~DÌZ ‡j·¾ cÓi-±j;jHDv@ˆH yh{AU‚€‰)AR‚X¾¿jgÙB¸‰ò ßÍäÞC—ŸÜf µé¿l | €{þ·2:©F‘sÎEEÎE‘hœi©‹\ù¥,wO˜ïJg\¹+©œNíwfCÖjt–žò -ÎÉ]ŠðÞr»“jh̘ã°*MoUT¬qˆ$ÜÉ8–¨¶ÁÒÝâôôÒ‚ ƒknàš]k_¡ž=òËî[ùùçO|î‚ûéOo÷ qy¢Ã}lÂ2ÐI7}½Ò(ÔA#ø6E`4`dÉÁ”CRüå „Ò™‚(¢¤$o•m“Ķõñú¢«ëjUîW=ríÚíÚ*³ß?YŸú*Æ_Á€ˆ¢5»8v.Ê‹ô8REp‘s‘ºH£(¬ˆBÉÓ©Š_HúÞŸÎ> ;áо+^!3RêT³¾Ð9±uέ¬oûõÉ •ål”“(CRkHÒ¨Å/y·‚éVmɲÌŇĉµ_3$“ËþÉ[°ëúÀï©¶*•ås3Å™ç_˜Ù~ø³³¥úÊÝ6 ó GAé@ÞÛ @„½‹hD‹ E`ÄÅ ÚÂz·r„"Ê¢ëL N€¡ ,*Þ{äÞC75Z}›?®O}•úÊ ÚÎçÙ»æŠ(vÎEââœÙ³c—HŠtã“êû}gÖwf¬3e¡WGÛ‡d¶$kÂ!$LÒ¬¶k3g*â;-¥–³•*}±Êû’*3l W q§r{ž v%é17=вý–u•Ä¥8)ÅQGI—žýѶü®G;n,`z&Y¶òhÓƒ–Á²"…³Ëæ Ó®3…A‹¦4‚Fð¨§E"õ¥27¸dnj²…2™„hÞ^Lšˆ’€„ÎÂ{õ7“,ã>ðø}å¯rr£CÂÉ›¬Ouù€Ÿv7úœ…á¾ ÀÄÜM´cIµ`²Y¾Ñoä, ”[¦Û7f‹"-oI—>ìö>+RT*"* ™³hÖ»©LNGëV†ºä², –µ,d ™ù”>¥ïX脦H×’8,kÕOˆƒK”æJC'\ˆ\t,eK Í»0éJ —ÕÔ:ji¤5&“¤_î*W“(;˜¸´ÏDé!_Z ªn¦êfšá&Ú;P–I,y[ÏJ;÷tÚ÷5f· îq.r.rùnµ‹Ö­ŸÙ°±që­íb8‰¤"òõ¿xÿÆÍÏ…–„6B"pNÔ‰s¢}Ò£„ÿç?íÝ{`æ¶[—[Ò²†Âà˜g 7ç+åT2´‘H×9)…˜²üÛÀØìøÔì™SÓ§Ä~D÷ €é©¥}ýcý§f¦ÏÛ×~›pþ€-¿ìWèUî=ôðÖH’عØ5j.rÅÎÅÍøˆäá‚æÁ—–VÜV¸l0¯Ìts¥÷!Æý¾},4¦¢R}`vÛÄÐ.‰£LË3æS³ØÅLAˆÐìÄaiT¶Ì €Aà sÌQ¥ÕQÇàUn£3mì ¿óûPŒÅïF®Ô5уç¦)|è#EgÍwžX à‘íÏoš‡yæ•»KÙdò Õîm’ßxêPµ^«VÊ[×µ-À2Z€zX Á ˜y e:‘³»9ÕšÎ?+T‹¸Þ_âýÊ 3zUÞÇM‹Ü'“'ˆÝ<È}2y‚ØM…Ü'“'ˆÝ<È}2y‚ØÂÔûþq’ÄIìâ8Jœ‹#rÊ›n­mØR[;’»¯¡®¤QÙŪ.®¹¸æ’ú|â éAÃá»ö7WiZ „uK qš¸–cæé@nLã%X"itl%Ãíd½‚l;q”ÿ´ã¸TKÛªÕ-êzW™û§·ß¸¥zSä>™×=rï὎KìÎÅ­ù`rÊEqc‹Â(Z E¢d>d<"šÊóaÄå#ï A¦Ä`ÃK­4Ô¾ IDATß½S(kÎ'­’&IÉtzi†qHˆÚgÒÊ–Vi™V5i ­ðAIuA5Äp„3x2b_¼u(nïL(7ž#¤S»œt—wöÕòÊÝü­ŸÕÄ@øóŸXÿÇß<ø_ùáßù¹—J+7l8îÛ“Ìš@òá-÷Üêœ:¼ì´è2Ô’þ,Íæ8Ýl4hœ´6‰¾pBKkÖ¯Ü/J7ÔÕFûOŠãÎ}—3áäj¡Gî=¼‡p…)+Ñé?QÝ•ãBQ€§m—B†p§@1Ň`÷ çaŽD mÇ$Š–Ï¸ý ÎÞräþãƒ;Ä‚†Tƒf"‘¸Öi¿H€„¤óºÒÊ@BÒJ%ÀBÕ§Bjpê#qop‡°6ZþbùÞrã9¥æ‹€tªo#Oü…Ç?èßÍoæ•;=4Â"+䂿>õÒ!ó *{åyWáàÝ«ÔÑí:fnûÀºá[¶ vMNU .ÑeÒf£}fr¢"å¼þïËNˆQŠˆ(ó ’e+Õ‚mëþËëî­JÜ{x—âZ¤eŌ⠤b”qîè2ÞU8¡),*ø|‚$LQA$8|}/fT´.Š%‡n}gxŸPÒÈ%õX«Íò¨KEÐAèC@Hfiùn! cšTL9ñQdžˆÑ®Ý‚ÜQj¾ÑNåÎËXÌ›"gí>õÃ_úàÖ/曜 ‰@¶g¯y ^4€ªy€C*Ó;F5&ËWŸ9½^#Zš§ ! ³Ç¦§mªZ//[1hc@cŸII„gškÁÃ$CX¥AuÐa؄زúOwýÙù÷6;÷dÐß÷™k½,WÜ{Ù=Ü(\·Ô×–ýÌûǾ‘ 0„jîX#"gv-fÌ;wÆ¡„#D™"ßxÃÛc”ÃÚ3‡ãʽηKÓ÷¶*¯*¡&TÆ•)Á(y[d÷Å ý8´dª„±B| :jH«eÉ>”íúÁÐVê½jS_…8—õÉš´rE#~n6dYçœ3ôgo¥NÞ°¸0žÿͨ„ÆdKÅbøÉ#¯3°óÙ#w<´bè–u§O`! M˜”¨âüžÀnµ.ͳÜ!ù×9]ˆ&6æ‘?Ý;ö³³s߀$)]ŸeéUî=¼“pcÓkDˆ Õz¯(Ü*¼Ï¨D>à³èpg·Ÿ‘N|ä-v^­aKµì²lç@â¸z°ø ¨vḭ̂=)$„Δ‹M@‚òãlÈäLNè9GåoŠVF³WÔÜæ“{ŽÔ‡(p>Y8—¸ËrcñÔ“kúðÞ‡>¼÷‡ß/†}ZÆÅ½©‘îߊl/ç“ @À¼„Ë`íéhˆ'~ÄÛç-úêŸv©ÿ†¯Ð ùî'÷^|ØÛÂM6OåS‡±ymßrÙ?u¡‡]ÏÔÈq^¶ÆË ·š÷@‘@EAîb }äƒ ¦>¸b›ž9 @˜âúûóÖKM;…Ä"ãØ«Ô "Õp¼éF¡B”ù³@˜·Å§ÃV:M 8ã_¨Âu» ìhmá/±S}?à}×m­ÎǕć]çïôPeº00F„¹S¨JP4Î?ˆóâ]ò¨ŸU·~/ªñäÎû‡7®Þp×’ý¯NZª ahÉQΕ‘KíŠÓ3kÄQNUäÌngºAóÀÑñ#î\Ù>8óNuÑ÷ïÿá«»çàÝOî=¼#p%Þ®'~°âá‡?ˆŠ1Сæ¤œ1ò> ! >ò¦~fî€u\ÞÔ$D Veñ¬¼÷h]ro·®í‚ôȽ‡kˆ.‘_ |gå}¿†\¢P‚OBp…{…‰¹×™µl8ÂQq%I6‡Ì“ i6›Ì+JmÀârÄ2JsȨ³Aª£Ëú ©½;h p#÷†ñ—Êš;ìdÈõÝb H¸ÁËtݱóµoÝòä­[žüþ÷¿DÏâ_hÓ­sDb˜@¦ð‡àÖ·6FbˆåY'dÀb´GFÿK«Õ,•à3?3÷—ýõ_¿v Ò#÷®Þ•<~AÔ§¾ê­÷…(„(+Jõ8Œµ^·Ô˜Ff°ñþÏ8ý;ÌÐŒ40f4 ä³<»é3$ˆ(`Rr–1d$Å2fš™)™ D†ˆüËB7¡\T:«X>)vñ¯½Ñët¦íùãS¿öçg¤eªø©û£¼r§ ï #èVŠsBÆ\ä¢)œ3yån1Ê#_Áìú¦ÎÆ|B—hWÑâG«€(“ÇÇCƒ¸·Î`GÐü@á" â8Î%£±SÿÀȲߺ«q5ɽç†|à½Ããç£6ýW”è•¿ðàÃÍïìÌ'UÓÅLídí—æOÉý4ÍÍ yp·™U˜Îî5?[¿ÖT”Tó!X–•p8óÞ‰zMUE¨J67z—m÷cÛ£ÑÛÛkX9´ÛÙt:½e892œ9Þ\#™®)øÄèê?«×K$[-$ZÈRßzÁ«ƒspW¸Õù–]ÖQYÁÏïÝßÌÌ´`væÊØ‚ GW.?qâÔîûû@¹ÿž²y>4˽UЈNÞö¾ƒ.A«µˆF–ýëk´½Ê½‡·À;e«óú€²ð¿LuæÛ/Âj¥~К¾Üèÿô…ž¡y -Ðh ‰ ½» šômôûtG‡rimè-x˼&!8ŸW#*îD$ó¹êZëX=Œ|cÉC×c-nJt:­jµ °^ýÍû—ÿº‹¨1Ÿ|=ë.5žø\W£:|ä1ýÁwíǯ„¼ÏuG4BËÄÒ¢r±tf¥Rò#ËÛÇ•aÐ\dÏ*¥„ÌÌÌ ddåÈhm’ÁŽb×í.ýÃÁ‘#ëÖo8~çÛ¶oßsñ˹7ä{‚Ü{nÈKDÎã/Æm‡VÆØ¹ïÌvCxüzS}3TgŸ€8"®Ì>)Ý!§ª?7Z;Ù4xJI§Ìƒ0£‰Y irÆeC¹[¦+»“Ulˆ5°_Ó¸S:Ba\a0Ò¬…,ËÃU˜ˆ œÜ¸'ìÝYŽ6«‚“›hÐù¸vnÈgNýjí‹Z 0jûÇnr–w1¾÷J@ÞZ@üè)Sjp+ȓ݌1,rÎ8·¦ZŸi‡>]¥ØD…(¤žw cp°ÙŠáѾ ó8zd¸0¹CV.FÄayülh QOï£dùð-!Ç{‚Ü{8ïeiåòP™ý^”žÌJkÑMïnö?~‰Ï% fSš£Q)LRP¼ 2À-£©Î¾’fv2*/wæLœ'Cæ5¯ÑEªx”"’e©Š8Å‘e¿VKðA¥<  Õ4É}Ý=U~ô}ª15¦Žà‡OÃ9"¡_HƒHW|§ª=²adèðʇZÓd Y&gv– ŽžŽ:ÍàŽ¯^³'tÄ::(â!Hk«ºBÉ©–Ã|pè5BÜßý¸ /L\s#^n6DÙqŠhõ=|éÏê&DÒ`È™¦P7¿• @„š7®Yf·KKŽ×^ôÙ8$ÕòRGÕúàí,ŠW8¬–èh&o ]÷ï]T«5g&UÚÂPø -È<Ëç=M~H˜w¸aàt^m‚IHÅ3g·¬{&¥1*… ƒü[7AÁŠ‘ñ8n„ô¤¥by2e˜@ R"hÌ¢õ©t'àGìÕ›^zzïo_£uè‘û» ½zü¡Üx–й¡¿õvŸXØaÌȃY€i—ܹÀï¤@„9¹C57bwŽÅ¯ ÄÙDŒ4+¯‚JH:!óyA¬À|¦ïòé;—„£G~úÖÍOܺù‰½ñ[®ÿ—¡ 1…»,QØt9±°û!u)xFŠQM ebžôFÃñcëG–]µll}Q¹W‘—ú¬X~"Ž›”qKÅRa&–‰¥"J(4Kh1EٷꮉqÛþÊË+ïu"xäöþäŽÿé*^~ãÄÓùAÜß©èñøõD»öÞþà‹5ƒÖœ2Ð`>7Ì$ÇCºJ@´¿  ¶ê®œ¬óà)(%¢[m›[>=wò†VÌctLË#"Ró©Ï½”*0¿4©œ=6±ö³óú{Ó0“£Z©å®JQX[dÞí¾¸§ @î€)´À Ó  ¹ó݃Áˆ-Ø«œgö‘åÇ¢¸ =¦Ä2a¾›„FЄ®DMèbÖFo?0–í|íÕßïò*^ßûO¿ýâï\ÆeÎóøq•ɽ熼FèYVÞ‘(ÔyÒ @Xptž¦ *êš_ͽêc¯Õp¯ëŽoVÀAhâ¨ÕVÕ/NÛQ:3AƲò2(E5tBèxQ(Ä%”ž$ÓE¥R˜rr…µ± Ï̳ü)Èð|ñžƒ€h?8Sˆ34³`¬ºå™c]µgtåžñS›r=‚åËŽDQSôŒy±¼`ÏS¼æœžÐ%ÔR^¹{ƒ‹ÙõÈSŸxðW¿ñÌÿ|‘+º8/Fmô#ùA¯r¿éÐãñw h¹{šÌ;™MitP—pª­¹­ypç)¼¼÷kw¯NŽJsF Sºl…+¯µN°É#±G1R]1Q iðihL×Ë›ÖWfŸjõ}ô_öM€r—ÜŸzî·yàŸwù]ºúÌËG‹Š÷®8€Z40`¼ äDD€!°lé¡(j¨›6/ ȧr[& Šj½DM¨%j‰Õ‘ÍNú=»v}üƒZ”í¹S^­ÇO¹_ùú÷7Û‹©¼Ô¢ÙZ„†ï;ç2çyü‚x¯ûÍé†|öÙݽudm¯ì»ø#¯3•ßTÙó¸ÜĉƊÑÚÉÑÚÉ‹ÏçcH‚Fó ¤Óîžjhí{CœT×m¹Lî8Í'ñâj|@ YbÎ(:åWãú×jPBWÙä19cÂŽöAĬ îMÞàÍ…kí†P­Vkµìã?±ï[½q¾x…µü‘9ˇ Ñ!v7G$ë0.!¿©U1˜tDó°zÝŽýàèº]ý¯OLn9L¹ió`@hS¡/¾åe»&t \B×Ù ö½±ÛEp1¡…[“ï¡K¶æ/Vž'}kñ6­îïr¿áèIäïAûùaü¯h4ZÞÊhÚ}©lÜ"óñðP@òbÞåün0sÁr'¦)^·ÊsÏ@D¡iíhå¹gÊsÏ´ëÞ +¾YðW¹òŸ8àñÇvû‰ÿøcÿl¿ËÙþHrªXR ›3È<¼Gè3Ä€žUªœ›ó.’¡Á>Cµ6n^Ä, P B×]BW‚K7І'žÚëûÙ"·"Âa˜zpQÂ9%yÒÞq‹Ð#÷«·Åãå&€Á5ïªÉ8=Ì£Hòb ½<Í1¸"½W «Œ+äÎ1î<*/®Ã’+3æõy}¸†¯ŸÿÊ9ƒ—?.7ž§h>néR¼_|ãk<þØîGÙ^®<ÚÑ'Dó`^Ú‚?²`y%ét,ÏëµüÜ´'P-´¥\­¬Ù:µi攎eí¬ƒ8 E\^³‹ [³Ó%ŒüZ8¾ƒQ-olZøÃa1ɇ¾^\fy»è‘û¡W÷pq´usÂ×iF†`N‚8)Ô>[SÚËæ­¬íΓÀ±wŸÀöÃòü&{0ŸA*Ê›:Ûµ”Ï(5_´]û@©ùB©ùBw4Ç{ŸüÔqZ Í `0Ò×j„"‰?šéS õ{ád¡¿Ïu¦-¨÷ƒ•Üÿ®“cVIHƒ:NLšÆz¹VOHQç;)àŕɣ˜•¤¸˜®DW¢–%·@ŒräûOB>òhà,üaÐç´. 0/ÙôkWÑö~õÉý]l˜émuöðö¡iùvνAó4cp20j©½ IÈ[U‹:P` ï9&¯pÏm)åÉÁ{ó¹ÈЮ݇‚ÜQj½e'}¼*¿ë½ì†œÇ\Ë€þjåŒÃC{Ñç+~Ž?Ò‘ý}21ÁÉÓ\Z×ÕÅ':=Å<¸^—¼kD©¦ÍI6[¬ÕaDAŠFt}Ô5A„U„Çž}†ãã ð½'h†Ð¤„ bÆÿ6¡±•ý›üÃéÊÅz•û…Ñãñ® Š¢›,ÏuZ0 ´ò,°ùŸõ¼÷°¼º_¼%Ìa{ëÞ¤Nõ¥ÖkY²6N¢õZ§rçµ»®› }äFO=¹ùœóŸú»¿Jæá›yH±{\í¥ ø#g!5†D ,í†#¬RNÏŒSÃ+”{¬Ù´*5Ѐ4Z+P „1ÄQbH Fj@‚#+a3°%4Ò¤+VÌYí¶Æ¸ò4à¹÷¤•®)@sÓëžÿ-X(-#M9]g©Ø\…ºÜ“7Ç8ÈS+¨Oýé8^¶‹F=¿º„.iå½²‹sß}/4æðüógÉP?ù3ÿ@6+yð Oúààìn wšË\u×9þH!ƒóÆ$fi›YÃ2¸TH9±÷péˆë̈äðtD¤  pÈ$†Ðå„QN¸ÿ!—7‡“óÛ§‹Õöqæ¹S¿`®ùïÔ«ÿ"¿œËˆ”Ïþ»¯Ò"/àžnFYfÓÀnÏ¿vì-y=y|ËhÀÍ“ 9 ÃÓn*7$€5ƒn67$€ÑÚIç¸!«3Ohö?ÀžøÍÁ5±:•™Z©”œÜöß\ükS_Ðüi•Ù'´úy»ïj89à&”ed?€+tCnÝö$€]¯?²øäcý³n!Œ¢(F—ß) "½M$µàcÝcm±6 ]Ìãg@`å*5 9'660¨ÛwÔA°lù*(C³IˆÔj"& ¢ â*#¥è”I(r/”~SBÏéÞüÿÏÞ›GÉq]gžß½ïEdVfí ö ÀMWÁ$AŠ„(ÊVËnKnÝ}Ú:=>ãa·—#Ûm·m¹[VK¶O÷ОîÓ£c÷xkk¼I¢,\@ AlÁHûZjɬ̌÷î?"2+«P {ˆßÁ)D"##² _~uß÷î#¶õÿ' ?] Ìœ‘¸_±Î}b?þéÙ'îKýxʤn,´ùÎ;lýü$ŸZh}2?°:?°ºÐòød*3W!ÃÃCõWÜýë¢!0±¾+TÑ„Ùùj\&w –œ=K÷ïÇ}2{.C%ÌàðÇ»w¿5Ø9wšy‚ó“”©XŠÇQ)PC ´)Tq<é1I$Ý8™ «ƒx"€3ºõÐ×äÿ_ ±0ü/Ïñ͹Ä}òu•lËB­ó®¿—“’2J­Š/áThùì™=½¶ÄÃÉ3W-­m=<Œãýÿ@áð+ŸyôGn˜4^M\k U“Ve=žû[›JàÜ£sThD§çòGe„ÔÇ)øxY[ÍåHAwÌw49x @ëœ.%V¥@”iPÙ,DÁ!Áª÷M&ðŠá8Æ‚?FZ—w¬ z\œ!£6T2ºé“¤¼žolP>×÷çò÷sêŒÎïŤ¤œŽD”ë—pš<ÅæG†Ö5 ­SšB3«/-ñ4ΦÙÛ0tðvà••÷<« à†¸êÓ‘´‹1K9€¯g]÷x{?çõuÊ"¾18®>^"5i±î™uÞàHoGG«ºtÐã…k︪Jp‘dAJ%ç Œ îz¼®S÷ø!”黃 °Õ ï}#¾»k>4îþä±s£.ˆ¸Ÿ¯4dYI¹ì!Fu §bÓʳ8ÁHÓŠ†á Ae_”YpO¿¬Ó'ŽßòâÐP€ÁVܲ:f$³{&¶çZ§à³]znª[» G[U0$¹æl‘â¹H‚õ?|Å3E@Q¥ÐÓ§À¬®vaç;½Óù·\ÏD¢rÈYø"É4–¤é-¯'†ÝCˆÀ°6Jltý¶Ñl{ÜŒþ¼0Uœ{YI¹"QpPÞí¹g´ÄÇ8FïÍ6ŸÇ«š‚L¾ëá°}V‡ËÌͬÏÚ~ Ã6npbÕÇ =Æ =µR]&EÑףྶN*ôdûG2 QyÄ¿ýfwÄ4T.GOðöõö)`AMM9ôÉÛ;IuÁm×G…f›U"ŽŽwrxh¬ W+0VmC"èd• ØêÚWG›¹/½ûM qËæ É9‹ÀÌ%÷TÇS®"E9×ÿh¥3ï&?•)~%×ìO]›p:~÷8Öó5DX·«lÌ`ÆŸN"î ÓÕ¤Q ×M04 ¡ES3©¢±½4Ôc•‰-fÌîÏŸ}ÔÝ„úØ[+²¿¼ÝÇ“Á6¾åÙÀ,_ÅëÖIãiJ€"›E4¢Ç‡ÔEèꢤ÷¾¢­ƒ¡j@>^8EÑäQ`Ëm !ÜñéÅñJ|¥C.×AGÔ‡” "j…?’Íd#×r3±uüF,è¹`9±!²…Á1±ìgŸwÖoÂÉ8KqO‡:SR®fÎba  ÇŒ ®\Îñö«ï3ˆ±y½gÆÒ{8Vs ÑwªÈ†()HqôPÜþ*˜3‡ Xñä}/ýpƒQ´a±R2(y¹Áƒ?ö@õDDûÑj!ޤ ÁPˆæö2ïQ¨®p!›5+˜-#'ÆÐñLxá… ;‹øôâ~v:žï9›kJII™:LŸãT­¥×Íl@ñà,އL7®µ;–ó1Õöxá¥æ& 9'äàà%€ 4›¢-aØB¤ÀÊÏ,wDý=„ŽNš3—Ò#=:£âÈHPBBP…8ÜN!P^¶àh¨o!‘}iíMáÍ#îçq¨ó î ™’ry1ù4äTÖñ 1uâÎsªò]'âªð#ë£BOµùMÀÌ™Õ' öíûícÖ›:g´¶u0)Htý7¬{îµëî^N0Ae” жñI!I…¥¢‚BH‚ÂjTB`èg<²r'ûüój!ª{ZAOK+))W$“”ò)¢ã'òø=¿ »Pk¤:Ö›¯_ (XéV«ø ¤µƒ1æ¹³:ÁÇGB:@„ø£`ÅË‚ó”Ï ± €Ô軃”«Ušµ ª lþËçžùƒGVîðøãûV¯>ƒRû™¦!Ç8÷TÇSR®H*C{úöœú°)«ã²z㮺ïWü'u³RëdzÝ6ðÀíªôÚñ€hÝ~…ÄàP •9%gCJl»‚û@3šBŸ äAÚ9ƒ÷I?Ú;ƒTýˆ€xò…ÑB¼„D¡r>þÕV Àãïû쇜:$sÖØ«MЧæJÙ;箟Y¼~fqª¥!Ó•²Ïˆ“­”}1™Ð7·OÐ4ø’Kù¹¯”í#‰6\±ö ðàíì£ {­¡Xmma<P(uT¼3M]Ž4™ëÂþ Áœ1Q\…á2@ 5!T™L«â8%ç% ©Ò@ª­ñFÌêÕóV<øÑÙÝød˜*3TSRRΔɗÈæùò™Ë¯Á©y~ó'–jI IDAT®¼ý«ãê*kßð¼Íø NÐñİ£*Ú(gÀr¨¥h¶‹¼’«Ð‰6QÒ‰€T‰ÀQÈJ4ìAèÈ4 rE3ŠlŠ—`/RÉI¹Z¥ ±b鯭ÛòMë^^¼ôî7—Þý愳RÏ‘TÜSR.Îy¨sÿy¼˜)ÅšíßzðS¿6ª× çÀWÆê¸&"cÉe«&šÎ"]¨âD=™öã#݈—DM~/ >¤,˜1-Y ƒØàJP‚22!ÅKªP x ªï¤µëG~˜$.¾‡tÿm¿þÊ›ÿ@¡X¸@ïL*î))S‹Ë.²rÉyù½oÞ¿ø×c_óŽèáOY‰Fë鵊<嚦‡à@)T]Oê\™(=Ù@¬ N*únTa‰ˆmÜ”@‰=ÉR6D2ÁU •äå|…¬µ È q+Æë{ÕËó4Å1­»Ì: OHª?#…±ëž®FqOI9œèÇ+Óà L¥üq䨝2å§u}íõÞÑ÷Þø4*î‰Ä‡ü¨jD úJy» ³:@FH«¡ÑšéV‚êqo›]>føÚGl`ÚÆ+»B7n€ü°z¯ÞÃ{ˆ÷ê…D /(¹¯+œªW-66~£1÷çëmIÅ=%e²œQô0Ï—,ê~Uq´ûßèìêÈæþ4×Ïåòö? `Ãߨ?ì‘•;܈«ˆ#Shj=\òÛ$" Q\EGò•Opª% ¸ £Kv2TJ)“¨ö€šŽj•†tëkzôº¦r áêÒ}ª jý'Þ¨BžGeG*î))r.CaþÈù¾œ”“2½ë÷ž®íY|Ýš\C¾¡!·iÓ-|¯ˆ/¯»õ¡‡7C­8hDâH#ל{­,£” ®BÏ®2jæekTà+ÄFa0Ðc`3Mò‚i3±üŠ!ðH<ÄQÝxO÷ýæzgRqO¹ÚI£‡WŒ§JE”ŠèëÅâëÖÄ;|èã\.Ëú‹k®°råë­Ý%yU#–A#âŒ4)ÈԒ隸xÕêPªŒÚl±5DTS…Ž€Œ.[a^[ë_}Yï{˜2sÆÄfĨ¯ÈŸg.¬¸§iÈ”)ÅÕ¬ãW|²ÆG;“…È|èc«WÏý7²«I„øk¢æUÝPM¦+AªIÍumáÙX%ØqÊRFlÞµú‡ Ü!uâE5Qcç©Û—xå£ÑTÏ;©sO¹bI£‡W9/¯]TÿðÑGßL kÄ‘FWfF‡RG£Z³íÒ[W“k² ç…J&«PñJn„cM‡Q˜¤Ï­fÃ6ÿÒwåá/rœ¥± ÔíA\»?»›š|ò*÷4 yFLý4ä”Òñ©Ð>ì2â|¥!O@RŠ©9w¶£ÙöÑ(dÕ¶K]MF…ÈćA²‘‘F“Õjyq#¡j}@ÔyÁí2sÛØ¶)Ñ\®RqO¹|©×ñŠÉ(~Â#SKžRã±Ç>ÌsÏ-~tIu$ D$q{"˜.êÖ `Ù2’žjM¦jÞ©n‘>fØ|¸¤ ¯×Vq#DžêÍû}·ØõÛÝšWeå=¬{)¸<_uo}s‚ E*î)S—«¹Džrž¡Dë^xö[Þóë±s;¥ lZ/GaÆL€tÔ³ëèôÔêñ TÈlCͼ'úî+T-ÑF½ùˆXíÇ ŒìÖç6þá½ãTÜS¦g§ãaS/€üÌ©5I5eª±jÕ>À<ûl²ìQ­&“„ «ƒ¨¯½$P|á§hÃZ}u=–-SpOf0Q²(Èÿé³®x«‘mCö‘†õ4Vß%‚VK4ܼüvôükþѻƎþ#°¹°5¤âžrñIýxÊE&^[£†¸¤2Ãau¾”H½$C©^€‘1™d@•FÓ2˜L±ØH•lSËÃE]åš¾û’J5ü›wÔïÁsÛ.¬mÇE÷4 y•3¥†:¯r®ž4ä8ŠÅbýCMj2¨–ÐÀú-ž°r‘âÁG°æ{Xÿ†Þû)® ¹'ÕvÏ=óˆ €5/Þüè£ï?·æ¥›W>ôÕ1_&µ`Áƒ7†/½WY½Å=¶Ä®}ç[ç®SçžrÞHu 9EJ+ ;L©Þæµö˜jiH3óG\´4dæÍߣ|KRÒÍö­Pj|Üaá~§pîÁðÛ¢Æ[‚¡7DMw\  G }à‚÷†¬®Ì/áTcÕ½¿ RÑó›ÇÌíîùmù|¾©©Ù»_¸Ð×6y¸÷9#2ñÆLײԹO9 ‡_‰š:÷œúÈÔ’§ÔSî5Ù.;û»•RÆ6ÜHâlïKOLþ$±[o@WVÁ½Æ8YyvÃÄýæ·ý‹=}14Ükøµ |]'¥æÇëià÷!Óµ¬¶Šû¥dŠøñ”+€†×¾å ›²*¤ T,¬Ùk0ÜÐóAüY‡Š?=©U‹ìK|³áŠ*¸Ÿ5 „íå&”ò±ù92iÍý’sF:4ägÞx!¯(åÊ¡sûGa†¼0G h¾6«Æª|Iæ4ÿíÁ~êóCÛÜ -¶gŸ€æ öhj»(w0¥¹¦ý_ÍõâÌ“Ôñz?>Jé½S?+÷ B:Ô™r1±a'd¼U†¾,Þ*[øÆ6ãWá9§inp­i¦ “#O^àfk*øî´.Ê}\ÉLRÇq2)?s.’¸_ÁiÈTÇS.- ?ù‹(È;ò¶BÄ„ Ìó¾¢@Êʹ.õ½¥ƒfÏÿ‡ƒ#_Ì{v†ŒD €Â `6‘W_6£”ŸŽèhqï^L›à$3³›ü±ý“Ï\‡2Câ„·WõšÌÃ÷‹z¶}Üy‰ÃÝS‹ïÇ'ÏEŠBârKC^r?1 9EHÓgÄMCÞzð‡hðS’Ÿ"ç÷‘—"ïS@ Ó½—È‹2‚œ5ù ÈZ:ÃýÓ~¹> ٙ߀#ûÙ›ÆÌMA9°•ªJ"ðƒ•í ÍgnT…ò ¿·ãôklá—?›£‹–†<#r~€þ¾þIÑt<,½ uî'¥2´@áðáÓ™úñ”©Ã]žs`†É¿§… dwXr~DÀ±€Õ{ï+ oÕæçKa_îÈ_bÞƒµóPÄ*DžàžDUቚ*(D!Åù:k÷:UŽÍzt« †ßŸß¦c_øÉDñý¿_|^Î6ê-y&7-ÈŸxØÅ÷ã“çê÷‰ýxÛÌ÷¥:žr ·@©/·Nüs¸|ß:G†âvWPÒÈbjØePfà„Ø“1Î{_V6ðɘcõ§òÝ»çlà Tf8x$ú®¢¤>yèGæJvœ¨Z÷ü(rÎEQùÆ/Ƭd@&éHˆË‡aØûâÇ”´YÇç¿ø±*~ð‹Îý´õœSde sÅŠûäë*aÓ|ù™çù'&%åÜÉô¯<©ƒzR7ÒùcìÝ!rÄP33L~‡))îRrmÈä#cà=‘q¢Rjs‹¤¸Ëíû÷dü$!rLž*%QH\™ª(¤4G¡,âE4.Æ‹¨¨f>ø¿½æÞŸ-j¼ÅÞ‡ŠËßtŽ÷þävÕdý»·Àç¿ø1€Ï}a—*TéGß?ãêÍÙ•Èßq¦/4¸BÄýÜ"+Ñù½˜””s¤Ò¼4بdã^`5®ÝógÃy8QsΠO' zIú‘«ª x“˜wxïJb`š®—áݾïcÆ/¶ÿ+ˆÉdoB…áÉW•½fÞõÊ,…2 ’øy…î'VcE<@T¿á\§>=ñc»ˆ’íïÿèëzæøÜ’»UŸÿäÙgNªïSy¨óâpñÄý|¥!ÓèaÊÕ@¥åžpàUå ÒòÐèÞ¡ÿI ¢x½ è+ÑâDææß’Ÿka÷›}åí¹ùw‡•¼!ö`C,.)+ Zn.~ܲÿ›ÈÏs7R…ɳ@”|üUc‡W <=QöÌa8Qçv©¨Æ-#¥6ÎpUŠG›ŽÝ]ß÷9G…ðƒï^;á1ÿTÝ¿êóŸ<þänßÿ³£“<ÿÙéøY¤!/§NCNiç~É#+))—JË}™ãë2Çו[WÄ{›»Åk„ÈÁ;Š<Ün­ó§ŠÂî·P3ÎJÑ Ž° ±'/®¢ñàj¾ãÚ‘·L¹—†c|2ŽZ«ÉˆÏ( ¬ªrôÝR¢P…BŸ¹ÈEÎd”3¿d0ü€¨ñÖ³»ßX©üðûãeý'ßù÷«¯ÃX?þ½?€ÏýLËç~¦åŸþjüêWªŸEds™®´WÇx.¸§~<%eò”ÛÊô¿˜é±ÜöH9[1.äb} ^WBÁ h~öí NB4~tQ&ßÎö¸bã¼—‘}h®±C‡ äT¬&IöªsÚÊZêeÛdôF9qQRy84²É€†¶ˆšÎ~–Ó£}à+(woòåáÑHÏO}¹9¹£‰üø‹/à‘Ç÷=òø¾WÏ;ëW¿"¹°â>NÇ+ÓNúî§:ž’r2ÊmdûžËö=·¡ý±»±Ñ@†V"&5ÍF))‘Çë=”¤J®‰Æ‰"¨(*ä²·Å÷ÌðærÓIHµ(” l3O»DrÌ‹¨jmLgˆ3¤g²`Ó„C:Q_òLײÏ=ø@ªáŸýDî‡ë&8¡ÊDÝo¯zΛ¸§~<%åÂQj,Ûû£lœ‡7PÙÂ@¡šŒ®’"@¼/)Ïhm9%i$éÿ„™;Ÿ*¾!z[ññh*Dá] ž¶¤I'Ž?j¢ïl™Câ ™Œ;êƒÎJóÝ'^ö$#+«¾ÔÓøâê¹™.ú@¦a!€U÷m§X£bçNôØò7ž{müâ!kžŸóУzôàÚfŸñ;{år–â~v‘•° plÂcSRRNE©ã³Ùžú ³éze(ÃrÄL,ÊÏiR ¡ÜYµÞ Emª@…(´Ž£!xòðå¦[•|”ª%ã½ %Ø^uªRÍЀÀ¡á ›ÆaK•æ»Ï)zÈä„uA]ö:`ø7$!¢‡—nyiËÒq«Ðøç_õœ^ÜÏcôð î ™’r(u>éûÿøØàûÍ72„á ‚Ä¿CI™a„ó¥dl•Fõ=Þ B®ÙRq„•Ú‰I ®AããŒ7ãÇ£²®Õ{?”I™Ë}°„æi±²ç+;ÖjLS‰lã5×§àOä¡GªÒÚǘl£NBñçTÌ·oX¿}L‹›—×ÌZñðá^÷ÒÎÏË1 9FܯÚÒJ˜9#ÒÀÌqŽ™q4\;³ðñÁî÷ÛZn0$ž”ËU˜a$žÏ©ô!jÕD P"…‚¢â6ņæ-í%w­j»²x%×0ªê‰¨+°#€Ø DÞ9rž¼'ç,‡œÉª÷%¶Æ´´s0ÒXþÀäfkn6À Ž¿*ØŽìv ÇÕkÈØŠù€.|à¶ ±g¯]êœ{¼qÏÍë6¾»âçI±§ô+LÇSR.köôýÚÑÙû_úÞoi¹Á@Â(2P5¾Wl³jÕ¼Çú¨ UH4À ´×¦‘t‚œB}†rC;†ÁñG&I™D$JUk=g¬É ,S™˜\nàHƒÉ1€rÛ#µëÌô¿H¸ì¼SÛöû:*ʯ¬^Û³üæu€­+²P< ::u«ºq×õk¶îXY;î•—§ß÷À±û8öêú‰ú_5<•ý èXçžêxJÊeAOÇ/åŽ}GŽ¿ßÜzC ĺ¢È9ê7äì`[<4Z«Ì¸âDŽÐ0<(F†a÷Z?MB–†?&Åò5ï^6ÓA°ùάÛeœ·‘3¡áИ£JOdgfo²ÄY³9/€ÇŸÜ-(£2(:S•„[E« ‰’ ©’(o~­=¾‘q)›¥7¼TóìcŠèñˆpÍÊ×yúzD¯®ÊûSÍ_‚s(„óÉÐy5kSAOI¹)NûŽþµöÐÔ… âh|9˜cr3APR"k“ì5ˆÛç#†SkBÃÆ°)ekæÈ-¦À¬dÕ|áKB0‚ rƒ ¨mVáѤýX–.ëݲ©cùýÝ"üÚ+]ñÎ;??¶PLT_–9Á¹Ó¯½ÒuϽ=÷ÜÛ³qCçù|¯§Ou|eT¸ÇŠø8ž.};Þ˜*3TSRRÎòE•úK;›§]K¦ÍØ…Á,5¨ÙJ¢À7h(I¥[ÎŽÀ(eÄ•á£!¦¼÷‘ òÜ0«šœ¤\yƒÀ†*JNÉ©1±aS.fÃóGîde+ÌàŽ_ÌÄZÃO—waӦޑÎ'O}åK—õÞµ¬w|Ä…˜NðäÉŸ ìÝ{2D/ûÊûS3¾2ˆO$åO~'_²#÷””Ëüáÿ)Ñ€BeB£Ã½L.—3CÚŠ²‰˜ˆì½ƒæ¨´p³/C‚¡SŸËÁ¢RÙOr£Ï^ƒò~îßšÍLç¸/ @‘À)y˜a6\QÊs‘BH»þm‚þ?)¥]´ÙÀœVÙlÙÔñäðƒïͪí4ÃǺ=Fâ Pº'ÎDË—½òÚ¦1µ•“Ú©ÇSó'2ã'±äO÷~ûLϱÅ=MC¦¤œµyKéuR ƒ² Zi¤‘/~ì?ÐkMØØ˜Vã¹bH®pªÓ½Š>‘´º@}D>w,«Óˬژ»»ùè5ïLÖeóG£ÁYD‰]沊¨Šp@0Bª  ¶c±‡›ý+­ñ¤¦î?ú› †YDw~<¹;:z$3mZiÉÒÞ×·$é&8–檮'ïg&™P‡$%ˆ˜ÙŽñéw,éïšVô®e=LJ¤ÄÊ¤Ä J6ˆ„k_8ã^ç’†|jÑdÍ8€§œ™ŽŸ, ™:÷„4 yF¤iÈ3âŒÒ'› 4“6¨WXU…ÉÏS͸Ru$¸‘ññ'ÖÞYjh ©×"âH!WéÔæ:í.ªæQ®èˆ;d%ÏaÄVB6P¸rez´Ò‘¨ªz¯eP>L@]ï-ø•iUÂÁÿ«w¶0XA èG“]aË¦Ž»–öN›Vºsi/Û¶vp`|ÙÇ/=&-S/ˆ4º›ìxqŸ1«¤À±#YâÓø÷‡?³—kž›?É«@ö!K¨Ùã`_Îó…†$¨øL&hõ"û%œnb¥Rq?y¾ÖÃ)ˆ zí/ÍX÷Gß#ð,s‹Ó ”ÐúSmÈë""±¨‹sòwݰ–,lÞ¾¶y´D®ª¶mé@UÜÇ:ñjZ&Žìc|’ëœû­·õ« »;3ÉoÇKÏÏðÈã{]µ‡ÏÿhÁ©ꮯ@a¤À#w*ÿàèø„¤âž’rž™Øw™÷bA‰lï ^œ¨ÂU’XÖ‰#x²AXc¬5Öc}3A¶--÷m® nDÓ²Ææ3Ôí12’Û+С¦Šf„’ß÷™J׊(û.ü°±¥©‹]YdJ‹|b{•R0ßYëE”Ú¢Ì1’xñVõ:Ãܼð+óªR."B"âU8QxŠÿb‘hÈ/_újÐhmÞ®ùÁ­Ç7¶v<ôû€–QP­s̧oéÁ;o·-YÚ{Fß©WÏtÕ=±›«ÿéO-Ÿtè𭩢㒊{JÊÙ3ùnY6?@§¯¹gûžÍjX~ß›UÒ¡H©KÊ6Ah­µ±¬ÇÊÞF$ÏWÊ´.-÷oª nìR%ãÌ~¨…ñN…`Fö½*sv¸F•~°À€É+çzgh@€Âƒ0æ`¹¸7VÔLëB…–f€X Þ¸I@Ñinr‰÷ÌF¯7vgUܹ&ôZSöXä]A ^…V~n[Ðh~8çöÛz·¿Ù€cñ‘ŒKËhMaÇFÝã²ÌÍ7÷«àÝwÛWÛ¶žÁpËS+¾‚´·— ¸îÞ,p?¾õÛr~€©Ö^æd¤âž’2Y&)å'ñãGNÿÄþµHŠÎ°¥£¨A‹‰[×âÈÖÚ °qÆXkµQ{¬T“¥L˲òÀFU„ùûÜÈa*u†š¢O””JëŽRÇû\Ö|_B±KÙSji<2·ä?Îw î#œÍºV ;ÂæÅ`Iúv1 û·d@Z¼VÀºuÃûK–ßÈ;Œ,"ÀÚ]Z'î5eW_{X–H<”œµ«ó IDAT 4oq¼àÞï>}ÌËèúÚU‰7 šTfˆ çlÃâÛº¿ûW×{'oÿÔ?â™™@̼ê¦ÿ `p¸ùdãœ}½Y(::KPüÎ39™ïò”bÂÀÌ%÷4 ™2õ97?c2Ç_©­}Zjÿ €ðƒwã Œ:ÒˆŒ ‚ÀXXkM\g·ÆZu‚±×¤5ÓtµÙ™Qù˜[ܵýæ#"°'[¡†£–T‡‹ñ¤žr;`@#å7BžÆM A Ò°ù:P­áLÜ-€ª‹`,ª¡Iï¼²òGÆ0ûkˆ`ìãEÈË¡÷"¬ñÃhÈCÉQ €X܉‰Æf´v£Î-çðÉû_Ùo}ùKêT}Ò“>¹^-ùa$­vèøPÀÓëÆÔUÿÜîS›¦loÈ Iû(iòŒ¸bÒçÔˆ|Òœ, n‚&n=^(€Ùþ(‚:‚#bkƒ0°ll,ìÆV={e:àkE„Xu´ª UuS( Úû•2{؃мY8ćÄGmŸ\²a~YeøµŠtghaMHÁ”È:„Ü‚%ŽÛT.þäýüâóÃÝáY•UY ïb\C:—Æî7£µø±õñ É€š[Í€yâç>Ô²ñp ðuHížF î-¹»g|F‰yÁç hmV/‚d&–R<Ãõ¥¿JÌÌÄLÄLDLÌLÝ·“™ž]¿8yÃ.›ùO“"÷”«ˆ‹£ã“'ÜbFv“zŸ™ ølßj¨'u–) "”úò56ˆÃŽÖZc‚XÛmiFuI¤íz]]F‰ê”*n‹JoD¥Þж“%ÀRÎ^ã ǽ©LßÞDªÚ<ÒØ8t¼¼2´±<°%Ó¾\6ÔL:ù…wÀXuEZ|ÿà ßû‹÷oú™5x5ü f?:¶‡Ø‹ËB__("ø0Ïq`F}2µV–¹kÚïB -P•Ä•Ç=pz6´vHTœJ„ÕÛ‰™ˆ)Qsbb26)ÐÇòΔ=éBÂú亲´=÷”+”©¦ã'Ãgç“z¨«í‘Ûˆˆ s~Z‰˜ˆ ` ’4yTT{ª¹9K)©=Œ“&`£A 0A{¹ÒE=ayU›½aú-™l»-Œ¨fv"\Nž›è*â>b†G¯*‚H 6¯‹†±ë¸î"÷×i aeÃ{YæA¤Ä°|¸Z|¯zõð¢®"ÙVk^zûÁƒ‰8i5@°õžšˆ…¨lêû­L ‰ú=å²öQ™Œe™(‘ñšÖJ;ˆY{U>]ê [Î)÷”Ëžq:î‚ìÉŽ¼´:~"÷:ïx/ý^`C¦½DI‰!.HZ+ãcÔ ½ZWÈT &cuÆ ýD{‰ÑN’%ÎÙì\¨’î!{+ÅävÃÎMjTí µ}ªÞ«J佨‡HÜ' \®X™eªj\vý£ªjx3³¯.ޤәAt4¶ízÛõ¿GP& ^ÂF£M‚ˆÆÜ¡[|M™„‰úˆ/J¶É4Îáá£aç žÊJlLMØcYGìßÁ5·®óI‰„A»½÷"~TÜ_xvÁ£«ö<ºjÏ Ï.8çïí¥'÷”ˉËÅŸ+ÿv°ñ¬µv¤=¬µ¡ BkƒòuV…à!ö¼Q<ðznÖ’D«E™`ˆ¶Î%¦’7uìâ> 6›7ÙE@¤6j1ʬ!ÉP”™ ¡°ýîJÿ–J÷ë ÙÛUDÅWDT¼Š@¼ŠU U ^K"nëÿzáÎ/?R/ïI ž•òu·üGÔ|;Eš4ŽW0ÁŽ diË®_¯ôE Ž«OÜjÔª ’ª¢ ˆgY?ìeX]AdØ¡aCD±oƒãÂÌhFç0 ýª^¼÷âýK›ÇôcÑ+¨#|*î)S—³‹¬Ø|/¿WtñƒÀXXcM`k­-ß`H”€¸ >¼ïMçgßÑ P1))ØD½€aU€ÁäBqM»ÈQcçuÃ=»ËÅ™ÆE±²ǵm5ÎH±ÉA¡!…V"ñ˺wª±Ä{Yû­×ø¥Ûïù·w¼üÍ×Tœz/Ç2Þ9esÓ]›þ8öBqM§£Mc Koýú!"Lªªå²oZÀD0¢ÑbÄëVR¢¶ÅWð®èÝ0|ѳ±Ö(C™¹Î¦3É,ާCö%†]Ä«—qoûå;¦zbòÒˆ{š†LÇEŽN}Ž/ùw³Þû±¬[kKŸbøú0»’€†Ã e€†2”Ã̤žÕ…> ¬ôZ×v_¥{#PÖdjBˆ0”<‹ã ÛS(6ÅW©D§*"ï«Êžè»÷zíMßjýÝxþ(©¶ÎÞBDªçT4éU>x÷7らv‘"ùCP4Ãð (`Þ/ ­õS™’¤–DÚÀ(ynØ»‚/ö’Œxcl&TboØ&E¦¤#Ó!LÈRõ.Vv/ck21§÷Ë( ™:÷1¤iÈ3âìÒº´reô†pèSÿæÚÝi­)~š! ÿQl²ù˜—ÁæRL,αī’A PÚÈ8j%쨔 mŠõ6ÓrOlÛI€óp‘#%눡€Ô΄U•” üØ/ªT]»ô ©m^ËÀ¤82Ø2£ùÍM¿ÈœÔ¾ë¨f%1uÄ ajÁ|h ™LÀC;÷êT&R%©(ˆ!UQ®nX¤è¥ÌÆf2ÄØj¹ÐE`òñ(’Ȩaïã0æÆ·ï÷ž§e™””Iqµ”È/»®ùßn:ôwÍï‘*S"â PÜ|Ѫ%U"°‚HYÁ‰!8Ÿ³ÑÈH‹¶CU3­w5Ó¤À›U›E¥h"ØÀ56ôÞù /@•ð×µêJZ5Rœz'û—_}hùÏÝ¡ŠWÿtƒ¨@ü­?~³GSuÂÊFY¹[™mu BYyÄkŽ+•Œ <Åì_”%Q@‘irÑ÷¯–M`bYÌAÿ&ÁÌæ%ª\/ëö¤.p.ltÔã‚‘áþCP%BؾÏRJ5qewýüÿ°"ñ ‚Qeg}4Ô§"ñÂÓ+ãKªÜÂPG@ÐPŠr·€øþæe±”«r¢æã"4ñ_ÜÇÔ:ºÈj܈ŒDp`â©L^Q“帆r%v~Ø“‹B¶É©1 †of­Ö â^xñ5YŸÌžïÅû×?ÿsx%Ùv¤âžrÔë¸Ï5(w0á‘©ŽŸ¬†IÕE @ Õ êÙÊ rÖû òaäl48ø‰*r€Z{›)ˆoÄíÿê¿QuF~\ßÓ ‹‚j‰\¶üù¿î9Þ  8ãgkÿ>sÞ3*êU½ªDÝÚú÷¯ÞùÅ{—üôg7~ï@|Ø;›qÛC¬N‹K1fl~F™ ³2+÷)3Sq‰ˆbqw9ðÞ+YKɧÈ {?,$D†ãÔ# ´ ‘˜Ø§k2Ÿ–Ð+®ZX¯*{Rj÷UÛïè*¿²v®RqO9 éPç%gÛœUwï_CjTi¯ngØùXw€²+k%–õ rA48ðÞ-ÿ狤 Ó¯D›˜˜jÿש֎ Plý³ŸÏõ‹QKš3jȰ˖m®Ô1½­÷Ø@îè_§ÿLü¼Ãþóµ3Ìiü;Û8GE—~ºP긥vÁ›ß€ûîyUAœW™Ø¿«*252[òÕ’RÀ à U•5(%£µù¶ÿâ†#âv6Ä–˜9x L¢·IP"êGlÏÕ‹ˆ÷Þ‹­öXÞÇådî[qX…^]7W©¸§Œr:nr2]S¨½ÌI !@ 6°,,”¡¬àÜ/CYã¡Gï ¨Š€à“Âz_qôæÿú7ˆMr2Ñ56ÄRµðÊ€ñbÔ[õ>ËÇJ2ñØïÂOÖ¶_Y;ý¾Gï[qôÕuÓëq76·ïR§ˆ N5‚:ñ¾V´­Øð 23,Åæ½Øóÿ”M™×G›q«ÿZ.ü̃DŒ,ˆHÑάd†a#F¿zïUÆ&Þƒõí]÷»+cnê¸4ä%÷4 yiIK䗯λÿÁ½›Kÿî÷æ h&l H•TY•$ò"%‚ pú†¿ùvëÁ?P¯ª‹4Bµèªõ­¹“ªWkI "¨!¨uV‡s¬/ÛqúË[7}ùýÝËïï~í•®xÅŒøƒc ïZ(:æì…Kô©:øÈ©r½…‡0,“ ¢Šªå†ŽÁÆ KJ‚H§€ŒÑ¤¢!²â<<‘žà]Mœ»W(‡¬y}ûøŸç{î;*J_ŽÉq¹¤!Sç>ž+/ yAuüŠé yq˜|òæ/µŒ €ÊäT[«¢æÖwüñÞùžÎ_"Rñâ†ô‘ï¤Ñ´L5‰Ï}"M6ØõF}ÐÚuúvâØß§ýóÓÞZGGÀç>PÇŽeºu3^ïøìϾPoäáT#x'â”YÁD"…Á?qÅà­ÈÀ1H3.ÿN®á?‚!¾!f¯UêÞ3{/^ãò‹“òaä”7½ºäÄ+™ÔØeG*îW©¿¹îË_#âõƒœu pið¾¸˼ñQEaT £ÃÅwÅx%ÉÏ›»»ø…1O ¸ÓŒ¨:ñðĆˆØ&m«“˜’D…€¡Æx«ÞÃnÈ,žòúï^Þ§¢Gd§Ï(çò~÷®„ zǽ*ØþfÇýï;$ ¼"lFMÓ«*ŸyDªÎ‹g5·yÞR11n‰¾]Áñáלjİ a²¸dõ=R"Œ–¥Z~ïE½ˆ(1Â6s¶4Ü;ŸX—.ëݲiÌçýÒe=*´eSç9} § Z:@J%n½3÷Ë•TÇ/SZ¾ðkä|H'×qïþÍøõ9z~04F.Œ–ßñâÅøæ9ó¢°òÉÀÆO;‰‘ŠsBðŒðü¬8€¨Å½[äçÞI±s˪D°Þ¨7p†•'9%¿^4—,é«mÿø—vj/Á½«HRQ(±±Æ¶†¡@ª&.»{Ϙ`]ïzî¹·ÀƱÎ÷õ×Û<þå·¤¢RΊSñP§³ ¢,äÆX6Y…hYZVžiŒVxZëÐ ñNžÈ<ƒØvoQ~Áí…½o8êßšÃKˆU"2Âê-œŸÖzÃÎã…†žŒt>9ñuñľ~åç·•ûU*:ERQu`c›šsñgeÀ¡âQRLª ¬GŒ xO‡[Zà‡½A¤°Œx£Øû Íÿ]$ ÃɲˆM:Ä8@6Бž‘¾sGí’n½½?ŽŽŒ"=gäߛЩöIJ9·Žï¥÷j›©¸_JΨ´brE™®ñk§\dÚ&W"p`"K>3ú•²Ç‘éIÉ‚‚ÌñuÊmLæYÄ:ZvñÎ"ÏdeP!îØÈÚxͧs{MŸ{÷lŸÇwÇíi˜Ô:ƒxdõä™ûV0. `ÅÊM•~H¤RAÜfÝZÃÆ”ËÞXÆ  UE)®ü—EEÕ©*šÛ~¾ÿøkˆît¼½È³‚5Üå˜"†! 8r†­eXeKlA†Þéd ²ôäÞ!¦=»f]—ǧ„ô“ŽOD}òRÞáU•†LKä—#UÇXW~"G'QZ9kLå0T|v€rÛÓ}KuT‰xõL†! qHVa”õÿoï܃£º¯;þ=çwwµ2ÂŒ€±Á&Ä Ï`Œ6ó0ñ¤iÓ™6ÓvÆ™4´¦Ót:i¦3ÍL:mÿhgúH§MgÚIÚ±¶Á¶xØØØ 0~ıyÚ 0Oñ–vïïœþq÷­•´+í.wW¿Ïxä«ËÝ«ßJ»ßùŸŽã‡/ȯÎÈÉXL´F­G¾Uß…å¾xÙy{vå›}ÑWߌ_Q›³IgÕSž1†Fˆª$€(ºUE€ÕDB#ËrwäÖôÅm§q ~tÌñ‹axF}Eâæw‡ßû¯æb<¹Ù¶JBDDŒñTEâ2|l£ª‚aoYbšxï©Óg&½hTö8[vî±eçÞØUâŸPT Y|j¥x•÷M(~}…AVCVÈãuÖ²Ò”Z Ùð›PŒÄ\œÇKí ½¶`¿ñ ñ;•ö•8¹ý)‘‹íÃ"!²LÒÈhª±»ÞË_þ\>lC«7aaϵ^WóêØåWb—_éýdÎí)¿:€ù³vǯ=ãÃ&is6†=`4“(4ˆÙ5XQÅžJ§Š *ˆFŸîŠï‰¨¯¤PUŬ ¹cìŠÏZRÊ7,`Ü$"!&jMÖË#xªê•¶ë¢"" Ô>ÿ‹Øÿ«Ì¯Æ7v[ºüìÒågwï(óÂÔê{¼ NîƒÂÅãµ7Ï àf%ãñb!Ïo¼¯ð†|}?Ž%Ò<.~þsævP“µÖúl)aÝz’ˆ†=0[¡DtÏ>-øT[À"Ïìñ=ƒ÷e+ÎØÕ2!}fîŒ(CŒI…êܽàÀct4‘(4X%›ô;n¨ˆXQQŠrd¸gh¹$”D„a9ÙW¦ñ°ß5Ã- H“ý‚§ÐO£'éÐ Ý7Ý>Ü©gÚ=ÙZ2û«9’ª“ÒºgŒÁ8«$š­uºªÉ¬J вf|/Ê¢& ø°¦zÓJBÅ1ÅîPøJ’WÁ·¨ûºMß(pzRëÌ"B|.ógìÜÿI&~ßõÚ„åO¶-²mÇ+ýtúÍó¸z7M¾ñU÷xAœÜ ãw´è¾x¡ïËœÇÃíõ*g¼DÁ+õç?­ÙÍÔJàóÎoÞÅÿìp X«Ö‡¡¤™­õ‡¡Î ('g6•H :Mç}Fï×V`€îQË®ìj¸²«{Ô2+VµhÙž±á‚‡v¥´žŸ‡aãa¥R1ÉõSt9'ZOQòšLd„‰w°Š`JX42ŒA¬±[lD ˆù ‹ná`×'Š(1hgb¡`ÿíäÿ­3‡Bù¿â™ ä—Ê>ÕY}œÜ{‰Ç›Çö<çT®7Õ9aëß…­A5¹H¿s'ýÉjw±V,‘޼DJ±ÄÝâùB6DJtAš`ÇäÙÀìÆK[cíÛ’5—©ZÌ•«OxmÛäì/äy^&ãeïÆè8‚h:C—2ZÏÛ9Ê^“1M¦ëZ„‚ú µJ1Àµì ³×Ïf6€¥ð Ð]ÄD|„“¹øl­1‘ТYo¼õácéÁ¿òóK«¾yÇ«>Þþ‹¾–âæyœìáÁþx*ÌÐ’{ñ)roØD wM­äpEÑ^hª³§Ê{äU&Œl¯ôØÂϵñßo:õãH¤Ó‡g K¤¢Ê° ßYKÃ>"ÑgSf¸ÏÄã·Æ¬µok¸ÒФåÞ3!ƒ”ÜS©˜¤Ü1Ñ@”D Uº ´¤žI¢7°i2¦Éë¼D^DA„¸€ÀQ€~‡õ­µª?!ÌÌÊD¤>!=‹ŸzMÐ86 k…„H˜òóí¿èüÚ·†}í[Ã^þŸŽàL˜Cò¾IWCÞf¹W®rÐS…ÿ¢wT”s…– ÉG„5EBÃ6‰C ˆø¾ªªÀÚ„µKŸLH¶kGc¢mc¢mÙ~ïj^Ýp¥€é:åǾ°jí§¶o’}ÿ¥sÞ1ÆË”<©˜‰‘ ÈžÏ©ØŒÖ³“ì"˜Ýk2^“Á%*Å•!ˆRGúE 6.—S"W%ÄrCÔ‰\&fBsöË„t¼ˆÏ&ÎÉŒ¾©™œ.zIç$çñj‘.dä%?¢K›|Ÿøˆù/oÆš Ç×l8þÒ¦û‚“«¿gŒÈ]¦‰ðg9¹õ@åAÞœlLŒM“dcL“Q›hlŒ!aÅãìneY)~U‰[gŽsd„éê莙HCÌ»ÕÑø™…™™Ì&fbÒ{ ™¬â“Iµ3‹ˆ¬Zthû[_Iß»à\BM¹»Õ@5ǹóœ'">HxEMs¿­tZÑpegÕݣWì[>/Âõ¼dç^™s\DÔÚ^Bõ䑊Ç8­u°éhKˆDˆð•®)F¤ºZÙ~·qkâ,qŽ6b5–%!©à=éwf&f6LÜÆL¬“ ¦w2gŠ5÷‰ ò:nƒÜó<îëum˜óxH8Xô<çƒÎãá„@ìòvé½jPwÊp=ÏÓéLG@GÅæª\{Q¼‰%“ìO4Üsë©ïµrG¬‚®Î„ûCå¿È HB‚iÕè¨(ŒšN&7ÀïLdË='ŠgæLLÄìO%›ÉÊ™>žWMÌTVî.¯9ÞÎõx©•/åz|ê˜kNTcŒŽÀ¶#7rÿú“‡–ÃùZÏ?¶™yTn4¦É\ovÜskã_ ï¾â7PT 7¯ßò¢¨ª*qêUGAŸ4¯$DVâ¼åßXÿìáû^§9¦éÚå~‡ï'ç÷ Š7Éψ11Ëý 9ÊV|¼©åÁžÏ«> ïþøÝ²Üh0ÿÊ}ÂØò‹“BU0`úøNe)˜y£—yžÙç'ý¦öa&l¶‚¤zÿV§`¦áÚ› Á*ÓÞmCnµ{OÖ?sÀæçîOŸyj釩Û^/jMd„·çE³çµx·µyݳS;Ûn¹|ö†!ãE"‘HÄ‹Fx ‘(‰ÒEñ}ßúÖ÷}ÆÑÑ‘–—“uèOÿÖIÈŽ¿ÃOtø¶Kzø‚¦‡÷ xî•ROê€ÍÏM+é{‡= „ÕÜÝTgͱ£¸¥@ ]^¥é¾óÑèµ½^÷›ÜÿÕýA$Æ`ÝÆÃ›þãfpf˦œÌ’¥‰¼ÌŒi4ï¼·$}E@Û¾±“¦]ºÙÑ¡V`8µ³kÐv ç Tsª³ç¶ƒðnkógŸ48ôîŒåO€Q2êwX±¶@–†RYšÔ™Í;gf º~Ò2ýËÝy¼æØ^tŠü1§ò!ƒé:©dºG..õ=»¬¼ðS<ý{ìÿöðÍÿ™ô{Þ’Î=ïõyS€ÙsÚtúbU­(kºY/(Ó‰2÷¥«ÁœjðÉœyí[›gÏiŸ=§}Ç‹sƒ“‹¾™èðýx,<¿ÚZ¸[ý¥eräîRä5Ç–¢KÈ—;mlã}ý^#Wê€\ï«eœ³ùyX»ñغo|ñ—ýß9ÆŽíºp>v µyÅ„ÏÔJ&fþc@’ SsFP«6.ïh0g^ûœùíÈmüõÖÛsx-ˆÖ÷ÜÏ'ëŸ9&J¥ædBŽ×¯ÐÇCÂæo>ô^jl°ÜêæÞBUÎãŽäµ.K#ò­/L{êéãk6W¥—7÷ÓˆiÑcTH•DyLs€­ÍÄŠZIm±‘lÊžšG š¿ Iâ688ØÚ¸'û>K—Ÿ°{Çøj ¼–‹Ü+·™jo-ûûT¹mª±—ùÒW~€°ös„„A—¬œ+ã`ÊÅ¥øÄž›©–Ô"ö~¸xÉì·³«Ýs³1Þ§‚°=´›©†¥Î(u{É?ªÉ?ë2)òžr_Üòý>îN¿»j÷’(Kµ{%¦:«Ù²xVê^*K? àõ9q÷ò­ÉR÷HD§G3uî-ûæÝI'\Ód¡Ëò'NØñê½g¿„³Ô=,‘û Ù÷è?§ð”Á Çã‹výqÇå¨Uê¾d% L—ïØ7ïÉE‡’ÓªYu5-ûæå]¹bU€–í•ý T»ÔžÜ,ü÷¤¸sRäXøú÷ª8.G­¶Õ@C‡]-_yúñ•§w¾–z§¦U)(r$dovš†èöçdÂL¨åþ4eníc)Ђ7¿S½a9jçñ°Á¦À;ú•½³×,ù U0£/îžÕóš¿qäæÍÈ«/ pÙÔP ,roÞ_lHO7#+?^€¹oÿ~UGæ¨Mò<®±hoW:ßFZ¶O\¹úäÊÕ'_Û–³‹ÈK¯iÊ_ƒ°©å¡¼‡,^ë[=ÑÙéÌÞ·AîÎþßô2Îì5ú=™½ïw«90Gââñš&H½ä5•°©å¡g¼#ÙgžÚp°A÷ö·::T\î}ùy…fU­æË­¿°ö†t„‡{œbíÐUöÕ'Õ©† xõå/¬Zûic£ÿµõ'ˆ”é¼ÏmOnpºnã1ÕdŠvÏV/]-ÂY YÎRÈgmÎäR²º¬äÉ}Ö¡oôv‡ÐÊÝUC–D¹ª!Ë[²2¤zÿžêTCf“–{ðqËóÓlxæh uUÚúËûäUC†„VC0r?<ó%$[e¡ïùÌ÷¾>б9†®ôБÞËiíÆcÖ?s” A´^Û$Uþå~tÆ«ÈM‘÷ÖeåÁ×—wpŽºÄ¥È}³õ…iÖ?sÀ¦çî¿ÝéUrä~âÈ´®íµ~ÀôÖTzdŽ:ÀyÜ1`6;­ïÓi»ô1Ûyÿ'OTs@Ž%Ûã:ürõXÁ+ÇŽ*¹O=òøí‡£†pñ¸Ã~¼)Ç–Þî1d¨ZoHGñ `ª“b×ðÈpõs”D5«!ë€VC†¨+d€«†,‰òVC–±d¥Ž{CVW Y®²ÂÒ~ÀQe\jÅá¨oœÜëçq‡câä^W8;Ž'÷Z%𸎺€\½ÖÇ•ÎãÇÐ;ÞœÜk·:ßápä‘öxAB'wW Y’Ç)Ö €GºÎŽòãª!K¢¢Õ}{<öppºRH ¥jȲ¤Èë¾7dyqÕ%áª!K¢,ÕðxAB¹×+nªÓápäQ.Äɽü8;Ž<*êñ‚8¹ çq‡Ã‘Gõ=^'÷ЮÏÈÕK}_æ<îp ò 3) I liked the physics of the way the ship moved -- it reminded me of > an ancient TRS-80 game called "Bounceoids", where you could actually > hit yourself with your own shots if you were moving backward and > decelerating while firing. I wished I could do something like hold > down the Shift key to move sideways, or even better to swing around on > a fixed-length umbilical. I wanted to keep the controls simple, so I avoided a "strafe" feature... but maybe the fixed length umbilical key would be interesting. It would give some physical presence to the umbilical and make it more central to the gameplay (right now, it is basically just a status display). From Adaska too: request for strafe. October 7, 2004 How to deal with counter-intuitive weak bullets that are visible but do nothing to minor antiglyphs? Maybe the bullet spectrum should have a particularly weak shape at 0 and then a much stronger shape at 0.01 so that the first collage piece causes a dramatic change. Also, the weakest bullet shape could cause minor antis to divide into 2. Maybe fun, but might be annoying. October 8, 2004 From David Greene: > But I suppose there's an argument that a bullet should be recognizable > as a bullet... I think I like the "early fade-out" idea best, but it > would probably be a lot more painful to do the looking-ahead to make > sure the bullet disappears in time for the fading to be visible. Well, I could just cause the bullet to start fading whenever it is within X pixels of an enemy. That would be pretty easy. > Maybe the bullet should *bounce off* the anti-glyph instead... > Other possible bonuses: two or three simultaneous bullets with every > shot (slightly different angles, or slightly different speeds). > Larger bullets -- increase the affected area by a factor of two or so. > Bullets that last indefinitely, until they go offscreen or hit > something -- this would be fun because you could leave "bombs" lying > around with just the right combination of turning and deceleration > (you could do that in Bounceoids...) or at least you could get very > slow-moving bullets. Or, my favorite idea, a crazy "heat-seaking" > variant: the bullet reverses direction at the extreme end of its > range and returns to the player's ship, giving you two chances to hit > a target -- or double damage to the boss glyph, if you set it up > right. Sort of like a boomerang, no? While thinking about this, I thought about a double-bullet bonus idea: When you shoot, one bullet goes straight out, as usual, but another bullet travels down the umbilical, all the way back to the center. Thus, if you are clever with your ship position, you can kill minor antis that are attacking your collage while you are out chasing the major antiglyph. October 13, 2004 Found potential cause of absent enemy bug on win32: When locking down maximum timejump per frame, we sometimes seen an attempted timejump of around 4 billion ms. This, of course, would cause the enemies to jump way passed the glyph and off the grid completely. It would take them forever to return. Of course, the "in grid" constraints for the glyph and major anti keep those in the picture even during a huge timejump. Need to figure out why Time class is returning such a huge value. In any event, limiting timejumps is an temporary fix. Started working on a PortalManager class for the exit portals. October 14, 2004 Got portal stuff working. Checked for memory leaks. Found bug in Time.h that botches MS calculation when the time delta goes beyond the next second (for example, taking diff between 1s001ms and 0s999ms would return a bogus value because of a sign error). This may be the cause of our "huge position jump" on win32. Since enemies would jump also during a large time jump, this fix handles the "no minor enemies" bug. Still seeing huge timejump bug in win32. Need to investigate more. Maybe need to put some special code in Time.h to detect the exact source of the problem. Found this bug. ANSI time() seconds and win32 GetLocalTime seconds are not calibrated (ANSI seconds can roll over after GetLocalTime seconds roll over) resulting in bad time delta calculations. Replaced this code with ANSI mktime code. It works now (after all these years of using incorrect win32 timing code... yikes!) Ready to start working on next level. Music notes for level 3: A# C E F# Note-to-hertz table: http://www.jhu.edu/~signals/listen/music1.html Official to-do for release 0.2 (IGF-2) X-Investigate "no minor enemies" bug on win32. X-Add a limit on how much "time" can pass during a single frame. X-Zoom way out while fading out and zoom in when fading back in. X-Parameterize grid size X-Strafe keys X-Limit the number of ship bullets on the screen at one time. (to make space-bar mashing ineffective) X-Make starting bullet very small and weak looking in all levels. X-Portal to exit after killing boss X-Fix key release bug. X-Add "skip level" key for the judges skip[--Add special ability when all collage pieces are in place (shots fired down the umbilical?)] x-Add level. x-Make sure all portals look appropriate (level 002 portal is just a copy of level 001) October 19, 2004 Several suggestions by David Greene (smooth back off, boss progress, bullet collision indicators). October 21, 2004 > > > I found myself wishing for some interaction between shots and glyphs again > > > (shots getting absorbed, bouncing off, fading out quickly, changing color?) > > > > I'll think about this... maybe something that shows when a bullet is in contact > > with an enemy... maybe an extra shape that "squirts" out, like little explosions > > or flashes, during every frame in which the bullet is in contact. > > Hmm... maybe if you morph the extra shape from a small-something to a > big-recognizable-something-else, depending on how badly the boss is > currently damaged, you could read the current status just by seeing > how close the extra shape is to the recognizable-something. Color > could also help -- start transparent and work toward some appropriate > color for the level? Excellent suggestion. This would add the needed "progress" display without covering up any part of the boss. > There are a couple more "jerky" spots that I thought I'd mention in passing: > > 1) when you pick up an element, it jumps instantaneously over to the > ship's location; > 2) when you drop an element, it jumps instantly to the nearest > Go-board position. August 21, 2005 Working on fixes for Moondance compilation release. August 22, 2005 Worked on more fixes. Currently working on boss damage indicator. Got code to compile, but it behaves oddly, then segfaults. To do for next release: X-Fix zoom behavior when strafing. X-Fix minor anti-glyph pop-in upon creation... they should fade in instead. X-Make minor anti "back off" behavior more smooth. X-Change to allow pick-ups while a piece is being jarred... maybe force a drop if the jar force is increasing? X-Make bullets fade out. X-Make pick-up radius wider to make game easier. X-Fix jerky pick-up and drop somehow... X-Add some kind of progress indicator to each boss. X-Add extra shapes to each level that are "spit out" whenever a bullet is in contact with a target (to indicate that the target is being hit). X-Spit out mini-explosions can change shape depending on the health of the boss. August 30, 2005 Ready for 0.3 release. Checked for memory leaks.transcend-0.3+dfsg2.orig/Transcend/doc/notes/possibleOptimizations.txt0000640000175000017500000000027310065543637024731 0ustar pabspabsCould do to increase framerate --Change ShipBullet.cpp and Enemy.cpp to not blend endpoints if range parameter is either 0 or 1 (i.e., use either endpoint directly without blending).transcend-0.3+dfsg2.orig/Transcend/doc/how_to_uninstall.txt0000640000175000017500000000023510116156607022547 0ustar pabspabsTranscend does not create or modify any files outside of its own folder. Thus, to uninstall, simply drag the Transcend folder into the Trash or Recycle Bin.transcend-0.3+dfsg2.orig/Transcend/doc/slamdance/0000750000175000017500000000000010305077141020337 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/slamdance/shortDescription.tex0000640000175000017500000000124210145112021024411 0ustar pabspabs\documentclass[12pt]{article} \begin{document} \title{Transcend} \maketitle \section{Short description} Transcend is an abstract, 2D shooter. The morphing graphics engine creates an alluring visual display. Using musical power-ups, players simultaneously construct churning visual collages, arrange unique pieces of music, and strengthen their projectiles. Transcend blurs the line between games and art---is it a video game or a multimedia sculpture? \section{Download instructions} Transcend can be downloaded from the following website: \begin{center} http://transcend.sf.net \end{center} \thispagestyle{empty} \end{document} transcend-0.3+dfsg2.orig/Transcend/doc/how_to_play.txt0000640000175000017500000001205410304376506021506 0ustar pabspabsTranscend -- a video game by Jason Rohrer More information at http://transcend.sf.net How to play Transcend Preparation: Stereo headphones or speakers with good stereo separation are highly recommended. The music generated by Transcend is in stereo, and how you interact with Transcend affects stereo positioning in the music. Transcend is playable on a monophonic sound system, but many subtle sound features will be missed. Basic Keyboard Controls: Arrow keys: direct your glyph around the plane Space bar: fire projectiles D key: pick up or drop an Element P key: pause the game Q key: quit Advanced Keyboard Controls: S key: slide your glyph to the left F key: slide your glyph to the right Quick instructions: To win a level, you must destroy the major anti-glyph. Your glyph starts out very weak---its initial projectiles are powerless (they cannot even destroy minor anti-glyphs). Build the strength of your projectiles by gathering Elements from the plane and dropping them near the center of the grid in a collage. Minor anti-glyphs will try to break apart your collage---destroy them with projectiles (you need at least one Element in your collage to have projectiles that are powerful enough to destroy minor anti-glyphs). After you destroy the major anti-glyph, a portal will appear. Pass through this portal to move on to the next level. A more detailed description is given below. Game Components: A level in Transcend contains the following components: Your glyph -- always at the center of the screen. This is the component that you control (i.e., "you" in traditional game-speak). The plane -- marked by a grid. Your glyph can travel anywhere on this grid. Umbilical -- A colored line that connects your glyph to the center of the grid. This line's color and width change according to your current projectile strength and attributes. Elements -- Stationary objects that start out strewn along the border of the grid. Your glyph can pick these up and move them around. Elements can be dropped at the center of the grid to form a power-up and music collage (see below). Music cursor -- A red, yellow, and green line that traverses your Element collage. This line moves to show the current music position in time. The vertical extent of the cursor represents the stereo space (red is right, green is left, and yellow is center). Minor anti-glyphs -- the enemy. These mobile components pursue your glyph and the Elements in your power-up collage. Major anti-glyph -- A large enemy that travels in a circular path around the grid. This enemy is your primary target: when you destroy the major anti-glyph, you can move on to the next level. Portal -- Appears after you destroy the major anti-glyph. While the portal is visible, minor anti-glyphs will stop attacking. You can explore the various Elements and musical possibilities without being bothered by enemies. Pass through the portal to move on to the next level. Enemy attacks: When you are hit with an enemy projectile, your glyph drops any Element that it is carrying and is propelled back toward the center of the grid. When an Element is hit by an enemy projectile, it is propelled away from the center of the grid. Once you have started building a collage of Elements, the minor anti-glyphs will try to knock Elements out and disrupt your collage. Power-up and music collage: Elements that are dropped near the center of the grid (where your umbilical ends) form a collage. Elements that are dropped near the collage also become part of the collage (thus, a collage can extend far away from the grid center as long as some Element in the collage is near the center). Each Element represents a projectile power-up. The Elements in your collage are combined to determine the strength and form of your glyph's projectile. After your collage contains at least one Element, your projectiles will be strong enough to destroy any minor anti-glyph in one shot. However, the major anti-glyph is much more durable, and a relatively large collage will be needed to make your projectiles strong enough to destroy it easily. If your collage grows to be asymmetrical and/or off-center, your projectiles will become weaker (they will appear transparent and your umbilical will become thinner). In addition to a projectile power-up, each Element represents a section of music. A cursor traverses your collage and plays the music associated with the Elements that you have assembled. Thus, a chain of elements that extends perpendicular to the cursor will be played sequentially in time. A chain that extends parallel to the cursor will be played simultaneously in time but spaced out in stereo. transcend-0.3+dfsg2.orig/Transcend/doc/posts/0000750000175000017500000000000010305077141017560 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/doc/posts/pr.txt0000640000175000017500000000047210141376533020753 0ustar pabspabsv0.1.1 Emailed www.the-underdogs.org pat@macgamer.com pr@megagames.com ed@ and richard@retailkings.com (way of the rodent) Submitted by web: diygames.com freshmeat.net Slashdot. 2004-September-24 Emailed: gendo_ikari@gamehippo.com web: www.gametunnel.com 2004-November-1 Been added to linuxlinks.com transcend-0.3+dfsg2.orig/Transcend/doc/posts/0_1_1-slashdot.txt0000640000175000017500000000144510124274227022747 0ustar pabspabsRe-inventing the 2D shooter Remember the glory days of arcade gaming? Solo creators, abstract graphics (exactly what is the Tempest "claw" supposed to look like?), and innovative game-play. Hardware was too primitive to do anything flashy, so designers had to innovate deeply in order to make engaging games. Inspired by this heritage, I recently coded-up a novel 2D shooter called Transcend. The graphics stretch the limit of what is possible with a 2D filled vector engine, and the game-play is unique (for example, to power-up your projectiles, you must construct a music collage). Along with trying to make a playable game, I was also thinking about Transcend as a work visual and auditory art---the result can be viewed as a kind of interactive sculpture. transcend-0.3+dfsg2.orig/Transcend/Makefile.minorGems_targets0000640000175000017500000001132310062106434022772 0ustar pabspabs# # Modification History # # 2003-July-27 Jason Rohrer # Created. Modified from konspire2b source. # Added p2p parts. # # 2003-August-7 Jason Rohrer # Added DuplicateMessageDetector. # # 2003-August-12 Jason Rohrer # Added protocolUtils. # # 2003-August-22 Jason Rohrer # Added encoding utils. # # 2003-September-4 Jason Rohrer # Added mime typer. # # 2003-September-5 Jason Rohrer # Added web server. # # 2003-September-8 Jason Rohrer # Added use of sed to fix auto-generated dependency path. # # 2003-October-9 Jason Rohrer # Added message limiter. # # 2004-January-4 Jason Rohrer # Added network function locks. # # 2004-April-1 Jason Rohrer # Added FinishedSignalThreads. # # 2004-May-10 Jason Rohrer # Added AIFF format. # # 2004-June-10 Jason Rohrer # Added ScreenGL. # ## # The common portion of Makefiles for all targets that use minorGems, # supplying target dependencies for minorGems targets. # # Should not be made manually---used by configure to build Makefiles. ## ## # Dependencies. ## MINOR_GEMS_SOURCE = \ ${HOST_ADDRESS_CPP} \ ${SOCKET_CPP} \ ${SOCKET_SERVER_CPP} \ ${SOCKET_CLIENT_CPP} \ ${NETWORK_FUNCTION_LOCKS_CPP} \ ${PATH_CPP} \ ${DIRECTORY_CPP} \ ${TYPE_IO_CPP} \ ${TIME_CPP} \ ${THREAD_CPP} \ ${MUTEX_LOCK_CPP} \ ${BINARY_SEMAPHORE_CPP} \ ${APP_LOG_CPP} \ ${PRINT_LOG_CPP} \ ${FILE_LOG_CPP} \ ${LOG_CPP} \ ${PRINT_UTILS_CPP} \ ${WEB_CLIENT_CPP} \ ${URL_UTILS_CPP} \ ${MIME_TYPER_CPP} \ ${STRING_BUFFER_OUTPUT_STREAM_CPP} \ ${XML_UTILS_CPP} \ ${HTML_UTILS_CPP} \ ${SETTINGS_MANAGER_CPP} \ ${STRING_UTILS_CPP} \ ${SHA1_CPP} \ ${AIFF_CPP} \ ${MEMORY_TRACK_CPP} \ ${DEBUG_MEMORY_CPP} \ ${HOST_CATCHER_CPP} \ ${OUTBOUND_CHANNEL_CPP} \ ${DUPLICATE_MESSAGE_DETECTOR_CPP} \ ${PROTOCOL_UTILS_CPP} \ ${MESSAGE_PER_SECOND_LIMITER_CPP} \ ${ENCODING_UTILS_CPP} \ ${WEB_SERVER_CPP} \ ${REQUEST_HANDLING_THREAD_CPP} \ ${THREAD_HANDLING_THREAD_CPP} \ ${CONNECTION_PERMISSION_HANDLER_CPP} \ ${STOP_SIGNAL_THREAD_CPP} \ ${FINISHED_SIGNAL_THREAD_CPP} \ ${SCREEN_GL_CPP} # sed command for fixing up the dependencies generated by g++ # g++ (pre-3.0) leaves the path off of the .o target # look for a .o file at the beginning of a line (in other words, one # without a path), and replace it with the full-path version. # This should be compatible with g++ 3.0, since we only replace .o names # that occur at the beginning of a line (using the "^" modifier) MINOR_GEMS_SED_FIX_COMMAND = sed ' \ s/^HostAddress.*\.o/$${HOST_ADDRESS_O}/; \ s/^Socket.*\.o/$${SOCKET_O}/; \ s/^SocketServer.*\.o/$${SOCKET_SERVER_O}/; \ s/^SocketClient.*\.o/$${SOCKET_CLIENT_O}/; \ s/^NetworkFunctionLocks.*\.o/$${NETWORK_FUNCTION_LOCKS_O}/; \ s/^Path.*\.o/$${PATH_O}/; \ s/^Directory.*\.o/$${DIRECTORY_O}/; \ s/^TypeIO.*\.o/$${TYPE_IO_O}/; \ s/^Time.*\.o/$${TIME_O}/; \ s/^Thread.*\.o/$${THREAD_O}/; \ s/^MutexLock.*\.o/$${MUTEX_LOCK_O}/; \ s/^BinarySemaphore.*\.o/$${BINARY_SEMAPHORE_O}/; \ s/^AppLog.*\.o/$${APP_LOG_O}/; \ s/^PrintLog.*\.o/$${PRINT_LOG_O}/; \ s/^FileLog.*\.o/$${FILE_LOG_O}/; \ s/^Log.*\.o/$${LOG_O}/; \ s/^PrintUtils.*\.o/$${PRINT_UTILS_O}/; \ s/^WebClient.*\.o/$${WEB_CLIENT_O}/; \ s/^URLUtils.*\.o/$${URL_UTILS_O}/; \ s/^MimeTyper.*\.o/$${MIME_TYPER_O}/; \ s/^StringBufferOutputStream.*\.o/$${STRING_BUFFER_OUTPUT_STREAM_O}/; \ s/^XMLUtils.*\.o/$${XML_UTILS_O}/; \ s/^HTMLUtils.*\.o/$${HTML_UTILS_O}/; \ s/^SettingsManager.*\.o/$${SETTINGS_MANAGER_O}/; \ s/^stringUtils.*\.o/$${STRING_UTILS_O}/; \ s/^sha1.*\.o/$${SHA1_O}/; \ s/^aiff.*\.o/$${AIFF_O}/; \ s/^MemoryTrack.*\.o/$${MEMORY_TRACK_O}/; \ s/^DebugMemory.*\.o/$${DEBUG_MEMORY_O}/; \ s/^HostCatcher.*\.o/$${HOST_CATCHER_O}/; \ s/^OutboundChannel.*\.o/$${OUTBOUND_CHANNEL_O}/; \ s/^DuplicateMessageDetector.*\.o/$${DUPLICATE_MESSAGE_DETECTOR_O}/; \ s/^protocolUtils.*\.o/$${PROTOCOL_UTILS_O}/; \ s/^MessagePerSecondLimiter.*\.o/$${MESSAGE_PER_SECOND_LIMITER_O}/; \ s/^encodingUtils.*\.o/$${ENCODING_UTILS_O}/; \ s/^WebServer.*\.o/$${WEB_SERVER_O }/; \ s/^RequestHandlingThread.*\.o/$${REQUEST_HANDLING_THREAD_O}/; \ s/^ThreadHandlingThread.*\.o/$${THREAD_HANDLING_THREAD_O}/; \ s/^ConnectionPermissionHandler.*\.o/$${CONNECTION_PERMISSION_HANDLER_O}/; \ s/^StopSignalThread.*\.o/$${STOP_SIGNAL_THREAD_O}/; \ s/^FinishedSignalThread.*\.o/$${FINISHED_SIGNAL_THREAD_O}/; \ s/^ScreenGL.*\.o/$${SCREEN_GL_O}/; \ ' MINOR_GEMS_DEPENDENCY_FILE = Makefile.minorGems_dependencies # build the dependency file ${MINOR_GEMS_DEPENDENCY_FILE}: ${MINOR_GEMS_SOURCE} rm -f ${MINOR_GEMS_DEPENDENCY_FILE} ${COMPILE} -I${ROOT_PATH} -MM ${MINOR_GEMS_SOURCE} >> ${MINOR_GEMS_DEPENDENCY_FILE}.temp cat ${MINOR_GEMS_DEPENDENCY_FILE}.temp | ${MINOR_GEMS_SED_FIX_COMMAND} >> ${MINOR_GEMS_DEPENDENCY_FILE} rm -f ${MINOR_GEMS_DEPENDENCY_FILE}.temp include ${MINOR_GEMS_DEPENDENCY_FILE} transcend-0.3+dfsg2.orig/Transcend/configure0000750000175000017500000000365110133736155017565 0ustar pabspabs#!/bin/sh # # Modification History # # 2004-June-10 Jason Rohrer # Copied/modified from Monolith project. # # 2004-August-25 Jason Rohrer # Added support for editors directory. # while [ -z "$platformSelection" ] do echo "select platform:" echo " 1 -- GNU/Linux X86" echo " 2 -- GNU/Linux PPC" echo " 3 -- FreeBSD X86" echo " 4 -- MacOSX" echo " 5 -- Solaris" echo " 6 -- Win32 using MinGW" echo " q -- quit" echo "" echo -n "> " read platformSelection if [ "$platformSelection" = "q" ] then exit fi # use ASCII comparison. if [[ "$platformSelection" > "6" ]] then platformSelection="" fi if [[ "$platformSelection" < "1" ]] then platformSelection="" fi done platformName="Generic" platformMakefile="generic" case "$platformSelection" in "1" ) platformName="GNU/Linux X86" platformMakefile="Makefile.GnuLinuxX86" ;; "2" ) platformName="GNU/Linux PPC" platformMakefile="Makefile.GnuLinuxPPC" ;; "3" ) platformName="FreeBSD X86" platformMakefile="Makefile.FreeBSDX86" ;; "4" ) platformName="MacOSX" platformMakefile="Makefile.MacOSX" ;; "5" ) platformName="Solaris" platformMakefile="Makefile.Solaris" ;; "6" ) platformName="Win32 MinGW" platformMakefile="Makefile.MinGW" ;; esac rm -f Makefile.temp echo "# Auto-generated by Transcend/configure for the $platformName platform. Do not edit manually." > Makefile.temp rm -f game/Makefile cat Makefile.temp $platformMakefile Makefile.common Makefile.minorGems game/Makefile.all Makefile.minorGems_targets > game/Makefile rm -f editors/Makefile cat Makefile.temp $platformMakefile Makefile.common Makefile.minorGems editors/Makefile.all Makefile.minorGems_targets > editors/Makefile rm Makefile.temp exit transcend-0.3+dfsg2.orig/Transcend/Makefile.common0000640000175000017500000000212510304640252020571 0ustar pabspabs# # Modification History # # 2004-April-30 Jason Rohrer # Created. Modified from MUTE source. # # 2005-August-29 Jason Rohrer # Added optimization options. # ## # The common portion of all Makefiles. # Should not be made manually---used by configure to build Makefiles. ## EXE_LINKER = ${GXX} RANLIB = ranlib LIBRARY_LINKER = ar DEBUG_ON_FLAG = -g #-DDEBUG_MEMORY DEBUG_OFF_FLAG = DEBUG_FLAG = ${DEBUG_OFF_FLAG} PROFILE_ON_FLAG = -pg -a -DUSE_GPROF_THREADS PROFILE_OFF_FLAG = PROFILE_FLAG = ${PROFILE_OFF_FLAG} OPTIMIZE_ON_FLAG = -O9 OPTIMIZE_OFF_FLAG = OPTIMIZE_FLAG = ${OPTIMIZE_ON_FLAG} COMPILE_FLAGS = -Wall ${DEBUG_FLAG} ${PLATFORM_COMPILE_FLAGS} ${PROFILE_FLAG} ${OPTIMIZE_FLAG} -I${ROOT_PATH} -I${ROOT_PATH}/Transcend/portaudio/pa_common COMPILE = ${GXX} ${COMPILE_FLAGS} -c EXE_LINK = ${EXE_LINKER} ${COMPILE_FLAGS} ${LINK_FLAGS} LIBRARY_LINK = ${LIBRARY_LINKER} cru # # Generic: # # Map all .cpp C++ and C files into .o object files # # $@ represents the name.o file # $< represents the name.cpp file # .cpp.o: ${COMPILE} -o $@ $< .c.o: ${COMPILE} -o $@ $< transcend-0.3+dfsg2.orig/Transcend/Makefile.GnuLinuxX860000640000175000017500000000165710133736155021401 0ustar pabspabs# # Modification History # # 2002-September-9 Jason Rohrer # Created. # # 2003-November-2 Jason Rohrer # Moved minorGems platform prefixes into platform-specific Makefile templates. # # 2004-September-26 Jason Rohrer # Added missing link flags. # ## # The common GnuLinuxX86 portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DLINUX # pthread library needed for linux # also need portaudio library (which in turn needs pthreads) PLATFORM_LINK_FLAGS = -L/usr/X11R6/lib -lGL -lglut -lGLU -lX11 -lXi -lXext -lXmu ${ROOT_PATH}/Transcend/portaudio/lib/libportaudio.a -lpthread # All platforms but OSX support g++ and need no linker hacks GXX=g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix transcend-0.3+dfsg2.orig/Transcend/Makefile.Solaris0000640000175000017500000000171610062106434020722 0ustar pabspabs# # Modification History # # 2002-September-9 Jason Rohrer # Created. # # 2002-November-14 Jason Rohrer # pthread *is* actually needed for solaris. # # 2003-May-21 Jason Rohrer # Added library needed for nanosleep (pointed out by a slashdotter). # # 2003-November-2 Jason Rohrer # Moved minorGems platform prefixes into platform-specific Makefile templates. # ## # The common Solaris portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DSOLARIS # all sorts of other libraries needed for Solaris, including pthread # -lrt is for nanosleep PLATFORM_LINK_FLAGS = -lsocket -lnsl -lresolv -lpthread -lrt # All platforms but OSX support g++ and need no linker hacks GXX=g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Linux PLATFORM_PATH = linux TIME_PLATFORM = Unix TIME_PLATFORM_PATH = unix DIRECTORY_PLATFORM = Unix DIRECTORY_PLATFORM_PATH = unix transcend-0.3+dfsg2.orig/Transcend/editors/0000750000175000017500000000000010305077141017314 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/editors/ObjectControlPointEditor.cpp0000640000175000017500000011373710302651654024771 0ustar pabspabs/* * Modification History * * 2004-August-25 Jason Rohrer * Created. * Copied from game/game.cpp * Added extra editing controls and parameterized grid/background color. * * 2004-August-27 Jason Rohrer * Added more editing features. * Added support for saving to file. * Added support for adding/removing triangles and border vertices. * * 2004-August-30 Jason Rohrer * Added safety features. * * 2004-October-13 Jason Rohrer * Added extra grid lines in fine movement mode. */ #include #include #include #include #include #include #include "minorGems/graphics/openGL/ScreenGL.h" #include "minorGems/graphics/openGL/SceneHandlerGL.h" #include "minorGems/util/random/StdRandomSource.h" #include "minorGems/util/stringUtils.h" #include "minorGems/system/Time.h" #include "minorGems/system/Thread.h" #include "minorGems/io/file/File.h" #include "Transcend/game/DrawableObject.h" #include "Transcend/game/ObjectParameterSpaceControlPoint.h" class EditorSceneHandler : public SceneHandlerGL, public KeyboardHandlerGL, public RedrawListenerGL { public: EditorSceneHandler(); virtual ~EditorSceneHandler(); ScreenGL *mScreen; // implements the SceneHandlerGL interface virtual void drawScene(); // implements the KeyboardHandlerGL interface virtual void keyPressed( unsigned char inKey, int inX, int inY ); virtual void specialKeyPressed( int inKey, int inX, int inY ); virtual void keyReleased( unsigned char inKey, int inX, int inY ); virtual void specialKeyReleased( int inKey, int inX, int inY ); // implements the RedrawListener interface virtual void fireRedraw(); protected: double mMaxXPosition; double mMinXPosition; double mMaxYPosition; double mMinYPosition; double mGridSpacing; Color *mBackgroundColor; Color *mGridColor; char mGridVisible; ObjectParameterSpaceControlPoint *mControlPoint; char mTriangleVertexEditMode; char mBorderVertexEditMode; char mWholeObjectMovementMode; int mCurrentTriangleVertexIndex; int mCurrentBorderVertexIndex; char mVertexGroupingLock; char mColorGroupingLock; Color *mClipboardColor; double mValueIncrement; double mMovementIncrement; }; EditorSceneHandler *sceneHandler; ScreenGL *screen; char *controlPointFileName = NULL; double baseViewZ = -10; // function that destroys object when exit is called. // exit is the only way to stop the GLUT-based ScreenGL void cleanUpAtExit() { printf( "exiting\n" ); delete sceneHandler; delete screen; if( controlPointFileName != NULL ) { delete [] controlPointFileName; } } int main( int inNumArgs, char **inArgs ) { #ifdef __mac__ // make sure working directory is the same as the directory // that the app resides in // this is especially important on the mac platform, which // doesn't set a proper working directory for double-clicked // app bundles // app arguments provided as a wxApp member // arg 0 is the path to the app executable char *appDirectoryPath = stringDuplicate( inArgs[0] ); char *appNamePointer = strstr( appDirectoryPath, "game1.app" ); // terminate full app path to get parent directory appNamePointer[0] = '\0'; chdir( appDirectoryPath ); delete [] appDirectoryPath; #endif // take a file name from our args if( inNumArgs > 1 ) { controlPointFileName = stringDuplicate( inArgs[1] ); } sceneHandler = new EditorSceneHandler(); // must pass args to GLUT before constructing the screen glutInit( &inNumArgs, inArgs ); screen = new ScreenGL( 300, 300, false, "editor", sceneHandler, NULL, sceneHandler ); sceneHandler->mScreen = screen; screen->addRedrawListener( sceneHandler ); Vector3D *move = new Vector3D( 0, 0, baseViewZ ); screen->moveView( move ); delete move; // register cleanup function, since screen->start() will never return atexit( cleanUpAtExit ); screen->start(); return 0; } EditorSceneHandler::EditorSceneHandler() : mMaxXPosition( 100 ), mMinXPosition( -100 ), mMaxYPosition( 100 ), mMinYPosition( -100 ), mGridSpacing( 1 ), mGridVisible( true ), mTriangleVertexEditMode( false ), mBorderVertexEditMode( false ), mWholeObjectMovementMode( false ), mCurrentTriangleVertexIndex( 0 ), mCurrentBorderVertexIndex( 0 ), mVertexGroupingLock( false ), mColorGroupingLock( false ), mClipboardColor( new Color( 0, 0, 0, 1.0 ) ), mValueIncrement( 0.1 ), mMovementIncrement( 1 ) { FILE *controlPointFile = fopen( controlPointFileName, "r" ); char error; mControlPoint = new ObjectParameterSpaceControlPoint( controlPointFile, &error ); if( controlPointFile != NULL ) { fclose( controlPointFile ); } FILE *gridColorFILE = fopen( "gridColor", "r" ); if( gridColorFILE != NULL ) { mGridColor = ObjectParameterSpaceControlPoint::readColorFromFile( gridColorFILE ); fclose( gridColorFILE ); } else { mGridColor = new Color( 0, 0, 0, 1 ); } FILE *backgroundColorFILE = fopen( "backgroundColor", "r" ); if( backgroundColorFILE != NULL ) { mBackgroundColor = ObjectParameterSpaceControlPoint::readColorFromFile( backgroundColorFILE ); fclose( backgroundColorFILE ); } else { mBackgroundColor = new Color( 0, 0, 0, 1 ); } } EditorSceneHandler::~EditorSceneHandler() { delete mControlPoint; delete mGridColor; delete mBackgroundColor; delete mClipboardColor; } void EditorSceneHandler::drawScene() { glClearColor( mBackgroundColor->r, mBackgroundColor->g, mBackgroundColor->b, mBackgroundColor->a ); glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); if( mGridVisible ) { glLineWidth( 2 ); glBegin( GL_LINES ); glColor4f( mGridColor->r, mGridColor->g, mGridColor->b, mGridColor->a ); for( double x=mMinXPosition; x<=mMaxXPosition; x+=mGridSpacing ) { glVertex2d( x, mMinYPosition ); glVertex2d( x, mMaxYPosition ); } for( double y=mMinYPosition; y<=mMaxYPosition; y+=mGridSpacing ) { glVertex2d( mMinXPosition, y ); glVertex2d( mMaxXPosition, y ); } if( mMovementIncrement < mGridSpacing ) { // draw additional transparent lines to show move increments glLineWidth( 1 ); glColor4f( mGridColor->r, mGridColor->g, mGridColor->b, mGridColor->a / 2 ); for( double x=mMinXPosition; x<=mMaxXPosition; x+=mMovementIncrement ) { glVertex2d( x, mMinYPosition ); glVertex2d( x, mMaxYPosition ); } for( double y=mMinYPosition; y<=mMaxYPosition; y+=mMovementIncrement ) { glVertex2d( mMinXPosition, y ); glVertex2d( mMaxXPosition, y ); } } // draw extra lines through origin glLineWidth( 2 ); glColor4f( 0, 1, 0, 1 ); glVertex2d( 0, mMinYPosition ); glVertex2d( 0, mMaxYPosition ); glVertex2d( mMinXPosition, 0 ); glVertex2d( mMaxYPosition, 0 ); glEnd(); // draw the clipboard color double x = 5 * mGridSpacing; double y = 5 * mGridSpacing; glBegin( GL_QUADS ); { glColor4f( mClipboardColor->r, mClipboardColor->g, mClipboardColor->b, mClipboardColor->a ); glVertex2d( x, y ); glVertex2d( x, y + mGridSpacing ); glVertex2d( x + mGridSpacing, y + mGridSpacing ); glVertex2d( x + mGridSpacing, y ); }; glEnd(); } int i; SimpleVector *pointObjects = mControlPoint->getDrawableObjects(); int numPointObjects = pointObjects->size(); int xOffset = 0; int yOffset = 0; Vector3D *offsetVector = new Vector3D( xOffset, yOffset, 0 ); // draw the bullets and enemies with no extra rotation Angle3D *zeroAngle = new Angle3D( 0, 0, 0 ); for( i=0; igetElement( i ) ); component->draw( 1, zeroAngle, offsetVector ); } delete zeroAngle; delete offsetVector; // delete the bullet and enemy objects for( i=0; igetElement( i ) ); } delete pointObjects; glLineWidth( 2 ); glBegin( GL_LINE_LOOP ); Vector3D *point = NULL; double boxRadius = mGridSpacing / 2; if( mTriangleVertexEditMode ) { point = new Vector3D( mControlPoint->mTriangleVertices[ mCurrentTriangleVertexIndex ] ); glColor4f( 1, 0, 0, 0.75 ); } else if( mBorderVertexEditMode ) { point = new Vector3D( mControlPoint->mBorderVertices[ mCurrentBorderVertexIndex ] ); glColor4f( .5, 0, 1, 0.75 ); } else if( mWholeObjectMovementMode ) { // take average of entire object (center) point = new Vector3D( 0, 0, 0 ); for( i=0; imNumBorderVertices; i++ ) { point->add( mControlPoint->mBorderVertices[ i ] ); } for( i=0; imNumTriangleVertices; i++ ) { point->add( mControlPoint->mTriangleVertices[ i ] ); } point->scale( 1.0 / ( mControlPoint->mNumTriangleVertices + mControlPoint->mNumBorderVertices ) ); // bigger box boxRadius *= 5; glColor4f( 0, 1, 0, .75 ); } if( point != NULL ) { glVertex2d( point->mX - boxRadius, point->mY - boxRadius ); glVertex2d( point->mX + boxRadius, point->mY - boxRadius ); glVertex2d( point->mX + boxRadius, point->mY + boxRadius ); glVertex2d( point->mX - boxRadius, point->mY + boxRadius ); delete point; } glEnd(); } void EditorSceneHandler::keyPressed( unsigned char inKey, int inX, int inY ) { if( inKey == ' ' ) { } else if( inKey == 't' || inKey == 'T' ) { // switch to triangle mode mTriangleVertexEditMode = ! mTriangleVertexEditMode; if( ! mTriangleVertexEditMode ) { mBorderVertexEditMode = true; printf( "Border mode\n" ); } else { mBorderVertexEditMode = false; printf( "Triangle mode\n" ); } mWholeObjectMovementMode = false; } else if( inKey == 'm' || inKey == 'M' ) { // exit from special modes mBorderVertexEditMode = false; mTriangleVertexEditMode = false; mWholeObjectMovementMode = true; printf( "Whole object movement mode\n" ); } else if( inKey == 'e' || inKey == 'E' ) { // exit from special modes mBorderVertexEditMode = false; mTriangleVertexEditMode = false; mWholeObjectMovementMode = false; printf( "Movement mode\n" ); } else if( inKey == 'v' || inKey == 'V' ) { // toggle grid mGridVisible = ! mGridVisible; } else if( inKey == 'l' || inKey == 'L' ) { // toggle lock mVertexGroupingLock = ! mVertexGroupingLock; printf( "Vertex grouping lock" ); if( mVertexGroupingLock ) { printf( " on\n" ); } else { printf( " off\n" ); } } else if( inKey == 'k' || inKey == 'K' ) { // toggle lock mColorGroupingLock = ! mColorGroupingLock; printf( "Color grouping lock" ); if( mColorGroupingLock ) { printf( " on\n" ); } else { printf( " off\n" ); } } else if( inKey == 'n' || inKey == 'N' ) { // cycle through vertices if( mBorderVertexEditMode ) { mCurrentBorderVertexIndex++; if( mCurrentBorderVertexIndex >= mControlPoint->mNumBorderVertices ) { mCurrentBorderVertexIndex = 0; } } else if( mTriangleVertexEditMode ) { mCurrentTriangleVertexIndex++; if( mCurrentTriangleVertexIndex >= mControlPoint->mNumTriangleVertices ) { mCurrentTriangleVertexIndex = 0; } } } else if( inKey == 'p' || inKey == 'P' ) { // cycle through vertices if( mBorderVertexEditMode ) { mCurrentBorderVertexIndex--; if( mCurrentBorderVertexIndex < 0 ) { mCurrentBorderVertexIndex = mControlPoint->mNumBorderVertices - 1; } } else if( mTriangleVertexEditMode ) { mCurrentTriangleVertexIndex--; if( mCurrentTriangleVertexIndex < 0 ) { mCurrentTriangleVertexIndex = mControlPoint->mNumTriangleVertices - 1; } } } else if( inKey == 'q' || inKey == 'Q' ) { // quit // save control point to be safe FILE *file = fopen( "rescue.txt", "w" ); if( file != NULL ) { mControlPoint->writeToFile( file ); fclose( file ); printf( "Saved control point to file: rescue.txt\n" ); } else { printf( "Failed to open rescue.txt for writing.\n" ); } ::exit( 0 ); } if( mBorderVertexEditMode || mTriangleVertexEditMode ) { Color *color; Vector3D *position; if( mBorderVertexEditMode ) { color = mControlPoint->mBorderVertexColors[ mCurrentBorderVertexIndex ]; position = mControlPoint->mBorderVertices[ mCurrentBorderVertexIndex ]; } else { color = mControlPoint->mTriangleVertexFillColors[ mCurrentTriangleVertexIndex ]; position = mControlPoint->mTriangleVertices[ mCurrentTriangleVertexIndex ]; } Color *oldColor = color->copy(); char colorChanged = false; if( inKey == 'o' || inKey == 'O' ) { // copy color to clipboard mClipboardColor->r = color->r; mClipboardColor->g = color->g; mClipboardColor->b = color->b; mClipboardColor->a = color->a; } else if( inKey == 'u' || inKey == 'U' ) { // copy clipboard to color color->r = mClipboardColor->r; color->g = mClipboardColor->g; color->b = mClipboardColor->b; color->a = mClipboardColor->a; colorChanged = true; } else if( inKey == 'i' || inKey == 'I' ) { // invert the color color->r = 1 - color->r; color->g = 1 - color->g; color->b = 1 - color->b; colorChanged = true; } else if( inKey == 'R' ) { color->r += mValueIncrement; if( color->r > 1 ) { color->r = 1; } colorChanged = true; } else if( inKey == 'G' ) { color->g += mValueIncrement; if( color->g > 1 ) { color->g = 1; } colorChanged = true; } else if( inKey == 'B' ) { color->b += mValueIncrement; if( color->b > 1 ) { color->b = 1; } colorChanged = true; } else if( inKey == 'A' ) { color->a += mValueIncrement; if( color->a > 1 ) { color->a = 1; } colorChanged = true; } else if( inKey == 'r' ) { color->r -= mValueIncrement; if( color->r < 0 ) { color->r = 0; } colorChanged = true; } else if( inKey == 'g' ) { color->g -= mValueIncrement; if( color->g < 0 ) { color->g = 0; } colorChanged = true; } else if( inKey == 'b' ) { color->b -= mValueIncrement; if( color->b < 0 ) { color->b = 0; } colorChanged = true; } else if( inKey == 'a' ) { color->a -= mValueIncrement; if( color->a < 0 ) { color->a = 0; } colorChanged = true; } if( colorChanged ) { printf( "Color = ( r=%.2f, g=%.2f, b=%.2f, a=%.2f )\n", color->r, color->g, color->b, color->a ); if( mVertexGroupingLock || mColorGroupingLock ) { // color other vertices that were in the same spot // or have the same color int i; for( i=0; imNumTriangleVertices; i++ ) { Vector3D *otherVertex = mControlPoint->mTriangleVertices[i]; Color *otherColor = mControlPoint->mTriangleVertexFillColors[i]; if( ( mVertexGroupingLock && position->getDistance( otherVertex ) <= 0.01 ) || ( mColorGroupingLock && oldColor->equals( otherColor ) ) ) { otherColor->r = color->r; otherColor->g = color->g; otherColor->b = color->b; otherColor->a = color->a; } } for( i=0; imNumBorderVertices; i++ ) { Vector3D *otherVertex = mControlPoint->mBorderVertices[i]; Color *otherColor = mControlPoint->mBorderVertexColors[i]; if( ( mVertexGroupingLock && position->getDistance( otherVertex ) <= 0.01 ) || ( mColorGroupingLock && oldColor->equals( otherColor ) ) ) { otherColor->r = color->r; otherColor->g = color->g; otherColor->b = color->b; otherColor->a = color->a; } } } } delete oldColor; if( inKey == 'y' || inKey == 'Y' ) { // add a new border vertex or triangle if( mTriangleVertexEditMode ) { int oldNumVertices = mControlPoint->mNumTriangleVertices; int newNumVertices = oldNumVertices + 3; Vector3D **oldTriangleVertices = mControlPoint->mTriangleVertices; Vector3D **newTriangleVertices = new Vector3D*[newNumVertices]; Color **oldTriangleVertexFillColors = mControlPoint->mTriangleVertexFillColors; Color **newTriangleVertexFillColors = new Color*[newNumVertices]; // copy the existing vertices and colors as pointers memcpy( newTriangleVertices, oldTriangleVertices, sizeof( Vector3D * ) * oldNumVertices ); memcpy( newTriangleVertexFillColors, oldTriangleVertexFillColors, sizeof( Color * ) * oldNumVertices ); // add a new default triangle with default colors newTriangleVertices[ oldNumVertices ] = new Vector3D( 5, 5, 0 ); newTriangleVertices[ oldNumVertices + 1 ] = new Vector3D( 5, 6, 0 ); newTriangleVertices[ oldNumVertices + 2 ] = new Vector3D( 6, 6, 0 ); newTriangleVertexFillColors[ oldNumVertices ] = new Color( 0, 0, 0, 1 ); newTriangleVertexFillColors[ oldNumVertices + 1] = new Color( 1, 0, 0, 1 ); newTriangleVertexFillColors[ oldNumVertices + 2 ] = new Color( 0, 1, 0, 1 ); // replace old arrays delete [] oldTriangleVertices; delete [] oldTriangleVertexFillColors; mControlPoint->mNumTriangleVertices = newNumVertices; mControlPoint->mTriangleVertices = newTriangleVertices; mControlPoint->mTriangleVertexFillColors = newTriangleVertexFillColors; mCurrentTriangleVertexIndex = newNumVertices - 1; } else if( mBorderVertexEditMode ) { int oldNumVertices = mControlPoint->mNumBorderVertices; int newNumVertices = oldNumVertices + 1; Vector3D **oldBorderVertices = mControlPoint->mBorderVertices; Vector3D **newBorderVertices = new Vector3D*[newNumVertices]; Color **oldBorderVertexColors = mControlPoint->mBorderVertexColors; Color **newBorderVertexColors = new Color*[newNumVertices]; // copy the existing vertices and colors as pointers memcpy( newBorderVertices, oldBorderVertices, sizeof( Vector3D * ) * oldNumVertices ); memcpy( newBorderVertexColors, oldBorderVertexColors, sizeof( Color * ) * oldNumVertices ); // add a new default border with default colors newBorderVertices[ oldNumVertices ] = new Vector3D( 5, 5, 0 ); newBorderVertexColors[ oldNumVertices ] = new Color( 0, 0, 0, 1 ); // replace old arrays delete [] oldBorderVertices; delete [] oldBorderVertexColors; mControlPoint->mNumBorderVertices = newNumVertices; mControlPoint->mBorderVertices = newBorderVertices; mControlPoint->mBorderVertexColors = newBorderVertexColors; mCurrentBorderVertexIndex = newNumVertices - 1; } } else if( inKey == 'h' || inKey == 'H' ) { printf( "Delete vertex: Are you sure? (y/n) " ); fflush( stdout ); char *buffer = new char[100]; scanf( "%99s", buffer ); char *lowerBuffer = stringToLowerCase( buffer ); delete [] buffer; char actuallyDestroy = false; if( strstr( lowerBuffer, "y" ) != NULL ) { actuallyDestroy = true; } delete [] lowerBuffer; // destroy current vertex or triangle if( actuallyDestroy && mTriangleVertexEditMode ) { int oldNumVertices = mControlPoint->mNumTriangleVertices; int newNumVertices = oldNumVertices - 3; int oldNumTriangles = oldNumVertices / 3; // make sure current vertex is first vertex in a triangle if( mCurrentTriangleVertexIndex % 3 != 0 ) { mCurrentTriangleVertexIndex -= ( mCurrentTriangleVertexIndex % 3 ); } Vector3D **oldTriangleVertices = mControlPoint->mTriangleVertices; Vector3D **newTriangleVertices = new Vector3D*[newNumVertices]; Color **oldTriangleVertexFillColors = mControlPoint->mTriangleVertexFillColors; Color **newTriangleVertexFillColors = new Color*[newNumVertices]; // copy triangles, skipping the one we are dropping int newIndex = 0; for( int oldIndex=0; oldIndexmNumTriangleVertices = newNumVertices; mControlPoint->mTriangleVertices = newTriangleVertices; mControlPoint->mTriangleVertexFillColors = newTriangleVertexFillColors; if( mCurrentTriangleVertexIndex >= newNumVertices ) { mCurrentTriangleVertexIndex = newNumVertices - 1; } } else if( actuallyDestroy && mBorderVertexEditMode ) { int oldNumVertices = mControlPoint->mNumBorderVertices; int newNumVertices = oldNumVertices - 1; Vector3D **oldBorderVertices = mControlPoint->mBorderVertices; Vector3D **newBorderVertices = new Vector3D*[newNumVertices]; Color **oldBorderVertexColors = mControlPoint->mBorderVertexColors; Color **newBorderVertexColors = new Color*[newNumVertices]; // copy the existing vertices and colors as pointers, // skipping the dropped vertex int newIndex = 0; for( int oldIndex=0; oldIndexmNumBorderVertices = newNumVertices; mControlPoint->mBorderVertices = newBorderVertices; mControlPoint->mBorderVertexColors = newBorderVertexColors; if( mCurrentBorderVertexIndex >= newNumVertices ) { mCurrentBorderVertexIndex = newNumVertices - 1; } } } } char borderWidthChanged = false; if( inKey == 'w' ) { mControlPoint->mBorderWidth -= 1; if( mControlPoint->mBorderWidth < 1 ) { mControlPoint->mBorderWidth = 1; } borderWidthChanged = true; } else if( inKey == 'W' ) { mControlPoint->mBorderWidth += 1; borderWidthChanged = true; } if( borderWidthChanged ) { printf( "Border width = %f\n", mControlPoint->mBorderWidth ); } char numRotatedCopiesChanged = false; if( inKey == 'c' ) { mControlPoint->mNumRotatedCopies -= 1; if( mControlPoint->mNumRotatedCopies < 0 ) { mControlPoint->mNumRotatedCopies = 0; } numRotatedCopiesChanged = true; } else if( inKey == 'C' ) { mControlPoint->mNumRotatedCopies += 1; numRotatedCopiesChanged = true; } if( numRotatedCopiesChanged ) { printf( "Num rotated copies= %f\n", mControlPoint->mNumRotatedCopies ); } char rotatedCopyScaleFactorChanged = false; if( inKey == 'x' ) { mControlPoint->mRotatedCopyScaleFactor -= mValueIncrement; if( mControlPoint->mRotatedCopyScaleFactor < 0 ) { mControlPoint->mRotatedCopyScaleFactor = 0; } rotatedCopyScaleFactorChanged = true; } else if( inKey == 'X' ) { mControlPoint->mRotatedCopyScaleFactor += mValueIncrement; rotatedCopyScaleFactorChanged = true; } if( rotatedCopyScaleFactorChanged ) { printf( "Rotated copy scale factor= %f\n", mControlPoint->mRotatedCopyScaleFactor ); } char rotatedCopyAngleScaleFactorChanged = false; if( inKey == 'd' ) { mControlPoint->mRotatedCopyAngleScaleFactor -= mValueIncrement; if( mControlPoint->mRotatedCopyAngleScaleFactor < 0 ) { mControlPoint->mRotatedCopyAngleScaleFactor = 0; } rotatedCopyAngleScaleFactorChanged = true; } else if( inKey == 'D' ) { mControlPoint->mRotatedCopyAngleScaleFactor += mValueIncrement; rotatedCopyAngleScaleFactorChanged = true; } if( rotatedCopyAngleScaleFactorChanged ) { printf( "Rotated copy anglescale factor= %f\n", mControlPoint->mRotatedCopyAngleScaleFactor ); } if( inKey == 'f' || inKey == 'F' ) { if( mValueIncrement == 0.01 ) { mValueIncrement = 0.1; mMovementIncrement = 1; } else { mValueIncrement = 0.01; mMovementIncrement = 0.1; } printf( "New movement increment = %f, new value increment = %f\n", mMovementIncrement, mValueIncrement ); } Vector3D *viewPosition = mScreen->getViewPosition(); if( inKey == 'z' ) { // zoom out viewPosition->mZ -= mGridSpacing * mMovementIncrement; } else if( inKey == 'Z' ) { // zoom in viewPosition->mZ += mGridSpacing * mMovementIncrement; } mScreen->setViewPosition( viewPosition ); delete viewPosition; if( inKey == 's' || inKey == 'S' ) { // ask for save location char *buffer = new char[100]; printf( "Saving -- Enter file name: " ); fflush( stdout); scanf( "%99s", buffer ); FILE *file = fopen( buffer, "w" ); if( file != NULL ) { mControlPoint->writeToFile( file ); fclose( file ); printf( "Saved control point to file: %s\n", buffer ); } else { printf( "Failed to open file for writing: %s\n", buffer ); } delete [] buffer; } } void EditorSceneHandler::keyReleased( unsigned char inKey, int inX, int inY ) { } void EditorSceneHandler::specialKeyPressed( int inKey, int inX, int inY ) { if( ! mBorderVertexEditMode && ! mTriangleVertexEditMode && ! mWholeObjectMovementMode ) { // move view Vector3D *viewPosition = mScreen->getViewPosition(); if( inKey == GLUT_KEY_UP ) { viewPosition->mY -= mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_DOWN ) { viewPosition->mY += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_LEFT ) { // zoom out viewPosition->mX += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_RIGHT ) { // zoom in viewPosition->mX -= mGridSpacing * mMovementIncrement; } mScreen->setViewPosition( viewPosition ); delete viewPosition; } else if( mWholeObjectMovementMode ) { int i; for( i=0; imNumTriangleVertices; i++ ) { Vector3D *vertex = mControlPoint->mTriangleVertices[i]; if( inKey == GLUT_KEY_UP ) { vertex->mY += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_DOWN ) { vertex->mY -= mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_LEFT ) { // zoom out vertex->mX += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_RIGHT ) { // zoom in vertex->mX -= mGridSpacing * mMovementIncrement; } } for( i=0; imNumBorderVertices; i++ ) { Vector3D *vertex = mControlPoint->mBorderVertices[i]; if( inKey == GLUT_KEY_UP ) { vertex->mY += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_DOWN ) { vertex->mY -= mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_LEFT ) { // zoom out vertex->mX += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_RIGHT ) { // zoom in vertex->mX -= mGridSpacing * mMovementIncrement; } } } else { Vector3D *vertex; if( mBorderVertexEditMode ) { vertex = mControlPoint->mBorderVertices[ mCurrentBorderVertexIndex ]; } else { vertex = mControlPoint->mTriangleVertices[ mCurrentTriangleVertexIndex ]; } Vector3D *oldPosition = new Vector3D( vertex ); if( inKey == GLUT_KEY_UP ) { vertex->mY += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_DOWN ) { vertex->mY -= mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_LEFT ) { // zoom out vertex->mX += mGridSpacing * mMovementIncrement; } else if( inKey == GLUT_KEY_RIGHT ) { // zoom in vertex->mX -= mGridSpacing * mMovementIncrement; } printf( "New vertex position = ( %f, %f )\n", vertex->mX, vertex->mY ); if( mVertexGroupingLock ) { // move other vertices that were in the same spot int i; for( i=0; imNumTriangleVertices; i++ ) { Vector3D *otherVertex = mControlPoint->mTriangleVertices[i]; if( oldPosition->getDistance( otherVertex ) <= 0.01 ) { otherVertex->mX = vertex->mX; otherVertex->mY = vertex->mY; } } for( i=0; imNumBorderVertices; i++ ) { Vector3D *otherVertex = mControlPoint->mBorderVertices[i]; if( oldPosition->getDistance( otherVertex ) <= 0.01 ) { otherVertex->mX = vertex->mX; otherVertex->mY = vertex->mY; } } } delete oldPosition; } } void EditorSceneHandler::specialKeyReleased( int inKey, int inX, int inY ) { } void EditorSceneHandler::fireRedraw() { } transcend-0.3+dfsg2.orig/Transcend/editors/Makefile.all0000640000175000017500000000266210302651654021536 0ustar pabspabs# # Modification History # # 2004-August-25 Jason Rohrer # Created. Copied from game/Makefile.all. # ## # The portion of editor Makefiles common to all # platforms. # Should not be made manually---used by game1/configure to build Makefiles. ## ROOT_PATH = ../.. GAME_PATH = ${ROOT_PATH}/Transcend/game LAYER_SOURCE = \ ObjectControlPointEditor.cpp \ ${GAME_PATH}/DrawableObject.cpp \ ${GAME_PATH}/NamedColorFactory.cpp \ ${GAME_PATH}/LevelDirectoryManager.cpp \ ${GAME_PATH}/ParameterSpaceControlPoint.cpp \ ${GAME_PATH}/ObjectParameterSpaceControlPoint.cpp LAYER_OBJECTS = ${LAYER_SOURCE:.cpp=.o} NEEDED_MINOR_GEMS_OBJECTS = \ ${SCREEN_GL_O} \ ${TYPE_IO_O} \ ${STRING_UTILS_O} \ ${STRING_BUFFER_OUTPUT_STREAM_O} \ ${PATH_O} \ ${TIME_O} \ ${THREAD_O} \ ${MUTEX_LOCK_O} TEST_SOURCE = TEST_OBJECTS = ${TEST_SOURCE:.cpp=.o} DEPENDENCY_FILE = Makefile.dependencies # targets all: objectControlPointEditor clean: rm -f ${DEPENDENCY_FILE} ${LAYER_OBJECTS} ${TEST_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} objectControlPointEditor objectControlPointEditor: ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${EXE_LINK} -o objectControlPointEditor ${LAYER_OBJECTS} ${NEEDED_MINOR_GEMS_OBJECTS} ${PLATFORM_LINK_FLAGS} # build the dependency file ${DEPENDENCY_FILE}: ${LAYER_SOURCE} ${TEST_SOURCE} rm -f ${DEPENDENCY_FILE} ${COMPILE} -MM ${LAYER_SOURCE} ${TEST_SOURCE} >> ${DEPENDENCY_FILE} include ${DEPENDENCY_FILE} transcend-0.3+dfsg2.orig/Transcend/editors/doc/0000750000175000017500000000000010305077141020061 5ustar pabspabstranscend-0.3+dfsg2.orig/Transcend/editors/doc/controls.txt0000640000175000017500000000333510302660731022472 0ustar pabspabsS -- saves the control point to file T -- toggle between border vertex and triangle vertex edit modes N -- select the next vertex M -- switch into whole-object movement mode E -- exit edit modes (switch to view movement mode) V -- toggle grid visibility L -- toggle vertex grouping lock (when locked, vertices that are in the same location move and are colored together) K -- toggle color grouping lock (when locked, colors that are the same location are adjusted together) F -- toggle adjustment increment (switch between 0.1 and 0.001 for values and 1 and 0.1 for movement) z,Z -- decrease,increase the view zoom c,C -- decrease,increase the number of rotated copies x,X -- decrease,increase the rotated copy scale factor d,D -- decrease,increase the scale factor for angles between rotated copies w,W -- decrease,increase the border width In triangle vertex or border vertex edit mode: r,R -- decrease,increase the red component of the selected vertex g,G -- decrease,increase the green component of the selected vertex b,B -- decrease,increase the blue component of the selected vertex a,A -- decrease,increase the alpha component of the selected vertex I -- invert the color of the currently selected vertex O -- copy the color of the currently selected vertex to the color clipboard U -- set the color of the currently selected vertex to the clipboard color Arrow keys -- move the selected vertex Y -- add a new triangle (3 new vertices) or border vertex, depending on the mode H -- remove the current triangle (3 vertices) or border vertex In whole-object movement mode Arrow keys -- move the whole object In view movement mode Arrow keys -- move the view transcend-0.3+dfsg2.orig/Transcend/Makefile.MinGW0000640000175000017500000000147510133736155020301 0ustar pabspabs# # Modification History # # 2003-November-2 Jason Rohrer # Created. # # 2003-November-10 Jason Rohrer # Removed pthread flag. # Changed LINUX flag to WIN_32 flag. # Added wsock32 library flag. # ## # The common MinGW (GNU for Win32) portion of Makefiles. # Should not be made manually---used by configure to build Makefiles. ## PLATFORM_COMPILE_FLAGS = -DWIN_32 # need various GL libraries, winmm, and portaudio PLATFORM_LINK_FLAGS = -lopengl32 -lglut32 -lglu32 ${ROOT_PATH}/Transcend/portaudio/lib/libportaudio.a -lwinmm # All platforms but OSX support g++ and need no linker hacks GXX=g++ LINK_FLAGS = ## # Platform-specific minorGems file path prefixes ## PLATFORM = Win32 PLATFORM_PATH = win32 TIME_PLATFORM = Win32 TIME_PLATFORM_PATH = win32 DIRECTORY_PLATFORM = Win32 DIRECTORY_PLATFORM_PATH = win32