From 8d8a1e0634e7d9600436644174ea91ce3c4b8624 Mon Sep 17 00:00:00 2001
From: Nicholas Novak <34256932+NickyBoy89@users.noreply.github.com>
Date: Thu, 14 Dec 2023 02:23:51 -0800
Subject: [PATCH] change: Finished almost my last draft on the paper
---
paper/document.tex | 229 ++++++++++++++++++------------------
paper/references.bib | 8 ++
paper/unity-file.drawio | 53 +++++++++
paper/unity-file.drawio.png | Bin 0 -> 17333 bytes
4 files changed, 178 insertions(+), 112 deletions(-)
create mode 100644 paper/unity-file.drawio
create mode 100644 paper/unity-file.drawio.png
diff --git a/paper/document.tex b/paper/document.tex
index 2e15f5c..8b7597c 100644
--- a/paper/document.tex
+++ b/paper/document.tex
@@ -27,7 +27,7 @@ or three-dimensional pixels.
% Applications of voxels
A voxel\cite{enwiki:1186283262} represents a single point or cube in a
three-dimensional grid, at a variable size. This feature allows them to
-approximately model many three-dimensional structures, in order to reduce the
+approximately model many three-dimensional structures, and to reduce the
computational complexity in analyzing the shape, which has led to many
data-related use cases outside of computer science. For example, to model the
inner workings of the brain, Neuroscientists track oxygen concentration through
@@ -37,7 +37,7 @@ reflections for visual effects\cite{museth2013vdb}. The output of MRI scans in
hospitals are very high-resolution voxel grids. Most recently, machine learning
models are being trained on the LIDAR data from self-driving
cars\cite{li2020deep} in order to better process their environments. However,
-voxels are not often thought of as a way to store three-dimensional shapes, and
+voxels are not often thought of as a way to permanently store three-dimensional shapes, and
existing research focuses mainly on efficiently representing and processing
shapes. My approach models this problem of voxel storage and representation, and
turns it into a problem of database design.
@@ -205,9 +205,7 @@ advantage of this speedup. In VDB\cite{museth2013vdb} Museth demonstrates that
by modeling a sparse voxel grid in different resolutions, a computer cluster can
efficiently approximate a physical structures such as a cloud, in order to
calculate expensive lighting operations.
-
-\subsection{Parallel Processing on Voxel Databases}
-
+% Parallel processing on voxels
Williams\cite{williams1992voxel} expands upon the uses of a voxel database to
model graph and mesh-based problems. Taking advantage of the parallelism in the
grid, many problems can be reframed in the representation of voxels, and solve
@@ -216,7 +214,7 @@ voxel is stored in shared memory, making this process only viable to solve
problems that can be modeled on one machine, and are far more computationally
expensive, rather than data-intensive.
-\subsection{Large Voxel Data Set Processing}
+\subsection{Storing Large Voxel Data Sets}
Another approach to the problem of storing voxel data is the distributed
approach in Gorte et. al. \cite{gorte2023analysis}. Since memory is limited
@@ -229,6 +227,28 @@ of the data that they are working on. In the paper, Gorte acknowledges the need
to split large datasets up into smaller regions, which is similar to the concept
of ``chunks'' in my implementation.
+\subsection{Chunk Systems in Other Games}
+
+The decision to choose chunks to represent game data has many justifications. As
+\cite{gorte2023analysis} mentions, an infinite grid of voxels needs to be broken
+up in a way where applications can store data in an efficient way, and many
+other games converge on this same implementation. Another voxel-based game,
+Veloren\cite{https://veloren.net} uses the same chunk-based system, although
+differs in its storage method. The game switches between several different
+storage implementations in each chunk, depending on how dense or sparse the voxel
+data within the chunk is. For sparser data, the game stores block information in
+a simple key-value hash map. As the number of voxels increase, the game further
+breaks this information up, and creates several smaller sections within the
+chunk. Finally, for very dense data, the game stores a compressed version using
+Zlib compression\cite{veloren32}. This gives many options for data compression
+in my database, but also shows how the database can be adapted to store sparser
+structures more efficiently if the focus of the project ever needs to change.
+Since this game is not based on Minecraft, but an independent project named cube
+world, the game comes up with a similar data structure, and shows the
+performance considerations for using such a structure. The benchmarks that they
+show suggest about an order-of-magnitude improvement over using a key-value
+store.
+
\subsection{Previous Special-Purpose Databases}
The design of my database was also inspired by the LSM tree and data-driven
@@ -242,11 +262,14 @@ and replicate these in real-time.
\section{Methods}
-Almost every part of the database was designed so that most operations could be
-done in constant time.
+\subsection{The Interface for the Database}
+
+For developers to interact with the database, the database is implemented as a
+library, and the database provides a simple application programming interface to
+read and write data, consisting of the following operations. The performance
+considerations for each of these operations can be found in the methods section
+below.
-The database provides a simple interface to read and write data, consisting of
-the following:
\begin{itemize}
\item Read a single block
\item Write a single block
@@ -254,34 +277,44 @@ the following:
\item Read a pre-defined ``chunk'' of blocks
\end{itemize}
+\subsection{Reading and Writing a Single Voxel}
-The process of fetching the data for a single point in the world starts at that
-point's $x, y$ and $z$ location. The world is infinite in size on the horizontal
-$x$ and $z$ axes, but limited in the vertical $y$ axis. In my database, the
-world is composed of an infinite grid of ``chunks'', or columns that are a fixed
-16 x 16 blocks in the $x$ and $z$ axes, but 256 blocks in the vertical $y$ axis.
+The process of updating the data for a single point in the world starts with the
+voxel's position. Because the world is infinite on the horizontal $x$ and $z$
+axes, this is implemented by a system of ``chunks'', which are fixed-size 16x16
+columns of voxels, 256 voxels high. The size of these chunks are chosen so that
+they are large enough to be efficiently cached, and many operations can occur
+within the same chunk, but not too large to the point where the hundred or so
+chunks sent to the user upon joining the world cause a network slowdown. Given a
+point's $x$ and $z$ positions, the chunk that that voxel belongs to can be found
+with a fast modulus operation, in constant time.
-Once you know a point's location, you can find with a modulus what chunk the
-point is located within. From there, the database only needs to retrieve the
-data for the chunk stored at that location.
+To fetch the data for that chunk, the database needs to read that data from
+disk. The database stores this information in combined files that I call ``unity
+files'' (shown in figure \ref{fig:unity}), which consist of a single file on disk, but with the encoded data for
+each chunk stored as a start index and size, so that the \verb|seek| syscall can
+be used to efficiently query this data, while only keeping one file open. This
+scheme was used over the previous system of storing chunk files separately,
+because the filesystem had a hard time searching through the hundreds of
+thousands of chunks in larger worlds. This start position and size are stored in
+an auxillary hash map that stores a mapping of every chunk's position to its
+metadata within the unity file. This structure uses a minimal amount of memory,
+and also allows for a file to be fetched from disk in a constant amount of time
+and disk reads.
-Initial implementations for my database focused on tree-based approaches for
-finding the files for chunks, but with their complexity and non-constant
-complexity, I decided to store each chunk separately. However, with worlds with
-chunk counts in the hundreds of thousands, the filesystem implementations had
-issues with searching through so many files, which led to performance problems.
-Finally, I settled on merging all the chunk data into one file, and use the
-filesystem's \verb|seek| syscall to lookup the offset for the correct chunk. A
-simple hash table was then used to store each chunk's location with its offset
-in the file, which keeps the memory cost low, even with chunk counts in the
-millions. This allows for constant-time searches for the chunk's data.
+\begin{figure}
+ \centering
+ \includegraphics[width=8cm]{unity-file.drawio.png}
+ \caption{The Layout of a Unity File}
+ \label{fig:unity}
+\end{figure}
-Once a chunk is retrieved from disk, the format of the chunk is broken down into
-smaller cubic slices of the chunk, called ``sections'' each section is a
-16x16x16 cubic area that keeps an index for every chunk. The point's $y$
-position tells the database what section the point is in, and a simple formula
-is done to convert the remaining $x$ and $z$ axes into an index within the
-section.
+Each chunk is further divided into sections, in this case each chunk consists of
+16 stacked 16x16x16 cubes of voxels, which results in a total of 4096 block
+states per section. Using the voxel's $y$ position, the section for a block can
+be found with another modulus. Once this is found, a perfect hash function is
+used to map the voxel's position to an array index within the section. Again,
+both of these steps are done in constant time respectively.
Every section additionally stores a look-up-table, that stores a mapping of a
\textit{palette index} to the state of a block. When the value for the point is
@@ -289,8 +322,8 @@ retrieved from the section, the value returned is not the block's state, but
simply an index into this palette. The palette lookup is done in constant time,
and when a new block is added into the section that needs an additional state in
the palette, this value is added in constant time as well. The existence of this
-palette supports the efficient operation of another part of the database, which
-is the ability to change large portions of blocks in the world.
+palette supports the efficient operation changing large portions of blocks in
+the world.
Once the value of the point is found in the palette, the value can be returned
to the user. A visual diagram of this process can be found in figure
@@ -407,28 +440,29 @@ chunks, so that chunk data could be retrieved without decoding the entire chunk.
However, this would require a much more constrained data layout, and limit the
implementation of different voxels.
-Additionally, compression
+Additionally, compression would also reduce the amount of data sent from the
+disk to the application.
\section{Ethical Considerations}
\subsection{Considerations of Computing Resources}
-Since databases are at the core part of most complex systems, they are often
-built to be run on hardware that the normal consumer can afford
+Since a database is at the core part of most software systems, it is important
+that the database is designed to work on a wide variety of computers, in order
+to ensure all parties are able to take advantage of the improvements. I
+designed my database to run on entry-level commodity hardware, as well as
+alongside existing application programs that can require far more resources.
+Additionally, by focusing on disk storage, which is far cheaper than equivalent
+capacities of memory, this further allows researchers or individuals to run
+large datasets on a single machine.
+
+My system targets far less memory usage than existing commercial applications
\footnote{\url{https://docs.oracle.com/en/database/oracle/oracle-database/12.2/ntdbi/oracle-database-minimum-hardware-requirements.html}}
\footnote{\url{https://wiki.lustre.org/Lustre_Server_Requirements_Guidelines}}.
+In the design of my application I had to take advantage of as much of the
+computing hardware as possible, but make sure that the approachability and
+accessibility for the application does not decrease as as result.
-The large hardware requirements of these databases come from the environments
-where they are implemented, and at many of these companies, the ability to
-keep buying faster hardware allows the company to work on other things that are
-more important. However, what this does to the player is effectively prices them
-out of the game that they would be already playing, especially since the
-database would also have to run alongside the existing Java application of
-Minecraft, which quickly exhaust system memory.
-
-In the design of my server I have to prioritize both performance to take
-advantage of the existing hardware, but make sure that the accessibility for
-the application does not decrease as a result.
\subsection{Considerations of Complexity}
Another factor to consider in the implementation of my database is how complex
@@ -436,22 +470,20 @@ the existing systems are. Some of the most popular SQL databases, PostgreSQL and
MySQL have 1.4 and 4.4 million lines of code respectively
\footnote{\url{https://news.ycombinator.com/item?id=24813239}}.
-With so much complexity going on, this significantly decreases the overall
-knowledge of the system, as well as the individual user who has to debug their
-game. Most of this is from the large amount of query logic that handles caching
-and speeding up certain queries, so knowing more about the specific problem that
-I am trying to solve removes this process from having to be done.
-
-Especially since most of the people in the Minecraft community are volunteers in
-the open-source community, debugging this large of an application would be out of
-scope for enjoying a game, and likely lead to it being replaced with something
-more simple. The reliability characteristics are also less than what are
-required for Minecraft, since they are being compared against a single-threaded
-Java program which has been tested to do the correct thing.
+Because these systems are so complex, this decreases the number of people who
+can effectively work with these systems and maintain them, effectively limiting
+this role to larger companies that can afford teams of people to solve these
+problems for them. By not focusing on the significant complexity that comes with
+caching logic, and keeping a simple implementation for the server, I allow more
+companies and developers to use this database for their own needs, and expand
+with them. In addition, many decisions were made to help in the debugging
+process, including the choice of JSON serialization for the chunk data, which
+allows users to read the contents of files easier, and recover potentially
+corrupted data.
\subsection{Considerations in Security}
-Since these databases are very complex, there is also the risk that having a
+Since databases are very complex, there is also the risk that having a
server exposed over the internet through the Minecraft game server might leave
it exposed to attacks. While this is a large issue, an even more important
implication is the ability to configure the database correctly. Since these
@@ -461,37 +493,31 @@ breaches\footnote{\url{https://www.zdnet.com/article/hacker-ransoms-23k-mongodb-
that involve a single server, even at larger companies that have dedicated teams
that involve a data breach.
-My plan to mitigate this risk is to implement the database in a memory-safe
-programming language, which should remove the risk class of memory-unsafety
+I mitigate this risk by implementing the database in a memory-safe
+programming language, Go, which should remove the risk class of memory-unsafety
bugs, which account for around 70\% of all bugs in the Chromium browser
engine\footnote{\url{https://www.chromium.org/Home/chromium-security/memory-safety/}},
which is entirely written in non-memory safe C++.
-And if the database information is ever able to be leaked through the Minecraft
-protocol, the attacker would have access to the full data, because I am planning
-to store it unencrypted for performance reasons, and rely on the encryption of
-the Minecraft client. And, the data involved does not involve personally
-identifying information, so the usefulness of the data would be close to
-nothing.
-
-But, perhaps the most important security risk is if an attacker is able to
-access the database directly and bypass all the isolation in the Minecraft
-protocol, in order to wipe or corrupt the data for malicious reasons. This would
-likely lead to the Minecraft server being unable to be played, and degrade the
-experience of the players. It is my plan to take advantage of the limitations of
-the types of Minecraft items to provide resilience and easy backups to the
-system, because of the purpose-built nature of the system
-\footnote{\url{https://twitter.com/eatonphil/status/1568247643788267521?s=20}}.
+However, there is the possibility that information stored in the database is
+exposed, whether the database not secured, or exposed via an application error.
+With this, my database follows the previous threat model of many other
+databases, and leaves the security up to the user implementing the application.
+Implementing features such as encryption would provide some additional layer of
+security, but would also likely decrease performance and increase complexity,
+which are also harmful to security in their own ways. Ultimately, I rely on a
+setting of defaults that doesn't many any assumptions about the security of the
+system.
\subsection{Considerations in Fairness}
In the implementation of databases, it can often be beneficial to make certain
operations faster, at the expense of others that are not done as often. For
-instance, if I notice that players often pull items in and out of their systems
-often, but almost never search through the list of items, I can take advantage
-of this to speed up the database for the most common operations. However, this
-can be problematic if the things that I choose to sacrifice affect a certain
-group of users.
+instance, if I notice that researchers often write more to the database, and
+adjust the application accordingly, I can take advantage of this assumption to
+speed up the database for the most common operations. However, this can be
+problematic if the things that I choose to sacrifice affect a certain group of
+users.
This tradeoff between speed and reliability occurs so often in Computer Science
and is described in terms of percentiles. For instance, if we notice that some
@@ -501,15 +527,9 @@ Similarly, if an event only occurs 1\% of the time, we can say it occurs in the
like this is make is written about by Google \cite{dean2013tail}, who have to make every
decision like this at their scale.
-My plan is to not have any tradeoffs that affect the normal gameplay of the
-server, and keep it within the 50ms timeframe that the Minecraft has allocated
-to itself. Apart from this, one of the main goals of the project is to give
-consistent performance, so any further decisions will be made around the
-existing implementation of the Minecraft server.
-
-%https://www.embedded.com/implementing-a-new-real-time-scheduling-policy-for-linux-part-1/
-%https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html
-%https://helix979.github.io/jkoo/post/os-scheduler/
+My database plans to keep a consistent set of gaurantees in regards to the
+complexity of the basic operations, and provide constant-time operations for
+most of these operations.
\subsection{Considerations in Accessibility}
@@ -518,24 +538,9 @@ require a certain type of computer. Requiring a certain operating system or a
more powerful computer would limit access to many of the people that were
playing the game before.
-However, by basing the goal of the project on improving the performance of the
-already existing implementation, any improvements would result in more people
-being able to play than before. Also, by designing the system for normal
-hardware and in a cross-platform way, this does not limit the people that are
-able to access the improvements.
-
-
-\subsection{Considerations in the Concentration of Power}
-
-With any improvements to performance to servers in Minecraft, this would allow
-many of the larger hosting companies, who rent servers monthly to individual
-people, to drive down their hosting costs, and allow them to have larger returns
-over the smaller providers. However, since this market is so competitive between
-companies, because of how easy it is to set up a company, and the options
-between companies aren't very different, I would expect any improvement to be
-quickly disappear into the competitive market, and benefit everyone equally.
-
-\section{Future Work, and Conclusion}
+However, with the previous performance goals, as well as an implementation in a
+portable language, the program is available for as many systems as the Go
+compiler supports.
\printbibliography
diff --git a/paper/references.bib b/paper/references.bib
index 7cc93b2..3c06d29 100644
--- a/paper/references.bib
+++ b/paper/references.bib
@@ -305,3 +305,11 @@ How storage works in database systems, and the evolution of how data is stored
year={2010},
publisher={ACM New York, NY, USA}
}
+
+@misc{veloren32,
+ title = "This Week In Veloren 32",
+ author = "AngelOnFira",
+ month = "September",
+ year = "2019",
+ url = "https://veloren.net/blog/devblog-32/"
+}
diff --git a/paper/unity-file.drawio b/paper/unity-file.drawio
new file mode 100644
index 0000000..c0c22b0
--- /dev/null
+++ b/paper/unity-file.drawio
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/paper/unity-file.drawio.png b/paper/unity-file.drawio.png
new file mode 100644
index 0000000000000000000000000000000000000000..7748b8e1622dfc662202c14af84a48e660aa0417
GIT binary patch
literal 17333
zcmeAS@N?(olHy`uVBq!ia0y~yVDx5SU^vOa#=yX^YKOp21_lPk;vjb?hIQv;UNSI9
z?<#SPD9Noz%gjk-P&IajuvCoY6nmU<>8rcn^}iRr0%P=kE)t1@$P5><`dO!W*@
zjSMt=6O%LZO7e>{RE?cfjSM{VN>X!FjSPGflU0oj{DW1E3?fvG3=9q84Nc?CHC2ra
z9199^Qo~b|yfaHcrWl*+8JmF&^7aVv^-(o)Ry8un$;?hwH8OBdP0r2-X-O$cEZ57-
zS2c1oGSV~919`zYqbNT&Rn^GN(9lHBK+n|N*i_HR!cx`90O3_PBU3#KJrh+UgW$xp
z#G*_j>p(`NmL#S_Lck;6AUM>?prkU`Gp*3yq{6SrqZs1zvecsD%=|ovGMMEc%_Wru
zsSweW)UwRvRFHCGS5*T?RU=Tas2V$`WG1E;CFZIc8RRAALe&N&rl;x}g47xrWI}^K
zBr(miC?zSc+$cFb((9i^=#mFF~
z5+Y)0X$cWXFM`OUuuJ`VOk5ATb?gBg8_GKyY$m4yI^$W=cs0#3dG{Q0IYEd!%NjXTWSW
zG=R$FCc+d$1&T8gQ}W9p`oTh&;Rp-OqWt_4Rb0|AMY$EusX3q&4GTxGp&(f|()Gea
zqX?d%C^0V0%+xs~-lyEerOGWc-aM$hyetrwyvh=DN}+)Z3Gw2RN?2$Y<(KBAfV01W
zsIEqUSz3-r3WhMT45VaW1WN^p(Bzj6HyNZ|
z)rfEusT#RKV;H~dL8U5MLckjO;H+z8fE4=98KrsIpdt_yL`DW!qX87o1fn4=GbhJ6
zKPSHkoV1P8($b8QlU0qKic5;}vr~~|Qp}RfOwH&Naj*;ujW`$=IpSb(gdB0O_#Qm*
zW(T#Sv6TsT#ZF<>#fU8at&V7H5FVKTyU)
zD(gVv0f{9gsYReF-%!=az!;vzVD*xbs<9Jl0Sd7YO9`2pf>P_@FJV9h97g$w6d$m>
zg`AIIVnwMri6xn3paNXg$Ou|KW6NB`I0501fc(t75=1EtPbJXYZUk#Rz-%hcFD*(=
zh3X=%1cq1!Awgvk~u^4{JqX4oNjg1&(iONn%Q3Ng_l7Dg73gXXfT4f)j39eqKp1v?2%9-N_l5IVnDg
zmHDNhmQis@VsbW&=ai9Olv$OZSCR;;$%{)8i%OtvKVy(?go@fJg5AUlKfn#@dWBQ
zi0`pvCMq^JjEqs+8?fpKDYqHI#E`R_B~%+M@eO=)!vYdqNU10o6v?W_pniw}q&EeL
zagYFJRaTN(Q35aFV2uc9A&K1LfC?q%WTt~k#+=kNkTy`1WF{x(I6{SUGgDGPQSVe-
z0O}Q{`+$|Xn1F16hy+2~@F1c5vecrq9B^AbBQqr>HBZ&psQ^^sfE#nBPO7F(ARid$
znW~z)fV^VrWTEcQbSla(Ni0c(Bt=l-
zBstyT&9+o%`$9`mSXv~M_+aV=PvV1iF|j8;BT$SP8GsWXs2T+iJRlWnqbUzoEJLy|
za*0Q$lxGGl&W2vfGlVvAkkS*Vtp%$Y^NUI{^3(J45_4Qr(o>T@p)6@=<%jsd*(ul@U;-U``~oP6KmXVD%z|2g=@{atAYdVU7p4mcZRP
zBdjqFE!-hZ96aiv#X2YiAgaI?;BTfNT3qlJkdXmRnvsr0MTwxUKCIk=7V$<#1cpx_
zE<`4=wGUxcGAOiFjf^ZzA;C?!>yKOH8mYD;dNI)4Sgf^6rMk!E-
z{Gd&9JgxQQ)Vz|^B2{B2ob7dNZDS(GI6!7nX%yHT+8%+H<)Bg-)T2jgp1@ix$el8J
zw2Q$dIF9K8upG+7400h4mW53PK_tLJ&^bD=$iJ2+{0s~V44y8IAr*0N=W@>JP5phm
zK2_6r)46~{3cezWTJLB7+cN#qveqSg@;!Pdm{sjfJGRetQi^!5kbYp$3BMGT-8nN4
zw#-yKHN)xO{QHmVmKi6XoAdmP@#i_|JKvp8KQ*V={`;rh?|#mE9e1uamg#|w%FftE
z?>qM#U(VMExWVO_S(;yz(J7-qUY~>=Kt^1!=pxDeCCvEk0Q*
zPdw$~(7BtJU}^QFh}--7njnXS%&C_YSGn7q7U0ysy33C-`rxPQ@%64ZH>G~OTYi6G
z;p1bGN&A1l+bv;VXR|H$_Ot5@l9G}f{QSq?-Q69y)Js%FMP)(g>M#y&ZsU|K`TPHh
zmEW)Z?pm3@?`Im1gaJeDtt|&9D!Xr~e}7{m^W|l}#vyqtj5&3ja~W6O*X6Qcnx^d5
zbD)J&_*2W%`1-%Cp{v6d1}=6xapsK6&reSaUtCZOSru|}!dfx?xSr~FJK3why*apa
zdfcq9XHuufwh5_vJy<@!?h?bj{q^-Z_xHs%Zxo(%;1XxpBr_{V#^VQz-rU$I#Zv$8
zXSzfV%&@BckCk3vInmT;l%fYo%kkS1)LZ$F=D1_p0^H^6tzq
z<-Kluy5Ihv#P)kt+NY*yHk;qC5uS65U&@5zc)$GhxXs=|5x0&CUE*7={lFtaFLsxR
zcG#K=Tk3zm-9B;3lqG93Uu}3I5;MQe-OVlQ|FZ8tZzT6WY?IEr@Zi^{)B4Z3PQQ5Z
zB4_>iIhMhneZHD{%TBwyyZrjHKP<&tv#wT1bFSO-$*bnWLH3NBo0jGtYdvtuY}tf1
zix^OHd2@5KM%WsO7fS1dGqCKQqJd#g&zlbK8(DnG{v@
z_WjQ1^A2aP-#blJ*D-XV6KlcZ_6<*FshZdH94b0FNmb#_k2llf>o}{HSq5*|Q1kES
z^Q|lGuIpOvO@DOEKO{ut#o1e`oH}olI5XQXi*AtBiLZPr3R3ms)#~*o`UMA@SZ{6l
zHre0qVjq)aqiW(&Wsm6Xc~_U+{St9{s&@PRy5GEykM%mY^U3-gE_!leqLfHGpKO<2
z?5+%HH-6hM0bz5?Bqf-t<;S{BU;GiIr
z6=Eq?rLU%JJ6ZhoYIyq9&v|_-hB;QoiR@U=kee78!1ic+QyY)uqRM6Ve?Bz7
zJ?AJEV*hul@`f*~HyjDld~@*mrcbf_-t*Q!ITp|@rdx2?*L>%hn#SGA8@}*+ciM_p
z{PEy>8X8yB`PxVf=7ul7?mIYsnP2a|?z;}RlZN_q#SJ16R+WElEqx0Ll|=%#
zK>}irK78`I$mGpGBMPMFfPwxOJuV&Y*hd#a4|KcDGRy6ovfE9>Lqck6oEcz@K9HL1zH9n!co*H^gaaEZq!&p1Kz^m8&M
z+1Gp$UdDTU)M=Jan^)_U@RI5BZ>OO5eeXiEKId~rJUf4i>9oQzdu727%QG%6a{D6UR=Lc}VcBOl
zeU4iBIN{_C|2-dmZM&Vf`^BeMzu)hdzhCp2*DUwe5?787(<{Ee{(ir{UwwX!(Tc*)
z58LIlrl);&%B_FdZ~u?O>RV_)FGu3DnuA8U*GrbXo7;V2)po@-C7~@-*9jfct62Q1
zN0@y}pIYuumWkJGnmS&jZ?c*k0Z3uoDEfA`{
z!+)v3w~M8r>bY|}KYTJzV^*`CDLm`WlZU*%Oy2vQ4;0Q@%ID2?E_sgT-);XjgO@dI
zTzPF>?ChtJm#R}%h_c;!ym$4VQ~LXVo#t5f``g>g>ul$kW{W*LH@Ca|UZwl#;{sYd
zy-pm(r|xdgkJnu$`aa`nwz}V(3pU>OHXe}P7Pc;i)6Ks2_1f(Pzu#`pHkP&hCYt~1
zk*medr^S!tQv)`fvDhZ-`T9}d?vu0q-mG4IYfti30hhZ{vrSdMzn!CR@-mi7=Pvu{
z#KUcw6D~Wi{va3H)K#z~J#u4`tL_ob_ieS0I@Pyyr*NEFzyF`qp7yB=msa}c%(W`r
zvf<+1>hIScc^bNK+lDuNz3gxAdc03|EB{0L|9^saZ|zQuz_hVMe}?kX)lyZqRa#sAcrn^G_By4SJh4oBiry&eB1IW2dH`joXG!qUaX
z{`Gc2nI}G944h45r)5?!@(bSGd}n8|de+rdOYdGiWpH-SjaiMqB<#C3q@*xZb@S{$
zWM5R(d*=UX{rwH)`(Iry@SFXT(}*c6D{Gb5nf~KvCC>C;fe7lJwEQhDUrIug9nRp^ETb**9il}7|7%!&8viTj&wl`2tXl3#)KQt*ZtRmR{vHji3=W{DQ9u?o5aI#~`
z9m(a_Pn0}6Bbhtv%vaZa>kdy!FXX&?Ep8dDG0N1qIsz{R2CyZYq~Ub(7idltsu=Y4+eTWEOf
zR90`ci;r(T{Pgs+se@3#7dy*bb)Elz-`8jVuY14uyFuY2m$TAOr~GMTm%Fe?sOJ0K
z@>{!9m6r*f_~O8sA{Y7Smg;r$pEk?y#E36dKmDyBH@Ls{=>5M>7qh?FB67tf#>TsP
zW_Uv8ExEqtrFG{f?Y=Ze?eg!%?AsF$v%UTQ%Vf@T9ZA{JA0Hmh^62UD@}F`bIb$jt
zuT;kUPf1gP-z63AousLz=60KRdHc!Jn|2nTD-BZ1yKzBz$@Np;zeq%W33}OUl<(x^
z6!u12E_c!P*ZT7_Uq{9?9JrKpkb9F=Q$*iI<}>%R7A9-j*vQPBIrA1jv}qrbEKZglKLZ
zUupk@%qXY#A_^NyHmqfxwyalugNg3Ka;^d%oqk#SPG$GYflG^Ar||s$edFLvvmqsHF
z9cE3H_vxAm*%8Z@{_m11m3O;+jX%jDhv(2?uLr+*Zx{Seo_kUEndFxj_tpg@%$&D{
z<>{$k?WxCJ`ka}!oh5kr`QwY0zW(s~V5&y!swGbkdnIH>6*Vr+d*|Ay
znT0!FIIOI*I6LgD#<#qA
z-8UGMFQ#9pII{n*(z^Ab%!ccE-F({BO@9bY%ly6H_5VU)!_S|OMr_X5w9Ri;^sP;~
z{CR)ewLj|o{XdgKC$?YUeaVGu8}@vkC~;-qIx9E6N?ZR3y;;8*-|c%W_9DB_S}6VD
z^CeSk5`+gv-SY`7PD3e%XLx}bhzdv~k<{d8Z~
ziAvMCbUuH7{*Wc&mZe+3zmVz6xx5=cZ)<(Jvgh)Os~c2bUF|%!A~AoFQ%YowRkq@-
z@^{_5+V*a1Uu4xKdQRVY&ptZ&XH(&=T`8B@t+J#Ye$7ga+wg?V-_1>J)s?(HL*=Th
z%S9h`KAZkynf7y%uiU1UOy62nS-tP#`k)es%wklDk#IYQ`lj
z7rCmc-R3=Bc<(Gj8H2>>*W3(im@b$)#6D)2!ghhxL9IcIVHslu1H=AP*IL}CH?!=S
z_%4^>fb@=brX8FIRx`YqC~y2}Lo>*#lr=fNq9Fg`#jBCQ@-SJ}JW%$E>z>49#R>cpyP;4b!+TzNT0=<
zz{2p&*I$%jL)_oy=Jo|UQx1NLoHu*YflDi|i9lWD{m5j}<-cKzu3g@O*hivP&HUCc1=+{MWjrm@dL=69xU
z>~jdtsG2rsmw))z3(|L&pSrO3X~x&zys!R$XMDlZ@c%x`gAKDjoew%Ku!r^c|B$$E
ztqO5(F2~)gs#R_P*8`S@@Q}T8rWJcd$1|RdKQ4Lu2`g)I)lr`>
zYSGLVYp>~WdRLwG*;8L&-B8Z>h3x|Gfoz6VtM*;konkY6wTKHdr=R}pIP<-)+nG|!
z9{fI*ICIN3aV2eYmx#DRVsBULEA^$hxDSU|g}1qaonVI_HWTwojLFFLZwY
zsr>2>ZN@F^7s3xtkemJfmh$pldlt?L4yr0LWq7e+$@7)RH4M2<8)lt-uNi93>{FON
z`@)~A{R`&&+a!1Q_fmDwg5tB2<@DFzc*NGvcluG*;}_-(XRkgJG@oeoJg6>~A^Nj{
z%2p}P2CD+grnr&?XEk>Bci*{{E;B82_cG@6O9Fvk&D6`TgyvpLsLW7al=n4u-l@Ia
ziY-g;ntp#CdCB_mti*HLpZFOn*coi?Wv8_$l~13~{$Xa5;PTCB37!uMy6y#Vp6H$V
z|LG;U_}Wta4^s12n%|$lK*8_*b@6E}((%e1{i~XOBp!IVqi&_Pe#iqM2HT_dpAFRZ
zdOlOWb9V>#y7IdgI>vpu?w58|T**7+>*TpGq#^jjis&lEH8#hq?s+D>441Fp@TB+r
z?JHZR#L4YdacT*EyPM(90vijarrCOT4m@OIXY_jC^SZ5O?uO4_bu4R_KR^CFqE7ki
z`#I8zbG`}hSTp%@irjzC(Bo5IY%Mz6E^V}rLHP#Xs;@425_4G&)YU9};Fs^;u6p~Z
z^w#-bBD0?y6ErX1vFug|<0oDQ8`cB+0-5!=3&QV;cLXwg)84V!ze=fLZi$)ch53T_
z&H22aEi13osuf=J>V+|ja;e9>rVxe9*FG)oSDcS*jp^c=!!phB@Fe~VyO)G7Q*Y{M
zkXjM4dxuR8%jiVoXz@{JW{x><7)6+Qvo!XkbmXYrlXG{
zZ(Y)1y`j;#6x0WvJiS{eV%ObYOx}%4Zy(>``d2t2N~Pq)D!~XY9cQy2cS1KFxD@;C
z&u;UNA3tW?+M@Yx$78;I5?$-9Z^hnnaBqKeuP$@`RxS>mwO4n4$W~T){OD1@{<_+n
zJ39m)MRuh}{#blY*px%(Zc>`;q&NOEjZzKZ0}S^MJc`_v`XTx{UOGy4vai5J&y+IMhs&P}6Q
z+e=MiPj+)zub;}L^Eo%n-%i3t=j;16_prUbzvC(*GV651f)ceR6gF*dX7r4pw@5%b)S)|eXpP$9|S;;t^zx2*vQ}u7IVh_)o2ek!cbS{^vJ_z|G=5qDnTfa$8
z3meK_2mF-Y%sBb&2~OXF$xD72orMuEKNlyg?&n{@(sqQqn
zQ!cIJT;wfTx3c_rjrJK=YBw@D9tcYMa3hVuQiH8O=fjP(D;~~1S_v}Kti^0}6;*v!
z#ZKt=E^)t{)s>zZ<>W7_u%TqtPS$D5biw_J*8o3MVqVg7_4~`#u9@dP?9TH8RXU(9U(vsWmCdaiQY^x&GI`$qn)b`Q{D-9Sob(q#
zO&P4G4L{upU3JG&Yv1?%Arp*_eLbh$%Y2|?LTpILF)@Z^D?KLkd_P;TtJ9ImZu>8(
z6*peKXH3Y5pXZxgamUUvCpK=2)gP7*}$+-#vwZiry%NP_UM1C)2*vI%`71N&lxP8aG
zgXF{XzlD4}@_OdBj=kIh5w~vJ{fkxyHP`Alq`Y}MKjh52lMK@sA|47W&j}7_dQ!;n
znK6fP1J8j9)?RjoD}nQ5=07f}&}eufbABt!2aSg1#b1_8`joJJ)%AD=115&+{^y@u
z%&wXy%it6K^}_4h%RjxoSG48VU$(FRe>0RY9r!PNqMflP=yF}vwWSPy;`^-hL*t7Q
zY6_d?+|#yqshSOPQ~?`mZ*%!w{Uyff=N3g6_T|1;ZCsi;`}x+iNWlZer<7F$W^k;z
zUKyLw_p(HrA^zh#A%=d9iVMQ2J^6=%zH{{Y9sb%U8W^Yk&^7BXJbZpgb
zy>WVltz-KmF-LE%#ZT)~A$e`dC%pr<@z@9aT|To|Ecx1<{K9v4EVtA>sx#UD-E4}+
z!L50(Cpjl%&Q>B*}M3l2}3
zx7=<FHn1Ap(6f>kxb-!sj
zz3LUyg}ry?@sW93$5pYy$!BaJsac@WL>
zAf4gMJwJ&T^WL$>JvRROELm9O$jiU;m=;t|Q@%Qh(Xk?A>Un-xm~ik&HvyY$t~I&s@|&gNX%`Byxf-XX30&9
zFQ)_l?6GI%p1#KQ@UN{OF5ajR(U0@_tiyTQXsP$3-Z|s~R;Z!D
zI^${UJdXT+Z0DTzN=hekVn<)>uZIWt`tlp@B}_MOOM3roXNRnTSS?$x`=8tTkA6j&
z3VgnmaHOOns98U8=C=RQU*xR>jqT%mOuN;xUoY9$xj?Vf)iY$@tj&5Er&;R%{8YF5
z-Qj*`4XFDV^?YlZ=le-$@b^5)+?K7?4&zU5=$I|xPv8?6uPBYAjGydzJ`=DT!UqsR0
zDQ~Y{H~E@%BWzpiy`=K5O16I6CQ8Tb>-1)6(Yl^8vx05w^jlAiaz&)Y6XGN8{r2F=
ziVv4nZZGit`)JAGqKkL<3%<*4cZe^(Gtq+EUVZ5kPVc7Dqt|0jG2ZKw?_KgF{=>lz
zIVm0My%&Tyjl~ikzk0c2nbq|J#s2E`)~)Ys+mjz%*%`4nL+D@S>V5nPy`U%_Zrd+x
zyPKF%6zhY_s;8Y~=S#0#)9hZ;Qra24^**P!?b>)d(O#(nwc^5CJ6@{a`Ts+EMa)Vj
zA+>MLYcu-%vnL<8wB=r7Lgua}Cyj=CF)R(NtpVFaW^M-!OUZSL=+!>9U9^Ur;MF+pEP(rY2wV2iVP
zcPRq{`&>^K#}J2I=`+Q3b{?EOabl;|*4fEtI_BNla>u;%&82wemp=kymhGIt5mL21
zOs#Qg=2qyi#I;9cNaTwO_Y3mM)
zsvazdUTS9G8)E(+HuI6WYkk|&``t2r2{X@XHKV4}KH`^3&28tcwXNwbo#XTH
zXY%Vw2QF>71|2i0K0Ikz!iBZYnky~l&g^^@7``|oFz=0|x6QA^#*^o4+pWu~;~O0U
z8l3ePO{kBkIwj*+5|$v@60tK~6s-xxspISYVkHyHG(Xer2QF=S%0k*GO-x?AxJm2^
zkq2?C74z>`%AMz&Ql?WOwRQG~Pbr>d0S*b7?AjjEdzLgDxU}$^ScKJ;f=fBawG=j#
zta!;Xjn8zB$?c6?96GXDR*g%YdDYLBtzl|I>QBcdt7CSkJGzdE8>~+eV0B^q&n3~8
zdTZUw12aurLY}f-oaD5!f$g-jv|wrWgB{1Z=nh?OPe`QcK~z+;K|nbHS~-g^v`1CWkA2n$<2Sc=kZm
zu}KFm&G4J#EyA(H;lhTL#yOLgznjN6$G*Mi@owg;itlHKD6Dz2l|w&VI#G~UAWZN3
z4u^lM1R`dM#LJ1ZPGS1lv~ggZEMJrB371ZdFC7L;9MWlxb(Hpti8@~!{;?U{rC6x!TDKNS7qAAc`$kFM@0V0
zXl0t$lv-0`^XSo|E%R^Oh|vDRTPe$Qdctf6sRu%=Hms*pPftty`ROUBPd3Xm`FOV!Wl%-!O^=zs7MlWlHz>w!yUY7^NkLl{9xBz?2XuH3{QUg<
ze7B!os6)cbpu=9roD??)^OmG
z(Mspwi3f~Kc6qOT2e#~P!FS$-mlHJ-rZUZIS~_R*maGU*+eHn(LGC)bx#T{>G`UWu
zFwF;=5mBnE=DwO}>TMnAeCtsPE6X&$ZSxwpaqIAQs6@IjGIMe0@OC_TqQm5^9_@cC
zPo0%zTG=Ky#VxK0ksOP>Ce90SNO)PanOi3|FX839RIUoHX`7_;Ztj@W<-&L!Wc(>h
z@9VwsFDGau%mlflV%oG`?Uf!P1#>~X*K5)jIB!bJbWk~|#3cdB#11Oz>4FhfH>)m{
zS!*h6*fQCaYoRXFX(xfut}jcNn7pUYj!k%3#O(cDC)gn&bLN$WD?Jh&vAn%=bxXn6^&1I6#q6S7*mT7D*MMYwbODC^Y&)q(miOJhKaSe
z={4gEijkX56egtzvhrvsY%o!nGo)|jf;q2*c?#Mrp>
z^jWeqN+
z2TEEWb?%xsEOlSh5DE%}u+FUIRVimL`CXf^%_5Q!T}=SeBOE
z2QHnwHhJ4^PsYZjbJqlNgS8R#