From c24e32ce06259c88bf40e23ac8b2cc5c45ffdd2e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 12:32:46 -0800 Subject: [PATCH 01/17] basic ParticleTree and ParticleTreeElement classes --- libraries/octree/src/Octree.h | 1 - libraries/particles/src/ParticleTree.cpp | 20 +++++- libraries/particles/src/ParticleTree.h | 36 +++++++++++ .../particles/src/ParticleTreeElement.cpp | 64 +++++++++++++++++++ libraries/particles/src/ParticleTreeElement.h | 47 ++++++++++++++ 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 libraries/particles/src/ParticleTree.h create mode 100644 libraries/particles/src/ParticleTreeElement.cpp create mode 100644 libraries/particles/src/ParticleTreeElement.h diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 1cdd789a3c..36588efb9e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -250,7 +250,6 @@ public slots: protected: void deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData); - void readCodeColorBufferToTreeRecursion(OctreeElement* node, void* extraData); int encodeTreeBitstreamRecursion(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag, diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a8215ba4cf..99580e974b 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -1 +1,19 @@ -// ParticleTree.cpp \ No newline at end of file +// +// ParticleTree.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include "ParticleTree.h" + +ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) { + _rootNode = createNewElement(); +} + +ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) const { + ParticleTreeElement* newElement = new ParticleTreeElement(octalCode); + return newElement; +} + diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h new file mode 100644 index 0000000000..821e1b0b49 --- /dev/null +++ b/libraries/particles/src/ParticleTree.h @@ -0,0 +1,36 @@ +// +// ParticleTree.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__ParticleTree__ +#define __hifi__ParticleTree__ + +#include +#include +#include +#include +#include +#include +#include + +#include "ParticleTreeElement.h" +#include "VoxelPacketData.h" +#include "VoxelSceneStats.h" +#include "VoxelEditPacketSender.h" + +class ReadCodeColorBufferToTreeArgs; + +class ParticleTree : public Octree { + Q_OBJECT +public: + ParticleTree(bool shouldReaverage = false); + virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL) const; + ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; } +private: +}; + +#endif /* defined(__hifi__ParticleTree__) */ diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp new file mode 100644 index 0000000000..81bc491ade --- /dev/null +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -0,0 +1,64 @@ +// +// ParticleTreeElement.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include "ParticleTree.h" +#include "ParticleTreeElement.h" + +ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement() { + init(octalCode); +}; + +ParticleTreeElement::~ParticleTreeElement() { + _voxelMemoryUsage -= sizeof(ParticleTreeElement); +} + +// This will be called primarily on addChildAt(), which means we're adding a child of our +// own type to our own tree. This means we should initialize that child with any tree and type +// specific settings that our children must have. One example is out VoxelSystem, which +// we know must match ours. +OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) const { + ParticleTreeElement* newChild = new ParticleTreeElement(octalCode); + return newChild; +} + +void ParticleTreeElement::init(unsigned char* octalCode) { + OctreeElement::init(octalCode); + _voxelMemoryUsage += sizeof(ParticleTreeElement); +} + +bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const { + // will write to the encoded stream all of the contents of this element + return true; +} + + +int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args) { + + // will read from the encoded stream all of the contents of this element + return 0; +} + +// will average a "common reduced LOD view" from the the child elements... +void ParticleTreeElement::calculateAverageFromChildren() { + // nothing to do here yet... +} + +// will detect if children are leaves AND collapsable into the parent node +// and in that case will collapse children and make this node +// a leaf, returns TRUE if all the leaves are collapsed into a +// single node +bool ParticleTreeElement::collapseChildren() { + // nothing to do here yet... + return false; +} + diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h new file mode 100644 index 0000000000..8a35d6e622 --- /dev/null +++ b/libraries/particles/src/ParticleTreeElement.h @@ -0,0 +1,47 @@ +// +// ParticleTreeElement.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__ParticleTreeElement__ +#define __hifi__ParticleTreeElement__ + +#include +#include +#include + +class ParticleTree; +class ParticleTreeElement; + +class ParticleTreeElement : public OctreeElement { + friend class ParticleTree; // to allow createElement to new us... + + ParticleTreeElement(unsigned char* octalCode = NULL); + + virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const; + +public: + virtual ~ParticleTreeElement(); + virtual void init(unsigned char * octalCode); + + virtual bool hasContent() const; + virtual void splitChildren() {} + virtual bool requiresSplit() const { return false; } + virtual bool appendElementData(OctreePacketData* packetData) const; + virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); + virtual void calculateAverageFromChildren(); + virtual bool collapseChildren(); + virtual bool isRendered() const; + + // type safe versions of OctreeElement methods + ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); } + ParticleTreeElement* addChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::addChildAtIndex(index); } + +protected: +}; + +#endif /* defined(__hifi__ParticleTreeElement__) */ \ No newline at end of file From 37466251d4312f109ba4d2fd5b6acd55cf9bf0ee Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 13:25:50 -0800 Subject: [PATCH 02/17] added some scripts, latest particle server work --- assignment-client/CMakeLists.txt | 1 + assignment-client/src/scipts/bug.js | 3259 +++++++++++++++++ assignment-client/src/scipts/gameoflife.js | 181 + .../src/scipts/hk-buildings-2.js | 513 +++ assignment-client/src/scipts/hk-buildings.js | 478 +++ assignment-client/src/scipts/hk-platform.js | 280 ++ assignment-client/src/scipts/sphere.js | 360 ++ ...CMakeLists.txt.disabled => CMakeLists.txt} | 0 .../particle-server/src/ParticleServer.h | 2 +- libraries/particles/src/ParticleTree.h | 12 - .../particles/src/ParticleTreeElement.cpp | 2 - libraries/particles/src/ParticleTreeElement.h | 2 - 12 files changed, 5073 insertions(+), 17 deletions(-) create mode 100644 assignment-client/src/scipts/bug.js create mode 100644 assignment-client/src/scipts/gameoflife.js create mode 100644 assignment-client/src/scipts/hk-buildings-2.js create mode 100644 assignment-client/src/scipts/hk-buildings.js create mode 100644 assignment-client/src/scipts/hk-platform.js create mode 100644 assignment-client/src/scipts/sphere.js rename libraries/particle-server/{CMakeLists.txt.disabled => CMakeLists.txt} (100%) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 5ba5a1d4e4..27ef8f6540 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -26,6 +26,7 @@ link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) include_directories(${ROOT_DIR}/externals/civetweb/include) diff --git a/assignment-client/src/scipts/bug.js b/assignment-client/src/scipts/bug.js new file mode 100644 index 0000000000..58523dd16b --- /dev/null +++ b/assignment-client/src/scipts/bug.js @@ -0,0 +1,3259 @@ +/******************************************************************************************************************************/ +// glMatrix +/******************************************************************************************************************************/ + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; +} + +if(!GLMAT_ARRAY_TYPE) { + var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; +} + +if(!GLMAT_RANDOM) { + var GLMAT_RANDOM = Math.random; +} + +/** + * @class Common utilities + * @name glMatrix + */ +var glMatrix = {}; + +/** + * Sets the type of array used when creating new vectors and matricies + * + * @param {Type} type Array type, such as Float32Array or Array + */ +glMatrix.setMatrixArrayType = function(type) { + GLMAT_ARRAY_TYPE = type; +} + +if(typeof(exports) !== 'undefined') { + exports.glMatrix = glMatrix; +} + +var degree = Math.PI / 180; + +/** +* Convert Degree To Radian +* +* @param {Number} Angle in Degrees +*/ +glMatrix.toRadian = function(a){ + return a * degree; +} + + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +/** + * @class 3x3 Matrix + * @name mat3 + */ +var mat3 = {}; + +/** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ +mat3.create = function() { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Copies the upper-left 3x3 values into the given mat3. + * + * @param {mat3} out the receiving 3x3 matrix + * @param {mat4} a the source 4x4 matrix + * @returns {mat3} out + */ +mat3.fromMat4 = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[4]; + out[4] = a[5]; + out[5] = a[6]; + out[6] = a[8]; + out[7] = a[9]; + out[8] = a[10]; + return out; +}; + +/** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ +mat3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ +mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; +}; + +/** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; +}; + +/** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; +}; + +/** + * Calculates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ +mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; +}; + +/** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ +mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); +}; + +/** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ +mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; +}; + +/** + * Alias for {@link mat3.multiply} + * @function + */ +mat3.mul = mat3.multiply; + +/** + * Translate a mat3 by the given vector + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to translate + * @param {vec2} v vector to translate by + * @returns {mat3} out + */ +mat3.translate = function(out, a, v) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + x = v[0], y = v[1]; + + out[0] = a00; + out[1] = a01; + out[2] = a02; + + out[3] = a10; + out[4] = a11; + out[5] = a12; + + out[6] = x * a00 + y * a10 + a20; + out[7] = x * a01 + y * a11 + a21; + out[8] = x * a02 + y * a12 + a22; + return out; +}; + +/** + * Rotates a mat3 by the given angle + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ +mat3.rotate = function (out, a, rad) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + s = Math.sin(rad), + c = Math.cos(rad); + + out[0] = c * a00 + s * a10; + out[1] = c * a01 + s * a11; + out[2] = c * a02 + s * a12; + + out[3] = c * a10 - s * a00; + out[4] = c * a11 - s * a01; + out[5] = c * a12 - s * a02; + + out[6] = a20; + out[7] = a21; + out[8] = a22; + return out; +}; + +/** + * Scales the mat3 by the dimensions in the given vec2 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat3} out + **/ +mat3.scale = function(out, a, v) { + var x = v[0], y = v[1]; + + out[0] = x * a[0]; + out[1] = x * a[1]; + out[2] = x * a[2]; + + out[3] = y * a[3]; + out[4] = y * a[4]; + out[5] = y * a[5]; + + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; +}; + +/** + * Copies the values from a mat2d into a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat2d} a the matrix to copy + * @returns {mat3} out + **/ +mat3.fromMat2d = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = 0; + + out[3] = a[2]; + out[4] = a[3]; + out[5] = 0; + + out[6] = a[4]; + out[7] = a[5]; + out[8] = 1; + return out; +}; + +/** +* Calculates a 3x3 matrix from the given quaternion +* +* @param {mat3} out mat3 receiving operation result +* @param {quat} q Quaternion to create matrix from +* +* @returns {mat3} out +*/ +mat3.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[3] = xy + wz; + out[6] = xz - wy; + + out[1] = xy - wz; + out[4] = 1 - (xx + zz); + out[7] = yz + wx; + + out[2] = xz + wy; + out[5] = yz - wx; + out[8] = 1 - (xx + yy); + + return out; +}; + +/** +* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix +* +* @param {mat3} out mat3 receiving operation result +* @param {mat4} a Mat4 to derive the normal matrix from +* +* @returns {mat3} out +*/ +mat3.normalFromMat4 = function (out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + + out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + + out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + + return out; +}; + +/** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat3 = mat3; +} + + + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +/** + * @class 4x4 Matrix + * @name mat4 + */ +var mat4 = {}; + +/** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ +mat4.create = function() { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +/** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ +mat4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; + +/** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; +}; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; + +/** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; +}; + +/** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ +mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +}; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; + +/** + * Alias for {@link mat4.multiply} + * @function + */ +mat4.mul = mat4.multiply; + +/** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ +mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; +}; + +/** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ +mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < GLMAT_EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; + +/** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +}; + +/** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +}; + +/** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; +}; + +/** +* Calculates a 4x4 matrix from the given quaternion +* +* @param {mat4} out mat4 receiving operation result +* @param {quat} q Quaternion to create matrix from +* +* @returns {mat4} out +*/ +mat4.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; +}; + +/** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; + +/** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < GLMAT_EPSILON && + Math.abs(eyey - centery) < GLMAT_EPSILON && + Math.abs(eyez - centerz) < GLMAT_EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; + +/** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.mat4 = mat4; +} + + + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +/** + * @class 3 Dimensional Vector + * @name vec3 + */ +var vec3 = {}; + +/** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ +vec3.create = function() { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = 0; + out[1] = 0; + out[2] = 0; + return out; +}; + +/** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ +vec3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ +vec3.fromValues = function(x, y, z) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ +vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +}; + +/** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ +vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; +}; + +/** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +}; + +/** + * Subtracts vector b from vector a + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; +}; + +/** + * Alias for {@link vec3.subtract} + * @function + */ +vec3.sub = vec3.subtract; + +/** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; +}; + +/** + * Alias for {@link vec3.multiply} + * @function + */ +vec3.mul = vec3.multiply; + +/** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; +}; + +/** + * Alias for {@link vec3.divide} + * @function + */ +vec3.div = vec3.divide; + +/** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; +}; + +/** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; +}; + +/** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec3} out + */ +vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +}; + +/** + * Adds two vec3's after scaling the second operand by a scalar value + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec3} out + */ +vec3.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + return out; +}; + +/** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ +vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Alias for {@link vec3.distance} + * @function + */ +vec3.dist = vec3.distance; + +/** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ +vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; +}; + +/** + * Alias for {@link vec3.squaredDistance} + * @function + */ +vec3.sqrDist = vec3.squaredDistance; + +/** + * Calculates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ +vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); +}; + +/** + * Alias for {@link vec3.length} + * @function + */ +vec3.len = vec3.length; + +/** + * Calculates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; +}; + +/** + * Alias for {@link vec3.squaredLength} + * @function + */ +vec3.sqrLen = vec3.squaredLength; + +/** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ +vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; +}; + +/** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ +vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; +}; + +/** + * Calculates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ +vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +}; + +/** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ +vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +}; + +/** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ +vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; +}; + +/** + * Generates a random vector with the given scale + * + * @param {vec3} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec3} out + */ +vec3.random = function (out, scale) { + scale = scale || 1.0; + + var r = GLMAT_RANDOM() * 2.0 * Math.PI; + var z = (GLMAT_RANDOM() * 2.0) - 1.0; + var zScale = Math.sqrt(1.0-z*z) * scale; + + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; +}; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; + return out; +}; + +/** + * Transforms the vec3 with a mat3. + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m the 3x3 matrix to transform with + * @returns {vec3} out + */ +vec3.transformMat3 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; +}; + +/** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ +vec3.transformQuat = function(out, a, q) { + // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations + + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ +vec3.forEach = (function() { + var vec = vec3.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec3 = vec3; +} + +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +/** + * @class 4 Dimensional Vector + * @name vec4 + */ +var vec4 = {}; + +/** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ +vec4.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + return out; +}; + +/** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ +vec4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ +vec4.fromValues = function(x, y, z, w) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ +vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ +vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +}; + +/** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; +}; + +/** + * Subtracts vector b from vector a + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; +}; + +/** + * Alias for {@link vec4.subtract} + * @function + */ +vec4.sub = vec4.subtract; + +/** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; +}; + +/** + * Alias for {@link vec4.multiply} + * @function + */ +vec4.mul = vec4.multiply; + +/** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; +}; + +/** + * Alias for {@link vec4.divide} + * @function + */ +vec4.div = vec4.divide; + +/** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; +}; + +/** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ +vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; +}; + +/** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec4} out + */ +vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; +}; + +/** + * Adds two vec4's after scaling the second operand by a scalar value + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec4} out + */ +vec4.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + out[3] = a[3] + (b[3] * scale); + return out; +}; + +/** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ +vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Alias for {@link vec4.distance} + * @function + */ +vec4.dist = vec4.distance; + +/** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ +vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Alias for {@link vec4.squaredDistance} + * @function + */ +vec4.sqrDist = vec4.squaredDistance; + +/** + * Calculates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ +vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); +}; + +/** + * Alias for {@link vec4.length} + * @function + */ +vec4.len = vec4.length; + +/** + * Calculates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; +}; + +/** + * Alias for {@link vec4.squaredLength} + * @function + */ +vec4.sqrLen = vec4.squaredLength; + +/** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ +vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; +}; + +/** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ +vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + out[3] = a[3] * len; + } + return out; +}; + +/** + * Calculates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ +vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +}; + +/** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ +vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; +}; + +/** + * Generates a random vector with the given scale + * + * @param {vec4} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec4} out + */ +vec4.random = function (out, scale) { + scale = scale || 1.0; + + //TODO: This is a pretty awful way of doing this. Find something better. + out[0] = GLMAT_RANDOM(); + out[1] = GLMAT_RANDOM(); + out[2] = GLMAT_RANDOM(); + out[3] = GLMAT_RANDOM(); + vec4.normalize(out, out); + vec4.scale(out, out, scale); + return out; +}; + +/** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ +vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; +}; + +/** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ +vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; +}; + +/** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ +vec4.forEach = (function() { + var vec = vec4.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec4 = vec4; +} +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ + +/** + * @class Quaternion + * @name quat + */ +var quat = {}; + +/** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ +quat.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quaternion to represent the shortest rotation from one + * vector to another. + * + * Both vectors are assumed to be unit length. + * + * @param {quat} out the receiving quaternion. + * @param {vec3} a the initial vector + * @param {vec3} b the destination vector + * @returns {quat} out + */ +quat.rotationTo = (function() { + var tmpvec3 = vec3.create(); + var xUnitVec3 = vec3.fromValues(1,0,0); + var yUnitVec3 = vec3.fromValues(0,1,0); + + return function(out, a, b) { + var dot = vec3.dot(a, b); + if (dot < -0.999999) { + vec3.cross(tmpvec3, xUnitVec3, a); + if (vec3.length(tmpvec3) < 0.000001) + vec3.cross(tmpvec3, yUnitVec3, a); + vec3.normalize(tmpvec3, tmpvec3); + quat.setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + vec3.cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return quat.normalize(out, out); + } + }; +})(); + +/** + * Sets the specified quaternion with values corresponding to the given + * axes. Each axis is a vec3 and is expected to be unit length and + * perpendicular to all other specified axes. + * + * @param {vec3} view the vector representing the viewing direction + * @param {vec3} right the vector representing the local "right" direction + * @param {vec3} up the vector representing the local "up" direction + * @returns {quat} out + */ +quat.setAxes = (function() { + var matr = mat3.create(); + + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + + matr[2] = view[0]; + matr[5] = view[1]; + matr[8] = view[2]; + + return quat.normalize(out, quat.fromMat3(out, matr)); + }; +})(); + +/** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + * @function + */ +quat.clone = vec4.clone; + +/** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + * @function + */ +quat.fromValues = vec4.fromValues; + +/** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + * @function + */ +quat.copy = vec4.copy; + +/** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + * @function + */ +quat.set = vec4.set; + +/** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ +quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +}; + +/** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ +quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +}; + +/** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + * @function + */ +quat.add = vec4.add; + +/** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ +quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +}; + +/** + * Alias for {@link quat.multiply} + * @function + */ +quat.mul = quat.multiply; + +/** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {quat} out + * @function + */ +quat.scale = vec4.scale; + +/** + * Rotates a quaternion by the given angle about the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; +}; + +/** + * Rotates a quaternion by the given angle about the Y axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; +}; + +/** + * Rotates a quaternion by the given angle about the Z axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ +quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; +}; + +/** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ +quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; +}; + +/** + * Calculates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + * @function + */ +quat.dot = vec4.dot; + +/** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + * @function + */ +quat.lerp = vec4.lerp; + +/** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ +quat.slerp = function (out, a, b, t) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + var omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = ax * bx + ay * by + az * bz + aw * bw; + // adjust signs (if necessary) + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + // calculate coefficients + if ( (1.0 - cosom) > 0.000001 ) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + + return out; +}; + +/** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ +quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; +}; + +/** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ +quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; +}; + +/** + * Calculates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + * @function + */ +quat.length = vec4.length; + +/** + * Alias for {@link quat.length} + * @function + */ +quat.len = quat.length; + +/** + * Calculates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + * @function + */ +quat.squaredLength = vec4.squaredLength; + +/** + * Alias for {@link quat.squaredLength} + * @function + */ +quat.sqrLen = quat.squaredLength; + +/** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + * @function + */ +quat.normalize = vec4.normalize; + +/** + * Creates a quaternion from the given 3x3 rotation matrix. + * + * NOTE: The resultant quaternion is not normalized, so you should be sure + * to renormalize the quaternion yourself where necessary. + * + * @param {quat} out the receiving quaternion + * @param {mat3} m rotation matrix + * @returns {quat} out + * @function + */ +quat.fromMat3 = function(out, m) { + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + + if ( fTrace > 0.0 ) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; // 1/(4w) + out[0] = (m[7]-m[5])*fRoot; + out[1] = (m[2]-m[6])*fRoot; + out[2] = (m[3]-m[1])*fRoot; + } else { + // |w| <= 1/2 + var i = 0; + if ( m[4] > m[0] ) + i = 1; + if ( m[8] > m[i*3+i] ) + i = 2; + var j = (i+1)%3; + var k = (i+2)%3; + + fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[k*3+j] - m[j*3+k]) * fRoot; + out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; + out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; + } + + return out; +}; + +/** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.quat = quat; +} + + + + +/******************************************************************************************************************************/ +// HiFi Code Starts Here +/******************************************************************************************************************************/ + + +/** +x: "15869.555" [ 0.968601 ] +y: "306.329" [ 0.0186968 ] +z: "869.039" [ 0.0530419 ] +s: "256" [ 0.015625 ] +**/ + + +var DELETE_COPIES = 10; + +var PI_OVER_180 = 3.141592 / 180.0; +//var BUG_VOXEL_SIZE = 0.0625 / TREE_SCALE; +var BUG_VOXEL_SIZE = 1 / TREE_SCALE; +var bugPosition = { x: BUG_VOXEL_SIZE * 20.0, y: BUG_VOXEL_SIZE * 30.0, z: BUG_VOXEL_SIZE * 20.0 }; // not used in circle +var VOXELS_PER_BUG = 18; + +var birardaVoxelCorner = { x: 0.953125, y: 0.015625, z: 0.046875 }; +var birardaVoxelSize = 0.015625; +var birardaVoxelSizeHalf = birardaVoxelSize/2; + +//var bugPathCenter = { x: BUG_VOXEL_SIZE * 150.0, y: BUG_VOXEL_SIZE * 30.0, z: BUG_VOXEL_SIZE * 150.0 }; +//var bugPathRadius = BUG_VOXEL_SIZE * 140.0; +//var bugPathRadius = BUG_VOXEL_SIZE * 150.0; + +//var bugPathCenter = { x: 15997 / TREE_SCALE, y: BUG_VOXEL_SIZE * 30.0, z: 997 / TREE_SCALE }; +//var bugPathCenter = { x: 15997 / TREE_SCALE, y: 580 / TREE_SCALE, z: 997 / TREE_SCALE }; +var bugPathCenter = { + x: birardaVoxelCorner.x + birardaVoxelSizeHalf, + y: birardaVoxelCorner.y + birardaVoxelSize, + z: birardaVoxelCorner.z + birardaVoxelSizeHalf }; + +var bugPathRadius = 192 / TREE_SCALE; + +var bugPathTheta = 0.0 * PI_OVER_180; +var bugRotation = 0.0 * PI_OVER_180; +var bugAngleDelta = 0.2 * PI_OVER_180; + +function rotatePoint(point, angle) { + var axis = vec3.fromValues(0,1,0); + var q = quat.create(); + q = quat.setAxisAngle(q, axis, angle); + var rotation = mat4.create(); // a mat4 + rotation = mat4.fromQuat(rotation, q); + var quatPoint = vec4.fromValues(point.x, point.y, point.z, 0); + var newPoint = vec4.create(); // vec4 newPoint = vec4 * mat4 = quatPoint * rotation; + newPoint = vec4.transformMat4(newPoint, quatPoint, rotation); + var result = { x: newPoint[0], y: newPoint[1], z: newPoint[2] }; + return result; +} + + +var bugParts = { + + // tail + 0: { x: 0, y: 0, z: -3, r:51, g:51, b: 153 } , + 1: { x: 0, y: 0, z: -2, r:51, g:51, b: 153 } , + 2: { x: 0, y: 0, z: -1, r:51, g:51, b: 153 } , + + // body + 3: { x: 0, y: 0, z: 0, r:255, g:200, b: 0 } , + 4: { x: 0, y: 0, z: 1, r:255, g:200, b: 0 } , + + // head + 5: { x: 0, y: 0, z: 2, r:200, g:0, b: 0 } , + + // eyes + 6: { x: 1, y: 0, z: 3, r:64, g:64, b:64 } , + 7: { x: -1, y: 0, z: 3, r:64, g:64, b:64 } , + + // wings + 8: { x: 3, y: 1, z: 1, r:0, g:153, b:0 } , + 9: { x: 2, y: 1, z: 1, r:0, g:153, b:0 } , + 10: { x: 1, y: 0, z: 1, r:0, g:153, b:0 } , + 11: { x: -1, y: 0, z: 1, r:0, g:153, b:0 } , + 12: { x: -2, y: 1, z: 1, r:0, g:153, b:0 } , + 13: { x: -3, y: 1, z: 1, r:0, g:153, b:0 } , + + 14: { x: 2, y: -1, z: 0, r:153, g:200, b:0 } , + 15: { x: 1, y: -1, z: 0, r:153, g:200, b:0 } , + 16: { x: -1, y: -1, z: 0, r:153, g:200, b:0 } , + 17: { x: -2, y: -1, z: 0, r:153, g:200, b:0 } +}; + +function renderMovingBug() { + var x, y, z, s, red, green, blue; + var i; + var partAt, rotatedPartAt, offsetPartAt; + + var lastPosition = bugPosition; + var lastRotation = bugRotation; + var lastBugPathTheta = bugPathTheta; + for (var copy = 0; copy < DELETE_COPIES; copy++) { + + // Generate voxels for where bug used to be + for (i = 0; i < VOXELS_PER_BUG; i++) { + s = BUG_VOXEL_SIZE; + + partAt = { + x: bugParts[i].x * BUG_VOXEL_SIZE, + y: bugParts[i].y * BUG_VOXEL_SIZE, + z: bugParts[i].z * BUG_VOXEL_SIZE + }; + + rotatedPartAt = rotatePoint(partAt, lastRotation); + offsetPartAt = { + x : rotatedPartAt.x + lastPosition.x, + y : rotatedPartAt.y + lastPosition.y, + z : rotatedPartAt.z + lastPosition.z + }; + + x = offsetPartAt.x; + y = offsetPartAt.y; + z = offsetPartAt.z; + + red = bugParts[i].r; + green = bugParts[i].g; + blue = bugParts[i].b; + + Voxels.queueVoxelDelete(x, y, z, s); + } + + // do this in the reverse order as moving, because we're rolling backwards to delete the trail behind us + lastBugPathTheta -= bugAngleDelta; // move slightly + lastRotation += bugAngleDelta; // rotate slightly + + // If we loop past end of circle, just reset back into normal range + if (lastBugPathTheta < 0) { + lastBugPathTheta = (360.0 * PI_OVER_180); + lastRotation = (360.0 * PI_OVER_180); //? + } + + lastPosition.x = bugPathCenter.x + bugPathRadius * Math.cos(lastBugPathTheta); + lastPosition.z = bugPathCenter.z + bugPathRadius * Math.sin(lastBugPathTheta); + lastPosition.y = bugPathCenter.y; + + } + + // Move the bug... + //printf("bugPathCenter=(%f,%f,%f)\n", bugPathCenter.x, bugPathCenter.y, bugPathCenter.z); + + bugPathTheta += bugAngleDelta; // move slightly + bugRotation -= bugAngleDelta; // rotate slightly + + // If we loop past end of circle, just reset back into normal range + if (bugPathTheta > (360.0 * PI_OVER_180)) { + bugPathTheta = 0; + bugRotation = 0; + } + + x = bugPathCenter.x + bugPathRadius * Math.cos(bugPathTheta); + z = bugPathCenter.z + bugPathRadius * Math.sin(bugPathTheta); + y = bugPathCenter.y; + + bugPosition = { x: x, y: y, z: z}; + //printf("bugPathTheta=%f\n", bugPathTheta); + //printf("bugRotation=%f\n", bugRotation); + + //printf("bugPosition=(%f,%f,%f)\n", bugPosition.x, bugPosition.y, bugPosition.z); + // would be nice to add some randomness here... + + // Generate voxels for where bug is going to + for (i = 0; i < VOXELS_PER_BUG; i++) { + s = BUG_VOXEL_SIZE; + + partAt = { + x: bugParts[i].x * BUG_VOXEL_SIZE, + y: bugParts[i].y * BUG_VOXEL_SIZE, + z: bugParts[i].z * BUG_VOXEL_SIZE + }; + + rotatedPartAt = rotatePoint(partAt, bugRotation); + offsetPartAt = { + x : rotatedPartAt.x + bugPosition.x, + y : rotatedPartAt.y + bugPosition.y, + z : rotatedPartAt.z + bugPosition.z + }; + + x = offsetPartAt.x; + y = offsetPartAt.y; + z = offsetPartAt.z; + + red = bugParts[i].r; + green = bugParts[i].g; + blue = bugParts[i].b; + + //print("bugAt:" + x + ", "+ y + ", "+ z + "\n"); + + Voxels.queueDestructiveVoxelAdd(x, y, z, s, red, green, blue); + } +} + +function getRandomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +// register the call back so it fires before each data send +Voxels.setPacketsPerSecond(1000); +Agent.willSendVisualDataCallback.connect(renderMovingBug); diff --git a/assignment-client/src/scipts/gameoflife.js b/assignment-client/src/scipts/gameoflife.js new file mode 100644 index 0000000000..c5abb5d2a2 --- /dev/null +++ b/assignment-client/src/scipts/gameoflife.js @@ -0,0 +1,181 @@ +// Add your JavaScript for assignment below this line + +// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) + +var NUMBER_OF_CELLS_EACH_DIMENSION = 36; +var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; + +var currentCells = []; +var nextCells = []; + +var METER_LENGTH = 1 / TREE_SCALE; +var cellScale = 4 * METER_LENGTH; + +print("TREE_SCALE = " + TREE_SCALE + "\n"); + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + currentCells[i][j] = Math.floor(Math.random() * 2); + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function isNeighbourAlive(i, j) { + if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION + || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { + return 0; + } else { + return currentCells[i][j]; + } +} + +function currentPopulation() { + var i = 0; + var j = 0; + var population = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (currentCells[i][j]) { + population++; + } + } + } + return population; +} + +function updateCells() { + var i = 0; + var j = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + // figure out the number of live neighbours for the i-j cell + var liveNeighbours = + isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + + isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + + isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); + + if (currentCells[i][j]) { + // live cell + + if (liveNeighbours < 2) { + // rule #1 - under-population - this cell will die + // mark it zero to mark the change + nextCells[i][j] = 0; + } else if (liveNeighbours < 4) { + // rule #2 - this cell lives + // mark it -1 to mark no change + nextCells[i][j] = -1; + } else { + // rule #3 - overcrowding - this cell dies + // mark it zero to mark the change + nextCells[i][j] = 0; + } + } else { + // dead cell + if (liveNeighbours == 3) { + // rule #4 - reproduction - this cell revives + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + + // here's another random mutation... 0.5% of all dead cells reanimate + var REANIMATE_PROBABILITY = 0.005; + if (Math.random() < REANIMATE_PROBABILITY) { + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + // this cell stays dead + // mark it -1 for no change + nextCells[i][j] = -1; + } + } + } + + /* + if (Math.random() < 0.001) { + // Random mutation to keep things interesting in there. + nextCells[i][j] = 1; + } + */ + } + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +function sendNextCells() { + var cellsSent = 0; + for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to the state of this cell, send it + + // find the x and z position for this voxel, y = 0 + var x = towerPlatformCorner.x + j * cellScale; + var y = towerPlatformCorner.y; + var z = towerPlatformCorner.z + i * cellScale; + + // queue a packet to add a voxel for the new cell + var color; + if (nextCells[i][j] == 1) { + color = {r: 255, g: 255, b: 255}; + } else { + color = {r: 128, g: 128, b: 128}; + } + Voxels.queueDestructiveVoxelAdd(x, 0, z, cellScale, color.r, color.g, color.b); + cellsSent++; + } + } + } + return cellsSent; +} + +var sentFirstBoard = false; + +var visualCallbacks = 0; +function step() { + if (sentFirstBoard) { + // we've already sent the first full board, perform a step in time + updateCells(); + } else { + // this will be our first board send + sentFirstBoard = true; + } + + var cellsSent = sendNextCells(); + + visualCallbacks++; + print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + + " visualCallbacks:" + visualCallbacks + + " currentPopulation:" + currentPopulation() + + " cellsSent:" + cellsSent + + " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + + " PPS:" + Voxels.getLifetimePPSQueued() + "," + + " Bytes:" + Voxels.getLifetimeBytesQueued() + "," + + //" Sent packets:" + Voxels.getLifetimePacketsSent() + "," + + //" PPS:" + Voxels.getLifetimePPS() + "," + + //" Bytes:" + Voxels.getLifetimeBytesSent() + + "\n"); + +} + +Voxels.setPacketsPerSecond(500); +Agent.willSendVisualDataCallback.connect(step); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-buildings-2.js b/assignment-client/src/scipts/hk-buildings-2.js new file mode 100644 index 0000000000..d018957026 --- /dev/null +++ b/assignment-client/src/scipts/hk-buildings-2.js @@ -0,0 +1,513 @@ +// Ported from Stefan Gustavson's java implementation +// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf +// Read Stefan's excellent paper for details on how this code works. +// +// Sean McCullough banksean@gmail.com + +/** + * You can pass in a random number generator object if you like. + * It is assumed to have a random() method. + */ +var SimplexNoise = function(r) { + if (r == undefined) r = Math; + this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], + [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], + [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; + this.p = []; + for (var i=0; i<256; i++) { + this.p[i] = Math.floor(r.random()*256); + } + // To remove the need for index wrapping, double the permutation table length + this.perm = []; + for(var i=0; i<512; i++) { + this.perm[i]=this.p[i & 255]; + } + + // A lookup table to traverse the simplex around a given point in 4D. + // Details can be found where this table is used, in the 4D noise method. + this.simplex = [ + [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], + [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], + [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], + [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], + [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], + [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], + [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], + [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]]; +}; + +SimplexNoise.prototype.dot = function(g, x, y) { + return g[0]*x + g[1]*y; +}; + +SimplexNoise.prototype.noise = function(xin, yin) { + var n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + var F2 = 0.5*(Math.sqrt(3.0)-1.0); + var s = (xin+yin)*F2; // Hairy factor for 2D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var G2 = (3.0-Math.sqrt(3.0))/6.0; + var t = (i+j)*G2; + var X0 = i-t; // Unskew the cell origin back to (x,y) space + var Y0 = j-t; + var x0 = xin-X0; // The x,y distances from the cell origin + var y0 = yin-Y0; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + var ii = i & 255; + var jj = j & 255; + var gi0 = this.perm[ii+this.perm[jj]] % 12; + var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; + var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; + // Calculate the contribution from the three corners + var t0 = 0.5 - x0*x0-y0*y0; + if(t0<0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.5 - x1*x1-y1*y1; + if(t1<0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); + } + var t2 = 0.5 - x2*x2-y2*y2; + if(t2<0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); +}; + +// 3D simplex noise +SimplexNoise.prototype.noise3d = function(xin, yin, zin) { + var n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + var F3 = 1.0/3.0; + var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var k = Math.floor(zin+s); + var G3 = 1.0/6.0; // Very nice and simple unskew factor, too + var t = (i+j+k)*G3; + var X0 = i-t; // Unskew the cell origin back to (x,y,z) space + var Y0 = j-t; + var Z0 = k-t; + var x0 = xin-X0; // The x,y,z distances from the cell origin + var y0 = yin-Y0; + var z0 = zin-Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order + } + else { // x0 MINIMUM_BEACON_HEIGHT) { + buildingBeacons.push([beaconCorner, 255, getRandomInt(0, 2), getRandomInt(4, 16)]); + } else { + ignoredBeacons++; + } + } +} + + + +var visualCallbacks = 0; + +function glowBeacons() { + for (var i = 0; i < buildingBeacons.length; i++) { + // place the block for the beacon + // and fade the beacon from black to red + var beaconPosition = buildingBeacons[i][0]; + var beaconColor = { r: 0, g: 0, b: 0}; + + if (buildingBeacons[i][2] === 0) { + beaconColor.r = buildingBeacons[i][1]; + } else if (buildingBeacons[i][2] == 1) { + beaconColor.g = buildingBeacons[i][1]; + } else { + beaconColor.b = buildingBeacons[i][1]; + } + + Voxels.queueDestructiveVoxelAdd(beaconPosition.x * METER, beaconPosition.y * METER, beaconPosition.z * METER, + BEACON_SIZE, beaconColor.r, beaconColor.g, beaconColor.b + ); + + buildingBeacons[i][1] += buildingBeacons[i][3]; + if (buildingBeacons[i][1] < 1 ) { + buildingBeacons[i][1] = 1; + buildingBeacons[i][3] *= -1; + } else if (buildingBeacons[i][1] > 255) { + buildingBeacons[i][1] = 255; + buildingBeacons[i][3] *= -1; + } + } +} + +// flicker lights every 100 visual callbacks +var NUM_LIGHT_FLICKER_ITERATIONS = 100; +var LIGHT_FLICKER_PROBABILITY = 0.01; + +var lightsThisCycle = 0; +function cityLights() { + lightsThisCycle = 0; + if (visualCallbacks % NUM_LIGHT_FLICKER_ITERATIONS === 0) { + for (var i = 0; i < windows.length; i++) { + // check if we change the state of this window + if (Math.random() < LIGHT_FLICKER_PROBABILITY) { + var thisWindow = windows[i]; + + // flicker this window to the other state + var newColor = thisWindow[1] ? BLACK_COLOR : WINDOW_COLOR; + Voxels.queueDestructiveVoxelAdd(thisWindow[0].x, thisWindow[0].y, thisWindow[0].z, + BUILDING_BLOCK_SIZE, newColor[0], newColor[1], newColor[2] + ); + lightsThisCycle++; + + // change the state of this window in the array + thisWindow[1] = !thisWindow[1]; + windows[i] = thisWindow; + } + } + } +} + +var NUM_BUILDINGS = 300; +var createdBuildings = 0; + +var INNER_CITY_RADIUS = CITY_SIZE_METERS / 8; +var SUBURB_RADIUS = CITY_SIZE_METERS / 4; +var SUBURB_LEVEL = 0.4; +var OUTSKIRT_RADIUS = CITY_SIZE_METERS - (INNER_CITY_RADIUS + SUBURB_RADIUS); +var OUTSKIRT_LEVEL = 0.05; + +var INNER_CITY_MAX_BUILDING_HEIGHT_METERS = 512; +var INNER_CITY_MIN_BUILDING_HEIGHT_METERS = 64; + +var SUBURB_MAX_BUILDING_HEIGHT_METERS = 64; +var SUBURB_MIN_BUILDING_HEIGHT_METERS = 16; + +var OUTSKIRT_MAX_BUILDING_HEIGHT_METERS = 32; +var OUTSKIRT_MIN_BUILDING_HEIGHT_METERS = 16; + +var doneBuilding = "no"; +function makeBuildings() { + if (createdBuildings < NUM_BUILDINGS) { + var randomPlacement = buildingRandom.random(); + var buildingCorner = {x: -1, z: -1}; + + var minRadiusPush = 0; + var maxRadiusPush = 0; + var minHeight = 0; + var maxHeight = 0; + + // pick a corner point for a new building, loop until it is inside the city limits + while (buildingCorner.x < 0 || buildingCorner.x > CITY_SIZE_METERS || buildingCorner.z < 0 || buildingCorner.z > CITY_SIZE_METERS) { + if (randomPlacement < OUTSKIRT_LEVEL) { + minRadiusPush = INNER_CITY_RADIUS + SUBURB_RADIUS; + maxRadiusPush = CITY_SIZE_METERS; + + minHeight = OUTSKIRT_MIN_BUILDING_HEIGHT_METERS; + maxHeight = OUTSKIRT_MAX_BUILDING_HEIGHT_METERS; + } else if (randomPlacement < SUBURB_LEVEL) { + minRadiusPush = INNER_CITY_RADIUS; + maxRadiusPush = SUBURB_RADIUS; + + minHeight = SUBURB_MIN_BUILDING_HEIGHT_METERS; + maxHeight = SUBURB_MAX_BUILDING_HEIGHT_METERS; + } else { + minRadiusPush = 0; + maxRadiusPush = INNER_CITY_RADIUS; + + minHeight = INNER_CITY_MIN_BUILDING_HEIGHT_METERS; + maxHeight = INNER_CITY_MAX_BUILDING_HEIGHT_METERS; + } + + var radiusPush = getRandomFloat(minRadiusPush, maxRadiusPush); + var randomAngle = getRandomFloat(0, 360); + + buildingCorner.x = cityCenter.x + (radiusPush * Math.cos(randomAngle)); + buildingCorner.z = cityCenter.z + (radiusPush * Math.sin(randomAngle)); + } + + makeBuilding(buildingCorner, minHeight, maxHeight); + + createdBuildings++; + } else { + doneBuilding = "yes"; + glowBeacons(); + cityLights(); + } + + visualCallbacks++; + var wantDebug = false; + if (wantDebug){ + print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + + " visualCallbacks:" + visualCallbacks + + " doneBuilding:" + doneBuilding + + " buildingBeacons.length:" + buildingBeacons.length + + " ignored beacons:" + ignoredBeacons + + " lightsThisCycle:" + lightsThisCycle + + " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + + " PPS:" + Voxels.getLifetimePPSQueued() + "," + + //" BPS:" + Voxels.getLifetimeBPSQueued() + "," + + " Sent packets:" + Voxels.getLifetimePacketsSent() + "," + + " PPS:" + Voxels.getLifetimePPS() + "," + + //" BPS:" + Voxels.getLifetimeBPS() + + "\n"); + } + +} + +Voxels.setPacketsPerSecond(500); + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(makeBuildings); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-buildings.js b/assignment-client/src/scipts/hk-buildings.js new file mode 100644 index 0000000000..bcef92e6ac --- /dev/null +++ b/assignment-client/src/scipts/hk-buildings.js @@ -0,0 +1,478 @@ +// Ported from Stefan Gustavson's java implementation +// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf +// Read Stefan's excellent paper for details on how this code works. +// +// Sean McCullough banksean@gmail.com + +/** + * You can pass in a random number generator object if you like. + * It is assumed to have a random() method. + */ +var SimplexNoise = function(r) { + if (r == undefined) r = Math; + this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], + [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], + [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; + this.p = []; + for (var i=0; i<256; i++) { + this.p[i] = Math.floor(r.random()*256); + } + // To remove the need for index wrapping, double the permutation table length + this.perm = []; + for(var i=0; i<512; i++) { + this.perm[i]=this.p[i & 255]; + } + + // A lookup table to traverse the simplex around a given point in 4D. + // Details can be found where this table is used, in the 4D noise method. + this.simplex = [ + [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], + [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], + [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], + [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], + [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], + [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], + [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], + [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]]; +}; + +SimplexNoise.prototype.dot = function(g, x, y) { + return g[0]*x + g[1]*y; +}; + +SimplexNoise.prototype.noise = function(xin, yin) { + var n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + var F2 = 0.5*(Math.sqrt(3.0)-1.0); + var s = (xin+yin)*F2; // Hairy factor for 2D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var G2 = (3.0-Math.sqrt(3.0))/6.0; + var t = (i+j)*G2; + var X0 = i-t; // Unskew the cell origin back to (x,y) space + var Y0 = j-t; + var x0 = xin-X0; // The x,y distances from the cell origin + var y0 = yin-Y0; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + var y1 = y0 - j1 + G2; + var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + var y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + var ii = i & 255; + var jj = j & 255; + var gi0 = this.perm[ii+this.perm[jj]] % 12; + var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; + var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; + // Calculate the contribution from the three corners + var t0 = 0.5 - x0*x0-y0*y0; + if(t0<0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + var t1 = 0.5 - x1*x1-y1*y1; + if(t1<0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); + } + var t2 = 0.5 - x2*x2-y2*y2; + if(t2<0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); +}; + +// 3D simplex noise +SimplexNoise.prototype.noise3d = function(xin, yin, zin) { + var n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + var F3 = 1.0/3.0; + var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D + var i = Math.floor(xin+s); + var j = Math.floor(yin+s); + var k = Math.floor(zin+s); + var G3 = 1.0/6.0; // Very nice and simple unskew factor, too + var t = (i+j+k)*G3; + var X0 = i-t; // Unskew the cell origin back to (x,y,z) space + var Y0 = j-t; + var Z0 = k-t; + var x0 = xin-X0; // The x,y,z distances from the cell origin + var y0 = yin-Y0; + var z0 = zin-Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order + } + else { // x0 255) { + buildingBeacons[i][1] = 255; + buildingBeacons[i][3] *= -1; + } + } +} + +// flicker lights every 1000 visual callbacks +var NUM_LIGHT_FLICKER_ITERATIONS = 100; +var LIGHT_FLICKER_PROBABILITY = 0.01; + +function cityLights() { + if (visualCallbacks % NUM_LIGHT_FLICKER_ITERATIONS === 0) { + for (var i = 0; i < windows.length; i++) { + // check if we change the state of this window + if (Math.random() < LIGHT_FLICKER_PROBABILITY) { + var thisWindow = windows[i]; + + // flicker this window to the other state + var newColor = thisWindow[1] ? BLACK_COLOR : WINDOW_COLOR; + Voxels.queueDestructiveVoxelAdd(thisWindow[0].x, thisWindow[0].y, thisWindow[0].z, + BUILDING_BLOCK_SIZE, newColor[0], newColor[1], newColor[2] + ); + + // change the state of this window in the array + thisWindow[1] = !thisWindow[1]; + windows[i] = thisWindow; + } + } + } +} + +var NUM_BUILDINGS = 300; +var createdBuildings = 0; + +var INNER_CITY_RADIUS = CITY_SIZE_METERS / 8; +var SUBURB_RADIUS = CITY_SIZE_METERS / 4; +var SUBURB_LEVEL = 0.4; +var OUTSKIRT_RADIUS = CITY_SIZE_METERS - (INNER_CITY_RADIUS + SUBURB_RADIUS); +var OUTSKIRT_LEVEL = 0.05; + +var INNER_CITY_MAX_BUILDING_HEIGHT_METERS = 512; +var INNER_CITY_MIN_BUILDING_HEIGHT_METERS = 64; + +var SUBURB_MAX_BUILDING_HEIGHT_METERS = 64; +var SUBURB_MIN_BUILDING_HEIGHT_METERS = 16; + +var OUTSKIRT_MAX_BUILDING_HEIGHT_METERS = 32; +var OUTSKIRT_MIN_BUILDING_HEIGHT_METERS = 16; + +function makeBuildings() { + if (createdBuildings < NUM_BUILDINGS) { + var randomPlacement = buildingRandom.random(); + var buildingCorner = {x: -1, z: -1}; + + var minRadiusPush = 0; + var maxRadiusPush = 0; + var minHeight = 0; + var maxHeight = 0; + + // pick a corner point for a new building, loop until it is inside the city limits + while (buildingCorner.x < 0 || buildingCorner.x > CITY_SIZE_METERS || buildingCorner.z < 0 || buildingCorner.z > CITY_SIZE_METERS) { + if (randomPlacement < OUTSKIRT_LEVEL) { + minRadiusPush = INNER_CITY_RADIUS + SUBURB_RADIUS; + maxRadiusPush = CITY_SIZE_METERS; + + minHeight = OUTSKIRT_MIN_BUILDING_HEIGHT_METERS; + maxHeight = OUTSKIRT_MAX_BUILDING_HEIGHT_METERS; + } else if (randomPlacement < SUBURB_LEVEL) { + minRadiusPush = INNER_CITY_RADIUS; + maxRadiusPush = SUBURB_RADIUS; + + minHeight = SUBURB_MIN_BUILDING_HEIGHT_METERS; + maxHeight = SUBURB_MAX_BUILDING_HEIGHT_METERS; + } else { + minRadiusPush = 0; + maxRadiusPush = INNER_CITY_RADIUS; + + minHeight = INNER_CITY_MIN_BUILDING_HEIGHT_METERS; + maxHeight = INNER_CITY_MAX_BUILDING_HEIGHT_METERS; + } + + var radiusPush = getRandomFloat(minRadiusPush, maxRadiusPush); + var randomAngle = getRandomFloat(0, 360); + + buildingCorner.x = cityCenter.x + (radiusPush * Math.cos(randomAngle)); + buildingCorner.z = cityCenter.z + (radiusPush * Math.sin(randomAngle)); + } + + makeBuilding(buildingCorner, minHeight, maxHeight); + + createdBuildings++; + } else { + glowBeacons(); + cityLights(); + } + + visualCallbacks++; +} + +Voxels.setPacketsPerSecond(200); + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(makeBuildings); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-platform.js b/assignment-client/src/scipts/hk-platform.js new file mode 100644 index 0000000000..6ced861f81 --- /dev/null +++ b/assignment-client/src/scipts/hk-platform.js @@ -0,0 +1,280 @@ +var wantDebug = false; + +var METER = 1.0 / TREE_SCALE; + +var LIVE_CELL_COLOR = {r: 255, g: 255, b: 255}; + + +var towerBaseLocation = { x: 2800 * METER, y: 0 * METER, z: 2240 * METER}; +var towerTop = 352 * METER; // the y location of top of tower +var towerBrickSize = 16 * METER; +var towerColor = { r: 128, g: 128, b: 255}; +var towerPlatformWidthInBricks = 36; // 9; +var towerPlatformOffsetInBricks = 16; // 4; +var towerPlatformBrickSize = 4 * METER; + +var towerPlatformCorner = { + x: towerBaseLocation.x - (towerPlatformOffsetInBricks * towerPlatformBrickSize), + y: towerTop, + z: towerBaseLocation.z - (towerPlatformOffsetInBricks * towerPlatformBrickSize) }; + + +var visualCallbacks = 0; +var platformBuilt = false; + +// Note: 16 meters, 9x9 = 660 bytes +// Note: 8 meters, 18x18 (324) = 2940 bytes... +// 8 meters = ~9 bytes , 166 per packet + +function buildPlatform() { + // Cut hole in ground for tower + for (var y = towerBaseLocation.y; y <= towerTop; y += towerBrickSize) { + Voxels.queueVoxelDelete(towerBaseLocation.x, y, towerBaseLocation.z, towerBrickSize); + } + + // Build the tower + for (var y = towerBaseLocation.y; y <= towerTop; y += towerBrickSize) { + Voxels.queueDestructiveVoxelAdd(towerBaseLocation.x, y, towerBaseLocation.z, + towerBrickSize, towerColor.r, towerColor.g, towerColor.b); + } + + /** + // Build the platform - this is actually handled buy the game of life board + for (var x = 0; x < towerPlatformWidthInBricks; x++) { + for (var z = 0; z < towerPlatformWidthInBricks; z++) { + var platformBrick = { + x: towerPlatformCorner.x + (x * towerPlatformBrickSize), + y: towerPlatformCorner.y, + z: towerPlatformCorner.z + (z * towerPlatformBrickSize) }; + + Voxels.queueDestructiveVoxelAdd(platformBrick.x, platformBrick.y, platformBrick.z, + towerPlatformBrickSize, towerColor.r, towerColor.g, towerColor.b); + } + } + **/ +} + + +// Add your JavaScript for assignment below this line + +// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) + +var NUMBER_OF_CELLS_EACH_DIMENSION = towerPlatformWidthInBricks; +var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; + +var currentCells = []; +var nextCells = []; + +print("TREE_SCALE = " + TREE_SCALE + "\n"); + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + currentCells[i][j] = Math.floor(Math.random() * 2); + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function currentPopulation() { + var i = 0; + var j = 0; + var population = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (currentCells[i][j]) { + population++; + } + } + } + return population; +} + + +function isNeighbourAlive(i, j) { + if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION + || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { + return 0; + } else { + return currentCells[i][j]; + } +} + +function updateCells() { + var i = 0; + var j = 0; + + var REANIMATE_PROBABILITY = 0.005; + + var population = currentPopulation(); + var totalPossiblePopulation = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; + // if current population is below 5% of total possible population, increase REANIMATE_PROBABILITY by 2x + if (population < (totalPossiblePopulation * 0.05)) { + REANIMATE_PROBABILITY = REANIMATE_PROBABILITY * 2; + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + // figure out the number of live neighbours for the i-j cell + var liveNeighbours = + isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + + isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + + isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); + + if (currentCells[i][j]) { + // live cell + + if (liveNeighbours < 2) { + // rule #1 - under-population - this cell will die + // mark it zero to mark the change + nextCells[i][j] = 0; + } else if (liveNeighbours < 4) { + // rule #2 - this cell lives + // mark it -1 to mark no change + nextCells[i][j] = -1; + } else { + // rule #3 - overcrowding - this cell dies + // mark it zero to mark the change + nextCells[i][j] = 0; + } + } else { + // dead cell + if (liveNeighbours == 3) { + // rule #4 - reproduction - this cell revives + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + + // here's another random mutation... 0.5% of all dead cells reanimate + if (Math.random() < REANIMATE_PROBABILITY) { + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + // this cell stays dead + // mark it -1 for no change + nextCells[i][j] = -1; + } + } + } + + /* + if (Math.random() < 0.001) { + // Random mutation to keep things interesting in there. + nextCells[i][j] = 1; + } + */ + } + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +var fadeRatio = 0.0; +var fadeRatioAdjust = 0.25; + +function sendNextCells() { + var cellsSent = 0; + for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to the state of this cell, send it + + // find the x and z position for this voxel, y = 0 + var x = towerPlatformCorner.x + j * towerPlatformBrickSize; + var y = towerPlatformCorner.y; + var z = towerPlatformCorner.z + i * towerPlatformBrickSize; + + // queue a packet to add a voxel for the new cell + var ratio = fadeRatio; + if (ratio > 1) { + ratio = 1; + } + + var fromColor, toColor; + var color = LIVE_CELL_COLOR; + if (nextCells[i][j] == 1) { + //print("to LIVE from tower\n"); + + color.r = Math.round((127 * ratio) + 128); + color.g = Math.round((127 * ratio) + 128); + color.b = 255; + + } else { + //print("to tower from LIVE\n"); + + + color.r = Math.round((127 * (1-ratio)) + 128); + color.g = Math.round((127 * (1-ratio)) + 128); + color.b = 255; + } + + + if (color.r < 100 || color.r > 255) { + print("ratio: " + ratio + " " + + "color: " + color.r + ", " + + color.g + ", " + + color.b + " " + + "\n"); + } + Voxels.queueDestructiveVoxelAdd(x, y, z, towerPlatformBrickSize, color.r, color.g, color.b); + cellsSent++; + } + } + } + return cellsSent; +} + +var visualCallbacks = 0; + +function animatePlatform() { + if (!platformBuilt) { + buildPlatform(); + platformBuilt = true; + } else { + fadeRatio += fadeRatioAdjust; + var cellsSent = sendNextCells(); + + //print("fadeRatio: " + fadeRatio + "\n") + if (fadeRatio >= 1.0) { + updateCells(); // send the initial game of life cells + fadeRatio = 0.0; + } + } + + visualCallbacks++; + + if (wantDebug) { + print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + + " visualCallbacks:" + visualCallbacks + + " currentPopulation:" + currentPopulation() + + " cellsSent:" + cellsSent + + " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + + " PPS:" + Voxels.getLifetimePPSQueued() + "," + + " Bytes:" + Voxels.getLifetimeBytesQueued() + "," + + //" Sent packets:" + Voxels.getLifetimePacketsSent() + "," + + //" PPS:" + Voxels.getLifetimePPS() + "," + + //" Bytes:" + Voxels.getLifetimeBytesSent() + + "\n"); + } + +} + +Voxels.setPacketsPerSecond(500); + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(animatePlatform); \ No newline at end of file diff --git a/assignment-client/src/scipts/sphere.js b/assignment-client/src/scipts/sphere.js new file mode 100644 index 0000000000..796f55f054 --- /dev/null +++ b/assignment-client/src/scipts/sphere.js @@ -0,0 +1,360 @@ +var oldMap= [ +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,1,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0], +[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +]; + +var map = [ +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",";","t","@","G","."," ","f","L","1","i","t","L","G","@","@","8","@","@","@","@","@","@","@","@","@","@","@","8","8","8","0","@","@","i"," "," "," "," ","."," "," "," "," "," "," ",".",".",".","i","i","C","@","0","@","f",":","1","i","i","t","f","i",":","1",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","."," "," "," "," ",".",";"," "," ",":",":","f","@","0","@","C",";"," ",".","f","@","C","L","0","@","@","1","f","G","8","@","@","@","@","@","@","@","t","i",";",";",":",".",":",";","@","@","@","@","0",";",":"," ","t","@",".",".","0","@","@","f","t","@","8","8","@","@","@","@","8","0","G","8","@","1","1","G",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","f",":","C","f","t","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","."," "," "," "," ",".","i","@","1",";"," "," "," "," "," "," ",":","C","@","@","8","@","0","@",";",";","@","i"," ",".",":",":",":",";","t","1","C","@","8","@","@","@","L","C","0","C","i","."," "," ","i","@","i","L","@","8","8","i","f",":",".","t","@","1",".","i","@","G","@","@","8","@","@","@","8","@","@","@","0","0","@","@","@","@","@","0",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," ",";",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," ",".","f","C",".","L","@","G","G","@","L"," "," ","t","1",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","C",".",";","@",":","i","@","0","."," ",";","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","f",";","i"," "," ",":","1","L","L","1","."," "," "," "," "," ","i","t","f","@","@","C","."," "," "," "," "," ",".","i","G","@","8","@","0","1",".","t","@","@","1","1","@","@","@","@","0","0","8","@","@","0","@","L",":","."," ",".","C","G","G","G","@","0","@","@","8","@","0","@","@","@","@","@","@","C","1","i","t","@","8","@","@","@","8","@","1",":",":","i","f","t","t","i","."," ",".","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",":"," ",".","i",".","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@","@","@","@",".",".","@","@","@","@",";"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","@","@","t","1","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","1","G","@","1","L","@",";"," "," "," ",".","1","@","1"," "," "," "," "," "," "," "," "," ",".","L","@",".",".","@","f","."," ",".","@","@","L","G","0","8","8","L",";","f","@","8","@","@","8","@","@","@","@","t","0","@","t",":"," "," "," ","f","@","@","0","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","8","@","@","@","0","8","@","1","i","i","L","@","."," ",".",";","L","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ",":","t","t","."," ","L","C"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","@","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",";","@","@","@","L",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","t","f","@","i",".","f",":","."," ",".","."," "," "," "," "," "," "," "," ",".","1"," "," ",".","1",".","L",";"," ","i","@","@",":"," ",":","@","@","L","8","i",":","i","1","1","1","f","C","@","8","@","t",";",":",".","."," ","."," "," "," "," "," "," ",".","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t",".",".","t","@","@","@","0","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","f",";",";","G","1"," "," ",".",".",".","i","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," "," "," ","i","C","1",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i",".",";","0","@","1","."," "," ",".","f",";"," "," "," "," "," ","."," "," "," "," "," "," "," "," ",";","i","."," "," "," "," ",".",".",".","L","@","f","C","@","@","@","@","@","@","8","@","f",":","."," "," "," "," "," "," "," "," ",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","i","0","@","@","0","@","@","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@",";",".",".",".","."," "," ","."," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","t","i","t","C",".",".","i","8","G","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","1","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","L","@","0","@","i"," "," ","i","C","."," "," "," ",":","i"," ",".",":","."," "," "," ",".","."," "," ","."," ",".",":"," "," ",";","@",".",":","@","8","C","@","@","@","@","@","C","L","."," ",".","i","1","f","8","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","8","@","0","f","L","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";"," ",":",":",":","."," ","."," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","L","@","@","8","8","0",":","1","@","8","8","0","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","G","@","@","@","@","@","@","@","@","@","@","f","G","C","t","1","1",";",":","."," ",".",";","@","L","1",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","t",";","1","C","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","f","G","@","@","f",";",";","0","L","t","@","."," ","i",";","i",";","t","@","8","."," ","1","@","@","."," "," "," "," ",".",".",".","C","L",";","1","C","C","@","@","@",":",".","."," "," ",".","f","@","0","0","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","i","G","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".",";",".","L","@","C","C","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","."," "," ",".",":","i","t","@","@","@","@","8","@","@","@","@","@","@","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","1","t","L",";",";","i","@","@","@","t","."," "," "," "," "," "," "," "," ",".",";","t","i","."," "," "," "," "," "," "," "," "," "," "," ","L","1"," ",".","i","@","@","@","@","@","@","@","@","@","8","0","8","8","0","8","0","8","8","@","0",".",";","@","0","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","@","@","C","@","0","0","0","8","0","G","f","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","@","i",".","."," "," "," "," ","1","G","C","C","0","0","f","G","@","@","i",".","."," ",".","i","1","f","@","C","."," "," "," ",".","i","@","@",";","i","@","@","8","@","@","8","@","8","f","f","t","."," "," "," "," "," "," ","."," ","i","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","i","f","i","L","G",":"," ","i","f","L","8","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," ","f","i","f","@","0","@","@","8","@","@","8","@","@","@","8","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","L","8","0","0","@","C",":","C","@","8","@","@","L","L","@","i"," ",":","C","@","@","8","0","0","@","@","."," "," "," "," ",".",";","i","f","L","@","0","t","i"," ",".","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","G","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","."," ",":","f","@","8","0","@","@","@","@","@","@","@","@","8","8","8","0","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@",";",".",".",":","."," "," ",":","0",":"," "," "," ",".","i","t",".","1","t","f","@","@","@",";"," "," "," "," "," "," ",".","t","i","i","1","1","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","L","t","@","@","@","1"," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","G","@","8","@","@","@","G","@","0","@","1",".","1","@","t","L","@","@",":",".","@","1"," "," "," "," "," "," "," "," "," "," "," "," ","f","@","@","@","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," ",";","8","@","@","8","@","@","8","@","@","@","@","8","8","8","@","@","@","8","@","@","@","@","@","8","@","f","i","t",";","i","@","0","0","0","8","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","0","0","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","G","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","8","@","@","@","@","C","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@",";"," ","t","@","0","G","f",":",".",".",".",".","."," "," ",".",":","."," "," "," "," "," "," "," "," "," ",".","f","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","t","@",".",":","f","@","1"," ","f","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","L",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","t","@","@","@","8","@","@","@","@","@","8","@","1",";",":",";","@","@","G","1","8","@","t",".","i","."," ",":",".",";","C","8","@","0",".",".","."," ",":","1","C","@","8","@","@","@","C","t","."," "," "," ","."," "," "," ",";","i",":",";","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","f","@","@","f","t","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","0","@","8","@","@",":",".",";","f","G","@","i","."," "," "," "," "," "," "," ",";","f","i","."," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","1","@","G","C","0","0","@","@","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," ",";","@","f","G","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","L","L","@",":"," ",".","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",";","@","8","0","@","@","@","@","@","@","@","@","@","@","f","1","i","C","@","@","@","8","@","8",":"," ",":","@","@","@","0","G","8","8","8","@","@","@","@","@","8","8","8","@","@","@","8","@","@","t",":","."," "," ",".","@","@","@","0","0","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",".",".",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","@",";"," ",":","G","@","8","0","8","C","0","@","t"," ",";",".",".","@","@","i","G",":"," "," "," ",":","G","@","L","C","."," ",":","L","f",":"," "," "," "," ",".",";","i","1","L","@","f","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","L","@","f","."," ",".",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i",":","C","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","i",".",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," ",".","."," "," "," "," ",".","i","f","L","8","@","@","8","@","@","@","@","G",".","1","@","@","."," "," ",".","@",".",".","@","L",":","."," ","L","@","0","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t",".","t","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," ",".",".",";","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","f","f","0","@",":"," "," ",";",":","i","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," ",".",";","t",";",".","1","1","t","f","i",".","1","1"," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","0","@","@","8","@","@","8","@",".",":","@","@",";"," "," "," ",".","C","@","@","0","@","G",".","f","C","f","@","C","."," ",".","1","@","@","f",":"," ",".",";",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",";",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","G","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","C","t","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","i",":","1",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","0","8","@","@","L","f","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","t",":","0","1","1","C","@","@","@","@","@","@",":",".","t","."," "," "," "," "," "," "," "," "," ",".",".",".","1","@","C","@","@","@","@","8","@","G","@","@","."," "," "," "," ",".","C","0","@","@",".",".","@",":"," "," "," "," ","1","@","@","1",".","i","C","@","8","@","i","t","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","t","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","G","8","0","@","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","@","8","8","@","@","@","@","@","8","@","@","t","f","@","@",":"," "," "," "," ","C","@","@","@","L",";",".","L","@","8","@","L","0","G","G","@",";"," "," "," "," "," "," ","i",":"," ",".","L","@","."," "," "," ",".","G","@","@","8",".",".","f","@","8","@","C",".",":",";",";","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","i",".",";","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","8","C",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","@","."," "," ",".","."," "," "," ",":",":"," ","L","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1",":"," "," "," "," "," "," "," ",":",";",";","L","f",":",";","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","C","@","8","@","@","@","@","@","8","@","@","@","@","@","8","0","@","@","@","G","."," "," ",";","@","@","@","8","@","@","@","G","L","L","L","@","@","8","0","@","i"," ",".","."," "," "," ",".","0","@","0","@",":"," "," ",".","G","@","8","@","1",".","@","@","0","@","L","t","L","t","f","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","C","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","C","L","."," "," "," "," "," "," "," "," "," "," "," "," ",".","1","f","L",";"," ",".",":"," ",".","t","L","1","."," ",".","t","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","f"," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","@","@","8","@","@","@","@","@","f","C","@","@","@","C","f"," ",";","@","@","f","t","@","@","@","L","f","f","f","8","L","1","C","1"," "," "," "," ",".","f","@","8","@","8","."," "," ",".","f",":","t","@","t","1","@","@","@","@","8","8","@","@","@","8","L","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f","t","0","L","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","0","0","."," ","f",".","t","f","f","@","0","@","@","@",":","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",".",".","i",":","i","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","1","G","G","@","C","C","8","0","@","@","@","8","@","G",";",";",".",".",".",".",";",";",".",".",":",":",":","t","@","@","@","."," "," "," "," ","t",";"," ","1","@","@","@",";","."," "," "," ",";","i","L","@","@","0","@","@","0","@","0","G","@","@","@","@","0","f","1","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","1","t","1",";",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@",".",".","@","8",";",":","i","i","G","@","0","@",";",":","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","t","C","@","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","@","@","@","@","C","i","i","0","@","@","@","8","@","C",";",".",".",".",";","."," ",".",".",":","L",";"," "," ","."," "," ",".",".","i","@","@","@","."," ",";","i","t","1","."," "," "," "," "," "," ",":",";","i","t","L","L","@","t",":","L","@","8","@","8","@","G","t","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","G",":","i","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","f",".","1","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," ","G","@","0","@","0","@","f"," ",":","@","@","@","@","@","8","@","8","@","t","i","G","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","i",":","f","@","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","i","i",";",":"," ",".","t","@","1","f","@","8","@","8","@","@","@","@","@","@","1",".","L","@","8","0","@","0",":"," "," "," "," ",".","t","i",".","."," ",".","t","@","@","t",".","0","."," "," "," ","i","@","@","L","."," ","."," "," ",".","t","@","0","@","@","@","8","G","L","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",":","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","f","@","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","@",";","."," "," "," "," "," "," "," "," "," "," ","f","@","@","C","1","@",";",".","G","@","0","0","8","8","@","@","@","8","@","0","C","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","8","0","@",";",".","0","@","8","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," ","i","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8",":"," ",".",".",".",".",".","8","@","8","@","@","1","i","@","8","@","8","@","t","i","8","@","8","@","@","0","@","@","@","@","C","f","C","t",".","f","@","@","8","@","@","C","@","t","."," ",".","G","@","@","G","@",";"," ",".",";"," ",".",";","t","@","@","8","@","@","8","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","@","f","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","8","1",":","8","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," "," "," "," "," "," ","i","@","@","0","@","@","@",".",".","@","1",":","t","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","f","1","f","@","i"," ",".","@","@","8","@","8","8","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","L","t","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","f","1","i","L","t","i","G","@","@","0","1","G","@","L","t","f","@","8","@","8","0","@","@","@","@","t","C","@","8","8","8","8","@","C","1","i","0","@","@","@","@","@","0","@",";"," ","t","@","0","0","@","i"," "," "," ",".","."," ",".",".",".",";","0","@","8","@","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@",":",".","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","i","1","t","i"," ",".","f","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",":","L","i",";","f","i","f","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","@","@","@","@","L","i","."," ",".","t","0","@","8","@","i"," ","i","@","@",":",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," ","i","@","."," "," ","t","@","0","0","@","t"," "," "," ",";",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ","t","@","i","f","@","8","@","@","@","@","@","@","@","@","@","8","@","@","@","G","L","0","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","0","@","@","@","C","f","@","@","8","@","@","f","L","@","C","1","@","@","@","@","@","@","@","@","@","L","t","i","1","t","L","@","8","@","@","@","@","@","@","8","@","@","G","@","@","@","0","L","@","0","@","G","."," "," "," ",".","@","@",":",":",".",":","@","8","@","8","@","G",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","C","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","1","L","L","@","C","@","@","@","G",";",".",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","L","."," "," "," ",".",".","."," "," ",";","i",".","i","@","@","@","8","0","@","@","0",";","C","@","8","f","0",";"," ","i","@","@","1","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," ","L","@","G","t",":"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","8","@","@","@","@","@","@","8","0","@","@",";",";","i","i","C","i","G","L","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","0","@","L","G","f","f","@","8","@","@","@","@","G","i","t","@","@","@","@","@","@","@","@","@","@","@","@","@","f","L","C","G","@","8","@","@","@","@","C","C","8","G","8","@","t",".","i","f","f","i"," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","8","@","G","C","i"," "," "," "," "," "," "," "," "," "," "," "," "," ",":","i","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","0","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","1","1","C","0","@","8","@","@","8","0","@","t",";","@","@","@","@","@","@","@","@","L",":"," "," "," "," "," "," ",".",".",".","."," "," "," "," "," ",":","t","L","t",";","f","@","@","8","@","@","8","@","@","@","@","@","@","@","8","@","@","@","@","8","@","."," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," ","f","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","L","@","8","@","@","8","@","8","@","0","C",".",".","L","@","@","@","1",".",".",";","@","@","0","8","@","@","@","@","@","t","L","C","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","C","i","G","0","G","@","8","@","8","f","t","L","G","t","i","t","@","8","@","@","@","@","@","0","f","t","L","1","f","0","t","@","@","@","@","@","@","@",":",":","@","1",";","i",".","."," "," "," "," "," "," "," "," "," "," "," ",".","L","@","@","8","@","@","@","8","8","@","@","G","1",":","."," "," "," "," "," "," "," "," "," ","1","L","t","1","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","1","f","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",";",";","G","8","f","0","@","8","@","@","@","@","8","@","@","8","@","@","@","8","@","@","@","0","8","@","C","1","."," "," "," "," ","f","@",";"," "," ",":","@","@","G","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f","i","i",";"," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@",";"," "," "," "," "," "," "," ",".","@","0","@","1",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","f","@","0","@","f"," "," ",":","1",":","."," "," ",".","."," "," "," ",";","L","C","@","0","8","@","@","@","@","@","@","G","C","@","8","@","@","@","0","L","@","@","@","@","@","@","@","@","@","@","8","@","C","f","@","0","@","f","1","@","0","0","@","@","L","L","@","f","1","0","@","8","@","@","8","@","@","@","L","i","t","t","8","@",":","i","8","@","0","8","@","@","G","@","@",".",":","@","@","f","."," "," "," "," "," "," ",".","."," "," ",".","t","@","@","@","0","@","f","i",".",":","t","L","@","G","i","."," "," "," "," "," "," "," "," ",".","i","L","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ",".","L","@","C","G","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","f","1","C","C","@","@","@","8","0","@","G","."," "," ","1","C","."," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","t","t","t","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","."," "," ",".","t","C",":","."," "," "," "," "," "," ",".","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","@","."," ",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","1","8","@","@","8","G","0","G","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","G","@","@","8","@",":",".","L","1",".",".","i","0","G","@","8","1","G","@","@","0","@",";"," ","f","L","G","@","f","C","@","@","@","0","t","1","@","f",".","1","."," ",":","@","8","@","G","@",":"," "," "," "," ","G","@","@","@","@","@","@","@",":","1","@","G","@","1"," "," ","i","@","."," "," "," "," "," "," "," "," "," "," "," ",".","t","L","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","C","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","0","@","@","@","C","G","@","@","@","@","@","@","@","@","@","@","@","8","@","0",";",";","f","@","@","@","@","@","@","1"," ",":",":",".","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","8","8","@","C","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@",";"," ",".",";","L","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","G","C","0","1","0","C","C","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","G","1",":","."," "," ",";",";","L","@","8","i","1","8","@","@","0","C","8","@","L","1","1","f","1","@","1",":","8","t","f","G","G","f","f","@","."," "," "," "," "," ",".","@","1"," "," ","i","8",":"," "," "," "," "," "," "," "," ",".","0","@","@","@","@","8","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","8","@","@","@","@","@","@","@","@","8","@","8","f","t",":",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1",";",".",".",".",".",".",";","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","8","@","L","f","@","8","@","8","@","@","@","@","@","@","@","@","8","@","t","t","8","@","f"," "," "," ","."," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","8","@","f",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","G",";","."," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","i","L","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","L","8","C","@","@","@","0","L","0","L","L","@","@","@","@","@","@","@","@","C","8","@","@","@","@","C","f","@","@","f","t","G","C","."," "," "," "," "," "," "," "," "," "," ",".","i",":"," "," "," "," ",".",":","."," "," "," "," "," ",".","C","@","@",";","1","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";",";","1","@","8","@","@","@","@","@","@","8","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","t","i","G","@","0","@","8","8","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","C","@","G","L","G","G","@","@","f","."," "," ",".","f","@","0","0","@","8","8","@","G","0","8",".",".",".","1","."," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","8","@","L",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","t","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1",":","1","C","@","@","@","G","C","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","0","@","8","@","@","@","@","@","@","@","@","L","G","@","@","@","@","@","@","i","t","G","L","1","@","L","C","@","L","0","@","L","L","."," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","1",";"," ","1","L",":",".",".","."," "," "," ",".","i","@","@","1"," ",":","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","0","8","@","8","0","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","8","@","L","G","@","8","8","@","i"," "," ",".","f","@","@","t","i","@","G","C","@","8","@","@","G",";",".",".",";","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","8","@","."," "," "," "," "," ",";","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","i","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","i",":","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","t",".","1","C","@","C","L","L","1","@","@","t","0","@","i","f","@","C","C","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","1"," "," "," "," "," ",".","1","1"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","8","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",";","t","@","@","L","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","C","C","@","8","@","@","G","@","@","0","@","f","."," ",":","@","@","8","@","@","@","@","C","G","@","8","@","C","t",";","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",";","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","t","i","t","@","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t","i","t",";","@","@","@","@","@","L","1","0","@","@","@","@","G","f","@","0","f","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","t",":","."," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","8","8","@","8","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","C","f","C","@","0","@","t","t","@","0","@","i"," "," ",".","L","@","0","@","C","1",";","t","i","f","f","C","0","C","@","@","t","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" ",".","."," "," "," ",".","."," ","."," "," ","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",";","i","G","@","8","@","@","@","8","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","8","@","@","@","C","@","1",":","@","f","C","@","0","1","L","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","8","8","C",".",".","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","C","f","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","C","@","8","0","@","C","G","@","@","@","0","@",":"," "," "," "," ","t","@","0","t","f","f","f","C","L","i","1","0","C","@","8","@","1"," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," ","."," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t",":","i","L","f","@","8","@","8","0","@","C","1","f","L","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1",";","8","@","8","C",".",".","@","C","f","@","@","@","8","@","0","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","@","@","0","@","1"," "," "," "," "," ","L","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","0","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","f","f","@","@","@","@","@","@","@","@","@","@","@",":"," "," "," "," ",";","@","0","f","f","t","f","@","1",";","@","1"," "," ",";","@","@","f",";","L","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","0","@",";"," ",":",";","."," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","C","t","L","@","@","C","8","@","8","@","@","@","@","@","@","C","f","8","@","@","@","@","@","8","L","t","f","@","@","G","C","@","G","i","i","@","@","f","i","i","t","G","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","0",":"," "," ",":","G","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","t","C","@","@","8","@","G","C","@","@","8","G","C","C","@","i"," ",".","."," ",".","1","@","L",";",".",".",".",".","i","0","C",".","i","@","@","@","8","@","L","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","t","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","f","f","0","@","8","@","@","@","@","@","@","8","i",":",".","@","@","1",".",".","f",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","L",".",".","t","G","L","@","@","G","L","G","G","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","@","8","@","@","@","f","L","G","f","0","G","8","@","0","C","f","t","@","@","@","@","f","C","@","@","8","C","@","@","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","8","C","@","@","@","8","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","8","@","@","8","@","t","0","@","0","0","@","8","C","i","f",";"," "," "," "," "," "," ",".","i","f","i",":","f","@","@","8","@","8","8","@","@","@","8","@","L","G","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f",";","t","f","@","@","@","@","8","@","0",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@",":","i","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," ",".","i","8","@","f","C","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G","C","@","L","1","@","L",";","L","L","f","@","f","C","G","0","G","G","8","@","@","@","@","@","8","@","@","@","0","@","t","."," "," "," "," "," "," "," "," ",".",";"," "," "," ","L","@","@","C","G","@","@","@","@","@","@","8","8","@","@","@","8","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," ",";","8","@","@","C","."," ",";","@",":",".","t","f","@","1","."," "," "," "," ",".",";",";","C","8","0","@",";","i","@","0","@","f","f","@","8","@","@","8","@","C",".","i","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","f","L","C","@","@","@","8","@","@",".",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","@","C","C","i","C","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ",":","1","t","@","8","@","@","@","@","0","C","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","C","G","@","@","L","f","f","t","C","@","f","i","L","0","L","t","1","8","0","8","@","8","@","@","@","0","@","@",":",".","."," "," "," "," "," "," "," ",":","G","@","@","L","0","@","8","@","@","8","@","@","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",".","L","G",".","."," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","L","@","@","L","8","@","."," ",".","."," "," "," ","."," "," ","C","@","8","@","L","C","@","8","@","@","8","@","@","@","@","@","8","@","@","8","G","L","f","t","f","@","8","@","@","@","@","8","@","@","@","@","@","@","f","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","0","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","0","t","@","@","@","8","@","L",".",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","G","@","8","@","@",";"," ",";","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","8","@","@","@","@","@","@","@","@","C","f","@","@","@","@","@","@","G","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","1"," ","t",":",":","@","0","8","i","1","0","C","0","0","L","G","@","8","@","@","@","8","8","8","@",";"," "," ",".","f","@","@","@","8","@","@","@","f","t","@","@","@","8","@","8","@","@","0","0","8","8","8","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L",":"," ",".","f","@","0","@","@",";"," ","."," "," "," ","t","@","0","G","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","0","f","t","t","t","t","L","@","@","8","@","0","t","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G",";","t","@","@","8","@","L","t","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","t","@","@","@","@",".",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," ","t","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",".",":","G","G","0","@","@","@","@","@","@","G","@","G","G","@","@","@","@","8","@","L","0","@","@","@","8","@","@","@","@","@","@","@","@","@","@","t",":","0",".",".","@","@","8","@","C","i","L","8","G","@","G","G","@","1","f","@","8","8","@","8","."," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@",":","i","@","8","@","C","f","1","t","0","@","@","0","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","G","L","i","."," "," "," "," "," "," "," "," "," "," "," ",".","G","@",";",".",";","i","@","1"," ","."," "," "," "," "," "," ","1","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","f","@","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","8","8","@","@","0","f","f",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","C","@","@","0","@","i"," ",".","8","G","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@",":",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","8","@","L","f","@","8","@","@","@","@","8","@","t","1","f","t",":","C","C","f","0","@","G","0","8","0","@","@","L","G","@","8","@","@","0","@","i",".","."," ",".","@","@","8","@","@","@","@","@","@","@","@","8","@","L","f","@","@","0","t","8","0","8","@","@","t",":","1","8","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@",";",".","1","t","@","C","."," "," "," "," "," "," "," "," "," "," "," ",";","t",".",":",":"," "," "," ","."," "," ",":","i",".",".","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","1","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","t","0","@","@","@","@","@","@","@","8","@","L","L","@","8","@","f","f","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","."," ","t","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",";",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","@","8","@","@","@","@","@","@","8","@","i","t","@",":","f","@","G","f","L","0","t","t","f","@","L","G","0","@","@","@","@","@","8","@","L"," "," ",";","@","8","@","@","@","8","8","@","@","@","@","@","@","@","@","@","t","t","1","L","@","8","@","@","@","@","8","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","C","."," "," "," ","f","@","@","i"," "," "," "," "," "," "," "," "," "," ",".","C","@","t","t","@","G",".","i","@","@","0","0","@","@","8","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","@","@","L","t","@","8","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," ","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i",";"," ","1","@","8","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","8","G","f","L","@","1",";","C","@","f","t","C","0","8","@","@","@","@","@","@","@","0","@","8","L","@","f","G","@","@","L",";","L","@","@","@","C","f","L","0","8","0","0","8","@","8","@","@","8","0","@","@","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","@","."," ","i","@","8","8","@","0","t","1","."," "," ",".","L","@","@","@","@","@","@","8","8","8","8","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","0","@","1",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," ",".","@","i"," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","0","1","0","@","8","@","@","@","@","@","8","@","C","L","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","8","8","0","@","t",".","L","@","@","@","G","@","0",":","t","@","C","@","@","@","@","@","@","@","@","8","@","@","f","L","@","8","8","@","@","@","@","G","G","@","@","@","@","t","f","L","f","G","@","@","@","@","L","."," ",".","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","L",";","."," "," ",":","L","@","G","0","8","0","8",";"," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","C","@","@","@","@","@","@","@","@","@","@","@","t","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","."," "," "," ",".",":"," ","."," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","C","f","G","@","@","0","L","f","0","@","@","@","@","@","8","@","G","f","@","@","@","0","@","@","@","t","."," ",":","0","@","8","@","@","@","@","@","1","f","@","8","@","@","8","0","@","8","@","@","@","8","@","@","C",";"," "," "," "," ","i","t","."," "," "," "," "," ","1","G","i","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","C","f","C","t","1","i",".",";","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","0","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1","i","@","8","L","@","8","8","@","@","8","@","t","i","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@",":"," "," "," "," "," ",":","t"," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","8","@","t","."," "," ",".","."," "," "," ","i","@","0","@","@","@","@","@","@","8","@","@","@","8","L","C","@","G","C","@","@",";",";","L","@","@","@","@","."," "," "," "," "," "," ",".","L","@","0","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","."," ",";","f","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","f","i","f","@","@","@","@","@","@","@","@","@","@","@","@","0","L","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","@","8","8","@","C",";","t","@","@","L","C","@","@","@","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G",".","t","@","G","@","i"," "," "," "," "," "," "," ","i","@","f",".",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","@","@","@","@","@","@","@","@","@","C","8","t","L","@","@","1",".","i",";",":","@","@","@","@","@","@","@","8","@","8","C","G","@","8","@","@","@","8","1","t","@","8","0","8","@","C","."," "," "," "," "," "," ",".",";",":","i",";","i","L","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","t","i",":","C","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","8","0","8","@","0","8","@","@","@","@","C","i","C","@","8","@","@","@","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","t","0","@","@","@","@","@","@","L","0","@","8","0","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","G","@","t"," "," "," "," "," "," "," ",".","i","@","G","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","8","@","@","@","@","@","@","@","@","@","@","8","8","8","0","@","@",";"," "," ",".","."," "," "," ",".","t","@","G","@","@","@","8","@","@","@","0","G","@","@","@","8","@","@","8","0","8","@",";",":",";"," "," ","t","."," "," "," "," ",".","."," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","8","@","@","@","@","@","@","@","@","@","@","8","@","G","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","f","t","L","@","@","i",":","1","@","@","8","@","@","@","@","@","@","@","@","0","@","@","1",".",".",".","i","@","0","@","@","@","@","@","8","@","C","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@",":",":","C",":"," "," "," "," "," "," "," ",".","f",";",".",";","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","L","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i"," ",";","@","0","@",":"," ",".",";",".","f","@","0","@","@","8","G","G","C","@","@","@","@","0","@","@","@","C","i","i",";","f","@","8","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ",":","i","t","1","."," "," "," ","1","@","@","@","@","@","@","@","@","@","@","@","@","1","."," "," "," "," ",".",":","@","8","@","@","@","8","@","f","."," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","0","@","L"," ",".","8","@","@","t","f",".","i","@","@","t",":",".",".",".","0","@","8","8","@","@","@","@","L",":","."," "," ","i","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","G","@","@","@","@","@","@","@","@","@","@","8","@","8","8","@","@",".",".","L","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","0","."," "," "," ",".","i","@","1",":",".",";","@","@","f","C","@","8","@","@","@","@","0","@",";"," "," "," "," ",".","1","@","@","@","@","@","@","@","8","@","L",":",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","f","0","@","8","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ",":","@","8","@","@","C","@","1","i",".","1","@","@","@","@","@","@","@","@","8","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","8","@","@","@","@","@","@","1",".",":","C","@","@",";"," "," ",".","t","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," ",".","@","@","@","@","@","@","@","@","@","8","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",".",":","8","8","8","@","0","@","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","8","@","i",".",".","t","@","@","@","@","@","8","@","@","8","@","@","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","@","@","@","@","@","8","G","@","@","@","8","@","0","."," "," ",":","."," "," ",".",".",".","f","@","0","@",":"," "," "," ",".","1","@","@","8","@","@","@","@","@","@","@","8","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","8","@","@","@","0","@","i"," "," "," "," "," "," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",";"," "," ","i","@","t",":",":","1","C","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","8","0","8","@","@","@","@","@","8","@","t",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@","@","@","8","@","8","8","@","@","8","8","@","L"," "," "," "," "," "," "," ","1",";"," "," ",".","f","@","@","L","i","."," "," "," ",".","G","@","8","@","8","8","@","@","@","0","@","f","."," "," "," ",".",";","f","C","C","1",";","."," "," "," "," "," ",".","f","@","8","@","@","@","@","8","0","@",";"," "," "," "," ",":","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","i","f","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";",".","."," "," ",":","."," "," "," ",".","0","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," ",".","L","1"," "," "," "," "," "," ","i","@","@","@","1",".",".","G","@","0","@","L","@","i",".",".","i","t",".",".","1","@","@","@","0","8","8","8","0","0","@","@","@","@","@","@","@","8","@","@","@","@","t","C","@","8","0","@","t","."," "," ",".","G","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8",";","."," ",".",":"," ","."," "," "," ",".",".","i","@","@","C","."," "," "," "," "," "," "," "," "," ",":",".",";","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","t","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","i","0","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","0","@","t"," "," "," ",".","."," "," "," "," "," "," ","L","L"," "," "," "," "," "," "," "," ","i","1"," "," "," ",".","f","@","@","L","."," "," "," ",":","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","@","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","C","@","8","1","."," "," "," "," "," ",":","@","8","@","L"," "," "," "," "," ","."," "," "," ","1","@","0","0","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","8","8","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i",";"," "," "," "," ",".",";","f","t","L","1","."," ",".","t","@","0","@","@","@","0","@","@","@","@","8","8","8","@","@","0","f","C","@","@","@","@","@","@","@","@","@","8","@","C"," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," ","t","@","@","@","."," "," "," ",".","f","@","@","@","@","C","C","@","i",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","t","i","G","@","@","@","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","t","@","i"," "," "," "," "," "," "," "," ",";","@",";"," ",".","."," "," ","i","@","@","@","@","8","@","@","0","@","@","@","G","0","@","C","G","@","8","@","@","@","@","@","@","@","@","@","@","8","@","@","1",".",".",".",":","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","8","@","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," ","1","G",":",":","1",":","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","."," "," "," "," "," "," ",":","L","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," ",".","."," ",".","."," "," ",".",".","."," "," ","i","@","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","C","L","@",";"," "," "," "," "," "," "," ",".","."," "," "," ",".","f","@","i",";","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","G","@","C","f","@","@","8","8","8","8","@","@","@","@","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," "," "," "," "," "," "," "," ",".","f",";"," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","t","@","8","@","@","@","@","@","@","8","C","f","@","1"," "," "," "," "," "," "," "," "," "," "," "," ",".","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","8","8","8","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","8","@","C","f","C","G","L","1","1","."," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","L"," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","0","0","0","@","8","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","G","1","."," "," "," "," "," ",".","C","@","@","G",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","0","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","G","@","0","G","@","@","@","@","f",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",":"," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","8","G",";",".",".",":",":","1","@","G","0","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","t","i","."," "," ",".","@","@","8","8","0","@","@","@","@","@","f","1",";","t","@","@","L","t","t","G","@","C","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","C","t","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","C","L","@","@","@","f","1","0","@","G","C","@","8","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","C",";"," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","f","."," "," "," "," ",".",";",":","."," "," "," "," "," "," "," "," ","i","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","8","@","@","C","@","@","8","@","@","@","@","@","@","8","8","8","0","0","0","8","@","8","f","8","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";"," "," ",".","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","C","@","@","@","@","C","0","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@",";"," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","i","L","t","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," ",".","C","@","@","0","8","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","8","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," ",";","@","0","@","@","@","@","@","@","@","@","@","8","@","L","L","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","."," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","G","."," "," "," ",".","i","L","G",";",".","t","@","0","0","8","0","8","@","8","8","0","0","0","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","0","."," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f",":","i"," "," "," "," "," ",";","i"," ",":",";","i","t","L","8","@","0","L","L","L","t","1","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," ",":","@","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","."," "," ",".","t","@","0","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," ",".",".",".","L","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," "," ",".","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","t","1",";",":"," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","."," "," "," ",".","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","8","@","8","@","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","L","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","C","1","f","f","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","@","i",":",".","."," "," "," "," "," "," ",":",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," "," ",".","."," ","."," "," "," ",":","i","L","1",".","."," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",":"," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","f",";","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",";",".",".","."," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","G","i",".",".","1","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","0","8","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," ","i","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","L","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," "," "," ","i","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," "," "," "," "," "," "," ",";","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," ",":","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","@","@","0","8","@","@","@","@","@","8","@",":"," "," "," "," "," "," ",";","@","8","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," ",":","1","@","8","@","8","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," ",";","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","C","@","@","0","0","@","@","8","@","@","C","8","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","i","."," "," ",".",".",".",":",":"," "," "," "," "," ",".",";","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","."," "," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","L","@","@","@","@","L","G","@","0","@","@","@","8","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","."," "," "," "," ","f","@","0","@","@","@","@","@","@","@","8","0","0","@","i",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","8","@","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","8","i"," ",".","G","@","8","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","0","0","0","8","@","@","@","@","@","@","8","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",":"," "," "," ","1","@","0","@","8","8","8","0","@","@","@","f",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","8","8","@","@","@","@","8","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","i","f","@","@","@","8","8","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","8","8","@","L",".",".","8","@","@","@","@","G",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ","t","@","C","f","@","@","0","@","0","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",":",":",";","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",".",".",".","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","L","@","8","@","@","0","@","t",":",".","."," "," "," "," "," "," "," "," "," "," "," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","@","1"," ",".",":","L","@","C","L","@","8","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," ",".",":","t",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",";","@","."," "," "," "," "," "," "," "," "," "," "," ",".",":","1","C","@",";",".","t","L","f",":"," "," "," "," "," ",".",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","G",";"," "," "," "," ",".",":",":",";","t","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","8","8","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","C","."," "," "," "," ",":","C","@","@","0","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",":","L",".",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","L","@",";"," "," "," "," ",".","."," "," "," ",".","@","@","G","@","L",".","f","@","0","0","@","@","@","@","G","8","@","@","f","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","8","@","t","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","0","0","0","G","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","0",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","."," "," "," "," "," "," "," ","f","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," ",";","i",".",".",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","1","1","@","@","i"," ",".","@",";","t","@","0","@","@","8","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","8","L","f","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","@","@","8","@","G","i","f","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G",";"," "," ","L","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," ",".","C","@",";"," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","i","i","1","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",":","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","t","@","8","0","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","L","@","8","8","@","@","@","@","L","G","@","8","@","@","0","@","1","f","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",":","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",".","."," ",".","8","C","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","@","@","@","@","@","@","G","L","C","C","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","0","@","@","@","@","@","0","@","@","@","8",";","."," "," "," "," "," ","t","@","0","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",".","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","f",":","."," ",".","."," "," "," "," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","8","@","0","f","G","8","0","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","i",";",".",".","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","1"," "," "," ","t","@","G","@","."," "," "," "," "," "," ","."," "," "," "," "," "," "," ",";","@","@","8","@","G",":"," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","@","@","8","."," ","i","@","@",":"," "," ",".","."," "," "," "," "," "," "," "," ",".","L","@","0","@","8","@",";"," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","i","t","G",";","t","C","8","@","@","@","@","@","@","@","@","@","@","0","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","1",".",".","C",";"," "," "," "," "," "," ",".",";",".",":","@","8","8","@","@","@","8","@","G","."," "," "," "," "," "," "," "," "," ","."," "," "," ",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","i","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","1","i","i","L","0","@","@","@","@","@","@","@","@","8","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","0","f",":"," "," "," "," "," "," "," ","i","@","8","@","@","@","@","@","@","@","0","@","i"," "," ",".","i","i","i","i",";","t",";"," "," "," "," ","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ","."," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","1","C","@","@","i","t","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","0","@","C","."," ",".","L","@","0","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","@","@","0","@","1",".","."," "," "," "," "," ",".","t","@","@","@","@","@","@","@","0","@","t"," "," "," ","1",";"," ",";","1","1","."," "," "," "," ",".","."," "," "," ",".",";","C","@","C",";"," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","f","@","0","8","0","@","@",";",":","i",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","L","0","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","@","L",";",";","t","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","8","@","@","i","i",";"," ","."," "," ",".","0","@","@","@","@","@","0","@","f"," "," "," ",".","8","@","@",";"," "," "," ",".",".","."," "," "," "," "," ","."," "," ",".",";",";",";"," "," ",".","t","@","@","C","i",":","."," "," "," "," "," "," "," ",".","."," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","i","@","@","8","8","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","0","@","C","."," ","."," "," "," "," "," ",".",".",".",".","L","L",":"," "," "," ",".","G","i",":","@",";"," "," "," "," ",".","f",":",".",":",".",";",";"," "," ","i","1","i","@","G","@","0","8","8","8","0","8","@","@","@","L",";","."," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C",".","L","@","8","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","0",";"," ",":",";",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","t","@","8","@","@","@","@","8","8","0","@","@",":"," "," "," "," "," ","."," ","1","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","0","G","@","@","@","@","@","0","C","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";",".","@","@","8","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","C","t","."," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," ",":","@","8","8","@","@","@","@","8","8","0","@","@","L","."," ",".",":",":"," "," "," "," "," ",":","t",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","t",":","@","8","@","8","8","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","1","1","L","@","@","@","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ","."," ",".",";"," "," "," "," "," "," "," "," ",".","t","L","@","@","0","C","0","@","i",":","t","@","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","f","t","8","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","8","@","L","L","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",".","i",";",":",".",".",":",":",".","."," ",".",";","t","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","L",":"," "," "," "," ","i","@","@","i"," "," "," "," "," ","."," "," "," "," "," "," "," "," "," ",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","8","@","8","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","L","@","8","8","@","L",":","@","@","8","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," ","."," ",".","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","8","@","t",".","f","@","0","@","@","0","@","C","."," "," "," ","."," "," "," "," "," "," "," ",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",".",":","i","."," "," "," ","."," "," "," "," "," "," "," ",";","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",":","t","@","0","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," ",".","i","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","0","@","@","@","@",";"," "," "," "," "," "," ",".","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","8","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","0","0","@","@","@","8","8","@","@","@","0","@","L","."," "," "," "," "," "," "," "," ",":","L","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","L","."," ",".","8","@","0","@","@","@","8","@","G",".","."," "," "," "," "," "," ",";","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","0","@","1",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","1","C","@","@","G","0","@","@","@","@","@","@","0","8","@","@","i"," "," "," "," "," "," ",";","L","8","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L","@","0","8","L","@","@","G","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," ","1","@","0","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","f","@","@","8","@","@","8","@","@","@","@","0","@","G","."," "," "," "," "," "," "," "," ",";","@","G","@","8","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",":","i","G","0","@","@","@","@","@","@","8","@","@","@","@","@","@","@","8","8","@","L",".",".","."," ",".","@","@","8","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","."," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","t","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","f","G","@","8","@","C","f","@","8","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1","."," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","t","1","@","@","8","@","8","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," ",":","1"," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","t","L","f","L","f","C","L","L","L","8","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","8","@","0","."," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","1","@","G","G","@","@","@","@","8","0","8","8","8","0","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","8","@","@","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",":","1","G","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","C","@","t","f","@","C","L","L","@","0","@","@","i",":",";",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f","."," "," "," "," "," "," "," "," "," "," ","1","@","8","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@","8","0","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","i"," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","8","1","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","8","@","@","@","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","8","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","@","f",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","8","G","@","@","8","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","t","f","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","8","0","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","t","G","@","G","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","0","@","@","@","@","@","@","@","0","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","0","0","0","0","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","L","@","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","t","t","f","i","."," "," "," "," ",";","f","@","@","8","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G",";",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","0","@","@","@","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","G","@","8","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","t",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@",":",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","8","8","G","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","i",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","C","C","@","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",".",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","8","0","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","G","@","@","@","@","@","@","@","@","@","1","f","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","0","@","0","@","@","8","@","8","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L","L","@","8","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","@","G",":","i","@","@","1",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t",":",";","@","0","@","@","@","@","@","@","@","@","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","@","@","8","@","C","f","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","C"," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","i","L","@","8","@","@","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","."," ",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","8","f",":"," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","1","@","@","@","@","8","L","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","0","@","L","f","0","@","8","8","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L",";",".",".","C","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","t","G","@","0","8","@","@","@","8","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","t","@","f","t","G","L","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","i","L","@","t","L","@","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","C","0","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," ",":",":","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","i","i","t","@","0","@","0","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","i","i","t",";","t","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","i","t",";",":","L","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","f","1","t","1","f","t",":"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], +[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "] +]; + + +var M_PI = 3.14159265; + +function createSphere(radius, center, voxelSize, rotation, solid, debug) { + var xc = center.x; + var yc = center.y; + var zc = center.z; + + var red = 255; + var green = 0; + var blue = 0; + + var thisVoxelSize = voxelSize; + var thisRadius = 0.0; + if (!solid) { + thisRadius = radius; // just the outer surface + } + + // If you also iterate form the interior of the sphere to the radius, making + // larger and larger spheres you'd end up with a solid sphere. And lots of voxels! + var lastLayer = false; + while (!lastLayer) { + lastLayer = (thisRadius + (voxelSize * 2.0) >= radius); + + // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize + // small enough to not skip any voxels we can calculate theta from our desired arc length + // lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R + // lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r + var angleDelta = (thisVoxelSize / thisRadius); + + if (debug) { + var percentComplete = 100 * (thisRadius/radius); + print("percentComplete="+ percentComplete+"\n"); + } + + + // what I really want to do is iterate over slices from top to bottom, calculating + // the radius and center for each circle for each slice at Y height + var yStart = center.y + radius; + var yEnd = center.y - radius; + var radiusSquared = radius * radius; + + var mapHeight = map.length; + var mapWidth = map[0].length; + var yMapStart = 0; + var yMapEnd = mapHeight; + var yRatio; + + var xMapStart = 0; + var xMapEnd = mapWidth; + + var phiStart = rotation; + var phiEnd = rotation + M_PI * 2; + + for (var yAt=yStart; yAt >=yEnd; yAt -= voxelSize) { + // at y, what is the radius of the circle at that slice? + // we know there's a triangle with one edge from the center up the pole to height yAt, + // and a hypotenuse of radius (from original sphere) and a right angle connecting these + // lines to make the third edge. The length of that edge solves... + // a^2+b^2=c^2 + // poleLength^2 + unknown^2 = radius^2 + // unknown^2 = radius^2 - poleLength^2 + // unknown^2 = radius^2 - (radius - (yAt - center.y))^2 + // unknown = sqrt(radius^2 - (distanceFromCenter^2)) + var distanceFromCenter; + if (yAt > center.y) { + distanceFromCenter = yAt - center.y; + } else { + distanceFromCenter = center.y - yAt; + } + var sliceRadius = Math.sqrt(radiusSquared - (distanceFromCenter * distanceFromCenter)); + + yRatio = (yAt - yStart)/(yEnd - yStart); + + for (var phi=phiStart; phi <= phiEnd; phi += angleDelta) { + + var y = yAt; + var x = xc + sliceRadius * Math.sin(phi); + var z = zc + sliceRadius * Math.cos(phi); + + var phiRatio = (phi-phiStart) / (phiEnd - phiStart); + + mapY = Math.floor(yMapStart + yRatio * (yMapEnd-yMapStart)); + mapX = Math.floor(xMapStart + phiRatio * (xMapEnd-xMapStart)); + + if (mapY < 0) { + mapY = 0; + } else if (mapY >= mapHeight) { + mapY = mapHeight-1; + } + + if (mapX < 0) { + mapX = 0; + } else if (mapX >= mapWidth) { + mapX = mapWidth - 1; + } + + var mapCode = map[mapY][mapX]; + if (mapCode == " ") { + red = 0; + green = 0; + blue = 255; + } else { + switch (mapCode) { + case "@": + red = 0; + green = 128; + blue = 0; + break; + default: + red = 10; + green = 120; + blue = 0; + break; + } + } + + Voxels.queueDestructiveVoxelAdd(x,y,z, voxelSize, red, green, blue); + //print("Voxels.packetsToSendCount()="+ Voxels.packetsToSendCount() + "\n"); + } + } + + thisRadius += thisVoxelSize; + } +} + +var rotation = 0; + +var currentIteration = 0; +var NUM_ITERATIONS_BEFORE_SEND = 15; + +function waitTillDrawn() { + if (currentIteration++ % NUM_ITERATIONS_BEFORE_SEND === 0) { + + var radius = 12.5 / TREE_SCALE; + //var center = { x: (radius*2), y: (radius*2), z: (radius*2) }; + var center = { + x: 1315/TREE_SCALE, + y: 465/TREE_SCALE, + z: 1885/TREE_SCALE }; + + var voxelSize = 0.5 / TREE_SCALE; + var solid = false; + var debug = false; + var rotationDelta = M_PI/30; + rotation += rotationDelta; + if (rotation >= M_PI * 2) { + rotation = 0; + } + createSphere(radius, center, voxelSize, rotation, solid, debug); + + print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + + " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + + " PPS:" + Voxels.getLifetimePPSQueued() + "," + + " BPS:" + Voxels.getLifetimeBPSQueued() + "," + + " Sent packets:" + Voxels.getLifetimePacketsSent() + "," + + " PPS:" + Voxels.getLifetimePPS() + "," + + " BPS:" + Voxels.getLifetimeBPS() + + "\n"); + + /** + if (Voxels.packetsToSendCount() === 0) { + print("DONE!!\n"); + + //print("Voxels.packetsToSendCount()="+ Voxels.packetsToSendCount() + "\n"); + + + Agent.stop(); + } + **/ + } +} + + +// register the call back so it fires before each data send +Voxels.setPacketsPerSecond(40000); +Agent.willSendVisualDataCallback.connect(waitTillDrawn); diff --git a/libraries/particle-server/CMakeLists.txt.disabled b/libraries/particle-server/CMakeLists.txt similarity index 100% rename from libraries/particle-server/CMakeLists.txt.disabled rename to libraries/particle-server/CMakeLists.txt diff --git a/libraries/particle-server/src/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h index d33bcd8eaa..b8296284a9 100644 --- a/libraries/particle-server/src/ParticleServer.h +++ b/libraries/particle-server/src/ParticleServer.h @@ -24,7 +24,7 @@ #include "ParticleServerPacketProcessor.h" /// Handles assignments of type ParticleServer - sending particles to various clients. -class ParticleServer : public Assignment { +class ParticleServer : public ThreadedAssignment { public: ParticleServer(const unsigned char* dataBuffer, int numBytes); diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 821e1b0b49..e53c314ed9 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -9,20 +9,8 @@ #ifndef __hifi__ParticleTree__ #define __hifi__ParticleTree__ -#include -#include -#include #include -#include -#include -#include - #include "ParticleTreeElement.h" -#include "VoxelPacketData.h" -#include "VoxelSceneStats.h" -#include "VoxelEditPacketSender.h" - -class ReadCodeColorBufferToTreeArgs; class ParticleTree : public Octree { Q_OBJECT diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 81bc491ade..e3a02b3275 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -7,8 +7,6 @@ // #include -#include -#include #include "ParticleTree.h" #include "ParticleTreeElement.h" diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 8a35d6e622..837814ccaa 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -10,9 +10,7 @@ #ifndef __hifi__ParticleTreeElement__ #define __hifi__ParticleTreeElement__ -#include #include -#include class ParticleTree; class ParticleTreeElement; From 5ff12ab8d13c419c6db70a7b28cca4048a15e304 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 17:41:52 -0800 Subject: [PATCH 03/17] first cut at getting voxel server based on octree server --- assignment-client/CMakeLists.txt | 1 + libraries/octree/src/Octree.h | 4 + .../voxel-server-library/src/NodeWatcher.cpp | 28 ----- .../voxel-server-library/src/NodeWatcher.h | 23 ---- .../src/VoxelNodeData.cpp | 5 + .../voxel-server-library/src/VoxelNodeData.h | 111 +----------------- .../src/VoxelPersistThread.cpp | 7 ++ .../src/VoxelPersistThread.h | 5 + .../src/VoxelSendThread.cpp | 3 + .../src/VoxelSendThread.h | 5 + .../voxel-server-library/src/VoxelServer.cpp | 66 ++++++----- .../voxel-server-library/src/VoxelServer.h | 72 ++---------- .../src/VoxelServerConsts.h | 15 --- .../src/VoxelServerPacketProcessor.cpp | 3 + .../src/VoxelServerPacketProcessor.h | 6 + libraries/voxels/src/VoxelTree.cpp | 51 ++++++++ libraries/voxels/src/VoxelTree.h | 5 + 17 files changed, 145 insertions(+), 265 deletions(-) delete mode 100644 libraries/voxel-server-library/src/NodeWatcher.cpp delete mode 100644 libraries/voxel-server-library/src/NodeWatcher.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 27ef8f6540..828e91e93f 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -27,6 +27,7 @@ link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) include_directories(${ROOT_DIR}/externals/civetweb/include) diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 36588efb9e..564184caf5 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -180,6 +180,10 @@ public: virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0; + virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; } + virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, + unsigned char* editData, int maxLength) { return 0; } + OctreeElement* getRoot() { return _rootNode; } void eraseAllOctreeElements(); diff --git a/libraries/voxel-server-library/src/NodeWatcher.cpp b/libraries/voxel-server-library/src/NodeWatcher.cpp deleted file mode 100644 index 03384569b8..0000000000 --- a/libraries/voxel-server-library/src/NodeWatcher.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// NodeWatcher.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads -// - -#include -#include "NodeWatcher.h" -#include "VoxelNodeData.h" - -void NodeWatcher::nodeAdded(Node* node) { - // do nothing -} - -void NodeWatcher::nodeKilled(Node* node) { - // Use this to cleanup our node - if (node->getType() == NODE_TYPE_AGENT) { - VoxelNodeData* nodeData = (VoxelNodeData*)node->getLinkedData(); - if (nodeData) { - node->setLinkedData(NULL); - delete nodeData; - } - } -}; diff --git a/libraries/voxel-server-library/src/NodeWatcher.h b/libraries/voxel-server-library/src/NodeWatcher.h deleted file mode 100644 index a730f7d575..0000000000 --- a/libraries/voxel-server-library/src/NodeWatcher.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// NodeWatcher.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads -// - -#ifndef __voxel_server__NodeWatcher__ -#define __voxel_server__NodeWatcher__ - -#include - -/// Voxel server's node watcher, which watches for nodes being killed and cleans up their data and threads -class NodeWatcher : public virtual NodeListHook { -public: - virtual void nodeAdded(Node* node); - virtual void nodeKilled(Node* node); -}; - -#endif // __voxel_server__NodeWatcher__ diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index a4c4bfebe2..2df418c82f 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -1,3 +1,6 @@ +/*** + + // // VoxelNodeData.cpp // hifi @@ -274,3 +277,5 @@ void VoxelNodeData::dumpOutOfView() { } } + +**/ diff --git a/libraries/voxel-server-library/src/VoxelNodeData.h b/libraries/voxel-server-library/src/VoxelNodeData.h index 7a39fb4362..92a5230567 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.h +++ b/libraries/voxel-server-library/src/VoxelNodeData.h @@ -9,114 +9,13 @@ #ifndef __hifi__VoxelNodeData__ #define __hifi__VoxelNodeData__ -#include -#include -#include -#include +#include +#include -#include -#include -#include -#include - -class VoxelSendThread; -class VoxelServer; - -class VoxelNodeData : public VoxelQuery { +class VoxelNodeData : public OctreeQueryNode { public: - VoxelNodeData(Node* owningNode); - virtual ~VoxelNodeData(); - - void resetVoxelPacket(bool lastWasSurpressed = false); // resets voxel packet to after "V" header - - void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet - - const unsigned char* getPacket() const { return _voxelPacket; } - int getPacketLength() const { return (MAX_PACKET_SIZE - _voxelPacketAvailableBytes); } - bool isPacketWaiting() const { return _voxelPacketWaiting; } - - bool packetIsDuplicate() const; - bool shouldSuppressDuplicatePacket(); - - int getAvailable() const { return _voxelPacketAvailableBytes; } - int getMaxSearchLevel() const { return _maxSearchLevel; } - void resetMaxSearchLevel() { _maxSearchLevel = 1; } - void incrementMaxSearchLevel() { _maxSearchLevel++; } - - int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; } - void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } - - OctreeElementBag nodeBag; - CoverageMap map; - - ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; } - ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; } - - // These are not classic setters because they are calculating and maintaining state - // which is set asynchronously through the network receive - bool updateCurrentViewFrustum(); - void updateLastKnownViewFrustum(); - - bool getViewSent() const { return _viewSent; } - void setViewSent(bool viewSent); - - bool getViewFrustumChanging() const { return _viewFrustumChanging; } - bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; } - - bool moveShouldDump() const; - - uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; } - void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; } - - bool getCurrentPacketIsColor() const { return _currentPacketIsColor; } - bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; } - bool getCurrentPacketFormatMatches() { - return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression()); - } - - bool hasLodChanged() const { return _lodChanged; }; - - VoxelSceneStats stats; - - void initializeVoxelSendThread(VoxelServer* voxelServer); - bool isVoxelSendThreadInitalized() { return _voxelSendThread; } - - void dumpOutOfView(); - -private: - VoxelNodeData(const VoxelNodeData &); - VoxelNodeData& operator= (const VoxelNodeData&); - - bool _viewSent; - unsigned char* _voxelPacket; - unsigned char* _voxelPacketAt; - int _voxelPacketAvailableBytes; - bool _voxelPacketWaiting; - - unsigned char* _lastVoxelPacket; - int _lastVoxelPacketLength; - int _duplicatePacketCount; - uint64_t _firstSuppressedPacket; - - int _maxSearchLevel; - int _maxLevelReachedInLastSearch; - ViewFrustum _currentViewFrustum; - ViewFrustum _lastKnownViewFrustum; - uint64_t _lastTimeBagEmpty; - bool _viewFrustumChanging; - bool _viewFrustumJustStoppedChanging; - bool _currentPacketIsColor; - bool _currentPacketIsCompressed; - - VoxelSendThread* _voxelSendThread; - - // watch for LOD changes - int _lastClientBoundaryLevelAdjust; - float _lastClientVoxelSizeScale; - bool _lodChanged; - bool _lodInitialized; - - VOXEL_PACKET_SEQUENCE _sequenceNumber; + VoxelNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { }; + virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_VOXEL_DATA; } }; #endif /* defined(__hifi__VoxelNodeData__) */ diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp index 6f0d8d6b1c..13176857a1 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ b/libraries/voxel-server-library/src/VoxelPersistThread.cpp @@ -1,3 +1,6 @@ + +/***** + // // VoxelPersistThread.cpp // voxel-server @@ -84,3 +87,7 @@ bool VoxelPersistThread::process() { } return isStillRunning(); // keep running till they terminate us } + + + +****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.h b/libraries/voxel-server-library/src/VoxelPersistThread.h index de6b4ab094..68d0efa86d 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.h +++ b/libraries/voxel-server-library/src/VoxelPersistThread.h @@ -1,3 +1,5 @@ +/**** + // // VoxelPersistThread.h // voxel-server @@ -42,3 +44,6 @@ private: }; #endif // __voxel_server__VoxelPersistThread__ + + +***/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 669979e0b0..4af48f3299 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -1,3 +1,5 @@ +/***** + // // VoxelSendThread.cpp // voxel-server @@ -578,3 +580,4 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod return truePacketsSent; } +****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelSendThread.h b/libraries/voxel-server-library/src/VoxelSendThread.h index 5fc41fbc72..797eb142fa 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.h +++ b/libraries/voxel-server-library/src/VoxelSendThread.h @@ -1,3 +1,5 @@ +/**** + // // VoxelSendThread.h // voxel-server @@ -46,3 +48,6 @@ private: }; #endif // __voxel_server__VoxelSendThread__ + + +****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 96323f7d1a..6e68b05170 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -6,44 +6,45 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include #include -#include "VoxelNodeData.h" -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include "Syssocket.h" -#include "Systime.h" -#else -#include -#include -#include -#endif #include "VoxelServer.h" #include "VoxelServerConsts.h" +#include "VoxelNodeData.h" + + +VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) { + // nothing special to do here... +} + +VoxelServer::~VoxelServer() { + // nothing special to do here... +} + +OctreeQueryNode* VoxelServer::createOctreeQueryNode(Node* newNode) { + return new VoxelNodeData(newNode); +} + +Octree* VoxelServer::createTree() { + return new VoxelTree(true); +} + +unsigned char VoxelServer::getMyNodeType() { + return NODE_TYPE_VOXEL_SERVER; +} + +const char* VOXEL_SERVER_LOGGING_TARGET_NAME = "voxel-server"; +const char* VoxelServer::getMyLoggingServerTargetName() { + return VOXEL_SERVER_LOGGING_TARGET_NAME; +} const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; -const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo"; +const char* VoxelServer::getMyDefaultPersistFilename() { + return LOCAL_VOXELS_PERSIST_FILE; +} + +/***** + void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { @@ -785,4 +786,5 @@ void VoxelServer::run() { qDebug() << "VoxelServer::run()... DONE\n"; } +**/ diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index f4923778c1..a456d3c38f 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -17,87 +17,37 @@ #include #include +#include #include "civetweb.h" -#include "NodeWatcher.h" + #include "VoxelPersistThread.h" #include "VoxelSendThread.h" #include "VoxelServerConsts.h" #include "VoxelServerPacketProcessor.h" /// Handles assignments of type VoxelServer - sending voxels to various clients. -class VoxelServer : public ThreadedAssignment { +class VoxelServer : public OctreeServer { public: VoxelServer(const unsigned char* dataBuffer, int numBytes); - ~VoxelServer(); - - - - /// allows setting of run arguments - void setArguments(int argc, char** argv); - bool wantsDebugVoxelSending() const { return _debugVoxelSending; } - bool wantsDebugVoxelReceiving() const { return _debugVoxelReceiving; } - bool wantsVerboseDebug() const { return _verboseDebug; } - bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; } bool wantSendEnvironments() const { return _sendEnvironments; } - bool wantDumpVoxelsOnMove() const { return _dumpVoxelsOnMove; } - bool wantDisplayVoxelStats() const { return _displayVoxelStats; } - - VoxelTree& getServerTree() { return _serverTree; } - JurisdictionMap* getJurisdiction() { return _jurisdiction; } - - int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; } EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; } int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); } - - static VoxelServer* GetInstance() { return _theInstance; } - - bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; } - time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; } - uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; } -public slots: - /// runs the voxel server assignment - void run(); - - void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); -private: - int _argc; - const char** _argv; - char** _parsedArgV; - char _voxelPersistFilename[MAX_FILENAME_LENGTH]; - int _packetsPerClientPerInterval; - VoxelTree _serverTree; // this IS a reaveraging tree - bool _wantVoxelPersist; - bool _wantLocalDomain; - bool _debugVoxelSending; - bool _shouldShowAnimationDebug; - bool _displayVoxelStats; - bool _debugVoxelReceiving; + // Subclasses must implement these methods + virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode); + virtual Octree* createTree(); + virtual unsigned char getMyNodeType(); + virtual const char* getMyLoggingServerTargetName(); + virtual const char* getMyDefaultPersistFilename(); + +private: bool _sendEnvironments; bool _sendMinimalEnvironment; - bool _dumpVoxelsOnMove; - bool _verboseDebug; - JurisdictionMap* _jurisdiction; - JurisdictionSender* _jurisdictionSender; - VoxelServerPacketProcessor* _voxelServerPacketProcessor; - VoxelPersistThread* _voxelPersistThread; EnvironmentData _environmentData[3]; - - NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed - - void parsePayload(); - - void initMongoose(int port); - - static int civetwebRequestHandler(struct mg_connection *connection); - static VoxelServer* _theInstance; - - time_t _started; - uint64_t _startedUSecs; }; #endif // __voxel_server__VoxelServer__ diff --git a/libraries/voxel-server-library/src/VoxelServerConsts.h b/libraries/voxel-server-library/src/VoxelServerConsts.h index 216ecd8f80..e6973812f9 100644 --- a/libraries/voxel-server-library/src/VoxelServerConsts.h +++ b/libraries/voxel-server-library/src/VoxelServerConsts.h @@ -9,21 +9,6 @@ #ifndef __voxel_server__VoxelServerConsts__ #define __voxel_server__VoxelServerConsts__ -#include -#include // for MAX_PACKET_SIZE -#include -#include - -#include "VoxelServerPacketProcessor.h" - - -const int MAX_FILENAME_LENGTH = 1024; -const int INTERVALS_PER_SECOND = 60; -const int VOXEL_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND; -const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000; -extern const char* LOCAL_VOXELS_PERSIST_FILE; -extern const char* VOXELS_PERSIST_FILE; - #endif // __voxel_server__VoxelServerConsts__ diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index 3a0bc70f42..ac303d3c89 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -1,3 +1,5 @@ +/****** + // // VoxelServerPacketProcessor.cpp // voxel-server @@ -221,3 +223,4 @@ SingleSenderStats::SingleSenderStats() { } +****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h index 4abdbad284..7249fff4ca 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h @@ -1,3 +1,6 @@ +/****** + + // // VoxelServerPacketProcessor.h // voxel-server @@ -81,3 +84,6 @@ private: NodeToSenderStatsMap _singleSenderStats; }; #endif // __voxel_server__VoxelServerPacketProcessor__ + + +***/ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index ff722143c4..b12a386a19 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -638,3 +638,54 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelTreeElement* node, ReadC node->handleSubtreeChanged(this); } } + +bool VoxelTree::handlesEditPacketType(PACKET_TYPE packetType) const { + // we handle these types of "edit" packets + switch (packetType) { + case PACKET_TYPE_SET_VOXEL: + case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: + case PACKET_TYPE_ERASE_VOXEL: + return true; + } + return false; +} + +int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, + unsigned char* editData, int maxLength) { + + int processedBytes = 0; + // we handle these types of "edit" packets + switch (packetType) { + case PACKET_TYPE_SET_VOXEL: + case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: { + bool destructive = (packetType == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); + int octets = numberOfThreeBitSectionsInCode(editData, maxLength); + + if (octets == OVERFLOWED_OCTCODE_BUFFER) { + printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), "); + printf("bailing processing of packet!\n"); + return processedBytes; + } + + const int COLOR_SIZE_IN_BYTES = 3; + int voxelCodeSize = bytesRequiredForCodeLength(octets); + int voxelDataSize = voxelCodeSize + COLOR_SIZE_IN_BYTES; + + if (voxelDataSize > maxLength) { + printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n"); + printf("bailing processing of packet!\n"); + return processedBytes; + } + + readCodeColorBufferToTree(editData, destructive); + + return voxelDataSize; + } break; + + case PACKET_TYPE_ERASE_VOXEL: + processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength); + return maxLength; + } + return processedBytes; +} + diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 2654663101..eae47885b2 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -53,6 +53,11 @@ public: void readCodeColorBufferToTree(const unsigned char* codeColorBuffer, bool destructive = false); + virtual bool handlesEditPacketType(PACKET_TYPE packetType) const; + virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, + unsigned char* editData, int maxLength); + void processSetVoxelsBitstream(const unsigned char* bitstream, int bufferSizeBytes); + /** signals: void importSize(float x, float y, float z); From fad3a5957d78b5295a7742bdac42a82fcd52b963 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 17:50:42 -0800 Subject: [PATCH 04/17] removing dead code --- .../src/VoxelNodeData.cpp | 281 ------------------ .../src/VoxelPersistThread.cpp | 93 ------ .../src/VoxelPersistThread.h | 49 --- .../src/VoxelSendThread.h | 53 ---- .../voxel-server-library/src/VoxelServer.h | 3 - .../src/VoxelServerPacketProcessor.cpp | 226 -------------- .../src/VoxelServerPacketProcessor.h | 89 ------ 7 files changed, 794 deletions(-) delete mode 100644 libraries/voxel-server-library/src/VoxelNodeData.cpp delete mode 100644 libraries/voxel-server-library/src/VoxelPersistThread.cpp delete mode 100644 libraries/voxel-server-library/src/VoxelPersistThread.h delete mode 100644 libraries/voxel-server-library/src/VoxelSendThread.h delete mode 100644 libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp delete mode 100644 libraries/voxel-server-library/src/VoxelServerPacketProcessor.h diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp deleted file mode 100644 index 2df418c82f..0000000000 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/*** - - -// -// VoxelNodeData.cpp -// hifi -// -// Created by Stephen Birarda on 3/21/13. -// -// - -#include "PacketHeaders.h" -#include "SharedUtil.h" -#include "VoxelNodeData.h" -#include -#include -#include "VoxelSendThread.h" - -VoxelNodeData::VoxelNodeData(Node* owningNode) : - VoxelQuery(owningNode), - _viewSent(false), - _voxelPacketAvailableBytes(MAX_PACKET_SIZE), - _maxSearchLevel(1), - _maxLevelReachedInLastSearch(1), - _lastTimeBagEmpty(0), - _viewFrustumChanging(false), - _viewFrustumJustStoppedChanging(true), - _currentPacketIsColor(true), - _currentPacketIsCompressed(false), - _voxelSendThread(NULL), - _lastClientBoundaryLevelAdjust(0), - _lastClientVoxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), - _lodChanged(false), - _lodInitialized(false) -{ - _voxelPacket = new unsigned char[MAX_PACKET_SIZE]; - _voxelPacketAt = _voxelPacket; - _lastVoxelPacket = new unsigned char[MAX_PACKET_SIZE]; - _lastVoxelPacketLength = 0; - _duplicatePacketCount = 0; - _sequenceNumber = 0; - resetVoxelPacket(true); // don't bump sequence -} - -void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) { - // Create voxel sending thread... - QUuid nodeUUID = getOwningNode()->getUUID(); - _voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer); - _voxelSendThread->initialize(true); -} - -bool VoxelNodeData::packetIsDuplicate() const { - // since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp - // of the entire packet, we need to compare only the packet content... - if (_lastVoxelPacketLength == getPacketLength()) { - return memcmp(_lastVoxelPacket + VOXEL_PACKET_HEADER_SIZE, - _voxelPacket+VOXEL_PACKET_HEADER_SIZE , getPacketLength() - VOXEL_PACKET_HEADER_SIZE) == 0; - } - return false; -} - -bool VoxelNodeData::shouldSuppressDuplicatePacket() { - bool shouldSuppress = false; // assume we won't suppress - - // only consider duplicate packets - if (packetIsDuplicate()) { - _duplicatePacketCount++; - - // If this is the first suppressed packet, remember our time... - if (_duplicatePacketCount == 1) { - _firstSuppressedPacket = usecTimestampNow(); - } - - // How long has it been since we've sent one, if we're still under our max time, then keep considering - // this packet for suppression - uint64_t now = usecTimestampNow(); - long sinceFirstSuppressedPacket = now - _firstSuppressedPacket; - const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second. - - if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) { - // Finally, if we know we've sent at least one duplicate out, then suppress the rest... - if (_duplicatePacketCount >= 1) { - shouldSuppress = true; - } - } else { - // Reset our count, we've reached our maximum time. - _duplicatePacketCount = 0; - } - } else { - // Reset our count, it wasn't a duplicate - _duplicatePacketCount = 0; - } - return shouldSuppress; -} - -void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) { - // Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has - // changed since we last reset it. Since we know that no two packets can ever be identical without being the same - // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing - // packet send rate. - _lastVoxelPacketLength = getPacketLength(); - memcpy(_lastVoxelPacket, _voxelPacket, _lastVoxelPacketLength); - - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - _currentPacketIsColor = getWantColor(); - _currentPacketIsCompressed = getWantCompression(); - VOXEL_PACKET_FLAGS flags = 0; - if (_currentPacketIsColor) { - setAtBit(flags,PACKET_IS_COLOR_BIT); - } - if (_currentPacketIsCompressed) { - setAtBit(flags,PACKET_IS_COMPRESSED_BIT); - } - - _voxelPacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, PACKET_TYPE_VOXEL_DATA); - _voxelPacketAt = _voxelPacket + numBytesPacketHeader; - _voxelPacketAvailableBytes -= numBytesPacketHeader; - - // pack in flags - VOXEL_PACKET_FLAGS* flagsAt = (VOXEL_PACKET_FLAGS*)_voxelPacketAt; - *flagsAt = flags; - _voxelPacketAt += sizeof(VOXEL_PACKET_FLAGS); - _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_FLAGS); - - // pack in sequence number - VOXEL_PACKET_SEQUENCE* sequenceAt = (VOXEL_PACKET_SEQUENCE*)_voxelPacketAt; - *sequenceAt = _sequenceNumber; - _voxelPacketAt += sizeof(VOXEL_PACKET_SEQUENCE); - _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SEQUENCE); - if (!(lastWasSurpressed || _lastVoxelPacketLength == VOXEL_PACKET_HEADER_SIZE)) { - _sequenceNumber++; - } - - // pack in timestamp - VOXEL_PACKET_SENT_TIME now = usecTimestampNow(); - VOXEL_PACKET_SENT_TIME* timeAt = (VOXEL_PACKET_SENT_TIME*)_voxelPacketAt; - *timeAt = now; - _voxelPacketAt += sizeof(VOXEL_PACKET_SENT_TIME); - _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SENT_TIME); - - _voxelPacketWaiting = false; -} - -void VoxelNodeData::writeToPacket(const unsigned char* buffer, int bytes) { - // compressed packets include lead bytes which contain compressed size, this allows packing of - // multiple compressed portions together - if (_currentPacketIsCompressed) { - *(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)_voxelPacketAt = bytes; - _voxelPacketAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } - if (bytes <= _voxelPacketAvailableBytes) { - memcpy(_voxelPacketAt, buffer, bytes); - _voxelPacketAvailableBytes -= bytes; - _voxelPacketAt += bytes; - _voxelPacketWaiting = true; - } -} - -VoxelNodeData::~VoxelNodeData() { - delete[] _voxelPacket; - delete[] _lastVoxelPacket; - - if (_voxelSendThread) { - _voxelSendThread->terminate(); - delete _voxelSendThread; - } -} - -bool VoxelNodeData::updateCurrentViewFrustum() { - bool currentViewFrustumChanged = false; - ViewFrustum newestViewFrustum; - // get position and orientation details from the camera - newestViewFrustum.setPosition(getCameraPosition()); - newestViewFrustum.setOrientation(getCameraOrientation()); - - // Also make sure it's got the correct lens details from the camera - float originalFOV = getCameraFov(); - float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; - - newestViewFrustum.setFieldOfView(wideFOV); // hack - newestViewFrustum.setAspectRatio(getCameraAspectRatio()); - newestViewFrustum.setNearClip(getCameraNearClip()); - newestViewFrustum.setFarClip(getCameraFarClip()); - newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition()); - - // if there has been a change, then recalculate - if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { - _currentViewFrustum = newestViewFrustum; - _currentViewFrustum.calculate(); - currentViewFrustumChanged = true; - } - - // Also check for LOD changes from the client - if (_lodInitialized) { - if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) { - _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); - _lodChanged = true; - } - if (_lastClientVoxelSizeScale != getOctreeSizeScale()) { - _lastClientVoxelSizeScale = getOctreeSizeScale(); - _lodChanged = true; - } - } else { - _lodInitialized = true; - _lastClientVoxelSizeScale = getOctreeSizeScale(); - _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); - _lodChanged = false; - } - - // When we first detect that the view stopped changing, we record this. - // but we don't change it back to false until we've completely sent this - // scene. - if (_viewFrustumChanging && !currentViewFrustumChanged) { - _viewFrustumJustStoppedChanging = true; - } - _viewFrustumChanging = currentViewFrustumChanged; - return currentViewFrustumChanged; -} - -void VoxelNodeData::setViewSent(bool viewSent) { - _viewSent = viewSent; - if (viewSent) { - _viewFrustumJustStoppedChanging = false; - _lodChanged = false; - } -} - -void VoxelNodeData::updateLastKnownViewFrustum() { - bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); - - if (frustumChanges) { - // save our currentViewFrustum into our lastKnownViewFrustum - _lastKnownViewFrustum = _currentViewFrustum; - } - - // save that we know the view has been sent. - uint64_t now = usecTimestampNow(); - setLastTimeBagEmpty(now); // is this what we want? poor names -} - - -bool VoxelNodeData::moveShouldDump() const { - glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition(); - glm::vec3 newPosition = _currentViewFrustum.getPosition(); - - // theoretically we could make this slightly larger but relative to avatar scale. - const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f; - if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) { - return true; - } - return false; -} - -void VoxelNodeData::dumpOutOfView() { - int stillInView = 0; - int outOfView = 0; - OctreeElementBag tempBag; - while (!nodeBag.isEmpty()) { - OctreeElement* node = nodeBag.extract(); - if (node->isInView(_currentViewFrustum)) { - tempBag.insert(node); - stillInView++; - } else { - outOfView++; - } - } - if (stillInView > 0) { - while (!tempBag.isEmpty()) { - OctreeElement* node = tempBag.extract(); - if (node->isInView(_currentViewFrustum)) { - nodeBag.insert(node); - } - } - } -} - - -**/ diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp deleted file mode 100644 index 13176857a1..0000000000 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ /dev/null @@ -1,93 +0,0 @@ - -/***** - -// -// VoxelPersistThread.cpp -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded voxel persistence -// - -#include -#include -#include -#include - -#include "VoxelPersistThread.h" -#include "VoxelServer.h" - -VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval) : - _tree(tree), - _filename(filename), - _persistInterval(persistInterval), - _initialLoadComplete(false), - _loadTimeUSecs(0) { -} - -bool VoxelPersistThread::process() { - - if (!_initialLoadComplete) { - uint64_t loadStarted = usecTimestampNow(); - qDebug("loading voxels from file: %s...\n", _filename); - - bool persistantFileRead; - - _tree->lockForWrite(); - { - PerformanceWarning warn(true, "Loading Voxel File", true); - persistantFileRead = _tree->readFromSVOFile(_filename); - } - _tree->unlock(); - - _loadCompleted = time(0); - uint64_t loadDone = usecTimestampNow(); - _loadTimeUSecs = loadDone - loadStarted; - - _tree->clearDirtyBit(); // the tree is clean since we just loaded it - qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - - unsigned long nodeCount = OctreeElement::getNodeCount(); - unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); - unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); - qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); - - double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls(); - qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n", - OctreeElement::getGetChildAtIndexTime(), OctreeElement::getGetChildAtIndexCalls(), usecPerGet); - - double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls(); - qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", - OctreeElement::getSetChildAtIndexTime(), OctreeElement::getSetChildAtIndexCalls(), usecPerSet); - - _initialLoadComplete = true; - _lastCheck = usecTimestampNow(); // we just loaded, no need to save again - } - - if (isStillRunning()) { - uint64_t MSECS_TO_USECS = 1000; - uint64_t USECS_TO_SLEEP = 100 * MSECS_TO_USECS; // every 100ms - usleep(USECS_TO_SLEEP); - uint64_t now = usecTimestampNow(); - uint64_t sinceLastSave = now - _lastCheck; - uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS; - - if (sinceLastSave > intervalToCheck) { - // check the dirty bit and persist here... - _lastCheck = usecTimestampNow(); - if (_tree->isDirty()) { - qDebug("saving voxels to file %s...\n",_filename); - _tree->writeToSVOFile(_filename); - _tree->clearDirtyBit(); // tree is clean after saving - qDebug("DONE saving voxels to file...\n"); - } - } - } - return isStillRunning(); // keep running till they terminate us -} - - - -****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.h b/libraries/voxel-server-library/src/VoxelPersistThread.h deleted file mode 100644 index 68d0efa86d..0000000000 --- a/libraries/voxel-server-library/src/VoxelPersistThread.h +++ /dev/null @@ -1,49 +0,0 @@ -/**** - -// -// VoxelPersistThread.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded voxel persistence -// - -#ifndef __voxel_server__VoxelPersistThread__ -#define __voxel_server__VoxelPersistThread__ - -#include -#include -#include - -/// Generalized threaded processor for handling received inbound packets. -class VoxelPersistThread : public virtual GenericThread { -public: - static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds - - VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); - - bool isInitialLoadComplete() const { return _initialLoadComplete; } - - time_t* getLoadCompleted() { return &_loadCompleted; } - uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; } - -protected: - /// Implements generic processing behavior for this thread. - virtual bool process(); -private: - VoxelTree* _tree; - const char* _filename; - int _persistInterval; - bool _initialLoadComplete; - - time_t _loadCompleted; - uint64_t _loadTimeUSecs; - uint64_t _lastCheck; -}; - -#endif // __voxel_server__VoxelPersistThread__ - - -***/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelSendThread.h b/libraries/voxel-server-library/src/VoxelSendThread.h deleted file mode 100644 index 797eb142fa..0000000000 --- a/libraries/voxel-server-library/src/VoxelSendThread.h +++ /dev/null @@ -1,53 +0,0 @@ -/**** - -// -// VoxelSendThread.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded object for sending voxels to a client -// - -#ifndef __voxel_server__VoxelSendThread__ -#define __voxel_server__VoxelSendThread__ - -#include -#include -#include -#include -#include "VoxelNodeData.h" -#include "VoxelServer.h" - -/// Threaded processor for sending voxel packets to a single client -class VoxelSendThread : public virtual GenericThread { -public: - VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer); - - static uint64_t _totalBytes; - static uint64_t _totalWastedBytes; - static uint64_t _totalPackets; - - static uint64_t _usleepTime; - static uint64_t _usleepCalls; - -protected: - /// Implements generic processing behavior for this thread. - virtual bool process(); - -private: - QUuid _nodeUUID; - VoxelServer* _myServer; - - int handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent); - int deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged); - - unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE]; // used by environment sending code - VoxelPacketData _packetData; -}; - -#endif // __voxel_server__VoxelSendThread__ - - -****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index a456d3c38f..3c20cf6d9a 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -21,10 +21,7 @@ #include "civetweb.h" -#include "VoxelPersistThread.h" -#include "VoxelSendThread.h" #include "VoxelServerConsts.h" -#include "VoxelServerPacketProcessor.h" /// Handles assignments of type VoxelServer - sending voxels to various clients. class VoxelServer : public OctreeServer { diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp deleted file mode 100644 index ac303d3c89..0000000000 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/****** - -// -// VoxelServerPacketProcessor.cpp -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded network packet processor for the voxel-server -// - -#include -#include - -#include "VoxelServer.h" -#include "VoxelServerConsts.h" -#include "VoxelServerPacketProcessor.h" - -static QUuid DEFAULT_NODE_ID_REF; - -VoxelServerPacketProcessor::VoxelServerPacketProcessor(VoxelServer* myServer) : - _myServer(myServer), - _receivedPacketCount(0), - _totalTransitTime(0), - _totalProcessTime(0), - _totalLockWaitTime(0), - _totalVoxelsInPacket(0), - _totalPackets(0) -{ -} - -void VoxelServerPacketProcessor::resetStats() { - _totalTransitTime = 0; - _totalProcessTime = 0; - _totalLockWaitTime = 0; - _totalVoxelsInPacket = 0; - _totalPackets = 0; - - _singleSenderStats.clear(); -} - - -void VoxelServerPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, - unsigned char* packetData, ssize_t packetLength) { - - bool debugProcessPacket = _myServer->wantsVerboseDebug(); - - if (debugProcessPacket) { - printf("VoxelServerPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength); - } - - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - - if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) { - bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); - PerformanceWarning warn(_myServer->wantShowAnimationDebug(), - destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", - _myServer->wantShowAnimationDebug()); - - _receivedPacketCount++; - - unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); - uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence)))); - uint64_t arrivedAt = usecTimestampNow(); - uint64_t transitTime = arrivedAt - sentAt; - int voxelsInPacket = 0; - uint64_t processTime = 0; - uint64_t lockWaitTime = 0; - - if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugVoxelReceiving()) { - printf("PROCESSING THREAD: got %s - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n", - destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", - _receivedPacketCount, packetLength, sequence, transitTime); - } - int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt); - unsigned char* voxelData = (unsigned char*)&packetData[atByte]; - while (atByte < packetLength) { - int maxSize = packetLength - atByte; - - if (debugProcessPacket) { - printf("VoxelServerPacketProcessor::processPacket() %s packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n", - destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", - packetData, packetLength, voxelData, atByte, maxSize); - } - - int octets = numberOfThreeBitSectionsInCode(voxelData, maxSize); - - if (octets == OVERFLOWED_OCTCODE_BUFFER) { - printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), "); - printf("bailing processing of packet!\n"); - break; - } - - const int COLOR_SIZE_IN_BYTES = 3; - int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES; - int voxelCodeSize = bytesRequiredForCodeLength(octets); - - if (atByte + voxelDataSize <= packetLength) { - if (_myServer->wantShowAnimationDebug()) { - int red = voxelData[voxelCodeSize + RED_INDEX]; - int green = voxelData[voxelCodeSize + GREEN_INDEX]; - int blue = voxelData[voxelCodeSize + BLUE_INDEX]; - - float* vertices = firstVertexForCode(voxelData); - printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue); - delete[] vertices; - } - - uint64_t startLock = usecTimestampNow(); - _myServer->getServerTree().lockForWrite(); - uint64_t startProcess = usecTimestampNow(); - _myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive); - _myServer->getServerTree().unlock(); - uint64_t endProcess = usecTimestampNow(); - - voxelsInPacket++; - - uint64_t thisProcessTime = endProcess - startProcess; - uint64_t thisLockWaitTime = startProcess - startLock; - - processTime += thisProcessTime; - lockWaitTime += thisLockWaitTime; - - // skip to next voxel edit record in the packet - voxelData += voxelDataSize; - atByte += voxelDataSize; - } else { - printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n"); - break; - } - } - - if (debugProcessPacket) { - printf("VoxelServerPacketProcessor::processPacket() DONE LOOPING FOR %s packetData=%p packetLength=%ld voxelData=%p atByte=%d\n", - destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", - packetData, packetLength, voxelData, atByte); - } - - // Make sure our Node and NodeList knows we've heard from this node. - Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); - QUuid& nodeUUID = DEFAULT_NODE_ID_REF; - if (senderNode) { - senderNode->setLastHeardMicrostamp(usecTimestampNow()); - nodeUUID = senderNode->getUUID(); - if (debugProcessPacket) { - qDebug() << "sender has uuid=" << nodeUUID << "\n"; - } - } else { - if (debugProcessPacket) { - qDebug() << "sender has no known nodeUUID.\n"; - } - } - trackInboundPackets(nodeUUID, sequence, transitTime, voxelsInPacket, processTime, lockWaitTime); - - } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) { - - _receivedPacketCount++; - - unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); - uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence)))); - uint64_t arrivedAt = usecTimestampNow(); - uint64_t transitTime = arrivedAt - sentAt; - - if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugVoxelReceiving()) { - printf("PROCESSING THREAD: got PACKET_TYPE_ERASE_VOXEL - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n", - _receivedPacketCount, packetLength, sequence, transitTime); - } - - // Send these bits off to the VoxelTree class to process them - _myServer->getServerTree().lockForWrite(); - _myServer->getServerTree().processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength); - _myServer->getServerTree().unlock(); - - // Make sure our Node and NodeList knows we've heard from this node. - Node* node = NodeList::getInstance()->nodeWithAddress(senderSockAddr); - if (node) { - node->setLastHeardMicrostamp(usecTimestampNow()); - } - } else { - printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]); - } -} - -void VoxelServerPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, - int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime) { - - _totalTransitTime += transitTime; - _totalProcessTime += processTime; - _totalLockWaitTime += lockWaitTime; - _totalVoxelsInPacket += voxelsInPacket; - _totalPackets++; - - // find the individual senders stats and track them there too... - // see if this is the first we've heard of this node... - if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) { - SingleSenderStats stats; - - stats._totalTransitTime += transitTime; - stats._totalProcessTime += processTime; - stats._totalLockWaitTime += lockWaitTime; - stats._totalVoxelsInPacket += voxelsInPacket; - stats._totalPackets++; - - _singleSenderStats[nodeUUID] = stats; - } else { - SingleSenderStats& stats = _singleSenderStats[nodeUUID]; - stats._totalTransitTime += transitTime; - stats._totalProcessTime += processTime; - stats._totalLockWaitTime += lockWaitTime; - stats._totalVoxelsInPacket += voxelsInPacket; - stats._totalPackets++; - } -} - - -SingleSenderStats::SingleSenderStats() { - _totalTransitTime = 0; - _totalProcessTime = 0; - _totalLockWaitTime = 0; - _totalVoxelsInPacket = 0; - _totalPackets = 0; -} - - -****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h deleted file mode 100644 index 7249fff4ca..0000000000 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.h +++ /dev/null @@ -1,89 +0,0 @@ -/****** - - -// -// VoxelServerPacketProcessor.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded network packet processor for the voxel-server -// - -#ifndef __voxel_server__VoxelServerPacketProcessor__ -#define __voxel_server__VoxelServerPacketProcessor__ - -#include - -#include -class VoxelServer; - -class SingleSenderStats { -public: - SingleSenderStats(); - - uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } - uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } - uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } - uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; } - uint64_t getTotalPacketsProcessed() const { return _totalPackets; } - uint64_t getAverageProcessTimePerVoxel() const - { return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; } - uint64_t getAverageLockWaitTimePerVoxel() const - { return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; } - - uint64_t _totalTransitTime; - uint64_t _totalProcessTime; - uint64_t _totalLockWaitTime; - uint64_t _totalVoxelsInPacket; - uint64_t _totalPackets; -}; - -typedef std::map NodeToSenderStatsMap; -typedef std::map::iterator NodeToSenderStatsMapIterator; - - -/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes -/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() -class VoxelServerPacketProcessor : public ReceivedPacketProcessor { - -public: - VoxelServerPacketProcessor(VoxelServer* myServer); - - uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } - uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } - uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } - uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; } - uint64_t getTotalPacketsProcessed() const { return _totalPackets; } - uint64_t getAverageProcessTimePerVoxel() const - { return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; } - uint64_t getAverageLockWaitTimePerVoxel() const - { return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; } - - void resetStats(); - - NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } - -protected: - virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); - -private: - void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, - int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime); - - VoxelServer* _myServer; - int _receivedPacketCount; - - uint64_t _totalTransitTime; - uint64_t _totalProcessTime; - uint64_t _totalLockWaitTime; - uint64_t _totalVoxelsInPacket; - uint64_t _totalPackets; - - NodeToSenderStatsMap _singleSenderStats; -}; -#endif // __voxel_server__VoxelServerPacketProcessor__ - - -***/ From b3e9d369e474e936b7c133894d7b3bcecf4af5db Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 19:20:11 -0800 Subject: [PATCH 05/17] removed all dead VoxelServer code, now mostly handled by OctreeServer --- .../src/VoxelSendThread.cpp | 583 -------------- .../voxel-server-library/src/VoxelServer.cpp | 740 +----------------- .../voxel-server-library/src/VoxelServer.h | 6 + 3 files changed, 22 insertions(+), 1307 deletions(-) delete mode 100644 libraries/voxel-server-library/src/VoxelSendThread.cpp diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp deleted file mode 100644 index 4af48f3299..0000000000 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/***** - -// -// VoxelSendThread.cpp -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded voxel packet sender -// - -#include -#include -#include -#include -#include - -extern EnvironmentData environmentData[3]; - - -#include "VoxelSendThread.h" -#include "VoxelServer.h" -#include "VoxelServerConsts.h" - - -uint64_t startSceneSleepTime = 0; -uint64_t endSceneSleepTime = 0; - - -VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) : - _nodeUUID(nodeUUID), - _myServer(myServer), - _packetData() -{ -} - -bool VoxelSendThread::process() { - uint64_t start = usecTimestampNow(); - bool gotLock = false; - - // don't do any send processing until the initial load of the voxels is complete... - if (_myServer->isInitialLoadComplete()) { - Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - - if (node) { - // make sure the node list doesn't kill our node while we're using it - if (node->trylock()) { - gotLock = true; - VoxelNodeData* nodeData = NULL; - - nodeData = (VoxelNodeData*) node->getLinkedData(); - - int packetsSent = 0; - - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); - } - - node->unlock(); // we're done with this node for now. - } - } - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n"); - } - } - - // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap - if (isStillRunning() && gotLock) { - // dynamically sleep until we need to fire off the next set of voxels - int elapsed = (usecTimestampNow() - start); - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed; - - if (usecToSleep > 0) { - PerformanceWarning warn(false,"VoxelSendThread... usleep()",false,&_usleepTime,&_usleepCalls); - usleep(usecToSleep); - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - std::cout << "Last send took too much time, not sleeping!\n"; - } - } - } - - return isStillRunning(); // keep running till they terminate us -} - -uint64_t VoxelSendThread::_usleepTime = 0; -uint64_t VoxelSendThread::_usleepCalls = 0; - -uint64_t VoxelSendThread::_totalBytes = 0; -uint64_t VoxelSendThread::_totalWastedBytes = 0; -uint64_t VoxelSendThread::_totalPackets = 0; - -int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) { - bool debug = _myServer->wantsDebugVoxelSending(); - uint64_t now = usecTimestampNow(); - - bool packetSent = false; // did we send a packet? - int packetsSent = 0; - // Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently - // obscure the packet and not send it. This allows the callers and upper level logic to not need to know about - // this rate control savings. - if (nodeData->shouldSuppressDuplicatePacket()) { - nodeData->resetVoxelPacket(true); // we still need to reset it though! - return packetsSent; // without sending... - } - - const unsigned char* messageData = nodeData->getPacket(); - int numBytesPacketHeader = numBytesForPacketHeader(messageData); - const unsigned char* dataAt = messageData + numBytesPacketHeader; - dataAt += sizeof(VOXEL_PACKET_FLAGS); - VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - - - // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { - // Send the stats message to the client - unsigned char* statsMessage = nodeData->stats.getStatsMessage(); - int statsMessageLength = nodeData->stats.getStatsMessageLength(); - - // If the size of the stats message and the voxel message will fit in a packet, then piggyback them - if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { - - // copy voxel message to back of stats message - memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); - statsMessageLength += nodeData->getPacketLength(); - - // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since - // there was nothing else to send. - int thisWastedBytes = 0; - _totalWastedBytes += thisWastedBytes; - _totalBytes += nodeData->getPacketLength(); - _totalPackets++; - if (debug) { - qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - - // actually send it - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - packetSent = true; - } else { - // not enough room in the packet, send two packets - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - - // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since - // there was nothing else to send. - int thisWastedBytes = 0; - _totalWastedBytes += thisWastedBytes; - _totalBytes += statsMessageLength; - _totalPackets++; - if (debug) { - qDebug("Sending separate stats packet at %llu [%llu]: size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - statsMessageLength, _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - - trueBytesSent += statsMessageLength; - truePacketsSent++; - packetsSent++; - - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - - packetSent = true; - - thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); - _totalWastedBytes += thisWastedBytes; - _totalBytes += nodeData->getPacketLength(); - _totalPackets++; - if (debug) { - qDebug("Sending packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - } - nodeData->stats.markAsSent(); - } else { - // If there's actually a packet waiting, then send it. - if (nodeData->isPacketWaiting()) { - // just send the voxel packet - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - packetSent = true; - - int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); - _totalWastedBytes += thisWastedBytes; - _totalBytes += nodeData->getPacketLength(); - _totalPackets++; - if (debug) { - qDebug("Sending packet at %llu [%llu]: sequence:%d size:%d [%llu] wasted bytes:%d [%llu]\n", - now, - _totalPackets, - sequence, nodeData->getPacketLength(), _totalBytes, - thisWastedBytes, _totalWastedBytes); - } - } - } - // remember to track our stats - if (packetSent) { - nodeData->stats.packetSent(nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - packetsSent++; - nodeData->resetVoxelPacket(); - } - - return packetsSent; -} - -/// Version of voxel distributor that sends the deepest LOD level at once -int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { - - int truePacketsSent = 0; - int trueBytesSent = 0; - int packetsSentThisInterval = 0; - bool somethingToSend = true; // assume we have something - - // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - bool wantColor = nodeData->getWantColor(); - bool wantCompression = nodeData->getWantCompression(); - - // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. - if (!nodeData->getCurrentPacketFormatMatches()) { - if (nodeData->isPacketWaiting()) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - } else { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } - nodeData->resetVoxelPacket(); - } - int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, - debug::valueOf(wantCompression), targetSize); - } - - _packetData.changeSettings(wantCompression, targetSize); - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - - // If the current view frustum has changed OR we have nothing to send, then search against - // the current view frustum for things to send. - if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } - - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (_myServer->wantDumpVoxelsOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) { - nodeData->dumpOutOfView(); - } - nodeData->map.erase(); - } - - if (!viewFrustumChanged && !nodeData->getWantDelta()) { - // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); - nodeData->setLastTimeBagEmpty(now); - } - - // track completed scenes and send out the stats packet accordingly - nodeData->stats.sceneCompleted(); - ::endSceneSleepTime = _usleepTime; - unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; - - unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); - unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - - if (_myServer->wantsDebugVoxelSending()) { - qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n", - usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes); - } - - if (_myServer->wantDisplayVoxelStats()) { - nodeData->stats.printDebugDetails(); - } - - // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) - && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - // If we're starting a full scene, then definitely we want to empty the nodeBag - if (isFullScene) { - nodeData->nodeBag.deleteAll(); - } - - if (_myServer->wantsDebugVoxelSending()) { - qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n", - usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes); - } - - ::startSceneSleepTime = _usleepTime; - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getServerTree().getRoot(), _myServer->getJurisdiction()); - - // This is the start of "resending" the scene. - bool dontRestartSceneOnMove = false; // this is experimental - if (dontRestartSceneOnMove) { - if (nodeData->nodeBag.isEmpty()) { - nodeData->nodeBag.insert(_myServer->getServerTree().getRoot()); // only in case of empty - } - } else { - nodeData->nodeBag.insert(_myServer->getServerTree().getRoot()); // original behavior, reset on move or empty - } - } - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - int bytesWritten = 0; - uint64_t start = usecTimestampNow(); - uint64_t startCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000; - uint64_t startCompressCalls = VoxelPacketData::getCompressContentCalls(); - - bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - - int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); - int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - int extraPackingAttempts = 0; - while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - bool lastNodeDidntFit = false; // assume each node fits - if (!nodeData->nodeBag.isEmpty()) { - OctreeElement* subTree = nodeData->nodeBag.extract(); - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - - float voxelSizeScale = nodeData->getOctreeSizeScale(); - int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - - int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - - - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, - WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, - nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats, _myServer->getJurisdiction()); - - - _myServer->getServerTree().lockForRead(); - nodeData->stats.encodeStarted(); - bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - - // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. - if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) { - if (_packetData.hasContent() && bytesWritten == 0 && - params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } - } else { - // in compressed mode and we are trying to pack more... and we don't care if the _packetData has - // content or not... because in this case even if we were unable to pack any data, we want to drop - // below to our sendNow logic, but we do want to track that we attempted to pack extra - extraPackingAttempts++; - if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - } - } - - nodeData->stats.encodeStopped(); - _myServer->getServerTree().unlock(); - } else { - // If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0 - bytesWritten = 0; - somethingToSend = false; // this will cause us to drop out of the loop... - } - - // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a - // little bit more in this packet. To do this we - - // We only consider sending anything if there is something in the _packetData to send... But - // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases - // mean we should send the previous packet contents and reset it. - if (lastNodeDidntFit) { - if (_packetData.hasContent()) { - // if for some reason the finalized size is greater than our available size, then probably the "compressed" - // form actually inflated beyond our padding, and in this case we will send the current packet, then - // write to out new packet... - int writtenSize = _packetData.getFinalizedSize() - + (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0); - - - if (writtenSize > nodeData->getAvailable()) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", - writtenSize, nodeData->getAvailable()); - } - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", - nodeData->getAvailable(), _packetData.getFinalizedSize(), - _packetData.getUncompressedSize(), _packetData.getTargetSize()); - } - nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); - extraPackingAttempts = 0; - } - - // If we're not running compressed, the we know we can just send now. Or if we're running compressed, but - // the packet doesn't have enough space to bother attempting to pack more... - bool sendNow = true; - - if (nodeData->getCurrentPacketIsCompressed() && - nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && - extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { - sendNow = false; // try to pack more - } - - int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; - if (sendNow) { - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } - } else { - // If we're in compressed mode, then we want to see if we have room for more in this wire packet. - // but we've finalized the _packetData, so we want to start a new section, we will do that by - // resetting the packet settings with the max uncompressed size of our current available space - // in the wire packet. We also include room for our section header, and a little bit of padding - // to account for the fact that whenc compressing small amounts of data, we sometimes end up with - // a larger compressed size then uncompressed size - targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; - } - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, - debug::valueOf(nodeData->getWantCompression()), targetSize); - } - _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset - } - } - - // send the environment packet - if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount(); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); - } - - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - trueBytesSent += envPacketLength; - truePacketsSent++; - packetsSentThisInterval++; - } - - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - - uint64_t endCompressCalls = VoxelPacketData::getCompressContentCalls(); - int elapsedCompressCalls = endCompressCalls - startCompressCalls; - - uint64_t endCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000; - int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; - - - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the voxels from the current view frustum - if (nodeData->nodeBag.isEmpty()) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - nodeData->map.printStats(); - } - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes - } - - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - - } // end if bag wasn't empty, and so we sent stuff... - - return truePacketsSent; -} - -****/ \ No newline at end of file diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 6e68b05170..6408d356a5 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -43,748 +43,40 @@ const char* VoxelServer::getMyDefaultPersistFilename() { return LOCAL_VOXELS_PERSIST_FILE; } -/***** - - -void attachVoxelNodeDataToNode(Node* newNode) { - if (newNode->getLinkedData() == NULL) { - VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode); - newNode->setLinkedData(voxelNodeData); - } +bool VoxelServer::hasSpecialPacketToSend() { + bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS); + return shouldSendEnvironments; } -VoxelServer* VoxelServer::_theInstance = NULL; - -VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : - ThreadedAssignment(dataBuffer, numBytes), - _serverTree(true) -{ - _argc = 0; - _argv = NULL; - - _packetsPerClientPerInterval = 10; - _wantVoxelPersist = true; - _wantLocalDomain = false; - _debugVoxelSending = false; - _shouldShowAnimationDebug = false; - _displayVoxelStats = false; - _debugVoxelReceiving = false; - _sendEnvironments = true; - _sendMinimalEnvironment = false; - _dumpVoxelsOnMove = false; - _verboseDebug = false; - _jurisdiction = NULL; - _jurisdictionSender = NULL; - _voxelServerPacketProcessor = NULL; - _voxelPersistThread = NULL; - _parsedArgV = NULL; +int VoxelServer::sendSpecialPacket(Node* node) { + int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount(); - _started = time(0); - _startedUSecs = usecTimestampNow(); - - _theInstance = this; -} - -VoxelServer::~VoxelServer() { - if (_parsedArgV) { - for (int i = 0; i < _argc; i++) { - delete[] _parsedArgV[i]; - } - delete[] _parsedArgV; - } -} - -void VoxelServer::initMongoose(int port) { - // setup the mongoose web server - struct mg_callbacks callbacks = {}; - - QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); - QString listenPort = QString("%1").arg(port); - - - // list of options. Last element must be NULL. - const char* options[] = { - "listening_ports", listenPort.toLocal8Bit().constData(), - "document_root", documentRoot.toLocal8Bit().constData(), - NULL }; - - callbacks.begin_request = civetwebRequestHandler; - - // Start the web server. - mg_start(&callbacks, NULL, options); -} - -int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { - const struct mg_request_info* ri = mg_get_request_info(connection); - - VoxelServer* theServer = GetInstance(); - -#ifdef FORCE_CRASH - if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) { - qDebug() << "About to force a crash!\n"; - int foo; - int* forceCrash = &foo; - mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); - mg_printf(connection, "%s", "forcing a crash....\r\n"); - delete[] forceCrash; - mg_printf(connection, "%s", "did it crash....\r\n"); - return 1; - } -#endif - - bool showStats = false; - if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) { - showStats = true; - } - - if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) { - theServer->_voxelServerPacketProcessor->resetStats(); - showStats = true; + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); } - if (showStats) { - uint64_t checkSum; - // return a 200 - mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n"); - mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n"); - mg_printf(connection, "%s", "\r\n"); - mg_printf(connection, "%s", "
\r\n");
-        mg_printf(connection, "%s", "Your Voxel Server is running... [RELOAD]\r\n");
-
-        tm* localtm = localtime(&theServer->_started);
-        const int MAX_TIME_LENGTH = 128;
-        char buffer[MAX_TIME_LENGTH];
-        strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
-        mg_printf(connection, "Running since: %s", buffer);
-
-        // Convert now to tm struct for UTC
-        tm* gmtm = gmtime(&theServer->_started);
-        if (gmtm != NULL) {
-            strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
-            mg_printf(connection, " [%s UTM] ", buffer);
-        }
-        mg_printf(connection, "%s", "\r\n");
-
-        uint64_t now  = usecTimestampNow();
-        const int USECS_PER_MSEC = 1000;
-        uint64_t msecsElapsed = (now - theServer->_startedUSecs) / USECS_PER_MSEC;
-        const int MSECS_PER_SEC = 1000;
-        const int SECS_PER_MIN = 60;
-        const int MIN_PER_HOUR = 60;
-        const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
-
-        float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
-        int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
-        int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
-
-        mg_printf(connection, "%s", "Uptime: ");
-        if (hours > 0) {
-            mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
-        }
-        if (minutes > 0) {
-            mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
-        }
-        if (seconds > 0) {
-            mg_printf(connection, "%.3f seconds ", seconds);
-        }
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-
-        // display voxel file load time
-        if (theServer->isInitialLoadComplete()) {
-            time_t* loadCompleted = theServer->getLoadCompleted();
-            if (loadCompleted) {
-                tm* voxelsLoadedAtLocal = localtime(loadCompleted);
-                const int MAX_TIME_LENGTH = 128;
-                char buffer[MAX_TIME_LENGTH];
-                strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
-                mg_printf(connection, "Voxels Loaded At: %s", buffer);
-
-                // Convert now to tm struct for UTC
-                tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
-                if (gmtm != NULL) {
-                    strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
-                    mg_printf(connection, " [%s UTM] ", buffer);
-                }
-            } else {
-                mg_printf(connection, "%s", "Voxel Persist Disabled...\r\n");
-            }
-            mg_printf(connection, "%s", "\r\n");
-
-
-            uint64_t msecsElapsed = theServer->getLoadElapsedTime() / USECS_PER_MSEC;;
-            float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
-            int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
-            int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
-
-            mg_printf(connection, "%s", "Voxels Load Took: ");
-            if (hours > 0) {
-                mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
-            }
-            if (minutes > 0) {
-                mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
-            }
-            if (seconds >= 0) {
-                mg_printf(connection, "%.3f seconds", seconds);
-            }
-            mg_printf(connection, "%s", "\r\n");
-
-        } else {
-            mg_printf(connection, "%s", "Voxels not yet loaded...\r\n");
-        }
-
-        mg_printf(connection, "%s", "\r\n");
-
-        mg_printf(connection, "%s", "\r\n");
-
-        mg_printf(connection, "%s", "Configuration:\r\n");
-
-        for (int i = 1; i < theServer->_argc; i++) {
-            mg_printf(connection, "%s ", theServer->_argv[i]);
-        }
-        mg_printf(connection, "%s", "\r\n"); // one to end the config line
-        mg_printf(connection, "%s", "\r\n"); // two more for spacing
-        mg_printf(connection, "%s", "\r\n");
-
-        // display scene stats
-        unsigned long nodeCount = OctreeElement::getNodeCount();
-        unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
-        unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
-        
-        QLocale locale(QLocale::English);
-        const float AS_PERCENT = 100.0;
-        mg_printf(connection, "%s", "Current Nodes in scene:\r\n");
-        mg_printf(connection, "       Total Nodes: %s nodes\r\n",
-                    locale.toString((uint)nodeCount).rightJustified(16, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "    Internal Nodes: %s nodes (%5.2f%%)\r\n",
-            locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
-            ((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "        Leaf Nodes: %s nodes (%5.2f%%)\r\n", 
-            locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
-            ((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-
-        // display outbound packet stats
-        mg_printf(connection, "%s", "Voxel Packet Statistics...\r\n");
-        uint64_t totalOutboundPackets = VoxelSendThread::_totalPackets;
-        uint64_t totalOutboundBytes = VoxelSendThread::_totalBytes;
-        uint64_t totalWastedBytes = VoxelSendThread::_totalWastedBytes;
-        uint64_t totalBytesOfOctalCodes = VoxelPacketData::getTotalBytesOfOctalCodes();
-        uint64_t totalBytesOfBitMasks = VoxelPacketData::getTotalBytesOfBitMasks();
-        uint64_t totalBytesOfColor = VoxelPacketData::getTotalBytesOfColor();
-
-        const int COLUMN_WIDTH = 10;
-        mg_printf(connection, "           Total Outbound Packets: %s packets\r\n",
-            locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "             Total Outbound Bytes: %s bytes\r\n",
-            locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "               Total Wasted Bytes: %s bytes\r\n",
-            locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "            Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
-        mg_printf(connection, "             Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
-        mg_printf(connection, "                Total Color Bytes: %s bytes (%5.2f%%)\r\n",
-            locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
-            ((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-        // display inbound packet stats
-        mg_printf(connection, "%s", "Voxel Edit Statistics... [RESET]\r\n");
-        uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageTransitTimePerPacket();
-        uint64_t averageProcessTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerPacket();
-        uint64_t averageLockWaitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerPacket();
-        uint64_t averageProcessTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerVoxel();
-        uint64_t averageLockWaitTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerVoxel();
-        uint64_t totalVoxelsProcessed = theServer->_voxelServerPacketProcessor->getTotalVoxelsProcessed();
-        uint64_t totalPacketsProcessed = theServer->_voxelServerPacketProcessor->getTotalPacketsProcessed();
-
-        float averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
-
-        mg_printf(connection, "           Total Inbound Packets: %s packets\r\n",
-            locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "            Total Inbound Voxels: %s voxels\r\n",
-            locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "   Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
-        mg_printf(connection, "     Average Transit Time/Packet: %s usecs\r\n", 
-            locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "     Average Process Time/Packet: %s usecs\r\n",
-            locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "   Average Wait Lock Time/Packet: %s usecs\r\n", 
-            locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "      Average Process Time/Voxel: %s usecs\r\n",
-            locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-        mg_printf(connection, "    Average Wait Lock Time/Voxel: %s usecs\r\n", 
-            locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-
-
-        int senderNumber = 0;
-        NodeToSenderStatsMap& allSenderStats = theServer->_voxelServerPacketProcessor->getSingleSenderStats();
-        for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
-            senderNumber++;
-            QUuid senderID = i->first;
-            SingleSenderStats& senderStats = i->second;
-
-            mg_printf(connection, "\r\n             Stats for sender %d uuid: %s\r\n", senderNumber, 
-                senderID.toString().toLocal8Bit().constData());
-
-            averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
-            averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
-            averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
-            averageProcessTimePerVoxel = senderStats.getAverageProcessTimePerVoxel();
-            averageLockWaitTimePerVoxel = senderStats.getAverageLockWaitTimePerVoxel();
-            totalVoxelsProcessed = senderStats.getTotalVoxelsProcessed();
-            totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
-
-            averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
-
-            mg_printf(connection, "               Total Inbound Packets: %s packets\r\n",
-                locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "                Total Inbound Voxels: %s voxels\r\n",
-                locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "       Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
-            mg_printf(connection, "         Average Transit Time/Packet: %s usecs\r\n", 
-                locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "         Average Process Time/Packet: %s usecs\r\n",
-                locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "       Average Wait Lock Time/Packet: %s usecs\r\n", 
-                locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "          Average Process Time/Voxel: %s usecs\r\n",
-                locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-            mg_printf(connection, "        Average Wait Lock Time/Voxel: %s usecs\r\n", 
-                locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
-
-        }
-
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-
-        // display memory usage stats
-        mg_printf(connection, "%s", "Current Memory Usage Statistics\r\n");
-        mg_printf(connection, "\r\nVoxelTreeElement size... %ld bytes\r\n", sizeof(VoxelTreeElement));
-        mg_printf(connection, "%s", "\r\n");
-
-        const char* memoryScaleLabel;
-        const float MEGABYTES = 1000000.f;
-        const float GIGABYTES = 1000000000.f;
-        float memoryScale;
-        if (OctreeElement::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
-            memoryScaleLabel = "MB";
-            memoryScale = MEGABYTES;
-        } else {
-            memoryScaleLabel = "GB";
-            memoryScale = GIGABYTES;
-        }
-
-        mg_printf(connection, "Voxel Node Memory Usage:         %8.2f %s\r\n", 
-            OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "Octcode Memory Usage:            %8.2f %s\r\n", 
-            OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "External Children Memory Usage:  %8.2f %s\r\n", 
-            OctreeElement::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
-        mg_printf(connection, "%s", "                                 -----------\r\n");
-        mg_printf(connection, "                         Total:  %8.2f %s\r\n", 
-            OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "OctreeElement Children Population Statistics...\r\n");
-        checkSum = 0;
-        for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
-            checkSum += OctreeElement::getChildrenCount(i);
-            mg_printf(connection, "    Nodes with %d children:      %s nodes (%5.2f%%)\r\n", i, 
-                locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
-                ((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
-        }
-        mg_printf(connection, "%s", "                                ----------------------\r\n");
-        mg_printf(connection, "                    Total:      %s nodes\r\n", 
-            locale.toString((uint)checkSum).rightJustified(16, ' ').toLocal8Bit().constData());
-
-#ifdef BLENDED_UNION_CHILDREN
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "OctreeElement Children Encoding Statistics...\r\n");
-        
-        mg_printf(connection, "    Single or No Children:      %10.llu nodes (%5.2f%%)\r\n",
-            OctreeElement::getSingleChildrenCount(), ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Two Children as Offset:     %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getTwoChildrenOffsetCount(), 
-            ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Two Children as External:   %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getTwoChildrenExternalCount(), 
-            ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Three Children as Offset:   %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getThreeChildrenOffsetCount(), 
-            ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Three Children as External: %10.llu nodes (%5.2f%%)\r\n", 
-            OctreeElement::getThreeChildrenExternalCount(), 
-            ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
-        mg_printf(connection, "    Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
-            OctreeElement::getExternalChildrenCount(), 
-            ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
-
-        checkSum = OctreeElement::getSingleChildrenCount() +
-                            OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() + 
-                            OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() + 
-                            OctreeElement::getExternalChildrenCount();
-
-        mg_printf(connection, "%s", "                                ----------------\r\n");
-        mg_printf(connection, "                         Total: %10.llu nodes\r\n", checkSum);
-        mg_printf(connection, "                      Expected: %10.lu nodes\r\n", nodeCount);
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "In other news....\r\n");
-        mg_printf(connection, "could store 4 children internally:     %10.llu nodes\r\n",
-            OctreeElement::getCouldStoreFourChildrenInternally());
-        mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n", 
-            OctreeElement::getCouldNotStoreFourChildrenInternally());
-#endif
-
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "\r\n");
-        mg_printf(connection, "%s", "
\r\n"); - - mg_printf(connection, "%s", "
"); - - return 1; - } else { - // have mongoose process this request from the document_root - return 0; - } + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + return envPacketLength; } -void VoxelServer::setArguments(int argc, char** argv) { - _argc = argc; - _argv = const_cast(argv); - - qDebug("VoxelServer::setArguments()\n"); - for (int i = 0; i < _argc; i++) { - qDebug("_argv[%d]=%s\n", i, _argv[i]); - } - -} - -void VoxelServer::parsePayload() { - - if (getNumPayloadBytes() > 0) { - QString config((const char*) _payload); - - // Now, parse the config - QStringList configList = config.split(" "); - - int argCount = configList.size() + 1; - - qDebug("VoxelServer::parsePayload()... argCount=%d\n",argCount); - - _parsedArgV = new char*[argCount]; - const char* dummy = "config-from-payload"; - _parsedArgV[0] = new char[strlen(dummy) + sizeof(char)]; - strcpy(_parsedArgV[0], dummy); - - for (int i = 1; i < argCount; i++) { - QString configItem = configList.at(i-1); - _parsedArgV[i] = new char[configItem.length() + sizeof(char)]; - strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData()); - qDebug("VoxelServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]); - } - - setArguments(argCount, _parsedArgV); - } -} - -void VoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - NodeList* nodeList = NodeList::getInstance(); - - if (dataByteArray[0] == PACKET_TYPE_VOXEL_QUERY) { - bool debug = false; - if (debug) { - qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow()); - } - - int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data()); - - // If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we - // need to make sure we have it in our nodeList. - QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader, - NUM_BYTES_RFC4122_UUID)); - - Node* node = nodeList->nodeWithUUID(nodeUUID); - - if (node) { - nodeList->updateNodeWithData(node, senderSockAddr, (unsigned char *) dataByteArray.data(), - dataByteArray.size()); - if (!node->getActiveSocket()) { - // we don't have an active socket for this node, but they're talking to us - // this means they've heard from us and can reply, let's assume public is active - node->activatePublicSocket(); - } - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); - if (nodeData && !nodeData->isVoxelSendThreadInitalized()) { - nodeData->initializeVoxelSendThread(this); - } - } - } else if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { - if (_jurisdictionSender) { - _jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } - } else if (_voxelServerPacketProcessor && - (dataByteArray[0] == PACKET_TYPE_SET_VOXEL - || dataByteArray[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE - || dataByteArray[0] == PACKET_TYPE_ERASE_VOXEL)) { - - - const char* messageName; - switch (dataByteArray[0]) { - case PACKET_TYPE_SET_VOXEL: - messageName = "PACKET_TYPE_SET_VOXEL"; - break; - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: - messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE"; - break; - case PACKET_TYPE_ERASE_VOXEL: - messageName = "PACKET_TYPE_ERASE_VOXEL"; - break; - } - _voxelServerPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } else { - // let processNodeData handle it. - NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), - dataByteArray.size()); - } -} - -//int main(int argc, const char * argv[]) { -void VoxelServer::run() { - - const char VOXEL_SERVER_LOGGING_TARGET_NAME[] = "voxel-server"; - - // change the logging target name while this is running - Logging::setTargetName(VOXEL_SERVER_LOGGING_TARGET_NAME); - - // Now would be a good time to parse our arguments, if we got them as assignment - if (getNumPayloadBytes() > 0) { - parsePayload(); - } - - qInstallMessageHandler(Logging::verboseMessageHandler); - - const char* STATUS_PORT = "--statusPort"; - const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); - if (statusPort) { - int statusPortNumber = atoi(statusPort); - initMongoose(statusPortNumber); - } - - - const char* JURISDICTION_FILE = "--jurisdictionFile"; - const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); - if (jurisdictionFile) { - qDebug("jurisdictionFile=%s\n", jurisdictionFile); - - qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); - _jurisdiction = new JurisdictionMap(jurisdictionFile); - qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); - } else { - const char* JURISDICTION_ROOT = "--jurisdictionRoot"; - const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); - if (jurisdictionRoot) { - qDebug("jurisdictionRoot=%s\n", jurisdictionRoot); - } - - const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; - const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); - if (jurisdictionEndNodes) { - qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); - } - - if (jurisdictionRoot || jurisdictionEndNodes) { - _jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); - } - } - - // should we send environments? Default is yes, but this command line suppresses sending - const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove"; - _dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE); - qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove)); - +void VoxelServer::beforeRun() { // should we send environments? Default is yes, but this command line suppresses sending const char* SEND_ENVIRONMENTS = "--sendEnvironments"; bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS); if (dontSendEnvironments) { qDebug("Sending environments suppressed...\n"); _sendEnvironments = false; - } else { + } else { + _sendEnvironments = true; // should we send environments? Default is yes, but this command line suppresses sending const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment"; _sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT); qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment)); } qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments)); - - NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER); - - // we need to ask the DS about agents so we can ping/reply with them - const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - - setvbuf(stdout, NULL, _IOLBF, 0); - - // tell our NodeList about our desire to get notifications - nodeList->addHook(&_nodeWatcher); - nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; - - nodeList->startSilentNodeRemovalThread(); - srand((unsigned)time(0)); - - const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; - _displayVoxelStats = cmdOptionExists(_argc, _argv, DISPLAY_VOXEL_STATS); - qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats)); - - const char* VERBOSE_DEBUG = "--verboseDebug"; - _verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG); - qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug)); - - const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; - _debugVoxelSending = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_SENDING); - qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending)); - - const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; - _debugVoxelReceiving = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_RECEIVING); - qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving)); - - const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; - _shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG); - qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug)); - - // By default we will voxel persist, if you want to disable this, then pass in this parameter - const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; - if (cmdOptionExists(_argc, _argv, NO_VOXEL_PERSIST)) { - _wantVoxelPersist = false; - } - qDebug("wantVoxelPersist=%s\n", debug::valueOf(_wantVoxelPersist)); - - // if we want Voxel Persistence, set up the local file and persist thread - if (_wantVoxelPersist) { - - // Check to see if the user passed in a command line option for setting packet send rate - const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename"; - const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME); - if (voxelsPersistFilenameParameter) { - strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter); - } else { - //strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); - strcpy(_voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE); - } - - qDebug("voxelPersistFilename=%s\n", _voxelPersistFilename); - - // now set up VoxelPersistThread - _voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename); - if (_voxelPersistThread) { - _voxelPersistThread->initialize(true); - } - } - - // Check to see if the user passed in a command line option for loading an old style local - // Voxel File. If so, load it now. This is not the same as a voxel persist file - const char* INPUT_FILE = "-i"; - const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE); - if (voxelsFilename) { - _serverTree.readFromSVOFile(voxelsFilename); - } - - // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND = "--packetsPerSecond"; - const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); - if (packetsPerSecond) { - _packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND; - if (_packetsPerClientPerInterval < 1) { - _packetsPerClientPerInterval = 1; - } - qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval); - } - - HifiSockAddr senderSockAddr; - - // set up our jurisdiction broadcaster... - _jurisdictionSender = new JurisdictionSender(_jurisdiction); - if (_jurisdictionSender) { - _jurisdictionSender->initialize(true); - } - - // set up our VoxelServerPacketProcessor - _voxelServerPacketProcessor = new VoxelServerPacketProcessor(this); - if (_voxelServerPacketProcessor) { - _voxelServerPacketProcessor->initialize(true); - } - - // Convert now to tm struct for local timezone - tm* localtm = localtime(&_started); - const int MAX_TIME_LENGTH = 128; - char localBuffer[MAX_TIME_LENGTH] = { 0 }; - char utcBuffer[MAX_TIME_LENGTH] = { 0 }; - strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm); - // Convert now to tm struct for UTC - tm* gmtm = gmtime(&_started); - if (gmtm != NULL) { - strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm); - } - qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n"; - - QTimer* domainServerTimer = new QTimer(this); - connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); - domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); - - QTimer* silentNodeTimer = new QTimer(this); - connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); - silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - - QTimer* pingNodesTimer = new QTimer(this); - connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); - pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - // loop to send to nodes requesting data - while (!_isFinished) { - QCoreApplication::processEvents(); - } - - // call NodeList::clear() so that all of our node specific objects, including our sending threads, are - // properly shutdown and cleaned up. - NodeList::getInstance()->clear(); - - if (_jurisdictionSender) { - _jurisdictionSender->terminate(); - delete _jurisdictionSender; - } - - if (_voxelServerPacketProcessor) { - _voxelServerPacketProcessor->terminate(); - delete _voxelServerPacketProcessor; - } - - if (_voxelPersistThread) { - _voxelPersistThread->terminate(); - delete _voxelPersistThread; - } - - // tell our NodeList we're done with notifications - nodeList->removeHook(&_nodeWatcher); - - delete _jurisdiction; - _jurisdiction = NULL; - - qDebug() << "VoxelServer::run()... DONE\n"; } - -**/ - diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index 3c20cf6d9a..aaaa68c01e 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -40,11 +40,17 @@ public: virtual unsigned char getMyNodeType(); virtual const char* getMyLoggingServerTargetName(); virtual const char* getMyDefaultPersistFilename(); + virtual bool hasSpecialPacketToSend(); + virtual int sendSpecialPacket(Node* node); + + // subclass may implement these method + virtual void beforeRun(); private: bool _sendEnvironments; bool _sendMinimalEnvironment; EnvironmentData _environmentData[3]; + unsigned char _tempOutputBuffer[MAX_PACKET_SIZE]; }; #endif // __voxel_server__VoxelServer__ From d9e60f502cdeac16951828ffab3cc354cc2bcb61 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 19:46:03 -0800 Subject: [PATCH 06/17] clean up server name --- .../voxel-server-library/src/VoxelServer.cpp | 17 +++-------------- .../voxel-server-library/src/VoxelServer.h | 9 ++++++--- .../src/VoxelServerConsts.h | 4 ++++ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 6408d356a5..4f0140d892 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -12,6 +12,9 @@ #include "VoxelServerConsts.h" #include "VoxelNodeData.h" +const char* VOXEL_SERVER_NAME = "Voxel"; +const char* VOXEL_SERVER_LOGGING_TARGET_NAME = "voxel-server"; +const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) { // nothing special to do here... @@ -29,20 +32,6 @@ Octree* VoxelServer::createTree() { return new VoxelTree(true); } -unsigned char VoxelServer::getMyNodeType() { - return NODE_TYPE_VOXEL_SERVER; -} - -const char* VOXEL_SERVER_LOGGING_TARGET_NAME = "voxel-server"; -const char* VoxelServer::getMyLoggingServerTargetName() { - return VOXEL_SERVER_LOGGING_TARGET_NAME; -} - -const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo"; -const char* VoxelServer::getMyDefaultPersistFilename() { - return LOCAL_VOXELS_PERSIST_FILE; -} - bool VoxelServer::hasSpecialPacketToSend() { bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS); return shouldSendEnvironments; diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index aaaa68c01e..e374ca01ad 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -37,9 +37,12 @@ public: // Subclasses must implement these methods virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode); virtual Octree* createTree(); - virtual unsigned char getMyNodeType(); - virtual const char* getMyLoggingServerTargetName(); - virtual const char* getMyDefaultPersistFilename(); + virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; } + virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_VOXEL_QUERY; } + virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; } + virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; } + virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; } + virtual bool hasSpecialPacketToSend(); virtual int sendSpecialPacket(Node* node); diff --git a/libraries/voxel-server-library/src/VoxelServerConsts.h b/libraries/voxel-server-library/src/VoxelServerConsts.h index e6973812f9..ce6fd86a32 100644 --- a/libraries/voxel-server-library/src/VoxelServerConsts.h +++ b/libraries/voxel-server-library/src/VoxelServerConsts.h @@ -9,6 +9,10 @@ #ifndef __voxel_server__VoxelServerConsts__ #define __voxel_server__VoxelServerConsts__ +extern const char* VOXEL_SERVER_NAME; +extern const char* VOXEL_SERVER_LOGGING_TARGET_NAME; +extern const char* LOCAL_VOXELS_PERSIST_FILE; + const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000; #endif // __voxel_server__VoxelServerConsts__ From 1b1f9cbf3bdef4be808035fade95fe03073f001d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 19:46:42 -0800 Subject: [PATCH 07/17] actually added the octree-server base class library --- libraries/octree-server/CMakeLists.txt | 40 + .../src/OctreeInboundPacketProcessor.cpp | 168 ++++ .../src/OctreeInboundPacketProcessor.h | 83 ++ .../octree-server/src/OctreePersistThread.cpp | 85 ++ .../octree-server/src/OctreePersistThread.h | 43 + .../octree-server/src/OctreeQueryNode.cpp | 275 +++++++ libraries/octree-server/src/OctreeQueryNode.h | 124 +++ .../octree-server/src/OctreeSendThread.cpp | 574 +++++++++++++ .../octree-server/src/OctreeSendThread.h | 45 ++ libraries/octree-server/src/OctreeServer.cpp | 752 ++++++++++++++++++ libraries/octree-server/src/OctreeServer.h | 100 +++ .../octree-server/src/OctreeServerConsts.h | 21 + 12 files changed, 2310 insertions(+) create mode 100644 libraries/octree-server/CMakeLists.txt create mode 100644 libraries/octree-server/src/OctreeInboundPacketProcessor.cpp create mode 100644 libraries/octree-server/src/OctreeInboundPacketProcessor.h create mode 100644 libraries/octree-server/src/OctreePersistThread.cpp create mode 100644 libraries/octree-server/src/OctreePersistThread.h create mode 100644 libraries/octree-server/src/OctreeQueryNode.cpp create mode 100644 libraries/octree-server/src/OctreeQueryNode.h create mode 100644 libraries/octree-server/src/OctreeSendThread.cpp create mode 100644 libraries/octree-server/src/OctreeSendThread.h create mode 100644 libraries/octree-server/src/OctreeServer.cpp create mode 100644 libraries/octree-server/src/OctreeServer.h create mode 100644 libraries/octree-server/src/OctreeServerConsts.h diff --git a/libraries/octree-server/CMakeLists.txt b/libraries/octree-server/CMakeLists.txt new file mode 100644 index 0000000000..97faf45243 --- /dev/null +++ b/libraries/octree-server/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 2.8) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME octree-server) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) + +# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS +FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*) + +setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS}) + +include_directories(${ROOT_DIR}/externals/civetweb/include) + +qt5_use_modules(${TARGET_NAME} Widgets) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + +# link ZLIB +find_package(ZLIB) +include_directories(${ZLIB_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) + +# link in the shared library +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# link in the hifi octree library +link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) + +# link in the hifi avatars library +#link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp new file mode 100644 index 0000000000..61d2e6f6d8 --- /dev/null +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp @@ -0,0 +1,168 @@ +// +// OctreeInboundPacketProcessor.cpp +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded network packet processor for the voxel-server +// + +#include +#include + +#include "OctreeServer.h" +#include "OctreeServerConsts.h" +#include "OctreeInboundPacketProcessor.h" + +static QUuid DEFAULT_NODE_ID_REF; + +OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) : + _myServer(myServer), + _receivedPacketCount(0), + _totalTransitTime(0), + _totalProcessTime(0), + _totalLockWaitTime(0), + _totalElementsInPacket(0), + _totalPackets(0) +{ +} + +void OctreeInboundPacketProcessor::resetStats() { + _totalTransitTime = 0; + _totalProcessTime = 0; + _totalLockWaitTime = 0; + _totalElementsInPacket = 0; + _totalPackets = 0; + + _singleSenderStats.clear(); +} + + +void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, + unsigned char* packetData, ssize_t packetLength) { + + bool debugProcessPacket = _myServer->wantsVerboseDebug(); + + if (debugProcessPacket) { + printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength); + } + + int numBytesPacketHeader = numBytesForPacketHeader(packetData); + + + // Ask our tree subclass if it can handle the incoming packet... + PACKET_TYPE packetType = packetData[0]; + if (_myServer->getOctree()->handlesEditPacketType(packetType)) { + PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket); + _receivedPacketCount++; + + unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); + uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence)))); + uint64_t arrivedAt = usecTimestampNow(); + uint64_t transitTime = arrivedAt - sentAt; + int editsInPacket = 0; + uint64_t processTime = 0; + uint64_t lockWaitTime = 0; + + if (_myServer->wantsDebugReceiving()) { + printf("PROCESSING THREAD: got %c - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n", + packetType, _receivedPacketCount, packetLength, sequence, transitTime); + } + int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt); + unsigned char* editData = (unsigned char*)&packetData[atByte]; + while (atByte < packetLength) { + int maxSize = packetLength - atByte; + + if (debugProcessPacket) { + printf("OctreeInboundPacketProcessor::processPacket() %c " + "packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n", + packetType, packetData, packetLength, editData, atByte, maxSize); + } + + uint64_t startLock = usecTimestampNow(); + _myServer->getOctree()->lockForWrite(); + uint64_t startProcess = usecTimestampNow(); + int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType, + packetData, packetLength, editData, maxSize); + _myServer->getOctree()->unlock(); + uint64_t endProcess = usecTimestampNow(); + + editsInPacket++; + uint64_t thisProcessTime = endProcess - startProcess; + uint64_t thisLockWaitTime = startProcess - startLock; + processTime += thisProcessTime; + lockWaitTime += thisLockWaitTime; + + // skip to next voxel edit record in the packet + editData += editDataBytesRead; + atByte += editDataBytesRead; + } + + if (debugProcessPacket) { + printf("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c " + "packetData=%p packetLength=%ld voxelData=%p atByte=%d\n", + packetType, packetData, packetLength, editData, atByte); + } + + // Make sure our Node and NodeList knows we've heard from this node. + Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); + QUuid& nodeUUID = DEFAULT_NODE_ID_REF; + if (senderNode) { + senderNode->setLastHeardMicrostamp(usecTimestampNow()); + nodeUUID = senderNode->getUUID(); + if (debugProcessPacket) { + qDebug() << "sender has uuid=" << nodeUUID << "\n"; + } + } else { + if (debugProcessPacket) { + qDebug() << "sender has no known nodeUUID.\n"; + } + } + trackInboundPackets(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime); + } else { + printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]); + } +} + +void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, + int editsInPacket, uint64_t processTime, uint64_t lockWaitTime) { + + _totalTransitTime += transitTime; + _totalProcessTime += processTime; + _totalLockWaitTime += lockWaitTime; + _totalElementsInPacket += editsInPacket; + _totalPackets++; + + // find the individual senders stats and track them there too... + // see if this is the first we've heard of this node... + if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) { + SingleSenderStats stats; + + stats._totalTransitTime += transitTime; + stats._totalProcessTime += processTime; + stats._totalLockWaitTime += lockWaitTime; + stats._totalElementsInPacket += editsInPacket; + stats._totalPackets++; + + _singleSenderStats[nodeUUID] = stats; + } else { + SingleSenderStats& stats = _singleSenderStats[nodeUUID]; + stats._totalTransitTime += transitTime; + stats._totalProcessTime += processTime; + stats._totalLockWaitTime += lockWaitTime; + stats._totalElementsInPacket += editsInPacket; + stats._totalPackets++; + } +} + + +SingleSenderStats::SingleSenderStats() { + _totalTransitTime = 0; + _totalProcessTime = 0; + _totalLockWaitTime = 0; + _totalElementsInPacket = 0; + _totalPackets = 0; +} + + diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.h b/libraries/octree-server/src/OctreeInboundPacketProcessor.h new file mode 100644 index 0000000000..51214b2617 --- /dev/null +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.h @@ -0,0 +1,83 @@ +// +// OctreeInboundPacketProcessor.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded network packet processor for the voxel-server +// + +#ifndef __octree_server__OctreeInboundPacketProcessor__ +#define __octree_server__OctreeInboundPacketProcessor__ + +#include + +#include +class OctreeServer; + +class SingleSenderStats { +public: + SingleSenderStats(); + + uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } + uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } + uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } + uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; } + uint64_t getTotalPacketsProcessed() const { return _totalPackets; } + uint64_t getAverageProcessTimePerElement() const + { return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; } + uint64_t getAverageLockWaitTimePerElement() const + { return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; } + + uint64_t _totalTransitTime; + uint64_t _totalProcessTime; + uint64_t _totalLockWaitTime; + uint64_t _totalElementsInPacket; + uint64_t _totalPackets; +}; + +typedef std::map NodeToSenderStatsMap; +typedef std::map::iterator NodeToSenderStatsMapIterator; + + +/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes +/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() +class OctreeInboundPacketProcessor : public ReceivedPacketProcessor { + +public: + OctreeInboundPacketProcessor(OctreeServer* myServer); + + uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; } + uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; } + uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; } + uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; } + uint64_t getTotalPacketsProcessed() const { return _totalPackets; } + uint64_t getAverageProcessTimePerElement() const + { return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; } + uint64_t getAverageLockWaitTimePerElement() const + { return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; } + + void resetStats(); + + NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } + +protected: + virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength); + +private: + void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime, + int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime); + + OctreeServer* _myServer; + int _receivedPacketCount; + + uint64_t _totalTransitTime; + uint64_t _totalProcessTime; + uint64_t _totalLockWaitTime; + uint64_t _totalElementsInPacket; + uint64_t _totalPackets; + + NodeToSenderStatsMap _singleSenderStats; +}; +#endif // __octree_server__OctreeInboundPacketProcessor__ diff --git a/libraries/octree-server/src/OctreePersistThread.cpp b/libraries/octree-server/src/OctreePersistThread.cpp new file mode 100644 index 0000000000..a3a2663342 --- /dev/null +++ b/libraries/octree-server/src/OctreePersistThread.cpp @@ -0,0 +1,85 @@ +// +// OctreePersistThread.cpp +// Octree-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded Octree persistence +// + +#include +#include +#include + +#include "OctreePersistThread.h" +#include "OctreeServer.h" + +OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) : + _tree(tree), + _filename(filename), + _persistInterval(persistInterval), + _initialLoadComplete(false), + _loadTimeUSecs(0) { +} + +bool OctreePersistThread::process() { + + if (!_initialLoadComplete) { + uint64_t loadStarted = usecTimestampNow(); + qDebug("loading Octrees from file: %s...\n", _filename); + + bool persistantFileRead; + + _tree->lockForWrite(); + { + PerformanceWarning warn(true, "Loading Octree File", true); + persistantFileRead = _tree->readFromSVOFile(_filename); + } + _tree->unlock(); + + _loadCompleted = time(0); + uint64_t loadDone = usecTimestampNow(); + _loadTimeUSecs = loadDone - loadStarted; + + _tree->clearDirtyBit(); // the tree is clean since we just loaded it + qDebug("DONE loading Octrees from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); + + unsigned long nodeCount = OctreeElement::getNodeCount(); + unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); + unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); + qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); + + double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls(); + qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n", + OctreeElement::getGetChildAtIndexTime(), OctreeElement::getGetChildAtIndexCalls(), usecPerGet); + + double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls(); + qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", + OctreeElement::getSetChildAtIndexTime(), OctreeElement::getSetChildAtIndexCalls(), usecPerSet); + + _initialLoadComplete = true; + _lastCheck = usecTimestampNow(); // we just loaded, no need to save again + } + + if (isStillRunning()) { + uint64_t MSECS_TO_USECS = 1000; + uint64_t USECS_TO_SLEEP = 100 * MSECS_TO_USECS; // every 100ms + usleep(USECS_TO_SLEEP); + uint64_t now = usecTimestampNow(); + uint64_t sinceLastSave = now - _lastCheck; + uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS; + + if (sinceLastSave > intervalToCheck) { + // check the dirty bit and persist here... + _lastCheck = usecTimestampNow(); + if (_tree->isDirty()) { + qDebug("saving Octrees to file %s...\n",_filename); + _tree->writeToSVOFile(_filename); + _tree->clearDirtyBit(); // tree is clean after saving + qDebug("DONE saving Octrees to file...\n"); + } + } + } + return isStillRunning(); // keep running till they terminate us +} diff --git a/libraries/octree-server/src/OctreePersistThread.h b/libraries/octree-server/src/OctreePersistThread.h new file mode 100644 index 0000000000..e5f261960b --- /dev/null +++ b/libraries/octree-server/src/OctreePersistThread.h @@ -0,0 +1,43 @@ +// +// OctreePersistThread.h +// Octree-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded Octree persistence +// + +#ifndef __Octree_server__OctreePersistThread__ +#define __Octree_server__OctreePersistThread__ + +#include +#include + +/// Generalized threaded processor for handling received inbound packets. +class OctreePersistThread : public virtual GenericThread { +public: + static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds + + OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); + + bool isInitialLoadComplete() const { return _initialLoadComplete; } + + time_t* getLoadCompleted() { return &_loadCompleted; } + uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; } + +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); +private: + Octree* _tree; + const char* _filename; + int _persistInterval; + bool _initialLoadComplete; + + time_t _loadCompleted; + uint64_t _loadTimeUSecs; + uint64_t _lastCheck; +}; + +#endif // __Octree_server__OctreePersistThread__ diff --git a/libraries/octree-server/src/OctreeQueryNode.cpp b/libraries/octree-server/src/OctreeQueryNode.cpp new file mode 100644 index 0000000000..0e84fe2dba --- /dev/null +++ b/libraries/octree-server/src/OctreeQueryNode.cpp @@ -0,0 +1,275 @@ +// +// OctreeQueryNode.cpp +// hifi +// +// Created by Stephen Birarda on 3/21/13. +// +// + +#include "PacketHeaders.h" +#include "SharedUtil.h" +#include "OctreeQueryNode.h" +#include +#include +#include "OctreeSendThread.h" + +OctreeQueryNode::OctreeQueryNode(Node* owningNode) : + OctreeQuery(owningNode), + _viewSent(false), + _octreePacketAvailableBytes(MAX_PACKET_SIZE), + _maxSearchLevel(1), + _maxLevelReachedInLastSearch(1), + _lastTimeBagEmpty(0), + _viewFrustumChanging(false), + _viewFrustumJustStoppedChanging(true), + _currentPacketIsColor(true), + _currentPacketIsCompressed(false), + _octreeSendThread(NULL), + _lastClientBoundaryLevelAdjust(0), + _lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE), + _lodChanged(false), + _lodInitialized(false) +{ + _octreePacket = new unsigned char[MAX_PACKET_SIZE]; + _octreePacketAt = _octreePacket; + _lastOctreePacket = new unsigned char[MAX_PACKET_SIZE]; + _lastOctreePacketLength = 0; + _duplicatePacketCount = 0; + _sequenceNumber = 0; +} + +void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer) { + // Create octree sending thread... + QUuid nodeUUID = getOwningNode()->getUUID(); + _octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer); + _octreeSendThread->initialize(true); +} + +bool OctreeQueryNode::packetIsDuplicate() const { + // since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp + // of the entire packet, we need to compare only the packet content... + if (_lastOctreePacketLength == getPacketLength()) { + return memcmp(_lastOctreePacket + OCTREE_PACKET_HEADER_SIZE, + _octreePacket+OCTREE_PACKET_HEADER_SIZE , getPacketLength() - OCTREE_PACKET_HEADER_SIZE == 0); + } + return false; +} + +bool OctreeQueryNode::shouldSuppressDuplicatePacket() { + bool shouldSuppress = false; // assume we won't suppress + + // only consider duplicate packets + if (packetIsDuplicate()) { + _duplicatePacketCount++; + + // If this is the first suppressed packet, remember our time... + if (_duplicatePacketCount == 1) { + _firstSuppressedPacket = usecTimestampNow(); + } + + // How long has it been since we've sent one, if we're still under our max time, then keep considering + // this packet for suppression + uint64_t now = usecTimestampNow(); + long sinceFirstSuppressedPacket = now - _firstSuppressedPacket; + const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second. + + if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) { + // Finally, if we know we've sent at least one duplicate out, then suppress the rest... + if (_duplicatePacketCount >= 1) { + shouldSuppress = true; + } + } else { + // Reset our count, we've reached our maximum time. + _duplicatePacketCount = 0; + } + } else { + // Reset our count, it wasn't a duplicate + _duplicatePacketCount = 0; + } + return shouldSuppress; +} + +void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { + // Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has + // changed since we last reset it. Since we know that no two packets can ever be identical without being the same + // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing + // packet send rate. + _lastOctreePacketLength = getPacketLength(); + memcpy(_lastOctreePacket, _octreePacket, _lastOctreePacketLength); + + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. + _currentPacketIsColor = getWantColor(); + _currentPacketIsCompressed = getWantCompression(); + OCTREE_PACKET_FLAGS flags = 0; + if (_currentPacketIsColor) { + setAtBit(flags,PACKET_IS_COLOR_BIT); + } + if (_currentPacketIsCompressed) { + setAtBit(flags,PACKET_IS_COMPRESSED_BIT); + } + + _octreePacketAvailableBytes = MAX_PACKET_SIZE; + int numBytesPacketHeader = populateTypeAndVersion(_octreePacket, getMyPacketType()); + _octreePacketAt = _octreePacket + numBytesPacketHeader; + _octreePacketAvailableBytes -= numBytesPacketHeader; + + // pack in flags + OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)_octreePacketAt; + *flagsAt = flags; + _octreePacketAt += sizeof(OCTREE_PACKET_FLAGS); + _octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS); + + // pack in sequence number + OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt; + *sequenceAt = _sequenceNumber; + _octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE); + _octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE); + if (!(lastWasSurpressed || _lastOctreePacketLength == OCTREE_PACKET_HEADER_SIZE)) { + _sequenceNumber++; + } + + // pack in timestamp + OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); + OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)_octreePacketAt; + *timeAt = now; + _octreePacketAt += sizeof(OCTREE_PACKET_SENT_TIME); + _octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SENT_TIME); + + _octreePacketWaiting = false; +} + +void OctreeQueryNode::writeToPacket(const unsigned char* buffer, int bytes) { + // compressed packets include lead bytes which contain compressed size, this allows packing of + // multiple compressed portions together + if (_currentPacketIsCompressed) { + *(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)_octreePacketAt = bytes; + _octreePacketAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + _octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + } + if (bytes <= _octreePacketAvailableBytes) { + memcpy(_octreePacketAt, buffer, bytes); + _octreePacketAvailableBytes -= bytes; + _octreePacketAt += bytes; + _octreePacketWaiting = true; + } +} + +OctreeQueryNode::~OctreeQueryNode() { + delete[] _octreePacket; + delete[] _lastOctreePacket; + + if (_octreeSendThread) { + _octreeSendThread->terminate(); + delete _octreeSendThread; + } +} + +bool OctreeQueryNode::updateCurrentViewFrustum() { + bool currentViewFrustumChanged = false; + ViewFrustum newestViewFrustum; + // get position and orientation details from the camera + newestViewFrustum.setPosition(getCameraPosition()); + newestViewFrustum.setOrientation(getCameraOrientation()); + + // Also make sure it's got the correct lens details from the camera + float originalFOV = getCameraFov(); + float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; + + newestViewFrustum.setFieldOfView(wideFOV); // hack + newestViewFrustum.setAspectRatio(getCameraAspectRatio()); + newestViewFrustum.setNearClip(getCameraNearClip()); + newestViewFrustum.setFarClip(getCameraFarClip()); + newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition()); + + // if there has been a change, then recalculate + if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { + _currentViewFrustum = newestViewFrustum; + _currentViewFrustum.calculate(); + currentViewFrustumChanged = true; + } + + // Also check for LOD changes from the client + if (_lodInitialized) { + if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) { + _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); + _lodChanged = true; + } + if (_lastClientOctreeSizeScale != getOctreeSizeScale()) { + _lastClientOctreeSizeScale = getOctreeSizeScale(); + _lodChanged = true; + } + } else { + _lodInitialized = true; + _lastClientOctreeSizeScale = getOctreeSizeScale(); + _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); + _lodChanged = false; + } + + // When we first detect that the view stopped changing, we record this. + // but we don't change it back to false until we've completely sent this + // scene. + if (_viewFrustumChanging && !currentViewFrustumChanged) { + _viewFrustumJustStoppedChanging = true; + } + _viewFrustumChanging = currentViewFrustumChanged; + return currentViewFrustumChanged; +} + +void OctreeQueryNode::setViewSent(bool viewSent) { + _viewSent = viewSent; + if (viewSent) { + _viewFrustumJustStoppedChanging = false; + _lodChanged = false; + } +} + +void OctreeQueryNode::updateLastKnownViewFrustum() { + bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); + + if (frustumChanges) { + // save our currentViewFrustum into our lastKnownViewFrustum + _lastKnownViewFrustum = _currentViewFrustum; + } + + // save that we know the view has been sent. + uint64_t now = usecTimestampNow(); + setLastTimeBagEmpty(now); // is this what we want? poor names +} + + +bool OctreeQueryNode::moveShouldDump() const { + glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition(); + glm::vec3 newPosition = _currentViewFrustum.getPosition(); + + // theoretically we could make this slightly larger but relative to avatar scale. + const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f; + if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) { + return true; + } + return false; +} + +void OctreeQueryNode::dumpOutOfView() { + int stillInView = 0; + int outOfView = 0; + OctreeElementBag tempBag; + while (!nodeBag.isEmpty()) { + OctreeElement* node = nodeBag.extract(); + if (node->isInView(_currentViewFrustum)) { + tempBag.insert(node); + stillInView++; + } else { + outOfView++; + } + } + if (stillInView > 0) { + while (!tempBag.isEmpty()) { + OctreeElement* node = tempBag.extract(); + if (node->isInView(_currentViewFrustum)) { + nodeBag.insert(node); + } + } + } +} + diff --git a/libraries/octree-server/src/OctreeQueryNode.h b/libraries/octree-server/src/OctreeQueryNode.h new file mode 100644 index 0000000000..865f1607ab --- /dev/null +++ b/libraries/octree-server/src/OctreeQueryNode.h @@ -0,0 +1,124 @@ +// +// OctreeQueryNode.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// +// + +#ifndef __hifi__OctreeQueryNode__ +#define __hifi__OctreeQueryNode__ + +#include +#include +#include +#include + +#include +#include +#include +#include + +class OctreeSendThread; +class OctreeServer; + +class OctreeQueryNode : public OctreeQuery { +public: + OctreeQueryNode(Node* owningNode); + virtual ~OctreeQueryNode(); + + virtual PACKET_TYPE getMyPacketType() const = 0; + + void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header + + void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet + + const unsigned char* getPacket() const { return _octreePacket; } + int getPacketLength() const { return (MAX_PACKET_SIZE - _octreePacketAvailableBytes); } + bool isPacketWaiting() const { return _octreePacketWaiting; } + + bool packetIsDuplicate() const; + bool shouldSuppressDuplicatePacket(); + + int getAvailable() const { return _octreePacketAvailableBytes; } + int getMaxSearchLevel() const { return _maxSearchLevel; } + void resetMaxSearchLevel() { _maxSearchLevel = 1; } + void incrementMaxSearchLevel() { _maxSearchLevel++; } + + int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; } + void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } + + OctreeElementBag nodeBag; + CoverageMap map; + + ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; } + ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; } + + // These are not classic setters because they are calculating and maintaining state + // which is set asynchronously through the network receive + bool updateCurrentViewFrustum(); + void updateLastKnownViewFrustum(); + + bool getViewSent() const { return _viewSent; } + void setViewSent(bool viewSent); + + bool getViewFrustumChanging() const { return _viewFrustumChanging; } + bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; } + + bool moveShouldDump() const; + + uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; } + void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; } + + bool getCurrentPacketIsColor() const { return _currentPacketIsColor; } + bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; } + bool getCurrentPacketFormatMatches() { + return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression()); + } + + bool hasLodChanged() const { return _lodChanged; }; + + OctreeSceneStats stats; + + void initializeOctreeSendThread(OctreeServer* octreeServer); + bool isOctreeSendThreadInitalized() { return _octreeSendThread; } + + void dumpOutOfView(); + +private: + OctreeQueryNode(const OctreeQueryNode &); + OctreeQueryNode& operator= (const OctreeQueryNode&); + + bool _viewSent; + unsigned char* _octreePacket; + unsigned char* _octreePacketAt; + int _octreePacketAvailableBytes; + bool _octreePacketWaiting; + + unsigned char* _lastOctreePacket; + int _lastOctreePacketLength; + int _duplicatePacketCount; + uint64_t _firstSuppressedPacket; + + int _maxSearchLevel; + int _maxLevelReachedInLastSearch; + ViewFrustum _currentViewFrustum; + ViewFrustum _lastKnownViewFrustum; + uint64_t _lastTimeBagEmpty; + bool _viewFrustumChanging; + bool _viewFrustumJustStoppedChanging; + bool _currentPacketIsColor; + bool _currentPacketIsCompressed; + + OctreeSendThread* _octreeSendThread; + + // watch for LOD changes + int _lastClientBoundaryLevelAdjust; + float _lastClientOctreeSizeScale; + bool _lodChanged; + bool _lodInitialized; + + OCTREE_PACKET_SEQUENCE _sequenceNumber; +}; + +#endif /* defined(__hifi__OctreeQueryNode__) */ diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp new file mode 100644 index 0000000000..eb53f5a3ad --- /dev/null +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -0,0 +1,574 @@ +// +// OctreeSendThread.cpp +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include + +#include "OctreeSendThread.h" +#include "OctreeServer.h" +#include "OctreeServerConsts.h" + +uint64_t startSceneSleepTime = 0; +uint64_t endSceneSleepTime = 0; + +OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) : + _nodeUUID(nodeUUID), + _myServer(myServer), + _packetData() +{ +} + +bool OctreeSendThread::process() { + uint64_t start = usecTimestampNow(); + bool gotLock = false; + + // don't do any send processing until the initial load of the octree is complete... + if (_myServer->isInitialLoadComplete()) { + Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); + + if (node) { + // make sure the node list doesn't kill our node while we're using it + if (node->trylock()) { + gotLock = true; + OctreeQueryNode* nodeData = NULL; + + nodeData = (OctreeQueryNode*) node->getLinkedData(); + + int packetsSent = 0; + + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + packetsSent = packetDistributor(node, nodeData, viewFrustumChanged); + } + + node->unlock(); // we're done with this node for now. + } + } + } else { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + qDebug("OctreeSendThread::process() waiting for isInitialLoadComplete()\n"); + } + } + + // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap + if (isStillRunning() && gotLock) { + // dynamically sleep until we need to fire off the next set of octree elements + int elapsed = (usecTimestampNow() - start); + int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed; + + if (usecToSleep > 0) { + PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls); + usleep(usecToSleep); + } else { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + } + + return isStillRunning(); // keep running till they terminate us +} + +uint64_t OctreeSendThread::_usleepTime = 0; +uint64_t OctreeSendThread::_usleepCalls = 0; + +uint64_t OctreeSendThread::_totalBytes = 0; +uint64_t OctreeSendThread::_totalWastedBytes = 0; +uint64_t OctreeSendThread::_totalPackets = 0; + +int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { + bool debug = _myServer->wantsDebugSending(); + uint64_t now = usecTimestampNow(); + + bool packetSent = false; // did we send a packet? + int packetsSent = 0; + // Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently + // obscure the packet and not send it. This allows the callers and upper level logic to not need to know about + // this rate control savings. + if (nodeData->shouldSuppressDuplicatePacket()) { + nodeData->resetOctreePacket(true); // we still need to reset it though! + return packetsSent; // without sending... + } + + const unsigned char* messageData = nodeData->getPacket(); + int numBytesPacketHeader = numBytesForPacketHeader(messageData); + const unsigned char* dataAt = messageData + numBytesPacketHeader; + dataAt += sizeof(OCTREE_PACKET_FLAGS); + OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); + dataAt += sizeof(OCTREE_PACKET_SEQUENCE); + + + // If we've got a stats message ready to send, then see if we can piggyback them together + if (nodeData->stats.isReadyToSend()) { + // Send the stats message to the client + unsigned char* statsMessage = nodeData->stats.getStatsMessage(); + int statsMessageLength = nodeData->stats.getStatsMessageLength(); + + // If the size of the stats message and the voxel message will fit in a packet, then piggyback them + if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { + + // copy voxel message to back of stats message + memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); + statsMessageLength += nodeData->getPacketLength(); + + // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since + // there was nothing else to send. + int thisWastedBytes = 0; + _totalWastedBytes += thisWastedBytes; + _totalBytes += nodeData->getPacketLength(); + _totalPackets++; + if (debug) { + qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", + now, + _totalPackets, + sequence, nodeData->getPacketLength(), _totalBytes, + thisWastedBytes, _totalWastedBytes); + } + + // actually send it + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + packetSent = true; + } else { + // not enough room in the packet, send two packets + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + + // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since + // there was nothing else to send. + int thisWastedBytes = 0; + _totalWastedBytes += thisWastedBytes; + _totalBytes += statsMessageLength; + _totalPackets++; + if (debug) { + qDebug("Sending separate stats packet at %llu [%llu]: size:%d [%llu] wasted bytes:%d [%llu]\n", + now, + _totalPackets, + statsMessageLength, _totalBytes, + thisWastedBytes, _totalWastedBytes); + } + + trueBytesSent += statsMessageLength; + truePacketsSent++; + packetsSent++; + + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + + packetSent = true; + + thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); + _totalWastedBytes += thisWastedBytes; + _totalBytes += nodeData->getPacketLength(); + _totalPackets++; + if (debug) { + qDebug("Sending packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n", + now, + _totalPackets, + sequence, nodeData->getPacketLength(), _totalBytes, + thisWastedBytes, _totalWastedBytes); + } + } + nodeData->stats.markAsSent(); + } else { + // If there's actually a packet waiting, then send it. + if (nodeData->isPacketWaiting()) { + // just send the voxel packet + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + packetSent = true; + + int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength(); + _totalWastedBytes += thisWastedBytes; + _totalBytes += nodeData->getPacketLength(); + _totalPackets++; + if (debug) { + qDebug("Sending packet at %llu [%llu]: sequence:%d size:%d [%llu] wasted bytes:%d [%llu]\n", + now, + _totalPackets, + sequence, nodeData->getPacketLength(), _totalBytes, + thisWastedBytes, _totalWastedBytes); + } + } + } + // remember to track our stats + if (packetSent) { + nodeData->stats.packetSent(nodeData->getPacketLength()); + trueBytesSent += nodeData->getPacketLength(); + truePacketsSent++; + packetsSent++; + nodeData->resetOctreePacket(); + } + + return packetsSent; +} + +/// Version of voxel distributor that sends the deepest LOD level at once +int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + + int truePacketsSent = 0; + int trueBytesSent = 0; + int packetsSentThisInterval = 0; + bool somethingToSend = true; // assume we have something + + // FOR NOW... node tells us if it wants to receive only view frustum deltas + bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); + + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. + bool wantColor = nodeData->getWantColor(); + bool wantCompression = nodeData->getWantCompression(); + + // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color + // then let's just send that waiting packet. + if (!nodeData->getCurrentPacketFormatMatches()) { + if (nodeData->isPacketWaiting()) { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + debug::valueOf(wantColor), debug::valueOf(wantCompression), + debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); + } + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + } else { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + debug::valueOf(wantColor), debug::valueOf(wantCompression), + debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); + } + nodeData->resetOctreePacket(); + } + int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; + if (wantCompression) { + targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + } + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + debug::valueOf(wantCompression), targetSize); + } + + _packetData.changeSettings(wantCompression, targetSize); + } + + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); + } + + const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; + + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + debug::valueOf(nodeData->getViewSent()) + ); + } + + // If the current view frustum has changed OR we have nothing to send, then search against + // the current view frustum for things to send. + if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { + uint64_t now = usecTimestampNow(); + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); + if (nodeData->getLastTimeBagEmpty() > 0) { + float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; + if (viewFrustumChanged) { + printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + } else { + printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + } + printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + debug::valueOf(wantColor)); + } + } + + // if our view has changed, we need to reset these things... + if (viewFrustumChanged) { + if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { + nodeData->dumpOutOfView(); + } + nodeData->map.erase(); + } + + if (!viewFrustumChanged && !nodeData->getWantDelta()) { + // only set our last sent time if we weren't resetting due to frustum change + uint64_t now = usecTimestampNow(); + nodeData->setLastTimeBagEmpty(now); + } + + // track completed scenes and send out the stats packet accordingly + nodeData->stats.sceneCompleted(); + ::endSceneSleepTime = _usleepTime; + unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; + + unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); + unsigned long elapsedTime = nodeData->stats.getElapsedTime(); + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + + if (_myServer->wantsDebugSending()) { + qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n", + usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes); + } + + // start tracking our stats + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) + && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); + + // If we're starting a full scene, then definitely we want to empty the nodeBag + if (isFullScene) { + nodeData->nodeBag.deleteAll(); + } + + if (_myServer->wantsDebugSending()) { + qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n", + usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes); + } + + ::startSceneSleepTime = _usleepTime; + nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _myServer->getJurisdiction()); + + // This is the start of "resending" the scene. + bool dontRestartSceneOnMove = false; // this is experimental + if (dontRestartSceneOnMove) { + if (nodeData->nodeBag.isEmpty()) { + nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // only in case of empty + } + } else { + nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // original behavior, reset on move or empty + } + } + + // If we have something in our nodeBag, then turn them into packets and send them out... + if (!nodeData->nodeBag.isEmpty()) { + int bytesWritten = 0; + uint64_t start = usecTimestampNow(); + uint64_t startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + uint64_t startCompressCalls = OctreePacketData::getCompressContentCalls(); + + int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); + int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); + + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); + } + + int extraPackingAttempts = 0; + while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); + } + + bool lastNodeDidntFit = false; // assume each node fits + if (!nodeData->nodeBag.isEmpty()) { + OctreeElement* subTree = nodeData->nodeBag.extract(); + bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); + CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; + + float voxelSizeScale = nodeData->getOctreeSizeScale(); + int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); + + int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); + + + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, + wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, + nodeData->getLastTimeBagEmpty(), + isFullScene, &nodeData->stats, _myServer->getJurisdiction()); + + + _myServer->getOctree()->lockForRead(); + nodeData->stats.encodeStarted(); + bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); + + // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. + if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) { + if (_packetData.hasContent() && bytesWritten == 0 && + params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; + } + } else { + // in compressed mode and we are trying to pack more... and we don't care if the _packetData has + // content or not... because in this case even if we were unable to pack any data, we want to drop + // below to our sendNow logic, but we do want to track that we attempted to pack extra + extraPackingAttempts++; + if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; + } + } + + nodeData->stats.encodeStopped(); + _myServer->getOctree()->unlock(); + } else { + // If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0 + bytesWritten = 0; + somethingToSend = false; // this will cause us to drop out of the loop... + } + + // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a + // little bit more in this packet. To do this we + + // We only consider sending anything if there is something in the _packetData to send... But + // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases + // mean we should send the previous packet contents and reset it. + if (lastNodeDidntFit) { + if (_packetData.hasContent()) { + // if for some reason the finalized size is greater than our available size, then probably the "compressed" + // form actually inflated beyond our padding, and in this case we will send the current packet, then + // write to out new packet... + int writtenSize = _packetData.getFinalizedSize() + + (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0); + + + if (writtenSize > nodeData->getAvailable()) { + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", + writtenSize, nodeData->getAvailable()); + } + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + } + + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", + nodeData->getAvailable(), _packetData.getFinalizedSize(), + _packetData.getUncompressedSize(), _packetData.getTargetSize()); + } + nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); + extraPackingAttempts = 0; + } + + // If we're not running compressed, the we know we can just send now. Or if we're running compressed, but + // the packet doesn't have enough space to bother attempting to pack more... + bool sendNow = true; + + if (nodeData->getCurrentPacketIsCompressed() && + nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && + extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { + sendNow = false; // try to pack more + } + + int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; + if (sendNow) { + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + if (wantCompression) { + targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + } + } else { + // If we're in compressed mode, then we want to see if we have room for more in this wire packet. + // but we've finalized the _packetData, so we want to start a new section, we will do that by + // resetting the packet settings with the max uncompressed size of our current available space + // in the wire packet. We also include room for our section header, and a little bit of padding + // to account for the fact that whenc compressing small amounts of data, we sometimes end up with + // a larger compressed size then uncompressed size + targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; + } + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + debug::valueOf(nodeData->getWantCompression()), targetSize); + } + _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset + } + } + + + // Here's where we can/should allow the server to send other data... + // send the environment packet + if (_myServer->hasSpecialPacketToSend()) { + trueBytesSent += _myServer->sendSpecialPacket(node); + truePacketsSent++; + packetsSentThisInterval++; + + /** + int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount(); + + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); + } + + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, + node->getActiveSocket()->getAddress(), + node->getActiveSocket()->getPort()); + trueBytesSent += envPacketLength; + truePacketsSent++; + packetsSentThisInterval++; + **/ + } + + + uint64_t end = usecTimestampNow(); + int elapsedmsec = (end - start)/1000; + + uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls(); + int elapsedCompressCalls = endCompressCalls - startCompressCalls; + + uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; + + + if (elapsedmsec > 100) { + if (elapsedmsec > 1000) { + int elapsedsec = (end - start)/1000000; + printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } else { + printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + } else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all + // the voxels from the current view frustum + if (nodeData->nodeBag.isEmpty()) { + nodeData->updateLastKnownViewFrustum(); + nodeData->setViewSent(true); + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + nodeData->map.printStats(); + } + nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes + } + + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { + printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); + } + + } // end if bag wasn't empty, and so we sent stuff... + + return truePacketsSent; +} + diff --git a/libraries/octree-server/src/OctreeSendThread.h b/libraries/octree-server/src/OctreeSendThread.h new file mode 100644 index 0000000000..a2de8fcb5d --- /dev/null +++ b/libraries/octree-server/src/OctreeSendThread.h @@ -0,0 +1,45 @@ +// +// OctreeSendThread.h +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded object for sending voxels to a client +// + +#ifndef __octree_server__OctreeSendThread__ +#define __octree_server__OctreeSendThread__ + +#include +#include +#include +#include "OctreeQueryNode.h" +#include "OctreeServer.h" + +/// Threaded processor for sending voxel packets to a single client +class OctreeSendThread : public virtual GenericThread { +public: + OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); + + static uint64_t _totalBytes; + static uint64_t _totalWastedBytes; + static uint64_t _totalPackets; + + static uint64_t _usleepTime; + static uint64_t _usleepCalls; + +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + QUuid _nodeUUID; + OctreeServer* _myServer; + + int handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); + int packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged); + + OctreePacketData _packetData; +}; + +#endif // __octree_server__OctreeSendThread__ diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp new file mode 100644 index 0000000000..652b84c527 --- /dev/null +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -0,0 +1,752 @@ +// +// OctreeServer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 9/16/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + + +/*** +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "VoxelNodeData.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include "Syssocket.h" +#include "Systime.h" +#else +#include +#include +#include +#endif +***/ + +#include +#include + +#include +#include + + +#include "OctreeServer.h" +#include "OctreeServerConsts.h" + +void OctreeServer::attachQueryNodeToNode(Node* newNode) { + if (newNode->getLinkedData() == NULL) { + if (GetInstance()) { + OctreeQueryNode* newQueryNodeData = GetInstance()->createOctreeQueryNode(newNode); + newQueryNodeData->resetOctreePacket(true); // don't bump sequence + newNode->setLinkedData(newQueryNodeData); + } + } +} + +void OctreeServer::nodeAdded(Node* node) { + // do nothing +} + +void OctreeServer::nodeKilled(Node* node) { + // Use this to cleanup our node + if (node->getType() == NODE_TYPE_AGENT) { + OctreeQueryNode* nodeData = (OctreeQueryNode*)node->getLinkedData(); + if (nodeData) { + node->setLinkedData(NULL); + delete nodeData; + } + } +}; + + +OctreeServer* OctreeServer::_theInstance = NULL; + +OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) : + ThreadedAssignment(dataBuffer, numBytes) +{ + _argc = 0; + _argv = NULL; + _tree = NULL; + _packetsPerClientPerInterval = 10; + _wantPersist = true; + _debugSending = false; + _debugReceiving = false; + _verboseDebug = false; + _jurisdiction = NULL; + _jurisdictionSender = NULL; + _octreeInboundPacketProcessor = NULL; + _persistThread = NULL; + _parsedArgV = NULL; + + _started = time(0); + _startedUSecs = usecTimestampNow(); + + _theInstance = this; +} + +OctreeServer::~OctreeServer() { + if (_parsedArgV) { + for (int i = 0; i < _argc; i++) { + delete[] _parsedArgV[i]; + } + delete[] _parsedArgV; + } +} + +void OctreeServer::initMongoose(int port) { + // setup the mongoose web server + struct mg_callbacks callbacks = {}; + + QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath()); + QString listenPort = QString("%1").arg(port); + + + // list of options. Last element must be NULL. + const char* options[] = { + "listening_ports", listenPort.toLocal8Bit().constData(), + "document_root", documentRoot.toLocal8Bit().constData(), + NULL }; + + callbacks.begin_request = civetwebRequestHandler; + + // Start the web server. + mg_start(&callbacks, NULL, options); +} + +int OctreeServer::civetwebRequestHandler(struct mg_connection* connection) { + const struct mg_request_info* ri = mg_get_request_info(connection); + + OctreeServer* theServer = GetInstance(); + +#ifdef FORCE_CRASH + if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) { + qDebug() << "About to force a crash!\n"; + int foo; + int* forceCrash = &foo; + mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); + mg_printf(connection, "%s", "forcing a crash....\r\n"); + delete[] forceCrash; + mg_printf(connection, "%s", "did it crash....\r\n"); + return 1; + } +#endif + + bool showStats = false; + if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) { + showStats = true; + } + + if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) { + theServer->_octreeInboundPacketProcessor->resetStats(); + showStats = true; + } + + if (showStats) { + uint64_t checkSum; + // return a 200 + mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n"); + mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n"); + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "
\r\n");
+        mg_printf(connection, "Your %s Server is running... [RELOAD]\r\n", theServer->getMyServerName());
+
+        tm* localtm = localtime(&theServer->_started);
+        const int MAX_TIME_LENGTH = 128;
+        char buffer[MAX_TIME_LENGTH];
+        strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
+        mg_printf(connection, "Running since: %s", buffer);
+
+        // Convert now to tm struct for UTC
+        tm* gmtm = gmtime(&theServer->_started);
+        if (gmtm != NULL) {
+            strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
+            mg_printf(connection, " [%s UTM] ", buffer);
+        }
+        mg_printf(connection, "%s", "\r\n");
+
+        uint64_t now  = usecTimestampNow();
+        const int USECS_PER_MSEC = 1000;
+        uint64_t msecsElapsed = (now - theServer->_startedUSecs) / USECS_PER_MSEC;
+        const int MSECS_PER_SEC = 1000;
+        const int SECS_PER_MIN = 60;
+        const int MIN_PER_HOUR = 60;
+        const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
+
+        float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
+        int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
+        int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
+
+        mg_printf(connection, "%s", "Uptime: ");
+        if (hours > 0) {
+            mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
+        }
+        if (minutes > 0) {
+            mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
+        }
+        if (seconds > 0) {
+            mg_printf(connection, "%.3f seconds ", seconds);
+        }
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "\r\n");
+
+
+        // display voxel file load time
+        if (theServer->isInitialLoadComplete()) {
+            time_t* loadCompleted = theServer->getLoadCompleted();
+            if (loadCompleted) {
+                tm* voxelsLoadedAtLocal = localtime(loadCompleted);
+                const int MAX_TIME_LENGTH = 128;
+                char buffer[MAX_TIME_LENGTH];
+                strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
+                mg_printf(connection, "%s file Loaded At: %s", theServer->getMyServerName(), buffer);
+
+                // Convert now to tm struct for UTC
+                tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
+                if (gmtm != NULL) {
+                    strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
+                    mg_printf(connection, " [%s UTM] ", buffer);
+                }
+            } else {
+                mg_printf(connection, "%s File Persist Disabled...\r\n", theServer->getMyServerName());
+            }
+            mg_printf(connection, "%s", "\r\n");
+
+
+            uint64_t msecsElapsed = theServer->getLoadElapsedTime() / USECS_PER_MSEC;;
+            float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
+            int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
+            int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
+
+            mg_printf(connection, "%s File Load Took: ", theServer->getMyServerName());
+            if (hours > 0) {
+                mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
+            }
+            if (minutes > 0) {
+                mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
+            }
+            if (seconds >= 0) {
+                mg_printf(connection, "%.3f seconds", seconds);
+            }
+            mg_printf(connection, "%s", "\r\n");
+
+        } else {
+            mg_printf(connection, "%s", "Voxels not yet loaded...\r\n");
+        }
+
+        mg_printf(connection, "%s", "\r\n");
+
+        mg_printf(connection, "%s", "\r\n");
+
+        mg_printf(connection, "%s", "Configuration:\r\n");
+
+        for (int i = 1; i < theServer->_argc; i++) {
+            mg_printf(connection, "%s ", theServer->_argv[i]);
+        }
+        mg_printf(connection, "%s", "\r\n"); // one to end the config line
+        mg_printf(connection, "%s", "\r\n"); // two more for spacing
+        mg_printf(connection, "%s", "\r\n");
+
+        // display scene stats
+        unsigned long nodeCount = OctreeElement::getNodeCount();
+        unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
+        unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
+        
+        QLocale locale(QLocale::English);
+        const float AS_PERCENT = 100.0;
+        mg_printf(connection, "%s", "Current Nodes in scene:\r\n");
+        mg_printf(connection, "       Total Nodes: %s nodes\r\n",
+                    locale.toString((uint)nodeCount).rightJustified(16, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "    Internal Nodes: %s nodes (%5.2f%%)\r\n",
+            locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
+            ((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "        Leaf Nodes: %s nodes (%5.2f%%)\r\n", 
+            locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
+            ((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "\r\n");
+
+
+        // display outbound packet stats
+        mg_printf(connection, "%s Outbound Packet Statistics...\r\n", theServer->getMyServerName());
+        uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
+        uint64_t totalOutboundBytes = OctreeSendThread::_totalBytes;
+        uint64_t totalWastedBytes = OctreeSendThread::_totalWastedBytes;
+        uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
+        uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
+        uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
+
+        const int COLUMN_WIDTH = 10;
+        mg_printf(connection, "           Total Outbound Packets: %s packets\r\n",
+            locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "             Total Outbound Bytes: %s bytes\r\n",
+            locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "               Total Wasted Bytes: %s bytes\r\n",
+            locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "            Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
+            locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
+            ((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
+        mg_printf(connection, "             Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
+            locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
+            ((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
+        mg_printf(connection, "                Total Color Bytes: %s bytes (%5.2f%%)\r\n",
+            locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
+            ((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
+
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "\r\n");
+
+        // display inbound packet stats
+        mg_printf(connection, "%s Edit Statistics... [RESET]\r\n", 
+                        theServer->getMyServerName());
+        uint64_t averageTransitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
+        uint64_t averageProcessTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
+        uint64_t averageLockWaitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
+        uint64_t averageProcessTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
+        uint64_t averageLockWaitTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
+        uint64_t totalElementsProcessed = theServer->_octreeInboundPacketProcessor->getTotalElementsProcessed();
+        uint64_t totalPacketsProcessed = theServer->_octreeInboundPacketProcessor->getTotalPacketsProcessed();
+
+        float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
+
+        mg_printf(connection, "           Total Inbound Packets: %s packets\r\n",
+            locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "          Total Inbound Elements: %s elements\r\n",
+            locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, " Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
+        mg_printf(connection, "     Average Transit Time/Packet: %s usecs\r\n", 
+            locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "     Average Process Time/Packet: %s usecs\r\n",
+            locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "   Average Wait Lock Time/Packet: %s usecs\r\n", 
+            locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "    Average Process Time/Element: %s usecs\r\n",
+            locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+        mg_printf(connection, "  Average Wait Lock Time/Element: %s usecs\r\n", 
+            locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+
+
+        int senderNumber = 0;
+        NodeToSenderStatsMap& allSenderStats = theServer->_octreeInboundPacketProcessor->getSingleSenderStats();
+        for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
+            senderNumber++;
+            QUuid senderID = i->first;
+            SingleSenderStats& senderStats = i->second;
+
+            mg_printf(connection, "\r\n             Stats for sender %d uuid: %s\r\n", senderNumber, 
+                senderID.toString().toLocal8Bit().constData());
+
+            averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
+            averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
+            averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
+            averageProcessTimePerElement = senderStats.getAverageProcessTimePerElement();
+            averageLockWaitTimePerElement = senderStats.getAverageLockWaitTimePerElement();
+            totalElementsProcessed = senderStats.getTotalElementsProcessed();
+            totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
+
+            averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
+
+            mg_printf(connection, "               Total Inbound Packets: %s packets\r\n",
+                locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "              Total Inbound Elements: %s elements\r\n",
+                locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "     Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
+            mg_printf(connection, "         Average Transit Time/Packet: %s usecs\r\n", 
+                locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "         Average Process Time/Packet: %s usecs\r\n",
+                locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "       Average Wait Lock Time/Packet: %s usecs\r\n", 
+                locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "        Average Process Time/Element: %s usecs\r\n",
+                locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+            mg_printf(connection, "      Average Wait Lock Time/Element: %s usecs\r\n", 
+                locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
+
+        }
+
+
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "\r\n");
+
+        // display memory usage stats
+        mg_printf(connection, "%s", "Current Memory Usage Statistics\r\n");
+        mg_printf(connection, "\r\nOctreeElement size... %ld bytes\r\n", sizeof(OctreeElement));
+        mg_printf(connection, "%s", "\r\n");
+
+        const char* memoryScaleLabel;
+        const float MEGABYTES = 1000000.f;
+        const float GIGABYTES = 1000000000.f;
+        float memoryScale;
+        if (OctreeElement::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
+            memoryScaleLabel = "MB";
+            memoryScale = MEGABYTES;
+        } else {
+            memoryScaleLabel = "GB";
+            memoryScale = GIGABYTES;
+        }
+
+        mg_printf(connection, "Element Node Memory Usage:       %8.2f %s\r\n", 
+            OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
+        mg_printf(connection, "Octcode Memory Usage:            %8.2f %s\r\n", 
+            OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
+        mg_printf(connection, "External Children Memory Usage:  %8.2f %s\r\n", 
+            OctreeElement::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
+        mg_printf(connection, "%s", "                                 -----------\r\n");
+        mg_printf(connection, "                         Total:  %8.2f %s\r\n", 
+            OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
+
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "OctreeElement Children Population Statistics...\r\n");
+        checkSum = 0;
+        for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
+            checkSum += OctreeElement::getChildrenCount(i);
+            mg_printf(connection, "    Nodes with %d children:      %s nodes (%5.2f%%)\r\n", i, 
+                locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
+                ((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
+        }
+        mg_printf(connection, "%s", "                                ----------------------\r\n");
+        mg_printf(connection, "                    Total:      %s nodes\r\n", 
+            locale.toString((uint)checkSum).rightJustified(16, ' ').toLocal8Bit().constData());
+
+#ifdef BLENDED_UNION_CHILDREN
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "OctreeElement Children Encoding Statistics...\r\n");
+        
+        mg_printf(connection, "    Single or No Children:      %10.llu nodes (%5.2f%%)\r\n",
+            OctreeElement::getSingleChildrenCount(), ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "    Two Children as Offset:     %10.llu nodes (%5.2f%%)\r\n", 
+            OctreeElement::getTwoChildrenOffsetCount(), 
+            ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "    Two Children as External:   %10.llu nodes (%5.2f%%)\r\n", 
+            OctreeElement::getTwoChildrenExternalCount(), 
+            ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "    Three Children as Offset:   %10.llu nodes (%5.2f%%)\r\n", 
+            OctreeElement::getThreeChildrenOffsetCount(), 
+            ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "    Three Children as External: %10.llu nodes (%5.2f%%)\r\n", 
+            OctreeElement::getThreeChildrenExternalCount(), 
+            ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
+        mg_printf(connection, "    Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
+            OctreeElement::getExternalChildrenCount(), 
+            ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
+
+        checkSum = OctreeElement::getSingleChildrenCount() +
+                            OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() + 
+                            OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() + 
+                            OctreeElement::getExternalChildrenCount();
+
+        mg_printf(connection, "%s", "                                ----------------\r\n");
+        mg_printf(connection, "                         Total: %10.llu nodes\r\n", checkSum);
+        mg_printf(connection, "                      Expected: %10.lu nodes\r\n", nodeCount);
+
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "In other news....\r\n");
+        mg_printf(connection, "could store 4 children internally:     %10.llu nodes\r\n",
+            OctreeElement::getCouldStoreFourChildrenInternally());
+        mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n", 
+            OctreeElement::getCouldNotStoreFourChildrenInternally());
+#endif
+
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "\r\n");
+        mg_printf(connection, "%s", "
\r\n"); + + mg_printf(connection, "%s", "
"); + + return 1; + } else { + // have mongoose process this request from the document_root + return 0; + } +} + + +void OctreeServer::setArguments(int argc, char** argv) { + _argc = argc; + _argv = const_cast(argv); + + qDebug("OctreeServer::setArguments()\n"); + for (int i = 0; i < _argc; i++) { + qDebug("_argv[%d]=%s\n", i, _argv[i]); + } + +} + +void OctreeServer::parsePayload() { + + if (getNumPayloadBytes() > 0) { + QString config((const char*) _payload); + + // Now, parse the config + QStringList configList = config.split(" "); + + int argCount = configList.size() + 1; + + qDebug("OctreeServer::parsePayload()... argCount=%d\n",argCount); + + _parsedArgV = new char*[argCount]; + const char* dummy = "config-from-payload"; + _parsedArgV[0] = new char[strlen(dummy) + sizeof(char)]; + strcpy(_parsedArgV[0], dummy); + + for (int i = 1; i < argCount; i++) { + QString configItem = configList.at(i-1); + _parsedArgV[i] = new char[configItem.length() + sizeof(char)]; + strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData()); + qDebug("OctreeServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]); + } + + setArguments(argCount, _parsedArgV); + } +} + +void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { + NodeList* nodeList = NodeList::getInstance(); + + PACKET_TYPE packetType = dataByteArray[0]; + + if (packetType == getMyQueryMessageType()) { + bool debug = false; + if (debug) { + qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow()); + } + + int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data()); + + // If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we + // need to make sure we have it in our nodeList. + QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader, + NUM_BYTES_RFC4122_UUID)); + + Node* node = nodeList->nodeWithUUID(nodeUUID); + + if (node) { + nodeList->updateNodeWithData(node, senderSockAddr, (unsigned char *) dataByteArray.data(), + dataByteArray.size()); + if (!node->getActiveSocket()) { + // we don't have an active socket for this node, but they're talking to us + // this means they've heard from us and can reply, let's assume public is active + node->activatePublicSocket(); + } + OctreeQueryNode* nodeData = (OctreeQueryNode*) node->getLinkedData(); + if (nodeData && !nodeData->isOctreeSendThreadInitalized()) { + nodeData->initializeOctreeSendThread(this); + } + } + } else if (_jurisdictionSender && packetType == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { + _jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), + dataByteArray.size()); + } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { + _octreeInboundPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), + dataByteArray.size()); + } else { + // let processNodeData handle it. + NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), + dataByteArray.size()); + } +} + +void OctreeServer::run() { + // Before we do anything else, create our tree... + _tree = createTree(); + + // change the logging target name while this is running + Logging::setTargetName(getMyLoggingServerTargetName()); + + // Now would be a good time to parse our arguments, if we got them as assignment + if (getNumPayloadBytes() > 0) { + parsePayload(); + } + + beforeRun(); // after payload has been processed + + qInstallMessageHandler(Logging::verboseMessageHandler); + + const char* STATUS_PORT = "--statusPort"; + const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); + if (statusPort) { + int statusPortNumber = atoi(statusPort); + initMongoose(statusPortNumber); + } + + + const char* JURISDICTION_FILE = "--jurisdictionFile"; + const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); + if (jurisdictionFile) { + qDebug("jurisdictionFile=%s\n", jurisdictionFile); + + qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + _jurisdiction = new JurisdictionMap(jurisdictionFile); + qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile); + } else { + const char* JURISDICTION_ROOT = "--jurisdictionRoot"; + const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); + if (jurisdictionRoot) { + qDebug("jurisdictionRoot=%s\n", jurisdictionRoot); + } + + const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; + const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); + if (jurisdictionEndNodes) { + qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes); + } + + if (jurisdictionRoot || jurisdictionEndNodes) { + _jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); + } + } + + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(getMyNodeType()); + + // we need to ask the DS about agents so we can ping/reply with them + const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER}; + nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + + setvbuf(stdout, NULL, _IOLBF, 0); + + // tell our NodeList about our desire to get notifications + nodeList->addHook(this); + nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode; + + nodeList->startSilentNodeRemovalThread(); + srand((unsigned)time(0)); + + const char* VERBOSE_DEBUG = "--verboseDebug"; + _verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG); + qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug)); + + const char* DEBUG_SENDING = "--debugSending"; + _debugSending = cmdOptionExists(_argc, _argv, DEBUG_SENDING); + qDebug("debugSending=%s\n", debug::valueOf(_debugSending)); + + const char* DEBUG_RECEIVING = "--debugReceiving"; + _debugReceiving = cmdOptionExists(_argc, _argv, DEBUG_RECEIVING); + qDebug("debugReceiving=%s\n", debug::valueOf(_debugReceiving)); + + // By default we will persist, if you want to disable this, then pass in this parameter + const char* NO_PERSIST = "--NoPersist"; + if (cmdOptionExists(_argc, _argv, NO_PERSIST)) { + _wantPersist = false; + } + qDebug("wantPersist=%s\n", debug::valueOf(_wantPersist)); + + // if we want Persistence, set up the local file and persist thread + if (_wantPersist) { + + // Check to see if the user passed in a command line option for setting packet send rate + const char* PERSIST_FILENAME = "--persistFilename"; + const char* persistFilenameParameter = getCmdOption(_argc, _argv, PERSIST_FILENAME); + if (persistFilenameParameter) { + strcpy(_persistFilename, persistFilenameParameter); + } else { + strcpy(_persistFilename, getMyDefaultPersistFilename()); + } + + qDebug("persistFilename=%s\n", _persistFilename); + + // now set up PersistThread + _persistThread = new OctreePersistThread(_tree, _persistFilename); + if (_persistThread) { + _persistThread->initialize(true); + } + } + + // Check to see if the user passed in a command line option for setting packet send rate + const char* PACKETS_PER_SECOND = "--packetsPerSecond"; + const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND); + if (packetsPerSecond) { + _packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND; + if (_packetsPerClientPerInterval < 1) { + _packetsPerClientPerInterval = 1; + } + qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval); + } + + HifiSockAddr senderSockAddr; + + // set up our jurisdiction broadcaster... + _jurisdictionSender = new JurisdictionSender(_jurisdiction); + if (_jurisdictionSender) { + _jurisdictionSender->initialize(true); + } + + // set up our OctreeServerPacketProcessor + _octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this); + if (_octreeInboundPacketProcessor) { + _octreeInboundPacketProcessor->initialize(true); + } + + // Convert now to tm struct for local timezone + tm* localtm = localtime(&_started); + const int MAX_TIME_LENGTH = 128; + char localBuffer[MAX_TIME_LENGTH] = { 0 }; + char utcBuffer[MAX_TIME_LENGTH] = { 0 }; + strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm); + // Convert now to tm struct for UTC + tm* gmtm = gmtime(&_started); + if (gmtm != NULL) { + strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm); + } + qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n"; + + QTimer* domainServerTimer = new QTimer(this); + connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); + domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); + + QTimer* silentNodeTimer = new QTimer(this); + connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); + + QTimer* pingNodesTimer = new QTimer(this); + connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); + pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + + // loop to send to nodes requesting data + while (!_isFinished) { + QCoreApplication::processEvents(); + } + + // call NodeList::clear() so that all of our node specific objects, including our sending threads, are + // properly shutdown and cleaned up. + NodeList::getInstance()->clear(); + + if (_jurisdictionSender) { + _jurisdictionSender->terminate(); + delete _jurisdictionSender; + } + + if (_octreeInboundPacketProcessor) { + _octreeInboundPacketProcessor->terminate(); + delete _octreeInboundPacketProcessor; + } + + if (_persistThread) { + _persistThread->terminate(); + delete _persistThread; + } + + // tell our NodeList we're done with notifications + nodeList->removeHook(this); + + delete _jurisdiction; + _jurisdiction = NULL; + + qDebug() << "OctreeServer::run()... DONE\n"; +} + + diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h new file mode 100644 index 0000000000..9e38b1068c --- /dev/null +++ b/libraries/octree-server/src/OctreeServer.h @@ -0,0 +1,100 @@ +// +// OctreeServer.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __octree_server__OctreeServer__ +#define __octree_server__OctreeServer__ + +#include +#include +#include + +#include +#include + +#include "civetweb.h" + +#include "OctreePersistThread.h" +#include "OctreeSendThread.h" +#include "OctreeServerConsts.h" +#include "OctreeInboundPacketProcessor.h" + +/// Handles assignments of type OctreeServer - sending octrees to various clients. +class OctreeServer : public ThreadedAssignment, public NodeListHook { +public: + OctreeServer(const unsigned char* dataBuffer, int numBytes); + ~OctreeServer(); + + /// allows setting of run arguments + void setArguments(int argc, char** argv); + + bool wantsDebugSending() const { return _debugSending; } + bool wantsDebugReceiving() const { return _debugReceiving; } + bool wantsVerboseDebug() const { return _verboseDebug; } + + Octree* getOctree() { return _tree; } + JurisdictionMap* getJurisdiction() { return _jurisdiction; } + + int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } + static OctreeServer* GetInstance() { return _theInstance; } + + bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; } + time_t* getLoadCompleted() { return (_persistThread) ? _persistThread->getLoadCompleted() : NULL; } + uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; } + + // Subclasses must implement these methods + virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode) = 0; + virtual Octree* createTree() = 0; + virtual unsigned char getMyNodeType() const = 0; + virtual PACKET_TYPE getMyQueryMessageType() const = 0; + virtual const char* getMyServerName() const = 0; + virtual const char* getMyLoggingServerTargetName() const = 0; + virtual const char* getMyDefaultPersistFilename() const = 0; + virtual bool hasSpecialPacketToSend() = 0; + virtual int sendSpecialPacket(Node* node) = 0; + + // subclass may implement these method + virtual void beforeRun() { }; + + static void attachQueryNodeToNode(Node* newNode); + + // NodeListHook + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); + +public slots: + /// runs the voxel server assignment + void run(); + void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); + +protected: + int _argc; + const char** _argv; + char** _parsedArgV; + + char _persistFilename[MAX_FILENAME_LENGTH]; + int _packetsPerClientPerInterval; + Octree* _tree; // this IS a reaveraging tree + bool _wantPersist; + bool _debugSending; + bool _debugReceiving; + bool _verboseDebug; + JurisdictionMap* _jurisdiction; + JurisdictionSender* _jurisdictionSender; + OctreeInboundPacketProcessor* _octreeInboundPacketProcessor; + OctreePersistThread* _persistThread; + + void parsePayload(); + void initMongoose(int port); + static int civetwebRequestHandler(struct mg_connection *connection); + static OctreeServer* _theInstance; + time_t _started; + uint64_t _startedUSecs; +}; + +#endif // __octree_server__OctreeServer__ diff --git a/libraries/octree-server/src/OctreeServerConsts.h b/libraries/octree-server/src/OctreeServerConsts.h new file mode 100644 index 0000000000..fae03a73ca --- /dev/null +++ b/libraries/octree-server/src/OctreeServerConsts.h @@ -0,0 +1,21 @@ +// OctreeServerConsts.h +// octree-server +// +// Created by Brad Hefta-Gaub on 12/4/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __octree_server__OctreeServerConsts__ +#define __octree_server__OctreeServerConsts__ + +#include +#include // for MAX_PACKET_SIZE +#include + +const int MAX_FILENAME_LENGTH = 1024; +const int INTERVALS_PER_SECOND = 60; +const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND; +const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels + +#endif // __octree_server__OctreeServerConsts__ From bfa8038f18cca79caa994020f9266633550dd415 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 19:51:16 -0800 Subject: [PATCH 08/17] removed scripts --- assignment-client/src/scipts/bug.js | 3259 ----------------- assignment-client/src/scipts/gameoflife.js | 181 - .../src/scipts/hk-buildings-2.js | 513 --- assignment-client/src/scipts/hk-buildings.js | 478 --- assignment-client/src/scipts/hk-platform.js | 280 -- assignment-client/src/scipts/sphere.js | 360 -- 6 files changed, 5071 deletions(-) delete mode 100644 assignment-client/src/scipts/bug.js delete mode 100644 assignment-client/src/scipts/gameoflife.js delete mode 100644 assignment-client/src/scipts/hk-buildings-2.js delete mode 100644 assignment-client/src/scipts/hk-buildings.js delete mode 100644 assignment-client/src/scipts/hk-platform.js delete mode 100644 assignment-client/src/scipts/sphere.js diff --git a/assignment-client/src/scipts/bug.js b/assignment-client/src/scipts/bug.js deleted file mode 100644 index 58523dd16b..0000000000 --- a/assignment-client/src/scipts/bug.js +++ /dev/null @@ -1,3259 +0,0 @@ -/******************************************************************************************************************************/ -// glMatrix -/******************************************************************************************************************************/ - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -if(!GLMAT_EPSILON) { - var GLMAT_EPSILON = 0.000001; -} - -if(!GLMAT_ARRAY_TYPE) { - var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; -} - -if(!GLMAT_RANDOM) { - var GLMAT_RANDOM = Math.random; -} - -/** - * @class Common utilities - * @name glMatrix - */ -var glMatrix = {}; - -/** - * Sets the type of array used when creating new vectors and matricies - * - * @param {Type} type Array type, such as Float32Array or Array - */ -glMatrix.setMatrixArrayType = function(type) { - GLMAT_ARRAY_TYPE = type; -} - -if(typeof(exports) !== 'undefined') { - exports.glMatrix = glMatrix; -} - -var degree = Math.PI / 180; - -/** -* Convert Degree To Radian -* -* @param {Number} Angle in Degrees -*/ -glMatrix.toRadian = function(a){ - return a * degree; -} - - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -/** - * @class 3x3 Matrix - * @name mat3 - */ -var mat3 = {}; - -/** - * Creates a new identity mat3 - * - * @returns {mat3} a new 3x3 matrix - */ -mat3.create = function() { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Copies the upper-left 3x3 values into the given mat3. - * - * @param {mat3} out the receiving 3x3 matrix - * @param {mat4} a the source 4x4 matrix - * @returns {mat3} out - */ -mat3.fromMat4 = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[4]; - out[4] = a[5]; - out[5] = a[6]; - out[6] = a[8]; - out[7] = a[9]; - out[8] = a[10]; - return out; -}; - -/** - * Creates a new mat3 initialized with values from an existing matrix - * - * @param {mat3} a matrix to clone - * @returns {mat3} a new 3x3 matrix - */ -mat3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copy the values from one mat3 to another - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Set a mat3 to the identity matrix - * - * @param {mat3} out the receiving matrix - * @returns {mat3} out - */ -mat3.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Transpose the values of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a12 = a[5]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a01; - out[5] = a[7]; - out[6] = a02; - out[7] = a12; - } else { - out[0] = a[0]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a[1]; - out[4] = a[4]; - out[5] = a[7]; - out[6] = a[2]; - out[7] = a[5]; - out[8] = a[8]; - } - - return out; -}; - -/** - * Inverts a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b01 = a22 * a11 - a12 * a21, - b11 = -a22 * a10 + a12 * a20, - b21 = a21 * a10 - a11 * a20, - - // Calculate the determinant - det = a00 * b01 + a01 * b11 + a02 * b21; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = b01 * det; - out[1] = (-a22 * a01 + a02 * a21) * det; - out[2] = (a12 * a01 - a02 * a11) * det; - out[3] = b11 * det; - out[4] = (a22 * a00 - a02 * a20) * det; - out[5] = (-a12 * a00 + a02 * a10) * det; - out[6] = b21 * det; - out[7] = (-a21 * a00 + a01 * a20) * det; - out[8] = (a11 * a00 - a01 * a10) * det; - return out; -}; - -/** - * Calculates the adjugate of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - out[0] = (a11 * a22 - a12 * a21); - out[1] = (a02 * a21 - a01 * a22); - out[2] = (a01 * a12 - a02 * a11); - out[3] = (a12 * a20 - a10 * a22); - out[4] = (a00 * a22 - a02 * a20); - out[5] = (a02 * a10 - a00 * a12); - out[6] = (a10 * a21 - a11 * a20); - out[7] = (a01 * a20 - a00 * a21); - out[8] = (a00 * a11 - a01 * a10); - return out; -}; - -/** - * Calculates the determinant of a mat3 - * - * @param {mat3} a the source matrix - * @returns {Number} determinant of a - */ -mat3.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); -}; - -/** - * Multiplies two mat3's - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the first operand - * @param {mat3} b the second operand - * @returns {mat3} out - */ -mat3.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b00 = b[0], b01 = b[1], b02 = b[2], - b10 = b[3], b11 = b[4], b12 = b[5], - b20 = b[6], b21 = b[7], b22 = b[8]; - - out[0] = b00 * a00 + b01 * a10 + b02 * a20; - out[1] = b00 * a01 + b01 * a11 + b02 * a21; - out[2] = b00 * a02 + b01 * a12 + b02 * a22; - - out[3] = b10 * a00 + b11 * a10 + b12 * a20; - out[4] = b10 * a01 + b11 * a11 + b12 * a21; - out[5] = b10 * a02 + b11 * a12 + b12 * a22; - - out[6] = b20 * a00 + b21 * a10 + b22 * a20; - out[7] = b20 * a01 + b21 * a11 + b22 * a21; - out[8] = b20 * a02 + b21 * a12 + b22 * a22; - return out; -}; - -/** - * Alias for {@link mat3.multiply} - * @function - */ -mat3.mul = mat3.multiply; - -/** - * Translate a mat3 by the given vector - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to translate - * @param {vec2} v vector to translate by - * @returns {mat3} out - */ -mat3.translate = function(out, a, v) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - x = v[0], y = v[1]; - - out[0] = a00; - out[1] = a01; - out[2] = a02; - - out[3] = a10; - out[4] = a11; - out[5] = a12; - - out[6] = x * a00 + y * a10 + a20; - out[7] = x * a01 + y * a11 + a21; - out[8] = x * a02 + y * a12 + a22; - return out; -}; - -/** - * Rotates a mat3 by the given angle - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat3} out - */ -mat3.rotate = function (out, a, rad) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - s = Math.sin(rad), - c = Math.cos(rad); - - out[0] = c * a00 + s * a10; - out[1] = c * a01 + s * a11; - out[2] = c * a02 + s * a12; - - out[3] = c * a10 - s * a00; - out[4] = c * a11 - s * a01; - out[5] = c * a12 - s * a02; - - out[6] = a20; - out[7] = a21; - out[8] = a22; - return out; -}; - -/** - * Scales the mat3 by the dimensions in the given vec2 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat3} out - **/ -mat3.scale = function(out, a, v) { - var x = v[0], y = v[1]; - - out[0] = x * a[0]; - out[1] = x * a[1]; - out[2] = x * a[2]; - - out[3] = y * a[3]; - out[4] = y * a[4]; - out[5] = y * a[5]; - - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copies the values from a mat2d into a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat2d} a the matrix to copy - * @returns {mat3} out - **/ -mat3.fromMat2d = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = 0; - - out[3] = a[2]; - out[4] = a[3]; - out[5] = 0; - - out[6] = a[4]; - out[7] = a[5]; - out[8] = 1; - return out; -}; - -/** -* Calculates a 3x3 matrix from the given quaternion -* -* @param {mat3} out mat3 receiving operation result -* @param {quat} q Quaternion to create matrix from -* -* @returns {mat3} out -*/ -mat3.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[3] = xy + wz; - out[6] = xz - wy; - - out[1] = xy - wz; - out[4] = 1 - (xx + zz); - out[7] = yz + wx; - - out[2] = xz + wy; - out[5] = yz - wx; - out[8] = 1 - (xx + yy); - - return out; -}; - -/** -* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix -* -* @param {mat3} out mat3 receiving operation result -* @param {mat4} a Mat4 to derive the normal matrix from -* -* @returns {mat3} out -*/ -mat3.normalFromMat4 = function (out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - - out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - - out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - - return out; -}; - -/** - * Returns a string representation of a mat3 - * - * @param {mat3} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat3.str = function (a) { - return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + - a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + - a[6] + ', ' + a[7] + ', ' + a[8] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat3 = mat3; -} - - - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -/** - * @class 4x4 Matrix - * @name mat4 - */ -var mat4 = {}; - -/** - * Creates a new identity mat4 - * - * @returns {mat4} a new 4x4 matrix - */ -mat4.create = function() { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Creates a new mat4 initialized with values from an existing matrix - * - * @param {mat4} a matrix to clone - * @returns {mat4} a new 4x4 matrix - */ -mat4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Copy the values from one mat4 to another - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Set a mat4 to the identity matrix - * - * @param {mat4} out the receiving matrix - * @returns {mat4} out - */ -mat4.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Transpose the values of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a03 = a[3], - a12 = a[6], a13 = a[7], - a23 = a[11]; - - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a01; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a02; - out[9] = a12; - out[11] = a[14]; - out[12] = a03; - out[13] = a13; - out[14] = a23; - } else { - out[0] = a[0]; - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a[1]; - out[5] = a[5]; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a[2]; - out[9] = a[6]; - out[10] = a[10]; - out[11] = a[14]; - out[12] = a[3]; - out[13] = a[7]; - out[14] = a[11]; - out[15] = a[15]; - } - - return out; -}; - -/** - * Inverts a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out; -}; - -/** - * Calculates the adjugate of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); - out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); - out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); - out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); - out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); - out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); - out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); - out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); - out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); - out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); - out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); - out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); - out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); - out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); - out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); - out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); - return out; -}; - -/** - * Calculates the determinant of a mat4 - * - * @param {mat4} a the source matrix - * @returns {Number} determinant of a - */ -mat4.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; -}; - -/** - * Multiplies two mat4's - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the first operand - * @param {mat4} b the second operand - * @returns {mat4} out - */ -mat4.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - // Cache only the current line of the second matrix - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; - out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; - out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; - out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - return out; -}; - -/** - * Alias for {@link mat4.multiply} - * @function - */ -mat4.mul = mat4.multiply; - -/** - * Translate a mat4 by the given vector - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to translate - * @param {vec3} v vector to translate by - * @returns {mat4} out - */ -mat4.translate = function (out, a, v) { - var x = v[0], y = v[1], z = v[2], - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23; - - if (a === out) { - out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; - out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; - out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; - out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; - } else { - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - - out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; - out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; - out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; - - out[12] = a00 * x + a10 * y + a20 * z + a[12]; - out[13] = a01 * x + a11 * y + a21 * z + a[13]; - out[14] = a02 * x + a12 * y + a22 * z + a[14]; - out[15] = a03 * x + a13 * y + a23 * z + a[15]; - } - - return out; -}; - -/** - * Scales the mat4 by the dimensions in the given vec3 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to scale - * @param {vec3} v the vec3 to scale the matrix by - * @returns {mat4} out - **/ -mat4.scale = function(out, a, v) { - var x = v[0], y = v[1], z = v[2]; - - out[0] = a[0] * x; - out[1] = a[1] * x; - out[2] = a[2] * x; - out[3] = a[3] * x; - out[4] = a[4] * y; - out[5] = a[5] * y; - out[6] = a[6] * y; - out[7] = a[7] * y; - out[8] = a[8] * z; - out[9] = a[9] * z; - out[10] = a[10] * z; - out[11] = a[11] * z; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Rotates a mat4 by the given angle - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @param {vec3} axis the axis to rotate around - * @returns {mat4} out - */ -mat4.rotate = function (out, a, rad, axis) { - var x = axis[0], y = axis[1], z = axis[2], - len = Math.sqrt(x * x + y * y + z * z), - s, c, t, - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23, - b00, b01, b02, - b10, b11, b12, - b20, b21, b22; - - if (Math.abs(len) < GLMAT_EPSILON) { return null; } - - len = 1 / len; - x *= len; - y *= len; - z *= len; - - s = Math.sin(rad); - c = Math.cos(rad); - t = 1 - c; - - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - - // Construct the elements of the rotation matrix - b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; - b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; - b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; - - // Perform rotation-specific matrix multiplication - out[0] = a00 * b00 + a10 * b01 + a20 * b02; - out[1] = a01 * b00 + a11 * b01 + a21 * b02; - out[2] = a02 * b00 + a12 * b01 + a22 * b02; - out[3] = a03 * b00 + a13 * b01 + a23 * b02; - out[4] = a00 * b10 + a10 * b11 + a20 * b12; - out[5] = a01 * b10 + a11 * b11 + a21 * b12; - out[6] = a02 * b10 + a12 * b11 + a22 * b12; - out[7] = a03 * b10 + a13 * b11 + a23 * b12; - out[8] = a00 * b20 + a10 * b21 + a20 * b22; - out[9] = a01 * b20 + a11 * b21 + a21 * b22; - out[10] = a02 * b20 + a12 * b21 + a22 * b22; - out[11] = a03 * b20 + a13 * b21 + a23 * b22; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - return out; -}; - -/** - * Rotates a matrix by the given angle around the X axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateX = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[4] = a10 * c + a20 * s; - out[5] = a11 * c + a21 * s; - out[6] = a12 * c + a22 * s; - out[7] = a13 * c + a23 * s; - out[8] = a20 * c - a10 * s; - out[9] = a21 * c - a11 * s; - out[10] = a22 * c - a12 * s; - out[11] = a23 * c - a13 * s; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Y axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateY = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c - a20 * s; - out[1] = a01 * c - a21 * s; - out[2] = a02 * c - a22 * s; - out[3] = a03 * c - a23 * s; - out[8] = a00 * s + a20 * c; - out[9] = a01 * s + a21 * c; - out[10] = a02 * s + a22 * c; - out[11] = a03 * s + a23 * c; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Z axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateZ = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c + a10 * s; - out[1] = a01 * c + a11 * s; - out[2] = a02 * c + a12 * s; - out[3] = a03 * c + a13 * s; - out[4] = a10 * c - a00 * s; - out[5] = a11 * c - a01 * s; - out[6] = a12 * c - a02 * s; - out[7] = a13 * c - a03 * s; - return out; -}; - -/** - * Creates a matrix from a quaternion rotation and vector translation - * This is equivalent to (but much faster than): - * - * mat4.identity(dest); - * mat4.translate(dest, vec); - * var quatMat = mat4.create(); - * quat4.toMat4(quat, quatMat); - * mat4.multiply(dest, quatMat); - * - * @param {mat4} out mat4 receiving operation result - * @param {quat4} q Rotation quaternion - * @param {vec3} v Translation vector - * @returns {mat4} out - */ -mat4.fromRotationTranslation = function (out, q, v) { - // Quaternion math - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - out[15] = 1; - - return out; -}; - -/** -* Calculates a 4x4 matrix from the given quaternion -* -* @param {mat4} out mat4 receiving operation result -* @param {quat} q Quaternion to create matrix from -* -* @returns {mat4} out -*/ -mat4.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - - return out; -}; - -/** - * Generates a frustum matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {Number} left Left bound of the frustum - * @param {Number} right Right bound of the frustum - * @param {Number} bottom Bottom bound of the frustum - * @param {Number} top Top bound of the frustum - * @param {Number} near Near bound of the frustum - * @param {Number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.frustum = function (out, left, right, bottom, top, near, far) { - var rl = 1 / (right - left), - tb = 1 / (top - bottom), - nf = 1 / (near - far); - out[0] = (near * 2) * rl; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = (near * 2) * tb; - out[6] = 0; - out[7] = 0; - out[8] = (right + left) * rl; - out[9] = (top + bottom) * tb; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (far * near * 2) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a perspective projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} fovy Vertical field of view in radians - * @param {number} aspect Aspect ratio. typically viewport width/height - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.perspective = function (out, fovy, aspect, near, far) { - var f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - out[0] = f / aspect; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = f; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (2 * far * near) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a orthogonal projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} left Left bound of the frustum - * @param {number} right Right bound of the frustum - * @param {number} bottom Bottom bound of the frustum - * @param {number} top Top bound of the frustum - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.ortho = function (out, left, right, bottom, top, near, far) { - var lr = 1 / (left - right), - bt = 1 / (bottom - top), - nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; - return out; -}; - -/** - * Generates a look-at matrix with the given eye position, focal point, and up axis - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {vec3} eye Position of the viewer - * @param {vec3} center Point the viewer is looking at - * @param {vec3} up vec3 pointing up - * @returns {mat4} out - */ -mat4.lookAt = function (out, eye, center, up) { - var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, - eyex = eye[0], - eyey = eye[1], - eyez = eye[2], - upx = up[0], - upy = up[1], - upz = up[2], - centerx = center[0], - centery = center[1], - centerz = center[2]; - - if (Math.abs(eyex - centerx) < GLMAT_EPSILON && - Math.abs(eyey - centery) < GLMAT_EPSILON && - Math.abs(eyez - centerz) < GLMAT_EPSILON) { - return mat4.identity(out); - } - - z0 = eyex - centerx; - z1 = eyey - centery; - z2 = eyez - centerz; - - len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); - z0 *= len; - z1 *= len; - z2 *= len; - - x0 = upy * z2 - upz * z1; - x1 = upz * z0 - upx * z2; - x2 = upx * z1 - upy * z0; - len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); - if (!len) { - x0 = 0; - x1 = 0; - x2 = 0; - } else { - len = 1 / len; - x0 *= len; - x1 *= len; - x2 *= len; - } - - y0 = z1 * x2 - z2 * x1; - y1 = z2 * x0 - z0 * x2; - y2 = z0 * x1 - z1 * x0; - - len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); - if (!len) { - y0 = 0; - y1 = 0; - y2 = 0; - } else { - len = 1 / len; - y0 *= len; - y1 *= len; - y2 *= len; - } - - out[0] = x0; - out[1] = y0; - out[2] = z0; - out[3] = 0; - out[4] = x1; - out[5] = y1; - out[6] = z1; - out[7] = 0; - out[8] = x2; - out[9] = y2; - out[10] = z2; - out[11] = 0; - out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); - out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); - out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); - out[15] = 1; - - return out; -}; - -/** - * Returns a string representation of a mat4 - * - * @param {mat4} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat4.str = function (a) { - return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + - a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + - a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + - a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat4 = mat4; -} - - - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -/** - * @class 3 Dimensional Vector - * @name vec3 - */ -var vec3 = {}; - -/** - * Creates a new, empty vec3 - * - * @returns {vec3} a new 3D vector - */ -vec3.create = function() { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = 0; - out[1] = 0; - out[2] = 0; - return out; -}; - -/** - * Creates a new vec3 initialized with values from an existing vector - * - * @param {vec3} a vector to clone - * @returns {vec3} a new 3D vector - */ -vec3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Creates a new vec3 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} a new 3D vector - */ -vec3.fromValues = function(x, y, z) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Copy the values from one vec3 to another - * - * @param {vec3} out the receiving vector - * @param {vec3} a the source vector - * @returns {vec3} out - */ -vec3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Set the components of a vec3 to the given values - * - * @param {vec3} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} out - */ -vec3.set = function(out, x, y, z) { - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Adds two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - return out; -}; - -/** - * Alias for {@link vec3.subtract} - * @function - */ -vec3.sub = vec3.subtract; - -/** - * Multiplies two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - return out; -}; - -/** - * Alias for {@link vec3.multiply} - * @function - */ -vec3.mul = vec3.multiply; - -/** - * Divides two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - return out; -}; - -/** - * Alias for {@link vec3.divide} - * @function - */ -vec3.div = vec3.divide; - -/** - * Returns the minimum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - return out; -}; - -/** - * Returns the maximum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - return out; -}; - -/** - * Scales a vec3 by a scalar number - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec3} out - */ -vec3.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - return out; -}; - -/** - * Adds two vec3's after scaling the second operand by a scalar value - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec3} out - */ -vec3.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} distance between a and b - */ -vec3.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.distance} - * @function - */ -vec3.dist = vec3.distance; - -/** - * Calculates the squared euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} squared distance between a and b - */ -vec3.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredDistance} - * @function - */ -vec3.sqrDist = vec3.squaredDistance; - -/** - * Calculates the length of a vec3 - * - * @param {vec3} a vector to calculate length of - * @returns {Number} length of a - */ -vec3.length = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.length} - * @function - */ -vec3.len = vec3.length; - -/** - * Calculates the squared length of a vec3 - * - * @param {vec3} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec3.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredLength} - * @function - */ -vec3.sqrLen = vec3.squaredLength; - -/** - * Negates the components of a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to negate - * @returns {vec3} out - */ -vec3.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - return out; -}; - -/** - * Normalize a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to normalize - * @returns {vec3} out - */ -vec3.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2]; - var len = x*x + y*y + z*z; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} dot product of a and b - */ -vec3.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -}; - -/** - * Computes the cross product of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.cross = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], - bx = b[0], by = b[1], bz = b[2]; - - out[0] = ay * bz - az * by; - out[1] = az * bx - ax * bz; - out[2] = ax * by - ay * bx; - return out; -}; - -/** - * Performs a linear interpolation between two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec3} out - */ -vec3.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec3} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec3} out - */ -vec3.random = function (out, scale) { - scale = scale || 1.0; - - var r = GLMAT_RANDOM() * 2.0 * Math.PI; - var z = (GLMAT_RANDOM() * 2.0) - 1.0; - var zScale = Math.sqrt(1.0-z*z) * scale; - - out[0] = Math.cos(r) * zScale; - out[1] = Math.sin(r) * zScale; - out[2] = z * scale; - return out; -}; - -/** - * Transforms the vec3 with a mat4. - * 4th vector component is implicitly '1' - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - return out; -}; - -/** - * Transforms the vec3 with a mat3. - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m the 3x3 matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat3 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = x * m[0] + y * m[3] + z * m[6]; - out[1] = x * m[1] + y * m[4] + z * m[7]; - out[2] = x * m[2] + y * m[5] + z * m[8]; - return out; -}; - -/** - * Transforms the vec3 with a quat - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec3} out - */ -vec3.transformQuat = function(out, a, q) { - // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations - - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec3s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec3.forEach = (function() { - var vec = vec3.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 3; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec3} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec3.str = function (a) { - return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec3 = vec3; -} - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -/** - * @class 4 Dimensional Vector - * @name vec4 - */ -var vec4 = {}; - -/** - * Creates a new, empty vec4 - * - * @returns {vec4} a new 4D vector - */ -vec4.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 0; - return out; -}; - -/** - * Creates a new vec4 initialized with values from an existing vector - * - * @param {vec4} a vector to clone - * @returns {vec4} a new 4D vector - */ -vec4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Creates a new vec4 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} a new 4D vector - */ -vec4.fromValues = function(x, y, z, w) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Copy the values from one vec4 to another - * - * @param {vec4} out the receiving vector - * @param {vec4} a the source vector - * @returns {vec4} out - */ -vec4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Set the components of a vec4 to the given values - * - * @param {vec4} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} out - */ -vec4.set = function(out, x, y, z, w) { - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Adds two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - out[3] = a[3] + b[3]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - out[3] = a[3] - b[3]; - return out; -}; - -/** - * Alias for {@link vec4.subtract} - * @function - */ -vec4.sub = vec4.subtract; - -/** - * Multiplies two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - out[3] = a[3] * b[3]; - return out; -}; - -/** - * Alias for {@link vec4.multiply} - * @function - */ -vec4.mul = vec4.multiply; - -/** - * Divides two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - out[3] = a[3] / b[3]; - return out; -}; - -/** - * Alias for {@link vec4.divide} - * @function - */ -vec4.div = vec4.divide; - -/** - * Returns the minimum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - out[3] = Math.min(a[3], b[3]); - return out; -}; - -/** - * Returns the maximum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - out[3] = Math.max(a[3], b[3]); - return out; -}; - -/** - * Scales a vec4 by a scalar number - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec4} out - */ -vec4.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - out[3] = a[3] * b; - return out; -}; - -/** - * Adds two vec4's after scaling the second operand by a scalar value - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec4} out - */ -vec4.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - out[3] = a[3] + (b[3] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} distance between a and b - */ -vec4.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.distance} - * @function - */ -vec4.dist = vec4.distance; - -/** - * Calculates the squared euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} squared distance between a and b - */ -vec4.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredDistance} - * @function - */ -vec4.sqrDist = vec4.squaredDistance; - -/** - * Calculates the length of a vec4 - * - * @param {vec4} a vector to calculate length of - * @returns {Number} length of a - */ -vec4.length = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.length} - * @function - */ -vec4.len = vec4.length; - -/** - * Calculates the squared length of a vec4 - * - * @param {vec4} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec4.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredLength} - * @function - */ -vec4.sqrLen = vec4.squaredLength; - -/** - * Negates the components of a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to negate - * @returns {vec4} out - */ -vec4.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = -a[3]; - return out; -}; - -/** - * Normalize a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to normalize - * @returns {vec4} out - */ -vec4.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - var len = x*x + y*y + z*z + w*w; - if (len > 0) { - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - out[3] = a[3] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} dot product of a and b - */ -vec4.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; -}; - -/** - * Performs a linear interpolation between two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec4} out - */ -vec4.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2], - aw = a[3]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - out[3] = aw + t * (b[3] - aw); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec4} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec4} out - */ -vec4.random = function (out, scale) { - scale = scale || 1.0; - - //TODO: This is a pretty awful way of doing this. Find something better. - out[0] = GLMAT_RANDOM(); - out[1] = GLMAT_RANDOM(); - out[2] = GLMAT_RANDOM(); - out[3] = GLMAT_RANDOM(); - vec4.normalize(out, out); - vec4.scale(out, out, scale); - return out; -}; - -/** - * Transforms the vec4 with a mat4. - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec4} out - */ -vec4.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2], w = a[3]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; - out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; - return out; -}; - -/** - * Transforms the vec4 with a quat - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec4} out - */ -vec4.transformQuat = function(out, a, q) { - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec4s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec4.forEach = (function() { - var vec = vec4.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 4; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec4} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec4.str = function (a) { - return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec4 = vec4; -} -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ - -/** - * @class Quaternion - * @name quat - */ -var quat = {}; - -/** - * Creates a new identity quat - * - * @returns {quat} a new quaternion - */ -quat.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quaternion to represent the shortest rotation from one - * vector to another. - * - * Both vectors are assumed to be unit length. - * - * @param {quat} out the receiving quaternion. - * @param {vec3} a the initial vector - * @param {vec3} b the destination vector - * @returns {quat} out - */ -quat.rotationTo = (function() { - var tmpvec3 = vec3.create(); - var xUnitVec3 = vec3.fromValues(1,0,0); - var yUnitVec3 = vec3.fromValues(0,1,0); - - return function(out, a, b) { - var dot = vec3.dot(a, b); - if (dot < -0.999999) { - vec3.cross(tmpvec3, xUnitVec3, a); - if (vec3.length(tmpvec3) < 0.000001) - vec3.cross(tmpvec3, yUnitVec3, a); - vec3.normalize(tmpvec3, tmpvec3); - quat.setAxisAngle(out, tmpvec3, Math.PI); - return out; - } else if (dot > 0.999999) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; - } else { - vec3.cross(tmpvec3, a, b); - out[0] = tmpvec3[0]; - out[1] = tmpvec3[1]; - out[2] = tmpvec3[2]; - out[3] = 1 + dot; - return quat.normalize(out, out); - } - }; -})(); - -/** - * Sets the specified quaternion with values corresponding to the given - * axes. Each axis is a vec3 and is expected to be unit length and - * perpendicular to all other specified axes. - * - * @param {vec3} view the vector representing the viewing direction - * @param {vec3} right the vector representing the local "right" direction - * @param {vec3} up the vector representing the local "up" direction - * @returns {quat} out - */ -quat.setAxes = (function() { - var matr = mat3.create(); - - return function(out, view, right, up) { - matr[0] = right[0]; - matr[3] = right[1]; - matr[6] = right[2]; - - matr[1] = up[0]; - matr[4] = up[1]; - matr[7] = up[2]; - - matr[2] = view[0]; - matr[5] = view[1]; - matr[8] = view[2]; - - return quat.normalize(out, quat.fromMat3(out, matr)); - }; -})(); - -/** - * Creates a new quat initialized with values from an existing quaternion - * - * @param {quat} a quaternion to clone - * @returns {quat} a new quaternion - * @function - */ -quat.clone = vec4.clone; - -/** - * Creates a new quat initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} a new quaternion - * @function - */ -quat.fromValues = vec4.fromValues; - -/** - * Copy the values from one quat to another - * - * @param {quat} out the receiving quaternion - * @param {quat} a the source quaternion - * @returns {quat} out - * @function - */ -quat.copy = vec4.copy; - -/** - * Set the components of a quat to the given values - * - * @param {quat} out the receiving quaternion - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} out - * @function - */ -quat.set = vec4.set; - -/** - * Set a quat to the identity quaternion - * - * @param {quat} out the receiving quaternion - * @returns {quat} out - */ -quat.identity = function(out) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quat from the given angle and rotation axis, - * then returns it. - * - * @param {quat} out the receiving quaternion - * @param {vec3} axis the axis around which to rotate - * @param {Number} rad the angle in radians - * @returns {quat} out - **/ -quat.setAxisAngle = function(out, axis, rad) { - rad = rad * 0.5; - var s = Math.sin(rad); - out[0] = s * axis[0]; - out[1] = s * axis[1]; - out[2] = s * axis[2]; - out[3] = Math.cos(rad); - return out; -}; - -/** - * Adds two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - * @function - */ -quat.add = vec4.add; - -/** - * Multiplies two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - */ -quat.multiply = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - out[0] = ax * bw + aw * bx + ay * bz - az * by; - out[1] = ay * bw + aw * by + az * bx - ax * bz; - out[2] = az * bw + aw * bz + ax * by - ay * bx; - out[3] = aw * bw - ax * bx - ay * by - az * bz; - return out; -}; - -/** - * Alias for {@link quat.multiply} - * @function - */ -quat.mul = quat.multiply; - -/** - * Scales a quat by a scalar number - * - * @param {quat} out the receiving vector - * @param {quat} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {quat} out - * @function - */ -quat.scale = vec4.scale; - -/** - * Rotates a quaternion by the given angle about the X axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateX = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + aw * bx; - out[1] = ay * bw + az * bx; - out[2] = az * bw - ay * bx; - out[3] = aw * bw - ax * bx; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Y axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateY = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - by = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw - az * by; - out[1] = ay * bw + aw * by; - out[2] = az * bw + ax * by; - out[3] = aw * bw - ay * by; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Z axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateZ = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bz = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + ay * bz; - out[1] = ay * bw - ax * bz; - out[2] = az * bw + aw * bz; - out[3] = aw * bw - az * bz; - return out; -}; - -/** - * Calculates the W component of a quat from the X, Y, and Z components. - * Assumes that quaternion is 1 unit in length. - * Any existing W component will be ignored. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate W component of - * @returns {quat} out - */ -quat.calculateW = function (out, a) { - var x = a[0], y = a[1], z = a[2]; - - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); - return out; -}; - -/** - * Calculates the dot product of two quat's - * - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {Number} dot product of a and b - * @function - */ -quat.dot = vec4.dot; - -/** - * Performs a linear interpolation between two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - * @function - */ -quat.lerp = vec4.lerp; - -/** - * Performs a spherical linear interpolation between two quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - */ -quat.slerp = function (out, a, b, t) { - // benchmarks: - // http://jsperf.com/quaternion-slerp-implementations - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - // adjust signs (if necessary) - if ( cosom < 0.0 ) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - // calculate coefficients - if ( (1.0 - cosom) > 0.000001 ) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // "from" and "to" quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - // calculate final values - out[0] = scale0 * ax + scale1 * bx; - out[1] = scale0 * ay + scale1 * by; - out[2] = scale0 * az + scale1 * bz; - out[3] = scale0 * aw + scale1 * bw; - - return out; -}; - -/** - * Calculates the inverse of a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate inverse of - * @returns {quat} out - */ -quat.invert = function(out, a) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, - invDot = dot ? 1.0/dot : 0; - - // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 - - out[0] = -a0*invDot; - out[1] = -a1*invDot; - out[2] = -a2*invDot; - out[3] = a3*invDot; - return out; -}; - -/** - * Calculates the conjugate of a quat - * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate conjugate of - * @returns {quat} out - */ -quat.conjugate = function (out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Calculates the length of a quat - * - * @param {quat} a vector to calculate length of - * @returns {Number} length of a - * @function - */ -quat.length = vec4.length; - -/** - * Alias for {@link quat.length} - * @function - */ -quat.len = quat.length; - -/** - * Calculates the squared length of a quat - * - * @param {quat} a vector to calculate squared length of - * @returns {Number} squared length of a - * @function - */ -quat.squaredLength = vec4.squaredLength; - -/** - * Alias for {@link quat.squaredLength} - * @function - */ -quat.sqrLen = quat.squaredLength; - -/** - * Normalize a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quaternion to normalize - * @returns {quat} out - * @function - */ -quat.normalize = vec4.normalize; - -/** - * Creates a quaternion from the given 3x3 rotation matrix. - * - * NOTE: The resultant quaternion is not normalized, so you should be sure - * to renormalize the quaternion yourself where necessary. - * - * @param {quat} out the receiving quaternion - * @param {mat3} m rotation matrix - * @returns {quat} out - * @function - */ -quat.fromMat3 = function(out, m) { - // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes - // article "Quaternion Calculus and Fast Animation". - var fTrace = m[0] + m[4] + m[8]; - var fRoot; - - if ( fTrace > 0.0 ) { - // |w| > 1/2, may as well choose w > 1/2 - fRoot = Math.sqrt(fTrace + 1.0); // 2w - out[3] = 0.5 * fRoot; - fRoot = 0.5/fRoot; // 1/(4w) - out[0] = (m[7]-m[5])*fRoot; - out[1] = (m[2]-m[6])*fRoot; - out[2] = (m[3]-m[1])*fRoot; - } else { - // |w| <= 1/2 - var i = 0; - if ( m[4] > m[0] ) - i = 1; - if ( m[8] > m[i*3+i] ) - i = 2; - var j = (i+1)%3; - var k = (i+2)%3; - - fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); - out[i] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; - out[3] = (m[k*3+j] - m[j*3+k]) * fRoot; - out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; - out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; - } - - return out; -}; - -/** - * Returns a string representation of a quatenion - * - * @param {quat} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -quat.str = function (a) { - return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.quat = quat; -} - - - - -/******************************************************************************************************************************/ -// HiFi Code Starts Here -/******************************************************************************************************************************/ - - -/** -x: "15869.555" [ 0.968601 ] -y: "306.329" [ 0.0186968 ] -z: "869.039" [ 0.0530419 ] -s: "256" [ 0.015625 ] -**/ - - -var DELETE_COPIES = 10; - -var PI_OVER_180 = 3.141592 / 180.0; -//var BUG_VOXEL_SIZE = 0.0625 / TREE_SCALE; -var BUG_VOXEL_SIZE = 1 / TREE_SCALE; -var bugPosition = { x: BUG_VOXEL_SIZE * 20.0, y: BUG_VOXEL_SIZE * 30.0, z: BUG_VOXEL_SIZE * 20.0 }; // not used in circle -var VOXELS_PER_BUG = 18; - -var birardaVoxelCorner = { x: 0.953125, y: 0.015625, z: 0.046875 }; -var birardaVoxelSize = 0.015625; -var birardaVoxelSizeHalf = birardaVoxelSize/2; - -//var bugPathCenter = { x: BUG_VOXEL_SIZE * 150.0, y: BUG_VOXEL_SIZE * 30.0, z: BUG_VOXEL_SIZE * 150.0 }; -//var bugPathRadius = BUG_VOXEL_SIZE * 140.0; -//var bugPathRadius = BUG_VOXEL_SIZE * 150.0; - -//var bugPathCenter = { x: 15997 / TREE_SCALE, y: BUG_VOXEL_SIZE * 30.0, z: 997 / TREE_SCALE }; -//var bugPathCenter = { x: 15997 / TREE_SCALE, y: 580 / TREE_SCALE, z: 997 / TREE_SCALE }; -var bugPathCenter = { - x: birardaVoxelCorner.x + birardaVoxelSizeHalf, - y: birardaVoxelCorner.y + birardaVoxelSize, - z: birardaVoxelCorner.z + birardaVoxelSizeHalf }; - -var bugPathRadius = 192 / TREE_SCALE; - -var bugPathTheta = 0.0 * PI_OVER_180; -var bugRotation = 0.0 * PI_OVER_180; -var bugAngleDelta = 0.2 * PI_OVER_180; - -function rotatePoint(point, angle) { - var axis = vec3.fromValues(0,1,0); - var q = quat.create(); - q = quat.setAxisAngle(q, axis, angle); - var rotation = mat4.create(); // a mat4 - rotation = mat4.fromQuat(rotation, q); - var quatPoint = vec4.fromValues(point.x, point.y, point.z, 0); - var newPoint = vec4.create(); // vec4 newPoint = vec4 * mat4 = quatPoint * rotation; - newPoint = vec4.transformMat4(newPoint, quatPoint, rotation); - var result = { x: newPoint[0], y: newPoint[1], z: newPoint[2] }; - return result; -} - - -var bugParts = { - - // tail - 0: { x: 0, y: 0, z: -3, r:51, g:51, b: 153 } , - 1: { x: 0, y: 0, z: -2, r:51, g:51, b: 153 } , - 2: { x: 0, y: 0, z: -1, r:51, g:51, b: 153 } , - - // body - 3: { x: 0, y: 0, z: 0, r:255, g:200, b: 0 } , - 4: { x: 0, y: 0, z: 1, r:255, g:200, b: 0 } , - - // head - 5: { x: 0, y: 0, z: 2, r:200, g:0, b: 0 } , - - // eyes - 6: { x: 1, y: 0, z: 3, r:64, g:64, b:64 } , - 7: { x: -1, y: 0, z: 3, r:64, g:64, b:64 } , - - // wings - 8: { x: 3, y: 1, z: 1, r:0, g:153, b:0 } , - 9: { x: 2, y: 1, z: 1, r:0, g:153, b:0 } , - 10: { x: 1, y: 0, z: 1, r:0, g:153, b:0 } , - 11: { x: -1, y: 0, z: 1, r:0, g:153, b:0 } , - 12: { x: -2, y: 1, z: 1, r:0, g:153, b:0 } , - 13: { x: -3, y: 1, z: 1, r:0, g:153, b:0 } , - - 14: { x: 2, y: -1, z: 0, r:153, g:200, b:0 } , - 15: { x: 1, y: -1, z: 0, r:153, g:200, b:0 } , - 16: { x: -1, y: -1, z: 0, r:153, g:200, b:0 } , - 17: { x: -2, y: -1, z: 0, r:153, g:200, b:0 } -}; - -function renderMovingBug() { - var x, y, z, s, red, green, blue; - var i; - var partAt, rotatedPartAt, offsetPartAt; - - var lastPosition = bugPosition; - var lastRotation = bugRotation; - var lastBugPathTheta = bugPathTheta; - for (var copy = 0; copy < DELETE_COPIES; copy++) { - - // Generate voxels for where bug used to be - for (i = 0; i < VOXELS_PER_BUG; i++) { - s = BUG_VOXEL_SIZE; - - partAt = { - x: bugParts[i].x * BUG_VOXEL_SIZE, - y: bugParts[i].y * BUG_VOXEL_SIZE, - z: bugParts[i].z * BUG_VOXEL_SIZE - }; - - rotatedPartAt = rotatePoint(partAt, lastRotation); - offsetPartAt = { - x : rotatedPartAt.x + lastPosition.x, - y : rotatedPartAt.y + lastPosition.y, - z : rotatedPartAt.z + lastPosition.z - }; - - x = offsetPartAt.x; - y = offsetPartAt.y; - z = offsetPartAt.z; - - red = bugParts[i].r; - green = bugParts[i].g; - blue = bugParts[i].b; - - Voxels.queueVoxelDelete(x, y, z, s); - } - - // do this in the reverse order as moving, because we're rolling backwards to delete the trail behind us - lastBugPathTheta -= bugAngleDelta; // move slightly - lastRotation += bugAngleDelta; // rotate slightly - - // If we loop past end of circle, just reset back into normal range - if (lastBugPathTheta < 0) { - lastBugPathTheta = (360.0 * PI_OVER_180); - lastRotation = (360.0 * PI_OVER_180); //? - } - - lastPosition.x = bugPathCenter.x + bugPathRadius * Math.cos(lastBugPathTheta); - lastPosition.z = bugPathCenter.z + bugPathRadius * Math.sin(lastBugPathTheta); - lastPosition.y = bugPathCenter.y; - - } - - // Move the bug... - //printf("bugPathCenter=(%f,%f,%f)\n", bugPathCenter.x, bugPathCenter.y, bugPathCenter.z); - - bugPathTheta += bugAngleDelta; // move slightly - bugRotation -= bugAngleDelta; // rotate slightly - - // If we loop past end of circle, just reset back into normal range - if (bugPathTheta > (360.0 * PI_OVER_180)) { - bugPathTheta = 0; - bugRotation = 0; - } - - x = bugPathCenter.x + bugPathRadius * Math.cos(bugPathTheta); - z = bugPathCenter.z + bugPathRadius * Math.sin(bugPathTheta); - y = bugPathCenter.y; - - bugPosition = { x: x, y: y, z: z}; - //printf("bugPathTheta=%f\n", bugPathTheta); - //printf("bugRotation=%f\n", bugRotation); - - //printf("bugPosition=(%f,%f,%f)\n", bugPosition.x, bugPosition.y, bugPosition.z); - // would be nice to add some randomness here... - - // Generate voxels for where bug is going to - for (i = 0; i < VOXELS_PER_BUG; i++) { - s = BUG_VOXEL_SIZE; - - partAt = { - x: bugParts[i].x * BUG_VOXEL_SIZE, - y: bugParts[i].y * BUG_VOXEL_SIZE, - z: bugParts[i].z * BUG_VOXEL_SIZE - }; - - rotatedPartAt = rotatePoint(partAt, bugRotation); - offsetPartAt = { - x : rotatedPartAt.x + bugPosition.x, - y : rotatedPartAt.y + bugPosition.y, - z : rotatedPartAt.z + bugPosition.z - }; - - x = offsetPartAt.x; - y = offsetPartAt.y; - z = offsetPartAt.z; - - red = bugParts[i].r; - green = bugParts[i].g; - blue = bugParts[i].b; - - //print("bugAt:" + x + ", "+ y + ", "+ z + "\n"); - - Voxels.queueDestructiveVoxelAdd(x, y, z, s, red, green, blue); - } -} - -function getRandomFloat(min, max) { - return Math.random() * (max - min) + min; -} - -function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -// register the call back so it fires before each data send -Voxels.setPacketsPerSecond(1000); -Agent.willSendVisualDataCallback.connect(renderMovingBug); diff --git a/assignment-client/src/scipts/gameoflife.js b/assignment-client/src/scipts/gameoflife.js deleted file mode 100644 index c5abb5d2a2..0000000000 --- a/assignment-client/src/scipts/gameoflife.js +++ /dev/null @@ -1,181 +0,0 @@ -// Add your JavaScript for assignment below this line - -// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) - -var NUMBER_OF_CELLS_EACH_DIMENSION = 36; -var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; - -var currentCells = []; -var nextCells = []; - -var METER_LENGTH = 1 / TREE_SCALE; -var cellScale = 4 * METER_LENGTH; - -print("TREE_SCALE = " + TREE_SCALE + "\n"); - -// randomly populate the cell start values -for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - // create the array to hold this row - currentCells[i] = []; - - // create the array to hold this row in the nextCells array - nextCells[i] = []; - - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - currentCells[i][j] = Math.floor(Math.random() * 2); - - // put the same value in the nextCells array for first board draw - nextCells[i][j] = currentCells[i][j]; - } -} - -function isNeighbourAlive(i, j) { - if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION - || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { - return 0; - } else { - return currentCells[i][j]; - } -} - -function currentPopulation() { - var i = 0; - var j = 0; - var population = 0; - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (currentCells[i][j]) { - population++; - } - } - } - return population; -} - -function updateCells() { - var i = 0; - var j = 0; - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - // figure out the number of live neighbours for the i-j cell - var liveNeighbours = - isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + - isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + - isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); - - if (currentCells[i][j]) { - // live cell - - if (liveNeighbours < 2) { - // rule #1 - under-population - this cell will die - // mark it zero to mark the change - nextCells[i][j] = 0; - } else if (liveNeighbours < 4) { - // rule #2 - this cell lives - // mark it -1 to mark no change - nextCells[i][j] = -1; - } else { - // rule #3 - overcrowding - this cell dies - // mark it zero to mark the change - nextCells[i][j] = 0; - } - } else { - // dead cell - if (liveNeighbours == 3) { - // rule #4 - reproduction - this cell revives - // mark it one to mark the change - nextCells[i][j] = 1; - } else { - - // here's another random mutation... 0.5% of all dead cells reanimate - var REANIMATE_PROBABILITY = 0.005; - if (Math.random() < REANIMATE_PROBABILITY) { - // mark it one to mark the change - nextCells[i][j] = 1; - } else { - // this cell stays dead - // mark it -1 for no change - nextCells[i][j] = -1; - } - } - } - - /* - if (Math.random() < 0.001) { - // Random mutation to keep things interesting in there. - nextCells[i][j] = 1; - } - */ - } - } - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to this cell, change the value in the currentCells array - currentCells[i][j] = nextCells[i][j]; - } - } - } -} - -function sendNextCells() { - var cellsSent = 0; - for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to the state of this cell, send it - - // find the x and z position for this voxel, y = 0 - var x = towerPlatformCorner.x + j * cellScale; - var y = towerPlatformCorner.y; - var z = towerPlatformCorner.z + i * cellScale; - - // queue a packet to add a voxel for the new cell - var color; - if (nextCells[i][j] == 1) { - color = {r: 255, g: 255, b: 255}; - } else { - color = {r: 128, g: 128, b: 128}; - } - Voxels.queueDestructiveVoxelAdd(x, 0, z, cellScale, color.r, color.g, color.b); - cellsSent++; - } - } - } - return cellsSent; -} - -var sentFirstBoard = false; - -var visualCallbacks = 0; -function step() { - if (sentFirstBoard) { - // we've already sent the first full board, perform a step in time - updateCells(); - } else { - // this will be our first board send - sentFirstBoard = true; - } - - var cellsSent = sendNextCells(); - - visualCallbacks++; - print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + - " visualCallbacks:" + visualCallbacks + - " currentPopulation:" + currentPopulation() + - " cellsSent:" + cellsSent + - " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + - " PPS:" + Voxels.getLifetimePPSQueued() + "," + - " Bytes:" + Voxels.getLifetimeBytesQueued() + "," + - //" Sent packets:" + Voxels.getLifetimePacketsSent() + "," + - //" PPS:" + Voxels.getLifetimePPS() + "," + - //" Bytes:" + Voxels.getLifetimeBytesSent() + - "\n"); - -} - -Voxels.setPacketsPerSecond(500); -Agent.willSendVisualDataCallback.connect(step); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-buildings-2.js b/assignment-client/src/scipts/hk-buildings-2.js deleted file mode 100644 index d018957026..0000000000 --- a/assignment-client/src/scipts/hk-buildings-2.js +++ /dev/null @@ -1,513 +0,0 @@ -// Ported from Stefan Gustavson's java implementation -// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf -// Read Stefan's excellent paper for details on how this code works. -// -// Sean McCullough banksean@gmail.com - -/** - * You can pass in a random number generator object if you like. - * It is assumed to have a random() method. - */ -var SimplexNoise = function(r) { - if (r == undefined) r = Math; - this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], - [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], - [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; - this.p = []; - for (var i=0; i<256; i++) { - this.p[i] = Math.floor(r.random()*256); - } - // To remove the need for index wrapping, double the permutation table length - this.perm = []; - for(var i=0; i<512; i++) { - this.perm[i]=this.p[i & 255]; - } - - // A lookup table to traverse the simplex around a given point in 4D. - // Details can be found where this table is used, in the 4D noise method. - this.simplex = [ - [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], - [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], - [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], - [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], - [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], - [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], - [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], - [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]]; -}; - -SimplexNoise.prototype.dot = function(g, x, y) { - return g[0]*x + g[1]*y; -}; - -SimplexNoise.prototype.noise = function(xin, yin) { - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var F2 = 0.5*(Math.sqrt(3.0)-1.0); - var s = (xin+yin)*F2; // Hairy factor for 2D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var G2 = (3.0-Math.sqrt(3.0))/6.0; - var t = (i+j)*G2; - var X0 = i-t; // Unskew the cell origin back to (x,y) space - var Y0 = j-t; - var x0 = xin-X0; // The x,y distances from the cell origin - var y0 = yin-Y0; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) - else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1.0 + 2.0 * G2; - // Work out the hashed gradient indices of the three simplex corners - var ii = i & 255; - var jj = j & 255; - var gi0 = this.perm[ii+this.perm[jj]] % 12; - var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; - var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0*x0-y0*y0; - if(t0<0) n0 = 0.0; - else { - t0 *= t0; - n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient - } - var t1 = 0.5 - x1*x1-y1*y1; - if(t1<0) n1 = 0.0; - else { - t1 *= t1; - n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); - } - var t2 = 0.5 - x2*x2-y2*y2; - if(t2<0) n2 = 0.0; - else { - t2 *= t2; - n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); - } - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70.0 * (n0 + n1 + n2); -}; - -// 3D simplex noise -SimplexNoise.prototype.noise3d = function(xin, yin, zin) { - var n0, n1, n2, n3; // Noise contributions from the four corners - // Skew the input space to determine which simplex cell we're in - var F3 = 1.0/3.0; - var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var k = Math.floor(zin+s); - var G3 = 1.0/6.0; // Very nice and simple unskew factor, too - var t = (i+j+k)*G3; - var X0 = i-t; // Unskew the cell origin back to (x,y,z) space - var Y0 = j-t; - var Z0 = k-t; - var x0 = xin-X0; // The x,y,z distances from the cell origin - var y0 = yin-Y0; - var z0 = zin-Z0; - // For the 3D case, the simplex shape is a slightly irregular tetrahedron. - // Determine which simplex we are in. - var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords - var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords - if(x0>=y0) { - if(y0>=z0) - { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order - else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order - else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order - } - else { // x0 MINIMUM_BEACON_HEIGHT) { - buildingBeacons.push([beaconCorner, 255, getRandomInt(0, 2), getRandomInt(4, 16)]); - } else { - ignoredBeacons++; - } - } -} - - - -var visualCallbacks = 0; - -function glowBeacons() { - for (var i = 0; i < buildingBeacons.length; i++) { - // place the block for the beacon - // and fade the beacon from black to red - var beaconPosition = buildingBeacons[i][0]; - var beaconColor = { r: 0, g: 0, b: 0}; - - if (buildingBeacons[i][2] === 0) { - beaconColor.r = buildingBeacons[i][1]; - } else if (buildingBeacons[i][2] == 1) { - beaconColor.g = buildingBeacons[i][1]; - } else { - beaconColor.b = buildingBeacons[i][1]; - } - - Voxels.queueDestructiveVoxelAdd(beaconPosition.x * METER, beaconPosition.y * METER, beaconPosition.z * METER, - BEACON_SIZE, beaconColor.r, beaconColor.g, beaconColor.b - ); - - buildingBeacons[i][1] += buildingBeacons[i][3]; - if (buildingBeacons[i][1] < 1 ) { - buildingBeacons[i][1] = 1; - buildingBeacons[i][3] *= -1; - } else if (buildingBeacons[i][1] > 255) { - buildingBeacons[i][1] = 255; - buildingBeacons[i][3] *= -1; - } - } -} - -// flicker lights every 100 visual callbacks -var NUM_LIGHT_FLICKER_ITERATIONS = 100; -var LIGHT_FLICKER_PROBABILITY = 0.01; - -var lightsThisCycle = 0; -function cityLights() { - lightsThisCycle = 0; - if (visualCallbacks % NUM_LIGHT_FLICKER_ITERATIONS === 0) { - for (var i = 0; i < windows.length; i++) { - // check if we change the state of this window - if (Math.random() < LIGHT_FLICKER_PROBABILITY) { - var thisWindow = windows[i]; - - // flicker this window to the other state - var newColor = thisWindow[1] ? BLACK_COLOR : WINDOW_COLOR; - Voxels.queueDestructiveVoxelAdd(thisWindow[0].x, thisWindow[0].y, thisWindow[0].z, - BUILDING_BLOCK_SIZE, newColor[0], newColor[1], newColor[2] - ); - lightsThisCycle++; - - // change the state of this window in the array - thisWindow[1] = !thisWindow[1]; - windows[i] = thisWindow; - } - } - } -} - -var NUM_BUILDINGS = 300; -var createdBuildings = 0; - -var INNER_CITY_RADIUS = CITY_SIZE_METERS / 8; -var SUBURB_RADIUS = CITY_SIZE_METERS / 4; -var SUBURB_LEVEL = 0.4; -var OUTSKIRT_RADIUS = CITY_SIZE_METERS - (INNER_CITY_RADIUS + SUBURB_RADIUS); -var OUTSKIRT_LEVEL = 0.05; - -var INNER_CITY_MAX_BUILDING_HEIGHT_METERS = 512; -var INNER_CITY_MIN_BUILDING_HEIGHT_METERS = 64; - -var SUBURB_MAX_BUILDING_HEIGHT_METERS = 64; -var SUBURB_MIN_BUILDING_HEIGHT_METERS = 16; - -var OUTSKIRT_MAX_BUILDING_HEIGHT_METERS = 32; -var OUTSKIRT_MIN_BUILDING_HEIGHT_METERS = 16; - -var doneBuilding = "no"; -function makeBuildings() { - if (createdBuildings < NUM_BUILDINGS) { - var randomPlacement = buildingRandom.random(); - var buildingCorner = {x: -1, z: -1}; - - var minRadiusPush = 0; - var maxRadiusPush = 0; - var minHeight = 0; - var maxHeight = 0; - - // pick a corner point for a new building, loop until it is inside the city limits - while (buildingCorner.x < 0 || buildingCorner.x > CITY_SIZE_METERS || buildingCorner.z < 0 || buildingCorner.z > CITY_SIZE_METERS) { - if (randomPlacement < OUTSKIRT_LEVEL) { - minRadiusPush = INNER_CITY_RADIUS + SUBURB_RADIUS; - maxRadiusPush = CITY_SIZE_METERS; - - minHeight = OUTSKIRT_MIN_BUILDING_HEIGHT_METERS; - maxHeight = OUTSKIRT_MAX_BUILDING_HEIGHT_METERS; - } else if (randomPlacement < SUBURB_LEVEL) { - minRadiusPush = INNER_CITY_RADIUS; - maxRadiusPush = SUBURB_RADIUS; - - minHeight = SUBURB_MIN_BUILDING_HEIGHT_METERS; - maxHeight = SUBURB_MAX_BUILDING_HEIGHT_METERS; - } else { - minRadiusPush = 0; - maxRadiusPush = INNER_CITY_RADIUS; - - minHeight = INNER_CITY_MIN_BUILDING_HEIGHT_METERS; - maxHeight = INNER_CITY_MAX_BUILDING_HEIGHT_METERS; - } - - var radiusPush = getRandomFloat(minRadiusPush, maxRadiusPush); - var randomAngle = getRandomFloat(0, 360); - - buildingCorner.x = cityCenter.x + (radiusPush * Math.cos(randomAngle)); - buildingCorner.z = cityCenter.z + (radiusPush * Math.sin(randomAngle)); - } - - makeBuilding(buildingCorner, minHeight, maxHeight); - - createdBuildings++; - } else { - doneBuilding = "yes"; - glowBeacons(); - cityLights(); - } - - visualCallbacks++; - var wantDebug = false; - if (wantDebug){ - print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + - " visualCallbacks:" + visualCallbacks + - " doneBuilding:" + doneBuilding + - " buildingBeacons.length:" + buildingBeacons.length + - " ignored beacons:" + ignoredBeacons + - " lightsThisCycle:" + lightsThisCycle + - " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + - " PPS:" + Voxels.getLifetimePPSQueued() + "," + - //" BPS:" + Voxels.getLifetimeBPSQueued() + "," + - " Sent packets:" + Voxels.getLifetimePacketsSent() + "," + - " PPS:" + Voxels.getLifetimePPS() + "," + - //" BPS:" + Voxels.getLifetimeBPS() + - "\n"); - } - -} - -Voxels.setPacketsPerSecond(500); - -// register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(makeBuildings); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-buildings.js b/assignment-client/src/scipts/hk-buildings.js deleted file mode 100644 index bcef92e6ac..0000000000 --- a/assignment-client/src/scipts/hk-buildings.js +++ /dev/null @@ -1,478 +0,0 @@ -// Ported from Stefan Gustavson's java implementation -// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf -// Read Stefan's excellent paper for details on how this code works. -// -// Sean McCullough banksean@gmail.com - -/** - * You can pass in a random number generator object if you like. - * It is assumed to have a random() method. - */ -var SimplexNoise = function(r) { - if (r == undefined) r = Math; - this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], - [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], - [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; - this.p = []; - for (var i=0; i<256; i++) { - this.p[i] = Math.floor(r.random()*256); - } - // To remove the need for index wrapping, double the permutation table length - this.perm = []; - for(var i=0; i<512; i++) { - this.perm[i]=this.p[i & 255]; - } - - // A lookup table to traverse the simplex around a given point in 4D. - // Details can be found where this table is used, in the 4D noise method. - this.simplex = [ - [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], - [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], - [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], - [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], - [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], - [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], - [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], - [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]]; -}; - -SimplexNoise.prototype.dot = function(g, x, y) { - return g[0]*x + g[1]*y; -}; - -SimplexNoise.prototype.noise = function(xin, yin) { - var n0, n1, n2; // Noise contributions from the three corners - // Skew the input space to determine which simplex cell we're in - var F2 = 0.5*(Math.sqrt(3.0)-1.0); - var s = (xin+yin)*F2; // Hairy factor for 2D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var G2 = (3.0-Math.sqrt(3.0))/6.0; - var t = (i+j)*G2; - var X0 = i-t; // Unskew the cell origin back to (x,y) space - var Y0 = j-t; - var x0 = xin-X0; // The x,y distances from the cell origin - var y0 = yin-Y0; - // For the 2D case, the simplex shape is an equilateral triangle. - // Determine which simplex we are in. - var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords - if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) - else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) - // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and - // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where - // c = (3-sqrt(3))/6 - var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords - var y1 = y0 - j1 + G2; - var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords - var y2 = y0 - 1.0 + 2.0 * G2; - // Work out the hashed gradient indices of the three simplex corners - var ii = i & 255; - var jj = j & 255; - var gi0 = this.perm[ii+this.perm[jj]] % 12; - var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; - var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; - // Calculate the contribution from the three corners - var t0 = 0.5 - x0*x0-y0*y0; - if(t0<0) n0 = 0.0; - else { - t0 *= t0; - n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient - } - var t1 = 0.5 - x1*x1-y1*y1; - if(t1<0) n1 = 0.0; - else { - t1 *= t1; - n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); - } - var t2 = 0.5 - x2*x2-y2*y2; - if(t2<0) n2 = 0.0; - else { - t2 *= t2; - n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); - } - // Add contributions from each corner to get the final noise value. - // The result is scaled to return values in the interval [-1,1]. - return 70.0 * (n0 + n1 + n2); -}; - -// 3D simplex noise -SimplexNoise.prototype.noise3d = function(xin, yin, zin) { - var n0, n1, n2, n3; // Noise contributions from the four corners - // Skew the input space to determine which simplex cell we're in - var F3 = 1.0/3.0; - var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D - var i = Math.floor(xin+s); - var j = Math.floor(yin+s); - var k = Math.floor(zin+s); - var G3 = 1.0/6.0; // Very nice and simple unskew factor, too - var t = (i+j+k)*G3; - var X0 = i-t; // Unskew the cell origin back to (x,y,z) space - var Y0 = j-t; - var Z0 = k-t; - var x0 = xin-X0; // The x,y,z distances from the cell origin - var y0 = yin-Y0; - var z0 = zin-Z0; - // For the 3D case, the simplex shape is a slightly irregular tetrahedron. - // Determine which simplex we are in. - var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords - var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords - if(x0>=y0) { - if(y0>=z0) - { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order - else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order - else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order - } - else { // x0 255) { - buildingBeacons[i][1] = 255; - buildingBeacons[i][3] *= -1; - } - } -} - -// flicker lights every 1000 visual callbacks -var NUM_LIGHT_FLICKER_ITERATIONS = 100; -var LIGHT_FLICKER_PROBABILITY = 0.01; - -function cityLights() { - if (visualCallbacks % NUM_LIGHT_FLICKER_ITERATIONS === 0) { - for (var i = 0; i < windows.length; i++) { - // check if we change the state of this window - if (Math.random() < LIGHT_FLICKER_PROBABILITY) { - var thisWindow = windows[i]; - - // flicker this window to the other state - var newColor = thisWindow[1] ? BLACK_COLOR : WINDOW_COLOR; - Voxels.queueDestructiveVoxelAdd(thisWindow[0].x, thisWindow[0].y, thisWindow[0].z, - BUILDING_BLOCK_SIZE, newColor[0], newColor[1], newColor[2] - ); - - // change the state of this window in the array - thisWindow[1] = !thisWindow[1]; - windows[i] = thisWindow; - } - } - } -} - -var NUM_BUILDINGS = 300; -var createdBuildings = 0; - -var INNER_CITY_RADIUS = CITY_SIZE_METERS / 8; -var SUBURB_RADIUS = CITY_SIZE_METERS / 4; -var SUBURB_LEVEL = 0.4; -var OUTSKIRT_RADIUS = CITY_SIZE_METERS - (INNER_CITY_RADIUS + SUBURB_RADIUS); -var OUTSKIRT_LEVEL = 0.05; - -var INNER_CITY_MAX_BUILDING_HEIGHT_METERS = 512; -var INNER_CITY_MIN_BUILDING_HEIGHT_METERS = 64; - -var SUBURB_MAX_BUILDING_HEIGHT_METERS = 64; -var SUBURB_MIN_BUILDING_HEIGHT_METERS = 16; - -var OUTSKIRT_MAX_BUILDING_HEIGHT_METERS = 32; -var OUTSKIRT_MIN_BUILDING_HEIGHT_METERS = 16; - -function makeBuildings() { - if (createdBuildings < NUM_BUILDINGS) { - var randomPlacement = buildingRandom.random(); - var buildingCorner = {x: -1, z: -1}; - - var minRadiusPush = 0; - var maxRadiusPush = 0; - var minHeight = 0; - var maxHeight = 0; - - // pick a corner point for a new building, loop until it is inside the city limits - while (buildingCorner.x < 0 || buildingCorner.x > CITY_SIZE_METERS || buildingCorner.z < 0 || buildingCorner.z > CITY_SIZE_METERS) { - if (randomPlacement < OUTSKIRT_LEVEL) { - minRadiusPush = INNER_CITY_RADIUS + SUBURB_RADIUS; - maxRadiusPush = CITY_SIZE_METERS; - - minHeight = OUTSKIRT_MIN_BUILDING_HEIGHT_METERS; - maxHeight = OUTSKIRT_MAX_BUILDING_HEIGHT_METERS; - } else if (randomPlacement < SUBURB_LEVEL) { - minRadiusPush = INNER_CITY_RADIUS; - maxRadiusPush = SUBURB_RADIUS; - - minHeight = SUBURB_MIN_BUILDING_HEIGHT_METERS; - maxHeight = SUBURB_MAX_BUILDING_HEIGHT_METERS; - } else { - minRadiusPush = 0; - maxRadiusPush = INNER_CITY_RADIUS; - - minHeight = INNER_CITY_MIN_BUILDING_HEIGHT_METERS; - maxHeight = INNER_CITY_MAX_BUILDING_HEIGHT_METERS; - } - - var radiusPush = getRandomFloat(minRadiusPush, maxRadiusPush); - var randomAngle = getRandomFloat(0, 360); - - buildingCorner.x = cityCenter.x + (radiusPush * Math.cos(randomAngle)); - buildingCorner.z = cityCenter.z + (radiusPush * Math.sin(randomAngle)); - } - - makeBuilding(buildingCorner, minHeight, maxHeight); - - createdBuildings++; - } else { - glowBeacons(); - cityLights(); - } - - visualCallbacks++; -} - -Voxels.setPacketsPerSecond(200); - -// register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(makeBuildings); \ No newline at end of file diff --git a/assignment-client/src/scipts/hk-platform.js b/assignment-client/src/scipts/hk-platform.js deleted file mode 100644 index 6ced861f81..0000000000 --- a/assignment-client/src/scipts/hk-platform.js +++ /dev/null @@ -1,280 +0,0 @@ -var wantDebug = false; - -var METER = 1.0 / TREE_SCALE; - -var LIVE_CELL_COLOR = {r: 255, g: 255, b: 255}; - - -var towerBaseLocation = { x: 2800 * METER, y: 0 * METER, z: 2240 * METER}; -var towerTop = 352 * METER; // the y location of top of tower -var towerBrickSize = 16 * METER; -var towerColor = { r: 128, g: 128, b: 255}; -var towerPlatformWidthInBricks = 36; // 9; -var towerPlatformOffsetInBricks = 16; // 4; -var towerPlatformBrickSize = 4 * METER; - -var towerPlatformCorner = { - x: towerBaseLocation.x - (towerPlatformOffsetInBricks * towerPlatformBrickSize), - y: towerTop, - z: towerBaseLocation.z - (towerPlatformOffsetInBricks * towerPlatformBrickSize) }; - - -var visualCallbacks = 0; -var platformBuilt = false; - -// Note: 16 meters, 9x9 = 660 bytes -// Note: 8 meters, 18x18 (324) = 2940 bytes... -// 8 meters = ~9 bytes , 166 per packet - -function buildPlatform() { - // Cut hole in ground for tower - for (var y = towerBaseLocation.y; y <= towerTop; y += towerBrickSize) { - Voxels.queueVoxelDelete(towerBaseLocation.x, y, towerBaseLocation.z, towerBrickSize); - } - - // Build the tower - for (var y = towerBaseLocation.y; y <= towerTop; y += towerBrickSize) { - Voxels.queueDestructiveVoxelAdd(towerBaseLocation.x, y, towerBaseLocation.z, - towerBrickSize, towerColor.r, towerColor.g, towerColor.b); - } - - /** - // Build the platform - this is actually handled buy the game of life board - for (var x = 0; x < towerPlatformWidthInBricks; x++) { - for (var z = 0; z < towerPlatformWidthInBricks; z++) { - var platformBrick = { - x: towerPlatformCorner.x + (x * towerPlatformBrickSize), - y: towerPlatformCorner.y, - z: towerPlatformCorner.z + (z * towerPlatformBrickSize) }; - - Voxels.queueDestructiveVoxelAdd(platformBrick.x, platformBrick.y, platformBrick.z, - towerPlatformBrickSize, towerColor.r, towerColor.g, towerColor.b); - } - } - **/ -} - - -// Add your JavaScript for assignment below this line - -// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) - -var NUMBER_OF_CELLS_EACH_DIMENSION = towerPlatformWidthInBricks; -var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; - -var currentCells = []; -var nextCells = []; - -print("TREE_SCALE = " + TREE_SCALE + "\n"); - -// randomly populate the cell start values -for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - // create the array to hold this row - currentCells[i] = []; - - // create the array to hold this row in the nextCells array - nextCells[i] = []; - - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - currentCells[i][j] = Math.floor(Math.random() * 2); - - // put the same value in the nextCells array for first board draw - nextCells[i][j] = currentCells[i][j]; - } -} - -function currentPopulation() { - var i = 0; - var j = 0; - var population = 0; - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (currentCells[i][j]) { - population++; - } - } - } - return population; -} - - -function isNeighbourAlive(i, j) { - if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION - || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { - return 0; - } else { - return currentCells[i][j]; - } -} - -function updateCells() { - var i = 0; - var j = 0; - - var REANIMATE_PROBABILITY = 0.005; - - var population = currentPopulation(); - var totalPossiblePopulation = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; - // if current population is below 5% of total possible population, increase REANIMATE_PROBABILITY by 2x - if (population < (totalPossiblePopulation * 0.05)) { - REANIMATE_PROBABILITY = REANIMATE_PROBABILITY * 2; - } - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - // figure out the number of live neighbours for the i-j cell - var liveNeighbours = - isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + - isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + - isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); - - if (currentCells[i][j]) { - // live cell - - if (liveNeighbours < 2) { - // rule #1 - under-population - this cell will die - // mark it zero to mark the change - nextCells[i][j] = 0; - } else if (liveNeighbours < 4) { - // rule #2 - this cell lives - // mark it -1 to mark no change - nextCells[i][j] = -1; - } else { - // rule #3 - overcrowding - this cell dies - // mark it zero to mark the change - nextCells[i][j] = 0; - } - } else { - // dead cell - if (liveNeighbours == 3) { - // rule #4 - reproduction - this cell revives - // mark it one to mark the change - nextCells[i][j] = 1; - } else { - - // here's another random mutation... 0.5% of all dead cells reanimate - if (Math.random() < REANIMATE_PROBABILITY) { - // mark it one to mark the change - nextCells[i][j] = 1; - } else { - // this cell stays dead - // mark it -1 for no change - nextCells[i][j] = -1; - } - } - } - - /* - if (Math.random() < 0.001) { - // Random mutation to keep things interesting in there. - nextCells[i][j] = 1; - } - */ - } - } - - for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to this cell, change the value in the currentCells array - currentCells[i][j] = nextCells[i][j]; - } - } - } -} - -var fadeRatio = 0.0; -var fadeRatioAdjust = 0.25; - -function sendNextCells() { - var cellsSent = 0; - for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { - for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { - if (nextCells[i][j] != -1) { - // there has been a change to the state of this cell, send it - - // find the x and z position for this voxel, y = 0 - var x = towerPlatformCorner.x + j * towerPlatformBrickSize; - var y = towerPlatformCorner.y; - var z = towerPlatformCorner.z + i * towerPlatformBrickSize; - - // queue a packet to add a voxel for the new cell - var ratio = fadeRatio; - if (ratio > 1) { - ratio = 1; - } - - var fromColor, toColor; - var color = LIVE_CELL_COLOR; - if (nextCells[i][j] == 1) { - //print("to LIVE from tower\n"); - - color.r = Math.round((127 * ratio) + 128); - color.g = Math.round((127 * ratio) + 128); - color.b = 255; - - } else { - //print("to tower from LIVE\n"); - - - color.r = Math.round((127 * (1-ratio)) + 128); - color.g = Math.round((127 * (1-ratio)) + 128); - color.b = 255; - } - - - if (color.r < 100 || color.r > 255) { - print("ratio: " + ratio + " " - + "color: " + color.r + ", " - + color.g + ", " - + color.b + " " - + "\n"); - } - Voxels.queueDestructiveVoxelAdd(x, y, z, towerPlatformBrickSize, color.r, color.g, color.b); - cellsSent++; - } - } - } - return cellsSent; -} - -var visualCallbacks = 0; - -function animatePlatform() { - if (!platformBuilt) { - buildPlatform(); - platformBuilt = true; - } else { - fadeRatio += fadeRatioAdjust; - var cellsSent = sendNextCells(); - - //print("fadeRatio: " + fadeRatio + "\n") - if (fadeRatio >= 1.0) { - updateCells(); // send the initial game of life cells - fadeRatio = 0.0; - } - } - - visualCallbacks++; - - if (wantDebug) { - print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + - " visualCallbacks:" + visualCallbacks + - " currentPopulation:" + currentPopulation() + - " cellsSent:" + cellsSent + - " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + - " PPS:" + Voxels.getLifetimePPSQueued() + "," + - " Bytes:" + Voxels.getLifetimeBytesQueued() + "," + - //" Sent packets:" + Voxels.getLifetimePacketsSent() + "," + - //" PPS:" + Voxels.getLifetimePPS() + "," + - //" Bytes:" + Voxels.getLifetimeBytesSent() + - "\n"); - } - -} - -Voxels.setPacketsPerSecond(500); - -// register the call back so it fires before each data send -Agent.willSendVisualDataCallback.connect(animatePlatform); \ No newline at end of file diff --git a/assignment-client/src/scipts/sphere.js b/assignment-client/src/scipts/sphere.js deleted file mode 100644 index 796f55f054..0000000000 --- a/assignment-client/src/scipts/sphere.js +++ /dev/null @@ -1,360 +0,0 @@ -var oldMap= [ -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,1,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0], -[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] -]; - -var map = [ -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",";","t","@","G","."," ","f","L","1","i","t","L","G","@","@","8","@","@","@","@","@","@","@","@","@","@","@","8","8","8","0","@","@","i"," "," "," "," ","."," "," "," "," "," "," ",".",".",".","i","i","C","@","0","@","f",":","1","i","i","t","f","i",":","1",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","."," "," "," "," ",".",";"," "," ",":",":","f","@","0","@","C",";"," ",".","f","@","C","L","0","@","@","1","f","G","8","@","@","@","@","@","@","@","t","i",";",";",":",".",":",";","@","@","@","@","0",";",":"," ","t","@",".",".","0","@","@","f","t","@","8","8","@","@","@","@","8","0","G","8","@","1","1","G",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","f",":","C","f","t","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","."," "," "," "," ",".","i","@","1",";"," "," "," "," "," "," ",":","C","@","@","8","@","0","@",";",";","@","i"," ",".",":",":",":",";","t","1","C","@","8","@","@","@","L","C","0","C","i","."," "," ","i","@","i","L","@","8","8","i","f",":",".","t","@","1",".","i","@","G","@","@","8","@","@","@","8","@","@","@","0","0","@","@","@","@","@","0",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," ",";",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," ",".","f","C",".","L","@","G","G","@","L"," "," ","t","1",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","C",".",";","@",":","i","@","0","."," ",";","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","f",";","i"," "," ",":","1","L","L","1","."," "," "," "," "," ","i","t","f","@","@","C","."," "," "," "," "," ",".","i","G","@","8","@","0","1",".","t","@","@","1","1","@","@","@","@","0","0","8","@","@","0","@","L",":","."," ",".","C","G","G","G","@","0","@","@","8","@","0","@","@","@","@","@","@","C","1","i","t","@","8","@","@","@","8","@","1",":",":","i","f","t","t","i","."," ",".","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",":"," ",".","i",".","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@","@","@","@",".",".","@","@","@","@",";"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","@","@","t","1","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","1","G","@","1","L","@",";"," "," "," ",".","1","@","1"," "," "," "," "," "," "," "," "," ",".","L","@",".",".","@","f","."," ",".","@","@","L","G","0","8","8","L",";","f","@","8","@","@","8","@","@","@","@","t","0","@","t",":"," "," "," ","f","@","@","0","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","8","@","@","@","0","8","@","1","i","i","L","@","."," ",".",";","L","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ",":","t","t","."," ","L","C"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","@","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",";","@","@","@","L",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","t","f","@","i",".","f",":","."," ",".","."," "," "," "," "," "," "," "," ",".","1"," "," ",".","1",".","L",";"," ","i","@","@",":"," ",":","@","@","L","8","i",":","i","1","1","1","f","C","@","8","@","t",";",":",".","."," ","."," "," "," "," "," "," ",".","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t",".",".","t","@","@","@","0","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","f",";",";","G","1"," "," ",".",".",".","i","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," "," "," ","i","C","1",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i",".",";","0","@","1","."," "," ",".","f",";"," "," "," "," "," ","."," "," "," "," "," "," "," "," ",";","i","."," "," "," "," ",".",".",".","L","@","f","C","@","@","@","@","@","@","8","@","f",":","."," "," "," "," "," "," "," "," ",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","i","0","@","@","0","@","@","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@",";",".",".",".","."," "," ","."," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","t","i","t","C",".",".","i","8","G","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","1","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","L","@","0","@","i"," "," ","i","C","."," "," "," ",":","i"," ",".",":","."," "," "," ",".","."," "," ","."," ",".",":"," "," ",";","@",".",":","@","8","C","@","@","@","@","@","C","L","."," ",".","i","1","f","8","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","8","@","0","f","L","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";"," ",":",":",":","."," ","."," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","L","@","@","8","8","0",":","1","@","8","8","0","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","G","@","@","@","@","@","@","@","@","@","@","f","G","C","t","1","1",";",":","."," ",".",";","@","L","1",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","t",";","1","C","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","f","G","@","@","f",";",";","0","L","t","@","."," ","i",";","i",";","t","@","8","."," ","1","@","@","."," "," "," "," ",".",".",".","C","L",";","1","C","C","@","@","@",":",".","."," "," ",".","f","@","0","0","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","i","G","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".",";",".","L","@","C","C","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","."," "," ",".",":","i","t","@","@","@","@","8","@","@","@","@","@","@","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","1","t","L",";",";","i","@","@","@","t","."," "," "," "," "," "," "," "," ",".",";","t","i","."," "," "," "," "," "," "," "," "," "," "," ","L","1"," ",".","i","@","@","@","@","@","@","@","@","@","8","0","8","8","0","8","0","8","8","@","0",".",";","@","0","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","@","@","C","@","0","0","0","8","0","G","f","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","@","i",".","."," "," "," "," ","1","G","C","C","0","0","f","G","@","@","i",".","."," ",".","i","1","f","@","C","."," "," "," ",".","i","@","@",";","i","@","@","8","@","@","8","@","8","f","f","t","."," "," "," "," "," "," ","."," ","i","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","i","f","i","L","G",":"," ","i","f","L","8","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," ","f","i","f","@","0","@","@","8","@","@","8","@","@","@","8","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","L","8","0","0","@","C",":","C","@","8","@","@","L","L","@","i"," ",":","C","@","@","8","0","0","@","@","."," "," "," "," ",".",";","i","f","L","@","0","t","i"," ",".","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","G","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","."," ",":","f","@","8","0","@","@","@","@","@","@","@","@","8","8","8","0","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@",";",".",".",":","."," "," ",":","0",":"," "," "," ",".","i","t",".","1","t","f","@","@","@",";"," "," "," "," "," "," ",".","t","i","i","1","1","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","L","t","@","@","@","1"," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","G","@","8","@","@","@","G","@","0","@","1",".","1","@","t","L","@","@",":",".","@","1"," "," "," "," "," "," "," "," "," "," "," "," ","f","@","@","@","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," ",";","8","@","@","8","@","@","8","@","@","@","@","8","8","8","@","@","@","8","@","@","@","@","@","8","@","f","i","t",";","i","@","0","0","0","8","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","0","0","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","G","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","8","@","@","@","@","C","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@",";"," ","t","@","0","G","f",":",".",".",".",".","."," "," ",".",":","."," "," "," "," "," "," "," "," "," ",".","f","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","t","@",".",":","f","@","1"," ","f","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","L",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","t","@","@","@","8","@","@","@","@","@","8","@","1",";",":",";","@","@","G","1","8","@","t",".","i","."," ",":",".",";","C","8","@","0",".",".","."," ",":","1","C","@","8","@","@","@","C","t","."," "," "," ","."," "," "," ",";","i",":",";","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","f","@","@","f","t","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","0","@","8","@","@",":",".",";","f","G","@","i","."," "," "," "," "," "," "," ",";","f","i","."," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","1","@","G","C","0","0","@","@","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," ",";","@","f","G","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","L","L","@",":"," ",".","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",";","@","8","0","@","@","@","@","@","@","@","@","@","@","f","1","i","C","@","@","@","8","@","8",":"," ",":","@","@","@","0","G","8","8","8","@","@","@","@","@","8","8","8","@","@","@","8","@","@","t",":","."," "," ",".","@","@","@","0","0","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",".",".",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","@",";"," ",":","G","@","8","0","8","C","0","@","t"," ",";",".",".","@","@","i","G",":"," "," "," ",":","G","@","L","C","."," ",":","L","f",":"," "," "," "," ",".",";","i","1","L","@","f","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","L","@","f","."," ",".",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i",":","C","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","i",".",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," ",".","."," "," "," "," ",".","i","f","L","8","@","@","8","@","@","@","@","G",".","1","@","@","."," "," ",".","@",".",".","@","L",":","."," ","L","@","0","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t",".","t","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," ",".",".",";","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","f","f","0","@",":"," "," ",";",":","i","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," ",".",";","t",";",".","1","1","t","f","i",".","1","1"," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","0","@","@","8","@","@","8","@",".",":","@","@",";"," "," "," ",".","C","@","@","0","@","G",".","f","C","f","@","C","."," ",".","1","@","@","f",":"," ",".",";",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",";",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","G","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","C","t","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","i",":","1",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","0","8","@","@","L","f","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","t",":","0","1","1","C","@","@","@","@","@","@",":",".","t","."," "," "," "," "," "," "," "," "," ",".",".",".","1","@","C","@","@","@","@","8","@","G","@","@","."," "," "," "," ",".","C","0","@","@",".",".","@",":"," "," "," "," ","1","@","@","1",".","i","C","@","8","@","i","t","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","t","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","G","8","0","@","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","@","8","8","@","@","@","@","@","8","@","@","t","f","@","@",":"," "," "," "," ","C","@","@","@","L",";",".","L","@","8","@","L","0","G","G","@",";"," "," "," "," "," "," ","i",":"," ",".","L","@","."," "," "," ",".","G","@","@","8",".",".","f","@","8","@","C",".",":",";",";","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","i",".",";","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","8","C",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","@","."," "," ",".","."," "," "," ",":",":"," ","L","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1",":"," "," "," "," "," "," "," ",":",";",";","L","f",":",";","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","C","@","8","@","@","@","@","@","8","@","@","@","@","@","8","0","@","@","@","G","."," "," ",";","@","@","@","8","@","@","@","G","L","L","L","@","@","8","0","@","i"," ",".","."," "," "," ",".","0","@","0","@",":"," "," ",".","G","@","8","@","1",".","@","@","0","@","L","t","L","t","f","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","C","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","C","L","."," "," "," "," "," "," "," "," "," "," "," "," ",".","1","f","L",";"," ",".",":"," ",".","t","L","1","."," ",".","t","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","f"," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","@","@","8","@","@","@","@","@","f","C","@","@","@","C","f"," ",";","@","@","f","t","@","@","@","L","f","f","f","8","L","1","C","1"," "," "," "," ",".","f","@","8","@","8","."," "," ",".","f",":","t","@","t","1","@","@","@","@","8","8","@","@","@","8","L","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f","t","0","L","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","0","0","."," ","f",".","t","f","f","@","0","@","@","@",":","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",".",".","i",":","i","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","1","G","G","@","C","C","8","0","@","@","@","8","@","G",";",";",".",".",".",".",";",";",".",".",":",":",":","t","@","@","@","."," "," "," "," ","t",";"," ","1","@","@","@",";","."," "," "," ",";","i","L","@","@","0","@","@","0","@","0","G","@","@","@","@","0","f","1","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","1","t","1",";",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@",".",".","@","8",";",":","i","i","G","@","0","@",";",":","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","t","C","@","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","@","@","@","@","C","i","i","0","@","@","@","8","@","C",";",".",".",".",";","."," ",".",".",":","L",";"," "," ","."," "," ",".",".","i","@","@","@","."," ",";","i","t","1","."," "," "," "," "," "," ",":",";","i","t","L","L","@","t",":","L","@","8","@","8","@","G","t","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","G",":","i","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","f",".","1","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","G","@","@","."," "," "," "," "," "," "," "," "," "," "," "," ","G","@","0","@","0","@","f"," ",":","@","@","@","@","@","8","@","8","@","t","i","G","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","i",":","f","@","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","i","i",";",":"," ",".","t","@","1","f","@","8","@","8","@","@","@","@","@","@","1",".","L","@","8","0","@","0",":"," "," "," "," ",".","t","i",".","."," ",".","t","@","@","t",".","0","."," "," "," ","i","@","@","L","."," ","."," "," ",".","t","@","0","@","@","@","8","G","L","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",":","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","f","@","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","@",";","."," "," "," "," "," "," "," "," "," "," ","f","@","@","C","1","@",";",".","G","@","0","0","8","8","@","@","@","8","@","0","C","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","8","0","@",";",".","0","@","8","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," ","i","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8",":"," ",".",".",".",".",".","8","@","8","@","@","1","i","@","8","@","8","@","t","i","8","@","8","@","@","0","@","@","@","@","C","f","C","t",".","f","@","@","8","@","@","C","@","t","."," ",".","G","@","@","G","@",";"," ",".",";"," ",".",";","t","@","@","8","@","@","8","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","@","f","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","8","1",":","8","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," "," "," "," "," "," ","i","@","@","0","@","@","@",".",".","@","1",":","t","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","f","1","f","@","i"," ",".","@","@","8","@","8","8","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","L","t","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","f","1","i","L","t","i","G","@","@","0","1","G","@","L","t","f","@","8","@","8","0","@","@","@","@","t","C","@","8","8","8","8","@","C","1","i","0","@","@","@","@","@","0","@",";"," ","t","@","0","0","@","i"," "," "," ",".","."," ",".",".",".",";","0","@","8","@","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@",":",".","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","i","1","t","i"," ",".","f","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",":","L","i",";","f","i","f","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","@","@","@","@","L","i","."," ",".","t","0","@","8","@","i"," ","i","@","@",":",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," ","i","@","."," "," ","t","@","0","0","@","t"," "," "," ",";",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ","t","@","i","f","@","8","@","@","@","@","@","@","@","@","@","8","@","@","@","G","L","0","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","0","@","@","@","C","f","@","@","8","@","@","f","L","@","C","1","@","@","@","@","@","@","@","@","@","L","t","i","1","t","L","@","8","@","@","@","@","@","@","8","@","@","G","@","@","@","0","L","@","0","@","G","."," "," "," ",".","@","@",":",":",".",":","@","8","@","8","@","G",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","C","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","1","L","L","@","C","@","@","@","G",";",".",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","L","."," "," "," ",".",".","."," "," ",";","i",".","i","@","@","@","8","0","@","@","0",";","C","@","8","f","0",";"," ","i","@","@","1","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," ","L","@","G","t",":"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","8","@","@","@","@","@","@","8","0","@","@",";",";","i","i","C","i","G","L","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","0","@","L","G","f","f","@","8","@","@","@","@","G","i","t","@","@","@","@","@","@","@","@","@","@","@","@","@","f","L","C","G","@","8","@","@","@","@","C","C","8","G","8","@","t",".","i","f","f","i"," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","8","@","G","C","i"," "," "," "," "," "," "," "," "," "," "," "," "," ",":","i","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","0","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","1","1","C","0","@","8","@","@","8","0","@","t",";","@","@","@","@","@","@","@","@","L",":"," "," "," "," "," "," ",".",".",".","."," "," "," "," "," ",":","t","L","t",";","f","@","@","8","@","@","8","@","@","@","@","@","@","@","8","@","@","@","@","8","@","."," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," ","f","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","L","@","8","@","@","8","@","8","@","0","C",".",".","L","@","@","@","1",".",".",";","@","@","0","8","@","@","@","@","@","t","L","C","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","C","i","G","0","G","@","8","@","8","f","t","L","G","t","i","t","@","8","@","@","@","@","@","0","f","t","L","1","f","0","t","@","@","@","@","@","@","@",":",":","@","1",";","i",".","."," "," "," "," "," "," "," "," "," "," "," ",".","L","@","@","8","@","@","@","8","8","@","@","G","1",":","."," "," "," "," "," "," "," "," "," ","1","L","t","1","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","1","f","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",";",";","G","8","f","0","@","8","@","@","@","@","8","@","@","8","@","@","@","8","@","@","@","0","8","@","C","1","."," "," "," "," ","f","@",";"," "," ",":","@","@","G","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f","i","i",";"," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@",";"," "," "," "," "," "," "," ",".","@","0","@","1",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","f","@","0","@","f"," "," ",":","1",":","."," "," ",".","."," "," "," ",";","L","C","@","0","8","@","@","@","@","@","@","G","C","@","8","@","@","@","0","L","@","@","@","@","@","@","@","@","@","@","8","@","C","f","@","0","@","f","1","@","0","0","@","@","L","L","@","f","1","0","@","8","@","@","8","@","@","@","L","i","t","t","8","@",":","i","8","@","0","8","@","@","G","@","@",".",":","@","@","f","."," "," "," "," "," "," ",".","."," "," ",".","t","@","@","@","0","@","f","i",".",":","t","L","@","G","i","."," "," "," "," "," "," "," "," ",".","i","L","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ",".","L","@","C","G","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","f","1","C","C","@","@","@","8","0","@","G","."," "," ","1","C","."," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","t","t","t","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","."," "," ",".","t","C",":","."," "," "," "," "," "," ",".","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","@","."," ",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","1","8","@","@","8","G","0","G","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","G","@","@","8","@",":",".","L","1",".",".","i","0","G","@","8","1","G","@","@","0","@",";"," ","f","L","G","@","f","C","@","@","@","0","t","1","@","f",".","1","."," ",":","@","8","@","G","@",":"," "," "," "," ","G","@","@","@","@","@","@","@",":","1","@","G","@","1"," "," ","i","@","."," "," "," "," "," "," "," "," "," "," "," ",".","t","L","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","C","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","0","@","@","@","C","G","@","@","@","@","@","@","@","@","@","@","@","8","@","0",";",";","f","@","@","@","@","@","@","1"," ",":",":",".","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","f",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","8","8","@","C","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@",";"," ",".",";","L","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","G","C","0","1","0","C","C","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","G","1",":","."," "," ",";",";","L","@","8","i","1","8","@","@","0","C","8","@","L","1","1","f","1","@","1",":","8","t","f","G","G","f","f","@","."," "," "," "," "," ",".","@","1"," "," ","i","8",":"," "," "," "," "," "," "," "," ",".","0","@","@","@","@","8","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","@","8","@","@","@","@","@","@","@","@","8","@","8","f","t",":",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1",";",".",".",".",".",".",";","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","8","@","8","@","L","f","@","8","@","8","@","@","@","@","@","@","@","@","8","@","t","t","8","@","f"," "," "," ","."," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","8","@","f",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","G",";","."," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","i","L","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","L","8","C","@","@","@","0","L","0","L","L","@","@","@","@","@","@","@","@","C","8","@","@","@","@","C","f","@","@","f","t","G","C","."," "," "," "," "," "," "," "," "," "," ",".","i",":"," "," "," "," ",".",":","."," "," "," "," "," ",".","C","@","@",";","1","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";",";","1","@","8","@","@","@","@","@","@","8","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","t","i","G","@","0","@","8","8","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","C","@","G","L","G","G","@","@","f","."," "," ",".","f","@","0","0","@","8","8","@","G","0","8",".",".",".","1","."," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","8","@","L",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","f","t","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1",":","1","C","@","@","@","G","C","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","0","@","8","@","@","@","@","@","@","@","@","L","G","@","@","@","@","@","@","i","t","G","L","1","@","L","C","@","L","0","@","L","L","."," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","1",";"," ","1","L",":",".",".","."," "," "," ",".","i","@","@","1"," ",":","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","0","8","@","8","0","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","8","@","L","G","@","8","8","@","i"," "," ",".","f","@","@","t","i","@","G","C","@","8","@","@","G",";",".",".",";","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","8","@","."," "," "," "," "," ",";","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","i","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","i",":","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","t",".","1","C","@","C","L","L","1","@","@","t","0","@","i","f","@","C","C","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","1"," "," "," "," "," ",".","1","1"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","8","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",";","t","@","@","L","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","C","C","@","8","@","@","G","@","@","0","@","f","."," ",":","@","@","8","@","@","@","@","C","G","@","8","@","C","t",";","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",";","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","t","i","t","@","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t","i","t",";","@","@","@","@","@","L","1","0","@","@","@","@","G","f","@","0","f","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","t",":","."," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","8","8","@","8","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","C","f","C","@","0","@","t","t","@","0","@","i"," "," ",".","L","@","0","@","C","1",";","t","i","f","f","C","0","C","@","@","t","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" ",".","."," "," "," ",".","."," ","."," "," ","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",";","i","G","@","8","@","@","@","8","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","8","@","@","@","C","@","1",":","@","f","C","@","0","1","L","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","8","8","C",".",".","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","C","f","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","C","@","8","0","@","C","G","@","@","@","0","@",":"," "," "," "," ","t","@","0","t","f","f","f","C","L","i","1","0","C","@","8","@","1"," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," ","."," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t",":","i","L","f","@","8","@","8","0","@","C","1","f","L","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1",";","8","@","8","C",".",".","@","C","f","@","@","@","8","@","0","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","@","@","0","@","1"," "," "," "," "," ","L","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","0","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","f","f","@","@","@","@","@","@","@","@","@","@","@",":"," "," "," "," ",";","@","0","f","f","t","f","@","1",";","@","1"," "," ",";","@","@","f",";","L","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","@","@","@","@","@","0","@",";"," ",":",";","."," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","C","t","L","@","@","C","8","@","8","@","@","@","@","@","@","C","f","8","@","@","@","@","@","8","L","t","f","@","@","G","C","@","G","i","i","@","@","f","i","i","t","G","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","0",":"," "," ",":","G","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","t","C","@","@","8","@","G","C","@","@","8","G","C","C","@","i"," ",".","."," ",".","1","@","L",";",".",".",".",".","i","0","C",".","i","@","@","@","8","@","L","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","t","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","f","f","0","@","8","@","@","@","@","@","@","8","i",":",".","@","@","1",".",".","f",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","L",".",".","t","G","L","@","@","G","L","G","G","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","@","8","@","@","@","f","L","G","f","0","G","8","@","0","C","f","t","@","@","@","@","f","C","@","@","8","C","@","@","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","8","C","@","@","@","8","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","8","@","@","8","@","t","0","@","0","0","@","8","C","i","f",";"," "," "," "," "," "," ",".","i","f","i",":","f","@","@","8","@","8","8","@","@","@","8","@","L","G","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f",";","t","f","@","@","@","@","8","@","0",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@",":","i","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," ",".","i","8","@","f","C","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G","C","@","L","1","@","L",";","L","L","f","@","f","C","G","0","G","G","8","@","@","@","@","@","8","@","@","@","0","@","t","."," "," "," "," "," "," "," "," ",".",";"," "," "," ","L","@","@","C","G","@","@","@","@","@","@","8","8","@","@","@","8","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," ",";","8","@","@","C","."," ",";","@",":",".","t","f","@","1","."," "," "," "," ",".",";",";","C","8","0","@",";","i","@","0","@","f","f","@","8","@","@","8","@","C",".","i","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","f","L","C","@","@","@","8","@","@",".",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","@","C","C","i","C","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ",":","1","t","@","8","@","@","@","@","0","C","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","C","G","@","@","L","f","f","t","C","@","f","i","L","0","L","t","1","8","0","8","@","8","@","@","@","0","@","@",":",".","."," "," "," "," "," "," "," ",":","G","@","@","L","0","@","8","@","@","8","@","@","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",".","L","G",".","."," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","L","@","@","L","8","@","."," ",".","."," "," "," ","."," "," ","C","@","8","@","L","C","@","8","@","@","8","@","@","@","@","@","8","@","@","8","G","L","f","t","f","@","8","@","@","@","@","8","@","@","@","@","@","@","f","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","0","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","0","t","@","@","@","8","@","L",".",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","G","@","8","@","@",";"," ",";","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","8","@","@","@","@","@","@","@","@","C","f","@","@","@","@","@","@","G","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","1"," ","t",":",":","@","0","8","i","1","0","C","0","0","L","G","@","8","@","@","@","8","8","8","@",";"," "," ",".","f","@","@","@","8","@","@","@","f","t","@","@","@","8","@","8","@","@","0","0","8","8","8","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L",":"," ",".","f","@","0","@","@",";"," ","."," "," "," ","t","@","0","G","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","0","f","t","t","t","t","L","@","@","8","@","0","t","t","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G",";","t","@","@","8","@","L","t","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","t","@","@","@","@",".",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," ","t","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",".",":","G","G","0","@","@","@","@","@","@","G","@","G","G","@","@","@","@","8","@","L","0","@","@","@","8","@","@","@","@","@","@","@","@","@","@","t",":","0",".",".","@","@","8","@","C","i","L","8","G","@","G","G","@","1","f","@","8","8","@","8","."," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@",":","i","@","8","@","C","f","1","t","0","@","@","0","@","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","1","G","L","i","."," "," "," "," "," "," "," "," "," "," "," ",".","G","@",";",".",";","i","@","1"," ","."," "," "," "," "," "," ","1","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","1","f","@","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","8","8","@","@","0","f","f",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","C","@","@","0","@","i"," ",".","8","G","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@",":",";","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","8","@","L","f","@","8","@","@","@","@","8","@","t","1","f","t",":","C","C","f","0","@","G","0","8","0","@","@","L","G","@","8","@","@","0","@","i",".","."," ",".","@","@","8","@","@","@","@","@","@","@","@","8","@","L","f","@","@","0","t","8","0","8","@","@","t",":","1","8","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@",";",".","1","t","@","C","."," "," "," "," "," "," "," "," "," "," "," ",";","t",".",":",":"," "," "," ","."," "," ",":","i",".",".","1","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","1","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","t","0","@","@","@","@","@","@","@","8","@","L","L","@","8","@","f","f","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","."," ","t","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",";",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","@","8","@","@","@","@","@","@","8","@","i","t","@",":","f","@","G","f","L","0","t","t","f","@","L","G","0","@","@","@","@","@","8","@","L"," "," ",";","@","8","@","@","@","8","8","@","@","@","@","@","@","@","@","@","t","t","1","L","@","8","@","@","@","@","8","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","C","."," "," "," ","f","@","@","i"," "," "," "," "," "," "," "," "," "," ",".","C","@","t","t","@","G",".","i","@","@","0","0","@","@","8","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","@","@","L","t","@","8","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," ","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i",";"," ","1","@","8","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","8","G","f","L","@","1",";","C","@","f","t","C","0","8","@","@","@","@","@","@","@","0","@","8","L","@","f","G","@","@","L",";","L","@","@","@","C","f","L","0","8","0","0","8","@","8","@","@","8","0","@","@","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","@","."," ","i","@","8","8","@","0","t","1","."," "," ",".","L","@","@","@","@","@","@","8","8","8","8","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","0","@","1",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," ",".","@","i"," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","0","1","0","@","8","@","@","@","@","@","8","@","C","L","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","8","8","0","@","t",".","L","@","@","@","G","@","0",":","t","@","C","@","@","@","@","@","@","@","@","8","@","@","f","L","@","8","8","@","@","@","@","G","G","@","@","@","@","t","f","L","f","G","@","@","@","@","L","."," ",".","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","L",";","."," "," ",":","L","@","G","0","8","0","8",";"," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","C","@","@","@","@","@","@","@","@","@","@","@","t","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","."," "," "," ",".",":"," ","."," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","C","f","G","@","@","0","L","f","0","@","@","@","@","@","8","@","G","f","@","@","@","0","@","@","@","t","."," ",":","0","@","8","@","@","@","@","@","1","f","@","8","@","@","8","0","@","8","@","@","@","8","@","@","C",";"," "," "," "," ","i","t","."," "," "," "," "," ","1","G","i","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","C","f","C","t","1","i",".",";","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","0","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1","i","@","8","L","@","8","8","@","@","8","@","t","i","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@",":"," "," "," "," "," ",":","t"," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","8","@","t","."," "," ",".","."," "," "," ","i","@","0","@","@","@","@","@","@","8","@","@","@","8","L","C","@","G","C","@","@",";",";","L","@","@","@","@","."," "," "," "," "," "," ",".","L","@","0","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","."," ",";","f","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","f","i","f","@","@","@","@","@","@","@","@","@","@","@","@","0","L","L","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","@","8","8","@","C",";","t","@","@","L","C","@","@","@","8","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G",".","t","@","G","@","i"," "," "," "," "," "," "," ","i","@","f",".",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","@","@","@","@","@","@","@","@","@","C","8","t","L","@","@","1",".","i",";",":","@","@","@","@","@","@","@","8","@","8","C","G","@","8","@","@","@","8","1","t","@","8","0","8","@","C","."," "," "," "," "," "," ",".",";",":","i",";","i","L","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","t","i",":","C","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","8","0","8","@","0","8","@","@","@","@","C","i","C","@","8","@","@","@","@","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","t","0","@","@","@","@","@","@","L","0","@","8","0","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","G","@","t"," "," "," "," "," "," "," ",".","i","@","G","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","L","8","@","@","@","@","@","@","@","@","@","@","8","8","8","0","@","@",";"," "," ",".","."," "," "," ",".","t","@","G","@","@","@","8","@","@","@","0","G","@","@","@","8","@","@","8","0","8","@",";",":",";"," "," ","t","."," "," "," "," ",".","."," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","8","@","@","@","@","@","@","@","@","@","@","8","@","G","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","f","t","L","@","@","i",":","1","@","@","8","@","@","@","@","@","@","@","@","0","@","@","1",".",".",".","i","@","0","@","@","@","@","@","8","@","C","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@",":",":","C",":"," "," "," "," "," "," "," ",".","f",";",".",";","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","L","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i"," ",";","@","0","@",":"," ",".",";",".","f","@","0","@","@","8","G","G","C","@","@","@","@","0","@","@","@","C","i","i",";","f","@","8","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ",":","i","t","1","."," "," "," ","1","@","@","@","@","@","@","@","@","@","@","@","@","1","."," "," "," "," ",".",":","@","8","@","@","@","8","@","f","."," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","0","@","L"," ",".","8","@","@","t","f",".","i","@","@","t",":",".",".",".","0","@","8","8","@","@","@","@","L",":","."," "," ","i","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","G","@","@","@","@","@","@","@","@","@","@","8","@","8","8","@","@",".",".","L","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","0","."," "," "," ",".","i","@","1",":",".",";","@","@","f","C","@","8","@","@","@","@","0","@",";"," "," "," "," ",".","1","@","@","@","@","@","@","@","8","@","L",":",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","f","0","@","8","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," ",":","@","8","@","@","C","@","1","i",".","1","@","@","@","@","@","@","@","@","8","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","8","@","@","@","@","@","@","1",".",":","C","@","@",";"," "," ",".","t","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," ",".","@","@","@","@","@","@","@","@","@","8","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",".",":","8","8","8","@","0","@","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","8","@","i",".",".","t","@","@","@","@","@","8","@","@","8","@","@","@","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","@","@","@","@","@","8","G","@","@","@","8","@","0","."," "," ",":","."," "," ",".",".",".","f","@","0","@",":"," "," "," ",".","1","@","@","8","@","@","@","@","@","@","@","8","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","8","@","@","@","0","@","i"," "," "," "," "," "," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",";"," "," ","i","@","t",":",":","1","C","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","L"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","8","0","8","@","@","@","@","@","8","@","t",":",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@","@","@","8","@","8","8","@","@","8","8","@","L"," "," "," "," "," "," "," ","1",";"," "," ",".","f","@","@","L","i","."," "," "," ",".","G","@","8","@","8","8","@","@","@","0","@","f","."," "," "," ",".",";","f","C","C","1",";","."," "," "," "," "," ",".","f","@","8","@","@","@","@","8","0","@",";"," "," "," "," ",":","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","i","f","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";",".","."," "," ",":","."," "," "," ",".","0","@","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," ",".","L","1"," "," "," "," "," "," ","i","@","@","@","1",".",".","G","@","0","@","L","@","i",".",".","i","t",".",".","1","@","@","@","0","8","8","8","0","0","@","@","@","@","@","@","@","8","@","@","@","@","t","C","@","8","0","@","t","."," "," ",".","G","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8",";","."," ",".",":"," ","."," "," "," ",".",".","i","@","@","C","."," "," "," "," "," "," "," "," "," ",":",".",";","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","t","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","i","0","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","0","@","t"," "," "," ",".","."," "," "," "," "," "," ","L","L"," "," "," "," "," "," "," "," ","i","1"," "," "," ",".","f","@","@","L","."," "," "," ",":","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","@","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","C","@","8","1","."," "," "," "," "," ",":","@","8","@","L"," "," "," "," "," ","."," "," "," ","1","@","0","0","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","8","8","0","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i",";"," "," "," "," ",".",";","f","t","L","1","."," ",".","t","@","0","@","@","@","0","@","@","@","@","8","8","8","@","@","0","f","C","@","@","@","@","@","@","@","@","@","8","@","C"," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," ","t","@","@","@","."," "," "," ",".","f","@","@","@","@","C","C","@","i",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","t","i","G","@","@","@","@","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ",".","t","@","i"," "," "," "," "," "," "," "," ",";","@",";"," ",".","."," "," ","i","@","@","@","@","8","@","@","0","@","@","@","G","0","@","C","G","@","8","@","@","@","@","@","@","@","@","@","@","8","@","@","1",".",".",".",":","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","8","@","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," ","1","G",":",":","1",":","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","."," "," "," "," "," "," ",":","L","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," ",".","."," ",".","."," "," ",".",".","."," "," ","i","@","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","8","@","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","L","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","C","L","@",";"," "," "," "," "," "," "," ",".","."," "," "," ",".","f","@","i",";","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","C",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","G","@","C","f","@","@","8","8","8","8","@","@","@","@","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," "," "," "," "," "," "," "," ",".","f",";"," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","t","@","8","@","@","@","@","@","@","8","C","f","@","1"," "," "," "," "," "," "," "," "," "," "," "," ",".","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","@","8","8","8","8","0","8","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","8","@","C","f","C","G","L","1","1","."," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","L"," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","0","0","0","@","8","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","G","1","."," "," "," "," "," ",".","C","@","@","G",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","0","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","8","G","@","0","G","@","@","@","@","f",":"," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@",":"," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","8","G",";",".",".",":",":","1","@","G","0","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","t","i","."," "," ",".","@","@","8","8","0","@","@","@","@","@","f","1",";","t","@","@","L","t","t","G","@","C","0","@","8","@","@","@","@","@","@","@","@","@","@","@","@","C","t","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","C","L","@","@","@","f","1","0","@","G","C","@","8","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","C",";"," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","f","."," "," "," "," ",".",";",":","."," "," "," "," "," "," "," "," ","i","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","8","@","@","C","@","@","8","@","@","@","@","@","@","8","8","8","0","0","0","8","@","8","f","8","@","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";"," "," ",".","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","C","@","@","@","@","C","0","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@",";"," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","i","L","t","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," ",".","C","@","@","0","8","8","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","@","8","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@",";"," "," ",";","@","0","@","@","@","@","@","@","@","@","@","8","@","L","L","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","G","."," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","G","."," "," "," ",".","i","L","G",";",".","t","@","0","0","8","0","8","@","8","8","0","0","0","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","C","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","0","."," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f",":","i"," "," "," "," "," ",";","i"," ",":",";","i","t","L","8","@","0","L","L","L","t","1","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," ",":","@","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","."," "," ",".","t","@","0","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," ",".",".",".","L","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," "," ",".","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","t","1",";",":"," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","."," "," "," ",".","8","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","8","@","8","@","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","L","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","C","1","f","f","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@","@","i",":",".","."," "," "," "," "," "," ",":",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," "," ",".","."," ","."," "," "," ",":","i","L","1",".","."," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",":"," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","f",";","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",";",".",".","."," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","G","i",".",".","1","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","0","8","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," ","i","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","L","."," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f"," "," "," ","i","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@",";"," "," "," "," "," "," "," "," ",";","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," ",":","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","@","@","0","8","@","@","@","@","@","8","@",":"," "," "," "," "," "," ",";","@","8","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," ",":","1","@","8","@","8","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," ",";","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","C","@","@","0","0","@","@","8","@","@","C","8","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","i","."," "," ",".",".",".",":",":"," "," "," "," "," ",".",";","."," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","."," "," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","L","@","@","@","@","L","G","@","0","@","@","@","8","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","."," "," "," "," ","f","@","0","@","@","@","@","@","@","@","8","0","0","@","i",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","8","@","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","8","i"," ",".","G","@","8","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","f","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","0","0","0","8","@","@","@","@","@","@","8","t","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L",":"," "," "," ","1","@","0","@","8","8","8","0","@","@","@","f",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","8","8","@","@","@","@","8","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","i","f","@","@","@","8","8","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","8","8","@","L",".",".","8","@","@","@","@","G",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ","t","@","C","f","@","@","0","@","0","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",":",":",";","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",".",".",".","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","L","@","8","@","@","0","@","t",":",".","."," "," "," "," "," "," "," "," "," "," "," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","@","1"," ",".",":","L","@","C","L","@","8","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",":","."," ",".",":","t",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",";","@","."," "," "," "," "," "," "," "," "," "," "," ",".",":","1","C","@",";",".","t","L","f",":"," "," "," "," "," ",".",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","G",";"," "," "," "," ",".",":",":",";","t","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","8","8","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",".","C","."," "," "," "," ",":","C","@","@","0","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",":","L",".",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","L","@",";"," "," "," "," ",".","."," "," "," ",".","@","@","G","@","L",".","f","@","0","0","@","@","@","@","G","8","@","@","f","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","8","@","t","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","0","0","0","G","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","0",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","."," "," "," "," "," "," "," ","f","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," ",";","i",".",".",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","1","1","@","@","i"," ",".","@",";","t","@","0","@","@","8","@","@","@","@","@","@","@","@","@","@","8","8","@","@","@","8","L","f","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","@","@","8","@","G","i","f","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G",";"," "," ","L","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," ",".","C","@",";"," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," ",".","i","i","1","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," ",":","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","f","t","@","8","0","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","L","@","8","8","@","@","@","@","L","G","@","8","@","@","0","@","1","f","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ",":","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",".","."," ",".","8","C","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","@","@","@","@","@","@","G","L","C","C","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","0","@","@","@","@","@","0","@","@","@","8",";","."," "," "," "," "," ","t","@","0","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," ",".","@","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","f",":","."," ",".","."," "," "," "," ",".",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","8","@","0","f","G","8","0","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","i",";",".",".","L","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","1"," "," "," ","t","@","G","@","."," "," "," "," "," "," ","."," "," "," "," "," "," "," ",";","@","@","8","@","G",":"," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","8","@","8","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","@","@","8","."," ","i","@","@",":"," "," ",".","."," "," "," "," "," "," "," "," ",".","L","@","0","@","8","@",";"," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","i","t","G",";","t","C","8","@","@","@","@","@","@","@","@","@","@","0","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","1",".",".","C",";"," "," "," "," "," "," ",".",";",".",":","@","8","8","@","@","@","8","@","G","."," "," "," "," "," "," "," "," "," ","."," "," "," ",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," ","i","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","@","1","i","i","L","0","@","@","@","@","@","@","@","@","8","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","0","f",":"," "," "," "," "," "," "," ","i","@","8","@","@","@","@","@","@","@","0","@","i"," "," ",".","i","i","i","i",";","t",";"," "," "," "," ","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," ","."," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","1","C","@","@","i","t","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","0","@","C","."," ",".","L","@","0","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",".","@","@","0","@","1",".","."," "," "," "," "," ",".","t","@","@","@","@","@","@","@","0","@","t"," "," "," ","1",";"," ",";","1","1","."," "," "," "," ",".","."," "," "," ",".",";","C","@","C",";"," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","f","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","f","@","0","8","0","@","@",";",":","i",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","0","@","@","@","@","@","@","@","@","@","L","0","@","@","@","@","@","@","@","@","@","@","@","@","@","G","C","@","@","L",";",";","t","@","@","@","@","@","@","8","@","0",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","8","@","@","i","i",";"," ","."," "," ",".","0","@","@","@","@","@","0","@","f"," "," "," ",".","8","@","@",";"," "," "," ",".",".","."," "," "," "," "," ","."," "," ",".",";",";",";"," "," ",".","t","@","@","C","i",":","."," "," "," "," "," "," "," ",".","."," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","@","@","@","8","0","@","@","@","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","i","@","@","8","8","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","0","@","C","."," ","."," "," "," "," "," ",".",".",".",".","L","L",":"," "," "," ",".","G","i",":","@",";"," "," "," "," ",".","f",":",".",":",".",";",";"," "," ","i","1","i","@","G","@","0","8","8","8","0","8","@","@","@","L",";","."," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","G","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","t","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C",".","L","@","8","@","@","@","@","@","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","0",";"," ",":",";",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","t","@","8","@","@","@","@","8","8","0","@","@",":"," "," "," "," "," ","."," ","1","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","0","G","@","@","@","@","@","0","C","@","@","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",";",".","@","@","8","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","C","t","."," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," ",":","@","8","8","@","@","@","@","8","8","0","@","@","L","."," ",".",":",":"," "," "," "," "," ",":","t",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","@","@","8","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","t",":","@","8","@","8","8","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","1","1","L","@","@","@","C","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," ","."," ",".",";"," "," "," "," "," "," "," "," ",".","t","L","@","@","0","C","0","@","i",":","t","@","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","f","t","8","@","@","@","8","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","t","@","@","@","@","8","@","L","L","@","@","@","@","0","@","f"," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".",".","i",";",":",".",".",":",":",".","."," ",".",";","t","1",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","1","L",":"," "," "," "," ","i","@","@","i"," "," "," "," "," ","."," "," "," "," "," "," "," "," "," ",".",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","@","@","8","@","8","@","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t","L","@","8","8","@","L",":","@","@","8","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," ","."," ",".","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".",".","i",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","0","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","8","@","t",".","f","@","0","@","@","0","@","C","."," "," "," ","."," "," "," "," "," "," "," ",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",".",":","i","."," "," "," ","."," "," "," "," "," "," "," ",";","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@",":","t","@","0","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," ",".","i","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","@","@","0","@","@","@","@",";"," "," "," "," "," "," ",".","C","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","8","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","0","0","@","@","@","8","8","@","@","@","0","@","L","."," "," "," "," "," "," "," "," ",":","L","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","L","."," ",".","8","@","0","@","@","@","8","@","G",".","."," "," "," "," "," "," ",";","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","0","@","1",".","L","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","0","1","C","@","@","G","0","@","@","@","@","@","@","0","8","@","@","i"," "," "," "," "," "," ",";","L","8","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L","@","0","8","L","@","@","G","@","@","@","@","@","8","@","@",":"," "," "," "," "," "," "," ","1","@","0","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","L","f","@","@","8","@","@","8","@","@","@","@","0","@","G","."," "," "," "," "," "," "," "," ",";","@","G","@","8","@","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":",":","i","G","0","@","@","@","@","@","@","8","@","@","@","@","@","@","@","8","8","@","L",".",".","."," ",".","@","@","8","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","."," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","t","t","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","f","G","@","8","@","C","f","@","8","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1","."," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","t","1","@","@","8","@","8","0","@",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," ",":","1"," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","t","L","f","L","f","C","L","L","L","8","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","8","@","0","."," "," "," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","0","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","1","@","G","G","@","@","@","@","8","0","8","8","8","0","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","8","@","@","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":",":","1","G","@","@","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","f","C","@","t","f","@","C","L","L","@","0","@","@","i",":",";",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","f","."," "," "," "," "," "," "," "," "," "," ","1","@","8","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","@","@","8","0","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","i"," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","8","1","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","C","@","8","@","@","@","@","@","@","@","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","@","@","8","@","8",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","C","0","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","G","@","f",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","0","@","@","@","@","@","@","@","@","@","8","@","@","@","@","@","@","@","0","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","G","@","8","@","@","@","@","@","@","@","@","@","0","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","8","G","@","@","8","@","@","8","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","8","@","@","@","@","@","@","@","@","@","L","C","@","8","@","@","@","@","@","@","@","@","@","@","8","@","1",";","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","t","f","@","@","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","L","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","8","@","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","8","0","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G","t","G","@","G","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","0","0","@","@","@","@","@","@","@","0","C",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","0","0","0","0","@","@","@","@","@","8","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","C","L","@","@","@","@","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i",";",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","8","@","@","@","@","@","@","@","@","@","@","@","t","t","f","i","."," "," "," "," ",";","f","@","@","8","@","@","8","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","G",";",".",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","t","@","8","@","0","@","@","@","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","G","@","8","f","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","@","8","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","@","@","t",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@",":",".","G","@","8","@","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","8","@","@","@","@","@","@","@","@","@","@","@","@","8","8","8","8","8","G","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ",":","i",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","@","0","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","0","@","@","@","@","@","@","@","@","@","@","@","@","C","C","@","@","C","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";",".",".","t","@","0","@","@","@","@","@","@","@","@","@","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","@","@","8","@","@","@","@","@","@","@","@","8","0","@","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","@","@","@","@","@","@","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","1","@","G","@","@","@","@","@","@","@","@","@","1","f","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","0","@","0","@","@","8","@","8","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","L","L","@","8","@","@","@","@","@","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","@","G",":","i","@","@","1",".",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t",":",";","@","0","@","@","@","@","@","@","@","@","i",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","t","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," ","1","@","0","@","@","@","8","@","C","f","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","@","C"," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",".","i","L","@","8","@","@","@","@","@","@","i"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","."," ",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";","@","0","@","8","f",":"," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","1","@","@","@","@","8","L","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","0","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","i","0","@","L","f","0","@","8","8","@","f",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","@","@","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","L",";",".",".","C","G","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","t","G","@","0","8","@","@","@","8","0","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","i","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","t","@","f","t","G","L","@","0","@","t"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",";",";"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","1"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",":","i","L","@","t","L","@","0","@","L","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","f","@","@","@","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",";","C","0","@","@","8","@",":"," "," "," "," "," "," "," "," "," "," "," ",":",":","i","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","C","@","@","8","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","i","i","t","@","0","@","0","."," "," "," "," "," "," "," "," "," ",".","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","@","0","0","@","f"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","i","i","t",";","t","@","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":","0","@","C","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","1","i","t",";",":","L","@","1","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".",":"," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ",".","i","f","1","t","1","f","t",":"," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","."," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "], -[" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "] -]; - - -var M_PI = 3.14159265; - -function createSphere(radius, center, voxelSize, rotation, solid, debug) { - var xc = center.x; - var yc = center.y; - var zc = center.z; - - var red = 255; - var green = 0; - var blue = 0; - - var thisVoxelSize = voxelSize; - var thisRadius = 0.0; - if (!solid) { - thisRadius = radius; // just the outer surface - } - - // If you also iterate form the interior of the sphere to the radius, making - // larger and larger spheres you'd end up with a solid sphere. And lots of voxels! - var lastLayer = false; - while (!lastLayer) { - lastLayer = (thisRadius + (voxelSize * 2.0) >= radius); - - // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize - // small enough to not skip any voxels we can calculate theta from our desired arc length - // lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R - // lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r - var angleDelta = (thisVoxelSize / thisRadius); - - if (debug) { - var percentComplete = 100 * (thisRadius/radius); - print("percentComplete="+ percentComplete+"\n"); - } - - - // what I really want to do is iterate over slices from top to bottom, calculating - // the radius and center for each circle for each slice at Y height - var yStart = center.y + radius; - var yEnd = center.y - radius; - var radiusSquared = radius * radius; - - var mapHeight = map.length; - var mapWidth = map[0].length; - var yMapStart = 0; - var yMapEnd = mapHeight; - var yRatio; - - var xMapStart = 0; - var xMapEnd = mapWidth; - - var phiStart = rotation; - var phiEnd = rotation + M_PI * 2; - - for (var yAt=yStart; yAt >=yEnd; yAt -= voxelSize) { - // at y, what is the radius of the circle at that slice? - // we know there's a triangle with one edge from the center up the pole to height yAt, - // and a hypotenuse of radius (from original sphere) and a right angle connecting these - // lines to make the third edge. The length of that edge solves... - // a^2+b^2=c^2 - // poleLength^2 + unknown^2 = radius^2 - // unknown^2 = radius^2 - poleLength^2 - // unknown^2 = radius^2 - (radius - (yAt - center.y))^2 - // unknown = sqrt(radius^2 - (distanceFromCenter^2)) - var distanceFromCenter; - if (yAt > center.y) { - distanceFromCenter = yAt - center.y; - } else { - distanceFromCenter = center.y - yAt; - } - var sliceRadius = Math.sqrt(radiusSquared - (distanceFromCenter * distanceFromCenter)); - - yRatio = (yAt - yStart)/(yEnd - yStart); - - for (var phi=phiStart; phi <= phiEnd; phi += angleDelta) { - - var y = yAt; - var x = xc + sliceRadius * Math.sin(phi); - var z = zc + sliceRadius * Math.cos(phi); - - var phiRatio = (phi-phiStart) / (phiEnd - phiStart); - - mapY = Math.floor(yMapStart + yRatio * (yMapEnd-yMapStart)); - mapX = Math.floor(xMapStart + phiRatio * (xMapEnd-xMapStart)); - - if (mapY < 0) { - mapY = 0; - } else if (mapY >= mapHeight) { - mapY = mapHeight-1; - } - - if (mapX < 0) { - mapX = 0; - } else if (mapX >= mapWidth) { - mapX = mapWidth - 1; - } - - var mapCode = map[mapY][mapX]; - if (mapCode == " ") { - red = 0; - green = 0; - blue = 255; - } else { - switch (mapCode) { - case "@": - red = 0; - green = 128; - blue = 0; - break; - default: - red = 10; - green = 120; - blue = 0; - break; - } - } - - Voxels.queueDestructiveVoxelAdd(x,y,z, voxelSize, red, green, blue); - //print("Voxels.packetsToSendCount()="+ Voxels.packetsToSendCount() + "\n"); - } - } - - thisRadius += thisVoxelSize; - } -} - -var rotation = 0; - -var currentIteration = 0; -var NUM_ITERATIONS_BEFORE_SEND = 15; - -function waitTillDrawn() { - if (currentIteration++ % NUM_ITERATIONS_BEFORE_SEND === 0) { - - var radius = 12.5 / TREE_SCALE; - //var center = { x: (radius*2), y: (radius*2), z: (radius*2) }; - var center = { - x: 1315/TREE_SCALE, - y: 465/TREE_SCALE, - z: 1885/TREE_SCALE }; - - var voxelSize = 0.5 / TREE_SCALE; - var solid = false; - var debug = false; - var rotationDelta = M_PI/30; - rotation += rotationDelta; - if (rotation >= M_PI * 2) { - rotation = 0; - } - createSphere(radius, center, voxelSize, rotation, solid, debug); - - print("Voxel Stats: " + Voxels.getLifetimeInSeconds() + " seconds," + - " Queued packets:" + Voxels.getLifetimePacketsQueued() + "," + - " PPS:" + Voxels.getLifetimePPSQueued() + "," + - " BPS:" + Voxels.getLifetimeBPSQueued() + "," + - " Sent packets:" + Voxels.getLifetimePacketsSent() + "," + - " PPS:" + Voxels.getLifetimePPS() + "," + - " BPS:" + Voxels.getLifetimeBPS() + - "\n"); - - /** - if (Voxels.packetsToSendCount() === 0) { - print("DONE!!\n"); - - //print("Voxels.packetsToSendCount()="+ Voxels.packetsToSendCount() + "\n"); - - - Agent.stop(); - } - **/ - } -} - - -// register the call back so it fires before each data send -Voxels.setPacketsPerSecond(40000); -Agent.willSendVisualDataCallback.connect(waitTillDrawn); From eb66064a182f3b7a0b08fed076202b45654c39b9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 20:04:26 -0800 Subject: [PATCH 09/17] tweak --- libraries/octree-server/src/OctreeServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 652b84c527..9a65941e35 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -213,7 +213,7 @@ int OctreeServer::civetwebRequestHandler(struct mg_connection* connection) { const int MAX_TIME_LENGTH = 128; char buffer[MAX_TIME_LENGTH]; strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal); - mg_printf(connection, "%s file Loaded At: %s", theServer->getMyServerName(), buffer); + mg_printf(connection, "%s File Loaded At: %s", theServer->getMyServerName(), buffer); // Convert now to tm struct for UTC tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted()); From 3ca7dc719629a42d0e2c99c08256e8f839431ba0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 20:19:49 -0800 Subject: [PATCH 10/17] added CMakeLists.txt --- libraries/particles/CMakeLists.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 libraries/particles/CMakeLists.txt diff --git a/libraries/particles/CMakeLists.txt b/libraries/particles/CMakeLists.txt new file mode 100644 index 0000000000..3bbff3b433 --- /dev/null +++ b/libraries/particles/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 2.8) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME particles) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +qt5_use_modules(${TARGET_NAME} Widgets) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) + +# link ZLIB +find_package(ZLIB) +include_directories(${ZLIB_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) From aec0e9f4aae71f0a381f6412bbf908897360ef52 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:00:00 -0800 Subject: [PATCH 11/17] added basic particle server renamed voxel packet names to fit standard --- animation-server/src/main.cpp | 16 ++-- assignment-client/CMakeLists.txt | 3 +- assignment-client/src/Agent.cpp | 2 +- assignment-client/src/AssignmentFactory.cpp | 3 + .../src/voxels/VoxelScriptingInterface.cpp | 6 +- domain-server/src/DomainServer.cpp | 42 ++++++++++ domain-server/src/DomainServer.h | 1 + libraries/octree-server/src/OctreeServer.cpp | 2 +- libraries/octree-server/src/OctreeServer.h | 4 +- libraries/octree/src/JurisdictionListener.cpp | 4 +- libraries/octree/src/JurisdictionListener.h | 6 +- libraries/octree/src/JurisdictionMap.cpp | 4 +- libraries/octree/src/JurisdictionSender.cpp | 2 +- libraries/octree/src/JurisdictionSender.h | 2 +- libraries/octree/src/OctreeSceneStats.cpp | 2 +- .../particle-server/src/ParticleNodeData.h | 22 ++++++ .../particle-server/src/ParticleServer.cpp | 37 +++++++++ .../particle-server/src/ParticleServer.h | 79 +++---------------- .../src/ParticleServerConsts.h | 16 ++++ libraries/particles/src/ParticleTreeElement.h | 4 +- libraries/shared/src/Assignment.cpp | 4 + libraries/shared/src/Assignment.h | 1 + libraries/shared/src/NodeTypes.h | 2 + libraries/shared/src/PacketHeaders.cpp | 8 +- libraries/shared/src/PacketHeaders.h | 16 ++-- .../CMakeLists.txt | 2 +- .../src/VoxelNodeData.h | 0 .../src/VoxelServer.cpp | 0 .../src/VoxelServer.h | 4 +- .../src/VoxelServerConsts.h | 0 .../voxels/src/VoxelEditPacketSender.cpp | 12 +-- libraries/voxels/src/VoxelTree.cpp | 18 ++--- 32 files changed, 202 insertions(+), 122 deletions(-) create mode 100644 libraries/particle-server/src/ParticleNodeData.h create mode 100644 libraries/particle-server/src/ParticleServer.cpp create mode 100644 libraries/particle-server/src/ParticleServerConsts.h rename libraries/{voxel-server-library => voxel-server}/CMakeLists.txt (96%) rename libraries/{voxel-server-library => voxel-server}/src/VoxelNodeData.h (100%) rename libraries/{voxel-server-library => voxel-server}/src/VoxelServer.cpp (100%) rename libraries/{voxel-server-library => voxel-server}/src/VoxelServer.h (100%) rename libraries/{voxel-server-library => voxel-server}/src/VoxelServerConsts.h (100%) diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index a77be03aa5..de64f8c38d 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -159,7 +159,7 @@ static void renderMovingBug() { } // send the "erase message" first... - PACKET_TYPE message = PACKET_TYPE_ERASE_VOXEL; + PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE; ::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details); // Move the bug... @@ -219,7 +219,7 @@ static void renderMovingBug() { } // send the "create message" ... - message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; + message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; ::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details); } @@ -254,7 +254,7 @@ static void sendVoxelBlinkMessage() { detail.green = 0 * ::intensity; detail.blue = 0 * ::intensity; - PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; + PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; ::voxelEditPacketSender->sendVoxelEditMessage(message, detail); } @@ -271,7 +271,7 @@ unsigned char onColor[3] = { 0, 255, 255 }; const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter static void sendBlinkingStringOfLights() { - PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully! + PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! float lightScale = STRING_OF_LIGHTS_SIZE; static VoxelDetail details[LIGHTS_PER_SEGMENT]; @@ -377,7 +377,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH]; void sendDanceFloor() { - PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully! + PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! float lightScale = DANCE_FLOOR_LIGHT_SIZE; static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET]; @@ -493,7 +493,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = { }; static void sendBillboard() { - PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully! + PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! float lightScale = BILLBOARD_LIGHT_SIZE; static VoxelDetail details[VOXELS_PER_PACKET]; @@ -564,7 +564,7 @@ void doBuildStreet() { return; } - PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully! + PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully! static VoxelDetail details[BRICKS_PER_PACKET]; for (int z = 0; z < ROAD_LENGTH; z++) { @@ -864,7 +864,7 @@ int main(int argc, const char * argv[]) nodeSockAddr.getPortPointer())) && packetVersionMatch(packetData)) { - if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { + if (packetData[0] == PACKET_TYPE_JURISDICTION) { if (::jurisdictionListener) { ::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes); } diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 828e91e93f..0d4fe1cc73 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -28,7 +28,8 @@ link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) include_directories(${ROOT_DIR}/externals/civetweb/include) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 95c3f91311..b4a0f3f52f 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -40,7 +40,7 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { } void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION) { + if (dataByteArray[0] == PACKET_TYPE_JURISDICTION) { _voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index aa173f920f..815186c4f5 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -12,6 +12,7 @@ #include "audio/AudioMixer.h" #include "avatars/AvatarMixer.h" #include +#include #include "AssignmentFactory.h" @@ -30,6 +31,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dat return new Agent(dataBuffer, numBytes); case Assignment::VoxelServerType: return new VoxelServer(dataBuffer, numBytes); + case Assignment::ParticleServerType: + return new ParticleServer(dataBuffer, numBytes); default: return NULL; } diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.cpp b/assignment-client/src/voxels/VoxelScriptingInterface.cpp index 1801c621c4..90755f8c65 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.cpp +++ b/assignment-client/src/voxels/VoxelScriptingInterface.cpp @@ -22,7 +22,7 @@ void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float sca VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue}; // queue the packet - queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail); + queueVoxelAdd(PACKET_TYPE_VOXEL_SET, addVoxelDetail); } void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale, @@ -31,7 +31,7 @@ void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue}; // queue the destructive add - queueVoxelAdd(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, addVoxelDetail); + queueVoxelAdd(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, addVoxelDetail); } void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float scale) { @@ -39,6 +39,6 @@ void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float // setup a VoxelDetail struct with data VoxelDetail deleteVoxelDetail = {x, y, z, scale, 0, 0, 0}; - _voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_ERASE_VOXEL, 1, &deleteVoxelDetail); + _voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_VOXEL_ERASE, 1, &deleteVoxelDetail); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b10006e6cc..319cee9807 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -390,6 +390,48 @@ void DomainServer::prepopulateStaticAssignmentFile() { Assignment rootVoxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType); freshStaticAssignments[numFreshStaticAssignments++] = rootVoxelServerAssignment; } + + // Handle Domain/Particle Server configuration command line arguments + if (_particleServerConfig) { + qDebug("Reading Particle Server Configuration.\n"); + qDebug() << "config: " << _particleServerConfig << "\n"; + + QString multiConfig((const char*) _particleServerConfig); + QStringList multiConfigList = multiConfig.split(";"); + + // read each config to a payload for a VS assignment + for (int i = 0; i < multiConfigList.size(); i++) { + QString config = multiConfigList.at(i); + + qDebug("config[%d]=%s\n", i, config.toLocal8Bit().constData()); + + // Now, parse the config to check for a pool + const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool"; + QString assignmentPool; + + int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION); + + if (poolIndex >= 0) { + int spaceBeforePoolIndex = config.indexOf(' ', poolIndex); + int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex); + + assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex); + qDebug() << "The pool for this particle-assignment is" << assignmentPool << "\n"; + } + + Assignment particleServerAssignment(Assignment::CreateCommand, + Assignment::ParticleServerType, + (assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData())); + + int payloadLength = config.length() + sizeof(char); + particleServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength); + + freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment; + } + } else { + Assignment rootParticleServerAssignment(Assignment::CreateCommand, Assignment::ParticleServerType); + freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment; + } qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.\n"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 755f73485c..7d63409be0 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -64,6 +64,7 @@ private: Assignment* _staticAssignments; const char* _voxelServerConfig; + const char* _particleServerConfig; bool _hasCompletedRestartHold; }; diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 9a65941e35..b5d9073d03 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -547,7 +547,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo nodeData->initializeOctreeSendThread(this); } } - } else if (_jurisdictionSender && packetType == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { + } else if (_jurisdictionSender && packetType == PACKET_TYPE_JURISDICTION_REQUEST) { _jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h index 9e38b1068c..c5ef4981f9 100644 --- a/libraries/octree-server/src/OctreeServer.h +++ b/libraries/octree-server/src/OctreeServer.h @@ -55,11 +55,11 @@ public: virtual const char* getMyServerName() const = 0; virtual const char* getMyLoggingServerTargetName() const = 0; virtual const char* getMyDefaultPersistFilename() const = 0; - virtual bool hasSpecialPacketToSend() = 0; - virtual int sendSpecialPacket(Node* node) = 0; // subclass may implement these method virtual void beforeRun() { }; + virtual bool hasSpecialPacketToSend() { return false; } + virtual int sendSpecialPacket(Node* node) { return 0; } static void attachQueryNodeToNode(Node* newNode); diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index 3213e11940..5a356c0b7c 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -42,7 +42,7 @@ void JurisdictionListener::nodeKilled(Node* node) { bool JurisdictionListener::queueJurisdictionRequest() { static unsigned char buffer[MAX_PACKET_SIZE]; unsigned char* bufferOut = &buffer[0]; - ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_VOXEL_JURISDICTION_REQUEST); + ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_JURISDICTION_REQUEST); int nodeCount = 0; NodeList* nodeList = NodeList::getInstance(); @@ -65,7 +65,7 @@ bool JurisdictionListener::queueJurisdictionRequest() { } void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { - if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { + if (packetData[0] == PACKET_TYPE_JURISDICTION) { Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress); if (node) { QUuid nodeUUID = node->getUUID(); diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h index a05601169b..e8d3ec32a9 100644 --- a/libraries/octree/src/JurisdictionListener.h +++ b/libraries/octree/src/JurisdictionListener.h @@ -17,8 +17,8 @@ #include "JurisdictionMap.h" -/// Sends out PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes -/// the PACKET_TYPE_VOXEL_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions +/// Sends out PACKET_TYPE_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes +/// the PACKET_TYPE_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions /// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets /// and adding them to the processing queue by calling queueReceivedPacket() class JurisdictionListener : public NodeListHook, public PacketSender, public ReceivedPacketProcessor { @@ -39,7 +39,7 @@ public: void nodeKilled(Node* node); protected: - /// Callback for processing of received packets. Will process any queued PACKET_TYPE_VOXEL_JURISDICTION and update the + /// Callback for processing of received packets. Will process any queued PACKET_TYPE_JURISDICTION and update the /// jurisdiction map member variable /// \param sockaddr& senderAddress the address of the sender /// \param packetData pointer to received data diff --git a/libraries/octree/src/JurisdictionMap.cpp b/libraries/octree/src/JurisdictionMap.cpp index bd2f703cdb..b104aee14d 100644 --- a/libraries/octree/src/JurisdictionMap.cpp +++ b/libraries/octree/src/JurisdictionMap.cpp @@ -263,7 +263,7 @@ bool JurisdictionMap::writeToFile(const char* filename) { int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION); + int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION); destinationBuffer += headerLength; // No root or end node details to pack! @@ -277,7 +277,7 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destination int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION); + int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION); destinationBuffer += headerLength; // add the root jurisdiction diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index 9bb729f66a..0428128e21 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -30,7 +30,7 @@ JurisdictionSender::~JurisdictionSender() { void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { - if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { + if (packetData[0] == PACKET_TYPE_JURISDICTION_REQUEST) { Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress); if (node) { QUuid nodeUUID = node->getUUID(); diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h index 5f61d41dea..3f5a3855d5 100644 --- a/libraries/octree/src/JurisdictionSender.h +++ b/libraries/octree/src/JurisdictionSender.h @@ -17,7 +17,7 @@ #include #include "JurisdictionMap.h" -/// Will process PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets and send out PACKET_TYPE_VOXEL_JURISDICTION packets +/// Will process PACKET_TYPE_JURISDICTION_REQUEST packets and send out PACKET_TYPE_JURISDICTION packets /// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets /// and adding them to the processing queue by calling queueReceivedPacket() class JurisdictionSender : public PacketSender, public ReceivedPacketProcessor { diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 646cc3a05c..076e2cc118 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -379,7 +379,7 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_STATS); + int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_OCTREE_STATS); destinationBuffer += headerLength; memcpy(destinationBuffer, &_start, sizeof(_start)); diff --git a/libraries/particle-server/src/ParticleNodeData.h b/libraries/particle-server/src/ParticleNodeData.h new file mode 100644 index 0000000000..991c4c063c --- /dev/null +++ b/libraries/particle-server/src/ParticleNodeData.h @@ -0,0 +1,22 @@ +// +// ParticleNodeData.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__ParticleNodeData__ +#define __hifi__ParticleNodeData__ + +#include +#include + +class ParticleNodeData : public OctreeQueryNode { +public: + ParticleNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { }; + virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; } +}; + +#endif /* defined(__hifi__ParticleNodeData__) */ diff --git a/libraries/particle-server/src/ParticleServer.cpp b/libraries/particle-server/src/ParticleServer.cpp new file mode 100644 index 0000000000..bc6c60f712 --- /dev/null +++ b/libraries/particle-server/src/ParticleServer.cpp @@ -0,0 +1,37 @@ +// +// ParticleServer.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13 +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include + +#include "ParticleServer.h" +#include "ParticleServerConsts.h" +#include "ParticleNodeData.h" + +const char* PARTICLE_SERVER_NAME = "Particle"; +const char* PARTICLE_SERVER_LOGGING_TARGET_NAME = "particle-server"; +const char* LOCAL_PARTICLES_PERSIST_FILE = "resources/particles.svo"; + +ParticleServer::ParticleServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) { + // nothing special to do here... +} + +ParticleServer::~ParticleServer() { + // nothing special to do here... +} + +OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) { + return new ParticleNodeData(newNode); +} + +Octree* ParticleServer::createTree() { + return new ParticleTree(true); +} + +void ParticleServer::beforeRun() { + // nothing special to do... +} diff --git a/libraries/particle-server/src/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h index b8296284a9..4655f697bb 100644 --- a/libraries/particle-server/src/ParticleServer.h +++ b/libraries/particle-server/src/ParticleServer.h @@ -10,82 +10,29 @@ #ifndef __particle_server__ParticleServer__ #define __particle_server__ParticleServer__ -#include -#include -#include +#include -#include - -#include "civetweb.h" - -#include "ParticlePersistThread.h" -#include "ParticleSendThread.h" #include "ParticleServerConsts.h" -#include "ParticleServerPacketProcessor.h" /// Handles assignments of type ParticleServer - sending particles to various clients. -class ParticleServer : public ThreadedAssignment { +class ParticleServer : public OctreeServer { public: ParticleServer(const unsigned char* dataBuffer, int numBytes); - ~ParticleServer(); - - /// runs the particle server assignment - void run(); - - /// allows setting of run arguments - void setArguments(int argc, char** argv); - bool wantsDebugParticleSending() const { return _debugParticleSending; } - bool wantsDebugParticleReceiving() const { return _debugParticleReceiving; } - bool wantsVerboseDebug() const { return _verboseDebug; } - bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; } - bool wantDumpParticlesOnMove() const { return _dumpParticlesOnMove; } - bool wantDisplayParticleStats() const { return _displayParticleStats; } + // Subclasses must implement these methods + virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode); + virtual Octree* createTree(); + virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; } + virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; } + virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; } + virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; } + virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; } + + // subclass may implement these method + virtual void beforeRun(); - ParticleTree& getServerTree() { return _serverTree; } - JurisdictionMap* getJurisdiction() { return _jurisdiction; } - - int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } - - static ParticleServer* GetInstance() { return _theInstance; } - - bool isInitialLoadComplete() const { return (_particlePersistThread) ? _particlePersistThread->isInitialLoadComplete() : true; } - time_t* getLoadCompleted() { return (_particlePersistThread) ? _particlePersistThread->getLoadCompleted() : NULL; } - uint64_t getLoadElapsedTime() const { return (_particlePersistThread) ? _particlePersistThread->getLoadElapsedTime() : 0; } - private: - int _argc; - const char** _argv; - char** _parsedArgV; - - char _particlePersistFilename[MAX_FILENAME_LENGTH]; - int _packetsPerClientPerInterval; - ParticleTree _serverTree; // this IS a reaveraging tree - bool _wantParticlePersist; - bool _wantLocalDomain; - bool _debugParticleSending; - bool _shouldShowAnimationDebug; - bool _displayParticleStats; - bool _debugParticleReceiving; - bool _dumpParticlesOnMove; - bool _verboseDebug; - JurisdictionMap* _jurisdiction; - JurisdictionSender* _jurisdictionSender; - ParticleServerPacketProcessor* _particleServerPacketProcessor; - ParticlePersistThread* _particlePersistThread; - - NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed - - void parsePayload(); - - void initMongoose(int port); - - static int civetwebRequestHandler(struct mg_connection *connection); - static ParticleServer* _theInstance; - - time_t _started; - uint64_t _startedUSecs; }; #endif // __particle_server__ParticleServer__ diff --git a/libraries/particle-server/src/ParticleServerConsts.h b/libraries/particle-server/src/ParticleServerConsts.h new file mode 100644 index 0000000000..5ac2a1534b --- /dev/null +++ b/libraries/particle-server/src/ParticleServerConsts.h @@ -0,0 +1,16 @@ +// ParticleServerConsts.h +// particle-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __particle_server__ParticleServerConsts__ +#define __particle_server__ParticleServerConsts__ + +extern const char* PARTICLE_SERVER_NAME; +extern const char* PARTICLE_SERVER_LOGGING_TARGET_NAME; +extern const char* LOCAL_PARTICLES_PERSIST_FILE; + +#endif // __particle_server__ParticleServerConsts__ diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 837814ccaa..e169db14b0 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -26,14 +26,14 @@ public: virtual ~ParticleTreeElement(); virtual void init(unsigned char * octalCode); - virtual bool hasContent() const; + virtual bool hasContent() const { return isLeaf(); } virtual void splitChildren() {} virtual bool requiresSplit() const { return false; } virtual bool appendElementData(OctreePacketData* packetData) const; virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual void calculateAverageFromChildren(); virtual bool collapseChildren(); - virtual bool isRendered() const; + virtual bool isRendered() const { return getShouldRender(); } // type safe versions of OctreeElement methods ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); } diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 59b0bb60d1..4943098812 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -27,6 +27,8 @@ Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) { return Assignment::AgentType; case NODE_TYPE_VOXEL_SERVER: return Assignment::VoxelServerType; + case NODE_TYPE_PARTICLE_SERVER: + return Assignment::ParticleServerType; default: return Assignment::AllTypes; } @@ -176,6 +178,8 @@ const char* Assignment::getTypeName() const { return "agent"; case Assignment::VoxelServerType: return "voxel-server"; + case Assignment::ParticleServerType: + return "particle-server"; default: return "unknown"; } diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index ec7ae7f74f..1aac273e36 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -28,6 +28,7 @@ public: AvatarMixerType, AgentType, VoxelServerType, + ParticleServerType, AllTypes }; diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h index a9deebf333..c9723f4477 100644 --- a/libraries/shared/src/NodeTypes.h +++ b/libraries/shared/src/NodeTypes.h @@ -19,6 +19,8 @@ typedef char NODE_TYPE; const NODE_TYPE NODE_TYPE_DOMAIN = 'D'; const NODE_TYPE NODE_TYPE_VOXEL_SERVER = 'V'; +const NODE_TYPE NODE_TYPE_PARTICLE_SERVER = 'P'; +const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E'; const NODE_TYPE NODE_TYPE_AGENT = 'I'; const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M'; const NODE_TYPE NODE_TYPE_AVATAR_MIXER = 'W'; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 6ca20141d4..d48ffdbaf1 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -28,7 +28,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_AVATAR_FACE_VIDEO: return 2; - case PACKET_TYPE_VOXEL_STATS: + case PACKET_TYPE_OCTREE_STATS: return 2; case PACKET_TYPE_DOMAIN: @@ -39,9 +39,9 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_VOXEL_QUERY: return 2; - case PACKET_TYPE_SET_VOXEL: - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: - case PACKET_TYPE_ERASE_VOXEL: + case PACKET_TYPE_VOXEL_SET: + case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: + case PACKET_TYPE_VOXEL_ERASE: return 1; case PACKET_TYPE_VOXEL_DATA: diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index 9f19291eff..ade4292918 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -40,12 +40,16 @@ const PACKET_TYPE PACKET_TYPE_DATA_SERVER_SEND = 'u'; const PACKET_TYPE PACKET_TYPE_DATA_SERVER_CONFIRM = 'c'; const PACKET_TYPE PACKET_TYPE_VOXEL_QUERY = 'q'; const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V'; -const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#'; -const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J'; -const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j'; -const PACKET_TYPE PACKET_TYPE_SET_VOXEL = 'S'; -const PACKET_TYPE PACKET_TYPE_SET_VOXEL_DESTRUCTIVE = 'O'; -const PACKET_TYPE PACKET_TYPE_ERASE_VOXEL = 'E'; +const PACKET_TYPE PACKET_TYPE_VOXEL_SET = 'S'; +const PACKET_TYPE PACKET_TYPE_VOXEL_SET_DESTRUCTIVE = 'O'; +const PACKET_TYPE PACKET_TYPE_VOXEL_ERASE = 'E'; +const PACKET_TYPE PACKET_TYPE_OCTREE_STATS = '#'; +const PACKET_TYPE PACKET_TYPE_JURISDICTION = 'J'; +const PACKET_TYPE PACKET_TYPE_JURISDICTION_REQUEST = 'j'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_QUERY = 'Q'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_DATA = 'v'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD = 'a'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_ERASE = 'x'; typedef char PACKET_VERSION; diff --git a/libraries/voxel-server-library/CMakeLists.txt b/libraries/voxel-server/CMakeLists.txt similarity index 96% rename from libraries/voxel-server-library/CMakeLists.txt rename to libraries/voxel-server/CMakeLists.txt index a44fffcc7c..a85165d6cc 100644 --- a/libraries/voxel-server-library/CMakeLists.txt +++ b/libraries/voxel-server/CMakeLists.txt @@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") -set(TARGET_NAME voxel-server-library) +set(TARGET_NAME voxel-server) find_package(Qt5Widgets REQUIRED) diff --git a/libraries/voxel-server-library/src/VoxelNodeData.h b/libraries/voxel-server/src/VoxelNodeData.h similarity index 100% rename from libraries/voxel-server-library/src/VoxelNodeData.h rename to libraries/voxel-server/src/VoxelNodeData.h diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server/src/VoxelServer.cpp similarity index 100% rename from libraries/voxel-server-library/src/VoxelServer.cpp rename to libraries/voxel-server/src/VoxelServer.cpp diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server/src/VoxelServer.h similarity index 100% rename from libraries/voxel-server-library/src/VoxelServer.h rename to libraries/voxel-server/src/VoxelServer.h index e374ca01ad..221857d290 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server/src/VoxelServer.h @@ -43,11 +43,11 @@ public: virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; } virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; } + // subclass may implement these method + virtual void beforeRun(); virtual bool hasSpecialPacketToSend(); virtual int sendSpecialPacket(Node* node); - // subclass may implement these method - virtual void beforeRun(); private: bool _sendEnvironments; diff --git a/libraries/voxel-server-library/src/VoxelServerConsts.h b/libraries/voxel-server/src/VoxelServerConsts.h similarity index 100% rename from libraries/voxel-server-library/src/VoxelServerConsts.h rename to libraries/voxel-server/src/VoxelServerConsts.h diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index bfc42da63f..467bf7934c 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -137,14 +137,14 @@ void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned ch const char* messageName; switch (buffer[0]) { - case PACKET_TYPE_SET_VOXEL: - messageName = "PACKET_TYPE_SET_VOXEL"; + case PACKET_TYPE_VOXEL_SET: + messageName = "PACKET_TYPE_VOXEL_SET"; break; - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: - messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE"; + case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: + messageName = "PACKET_TYPE_VOXEL_SET_DESTRUCTIVE"; break; - case PACKET_TYPE_ERASE_VOXEL: - messageName = "PACKET_TYPE_ERASE_VOXEL"; + case PACKET_TYPE_VOXEL_ERASE: + messageName = "PACKET_TYPE_VOXEL_ERASE"; break; } printf("VoxelEditPacketSender::queuePacketToNode() queued %s - command to node bytes=%ld sequence=%d transitTimeSoFar=%llu usecs\n", diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b12a386a19..986d70665e 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -355,7 +355,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) { glm::vec3 nudge = args->nudgeVec; // delete the old element - args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxelDetails); + args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxelDetails); // nudge the old element voxelDetails.x += nudge.x; @@ -363,7 +363,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) { voxelDetails.z += nudge.z; // create a new voxel in its stead - args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, voxelDetails); + args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, voxelDetails); } // Recurses voxel element with an operation function @@ -642,9 +642,9 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelTreeElement* node, ReadC bool VoxelTree::handlesEditPacketType(PACKET_TYPE packetType) const { // we handle these types of "edit" packets switch (packetType) { - case PACKET_TYPE_SET_VOXEL: - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: - case PACKET_TYPE_ERASE_VOXEL: + case PACKET_TYPE_VOXEL_SET: + case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: + case PACKET_TYPE_VOXEL_ERASE: return true; } return false; @@ -656,9 +656,9 @@ int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* pack int processedBytes = 0; // we handle these types of "edit" packets switch (packetType) { - case PACKET_TYPE_SET_VOXEL: - case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE: { - bool destructive = (packetType == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); + case PACKET_TYPE_VOXEL_SET: + case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: { + bool destructive = (packetType == PACKET_TYPE_VOXEL_SET_DESTRUCTIVE); int octets = numberOfThreeBitSectionsInCode(editData, maxLength); if (octets == OVERFLOWED_OCTCODE_BUFFER) { @@ -682,7 +682,7 @@ int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* pack return voxelDataSize; } break; - case PACKET_TYPE_ERASE_VOXEL: + case PACKET_TYPE_VOXEL_ERASE: processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength); return maxLength; } From 821b490ff128aa9d080c806d1f2175fa47b60211 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:00:17 -0800 Subject: [PATCH 12/17] added basic particle server renamed voxel packet names to fit standard --- interface/src/Application.cpp | 12 ++++++------ interface/src/VoxelPacketProcessor.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 833f811bf8..d8c8f64539 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1478,7 +1478,7 @@ void Application::removeVoxel(glm::vec3 position, voxel.y = position.y / TREE_SCALE; voxel.z = position.z / TREE_SCALE; voxel.s = scale / TREE_SCALE; - _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxel); + _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel); // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); @@ -1498,7 +1498,7 @@ void Application::makeVoxel(glm::vec3 position, voxel.red = red; voxel.green = green; voxel.blue = blue; - PACKET_TYPE message = isDestructive ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL; + PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET; _voxelEditSender.sendVoxelEditMessage(message, voxel); // create the voxel locally so it appears immediately @@ -1580,7 +1580,7 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX]; codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX]; - getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, + getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, codeColorBuffer, codeAndColorLength); delete[] codeColorBuffer; @@ -3977,7 +3977,7 @@ bool Application::maybeEditVoxelUnderCursor() { void Application::deleteVoxelUnderCursor() { if (_mouseVoxel.s != 0) { // sending delete to the server is sufficient, server will send new version so we see updates soon enough - _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel); + _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, _mouseVoxel); // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); @@ -4235,8 +4235,8 @@ void* Application::networkReceive(void* args) { app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived); break; case PACKET_TYPE_VOXEL_DATA: - case PACKET_TYPE_ERASE_VOXEL: - case PACKET_TYPE_VOXEL_STATS: + case PACKET_TYPE_VOXEL_ERASE: + case PACKET_TYPE_OCTREE_STATS: case PACKET_TYPE_ENVIRONMENT_DATA: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index a591fbdbaa..0b0f5f5694 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -34,10 +34,10 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns app->_wantToKillLocalVoxels = false; } - // note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA - // immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first + // note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA + // immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first // then process any remaining bytes as if it was another packet - if (packetData[0] == PACKET_TYPE_VOXEL_STATS) { + if (packetData[0] == PACKET_TYPE_OCTREE_STATS) { int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderSockAddr); wasStatsPacket = true; From 0acd9d0fce4b7dba1d471cc39e1d606f483fd1a2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:10:01 -0800 Subject: [PATCH 13/17] tweak to cause rebuild --- assignment-client/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 0d4fe1cc73..5bc4829117 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -30,6 +30,7 @@ link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) +#testing include_directories(${ROOT_DIR}/externals/civetweb/include) From 33b3cbaa4361641548ac57088c2ae52e9fe0e1cc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:18:40 -0800 Subject: [PATCH 14/17] fix cmake --- libraries/octree-server/CMakeLists.txt | 2 -- libraries/particle-server/CMakeLists.txt | 4 +--- libraries/voxel-server/CMakeLists.txt | 6 +----- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/libraries/octree-server/CMakeLists.txt b/libraries/octree-server/CMakeLists.txt index 97faf45243..45fd66cd23 100644 --- a/libraries/octree-server/CMakeLists.txt +++ b/libraries/octree-server/CMakeLists.txt @@ -36,5 +36,3 @@ link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) # link in the hifi octree library link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) -# link in the hifi avatars library -#link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) diff --git a/libraries/particle-server/CMakeLists.txt b/libraries/particle-server/CMakeLists.txt index 9a1b7a7223..1d181b51bd 100644 --- a/libraries/particle-server/CMakeLists.txt +++ b/libraries/particle-server/CMakeLists.txt @@ -34,9 +34,7 @@ find_package(ZLIB) include_directories(${ZLIB_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) -# link in the hifi octree library link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) - -# link in the hifi particles library +link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) diff --git a/libraries/voxel-server/CMakeLists.txt b/libraries/voxel-server/CMakeLists.txt index a85165d6cc..67e3168f0f 100644 --- a/libraries/voxel-server/CMakeLists.txt +++ b/libraries/voxel-server/CMakeLists.txt @@ -35,9 +35,5 @@ link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) # link in the hifi octree library link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) - -# link in the hifi voxels library +link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) - -# link in the hifi avatars library -link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) From 2930ae0f9bb77730254e2ba87045474f68500948 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:29:25 -0800 Subject: [PATCH 15/17] attempt to fix build buster --- libraries/octree-server/src/OctreeServer.cpp | 33 +------------------- libraries/octree-server/src/OctreeServer.h | 2 -- libraries/particle-server/CMakeLists.txt | 5 --- libraries/voxel-server/CMakeLists.txt | 5 --- libraries/voxel-server/src/VoxelServer.h | 1 - 5 files changed, 1 insertion(+), 45 deletions(-) diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index b5d9073d03..8a8766851d 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -6,44 +6,13 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // - -/*** -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include "VoxelNodeData.h" -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include "Syssocket.h" -#include "Systime.h" -#else -#include -#include -#include -#endif -***/ - #include #include #include #include +#include "civetweb.h" #include "OctreeServer.h" #include "OctreeServerConsts.h" diff --git a/libraries/octree-server/src/OctreeServer.h b/libraries/octree-server/src/OctreeServer.h index c5ef4981f9..39fb9ee990 100644 --- a/libraries/octree-server/src/OctreeServer.h +++ b/libraries/octree-server/src/OctreeServer.h @@ -17,8 +17,6 @@ #include #include -#include "civetweb.h" - #include "OctreePersistThread.h" #include "OctreeSendThread.h" #include "OctreeServerConsts.h" diff --git a/libraries/particle-server/CMakeLists.txt b/libraries/particle-server/CMakeLists.txt index 1d181b51bd..5e14902f45 100644 --- a/libraries/particle-server/CMakeLists.txt +++ b/libraries/particle-server/CMakeLists.txt @@ -12,13 +12,8 @@ find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) -# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS -FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*) - setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS}) -include_directories(${ROOT_DIR}/externals/civetweb/include) - qt5_use_modules(${TARGET_NAME} Widgets) # inluce GLM diff --git a/libraries/voxel-server/CMakeLists.txt b/libraries/voxel-server/CMakeLists.txt index 67e3168f0f..0db5fc5575 100644 --- a/libraries/voxel-server/CMakeLists.txt +++ b/libraries/voxel-server/CMakeLists.txt @@ -12,13 +12,8 @@ find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) -# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS -FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*) - setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS}) -include_directories(${ROOT_DIR}/externals/civetweb/include) - qt5_use_modules(${TARGET_NAME} Widgets) include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/voxel-server/src/VoxelServer.h b/libraries/voxel-server/src/VoxelServer.h index 221857d290..e785d2f65e 100644 --- a/libraries/voxel-server/src/VoxelServer.h +++ b/libraries/voxel-server/src/VoxelServer.h @@ -18,7 +18,6 @@ #include #include -#include "civetweb.h" #include "VoxelServerConsts.h" From 9654f025b4da2b522c5def674d191f453e246bae Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:33:59 -0800 Subject: [PATCH 16/17] fix build buster --- libraries/octree-server/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/octree-server/CMakeLists.txt b/libraries/octree-server/CMakeLists.txt index 45fd66cd23..d9ee7b0e2f 100644 --- a/libraries/octree-server/CMakeLists.txt +++ b/libraries/octree-server/CMakeLists.txt @@ -36,3 +36,7 @@ link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) # link in the hifi octree library link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) +# link dl library on UNIX for civetweb +if (UNIX AND NOT APPLE) + target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) +endif (UNIX AND NOT APPLE) \ No newline at end of file From 50833cf04c2c70bec56c1de3a134c3fe9c6b1fde Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 4 Dec 2013 21:37:31 -0800 Subject: [PATCH 17/17] add --particleServerConfig to domain server --- domain-server/src/DomainServer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 319cee9807..4f476947a9 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -318,6 +318,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig"; _voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION); + + const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig"; + _particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION); // setup the mongoose web server struct mg_callbacks callbacks = {};