root / proj / doc / report / report.tex @ 399
History | View | Annotate | Download (21.7 KB)
1 |
\documentclass{article} |
---|---|
2 |
% Hyperreferences |
3 |
\usepackage{hyperref} |
4 |
% Margins |
5 |
\usepackage[top=35mm,bottom=35mm,left=25mm,right=25mm]{geometry} |
6 |
% Graphics and images |
7 |
\usepackage{graphicx} \graphicspath{{./images/}} |
8 |
\usepackage{subcaption} |
9 |
\usepackage{float} |
10 |
% Encodings (to render letters with diacritics and special characters) |
11 |
\usepackage[utf8]{inputenc} |
12 |
% Language |
13 |
\usepackage[english]{babel} |
14 |
% Section pagebreaks |
15 |
\usepackage{titlesec} |
16 |
\newcommand{\sectionbreak}{\clearpage} |
17 |
\newcommand{\sectionnobreak}{% for when I want a section that does not break |
18 |
\global\toggletrue{afterpart}% |
19 |
\section |
20 |
} |
21 |
% Source code |
22 |
\usepackage{listings} |
23 |
\usepackage{xcolor} |
24 |
\renewcommand{\lstlistingname}{File} |
25 |
\lstset{ |
26 |
frame=tb, % draw frame at top and bottom of the code |
27 |
tabsize=4, % tab space width |
28 |
numbers=left, % display line numbers on the left |
29 |
showstringspaces=false, % don't mark spaces in strings |
30 |
commentstyle=\color{green}, % comment color |
31 |
keywordstyle=\color{blue}, % keyword color |
32 |
stringstyle=\color{red} % string color |
33 |
} |
34 |
\lstdefinelanguage{Maxima}{ |
35 |
keywords={log,jacobian,determinant,subst}, |
36 |
sensitive=true, |
37 |
comment=[n][\itshape]{/*}{*/} |
38 |
} |
39 |
% Tables with bold rows |
40 |
\usepackage{tabularx} |
41 |
\newcommand\setrow[1]{\gdef\rowmac{#1}#1\ignorespaces} |
42 |
\newcommand\clearrow{\global\let\rowmac\relax} |
43 |
\clearrow |
44 |
\usepackage{multirow} |
45 |
% Math stuff |
46 |
\usepackage[mathscr]{euscript} |
47 |
\usepackage{amsmath,amssymb} |
48 |
\usepackage{mathtools} |
49 |
\usepackage{enumitem} |
50 |
\newcommand{\expnumber}[2]{{#1}\mathrm{e}{#2}} % scientific notation |
51 |
% Definitions, theorems, remarks,... |
52 |
\usepackage{amsthm} |
53 |
\newtheorem{definition}{Definition}[section] |
54 |
\newtheorem{theorem}{Theorem}[section] |
55 |
\newtheorem{corollary}{Corollary}[theorem] |
56 |
\newtheorem{lemma}[theorem]{Lemma} |
57 |
\renewcommand\qedsymbol{$\blacksquare$} |
58 |
\theoremstyle{remark} |
59 |
\newtheorem*{remark}{Remark} |
60 |
% Contents title |
61 |
\addto\captionsenglish{\renewcommand*\contentsname{Table of contents}} |
62 |
% Headers and footers |
63 |
\usepackage{fancyhdr} |
64 |
\pagestyle{fancyplain} |
65 |
\fancyhf{} |
66 |
\lhead{ \fancyplain{}{LabWars - Final report (LCOM 2019/20)}} |
67 |
\lfoot{ \fancyplain{}{T5G03}} |
68 |
\rfoot{ \fancyplain{}{\thepage} } |
69 |
% |
70 |
\newcommand{\email}[1]{ |
71 |
{\texttt{\href{mailto:#1}{#1}} } |
72 |
} |
73 |
\newcommand{\role}[1]{ |
74 |
\begin{tabular}{l l} |
75 |
\begin{minipage}[t]{30mm} \textbf{Roles} \end{minipage} & |
76 |
\begin{minipage}[t]{125mm} #1 \end{minipage} |
77 |
\end{tabular}\\ |
78 |
} |
79 |
\newcommand{\func}[1]{ |
80 |
\begin{tabular}{l l} |
81 |
\begin{minipage}[t]{30mm} \textbf{Functionalities} \end{minipage} & |
82 |
\begin{minipage}[t]{125mm} #1 \end{minipage} |
83 |
\end{tabular}\\ |
84 |
} |
85 |
% Metadata |
86 |
\title{\Huge LabWars \\ \Large Final report \\ \vspace*{4pt} \large LCOM 2019/20} |
87 |
\author{ |
88 |
T5G03\\ |
89 |
\begin{tabular}{r l} |
90 |
\email{up201806429@fe.up.pt} & Diogo Miguel Ferreira Rodrigues \\ |
91 |
\email{up201806554@fe.up.pt} & Telmo Alexandre Espirito Santo Baptista |
92 |
\end{tabular} |
93 |
} |
94 |
\date{06/01/2020} |
95 |
% Document |
96 |
\begin{document} |
97 |
%\begingroup |
98 |
\maketitle |
99 |
% \let\clearpage\relax |
100 |
% \setcounter{tocdepth}{2} |
101 |
\tableofcontents |
102 |
%\endgroup |
103 |
\section{User instructions} |
104 |
\subsection{How to play} |
105 |
In all game modes, the controls are the same: |
106 |
\begin{itemize} |
107 |
\item \textbf{WASD} to move North, East, South and West respectively. |
108 |
\item \textbf{Mouse left-click} to fire a bullet. |
109 |
\item \textbf{Ctrl+'+'} and \textbf{Ctrl+'-'} to zoom in and out. |
110 |
\item \textbf{ESC} to escape game mode (go back). |
111 |
\end{itemize} |
112 |
\subsection{Main menu} |
113 |
On startup, users are greeted by a \texttt{Loading...} message, briefly followed by the main screen. |
114 |
\begin{figure}[H] \centering |
115 |
\includegraphics[scale=0.45]{main_menu} |
116 |
\caption{Main menu} |
117 |
\end{figure} |
118 |
Using the mouse movement and clicks, the user can select one of the avaliable options: |
119 |
\begin{itemize} |
120 |
\item \textbf{Single player}: Go to single player selection menu, to select one of the single player game modes. |
121 |
\item \textbf{Multiplayer}: Go to multiplayer mode, allowing to select more options. |
122 |
\item \textbf{Chat}: Exchange text messages with another connected computer. |
123 |
\item \textbf{Exit}: Exit the game |
124 |
\end{itemize} |
125 |
The user can also exit the game by presing \textbf{ESC}. |
126 |
\pagebreak |
127 |
\subsection{Single player} |
128 |
Upon entering into single player mode, the user is presented with a menu from which he can choose one of the options. \par |
129 |
\begin{figure}[H] \centering |
130 |
\includegraphics[scale=0.45]{singleplayer01} |
131 |
\caption{Single player menu} |
132 |
\end{figure} |
133 |
\begin{itemize} |
134 |
\item \textbf{Campaign}: campaign mode; kill all autonomous opponents. |
135 |
\item \textbf{Zombies}: zombies mode; kill as many zombies and survive as much time as possible. |
136 |
\item \textbf{Zombies Ranking}: Scoreboard with the highest scores obtained on zombies mode. |
137 |
\item \textbf{Back}: go back to main menu. |
138 |
\end{itemize} |
139 |
The user can also go back to main menu by pressing \textbf{ESC}. |
140 |
\pagebreak |
141 |
\subsubsection{Campaign} |
142 |
In campaign mode the goal is to kill all the opponents in the map as fast as possible, while sustaining as little damage as possible. |
143 |
\begin{figure}[H] \centering |
144 |
\includegraphics[scale=0.45]{campaign01} |
145 |
\caption{Campaign mode} |
146 |
\end{figure} |
147 |
\pagebreak |
148 |
\subsubsection{Zombies} |
149 |
In zombie mode the goal is to kill as many zombies and survive as much time as possible. Zombies slowly follow the player and attack the player when in short range. Once the player kills a zombie, a new zombie spawns in a random part of the map, with more life than all previous zombies. |
150 |
\begin{figure}[H] \centering |
151 |
\includegraphics[scale=0.39]{zombies01} |
152 |
\caption{Zombies mode} |
153 |
\end{figure} |
154 |
After exiting zombie mode, the player can check the scoreboard by selecting \textbf{Zombies Ranking}. |
155 |
\begin{figure}[H] \centering |
156 |
\includegraphics[scale=0.39]{zombies_scoreboard01} |
157 |
\caption{Zombies mode scoreboard} |
158 |
\end{figure} |
159 |
\pagebreak |
160 |
\subsection{Chat} |
161 |
This chat tool was initially designed as a simple, text mode, test communication between different machines. We have however decided to include it as a functionality in the project for a number of reasons: |
162 |
\begin{enumerate} |
163 |
\item It was easy to develop the graphical part and integrate in the project. |
164 |
\item Having a friendly functionality that uses the communication modules allows for faster debugging; in case the computers are not properly connected, or if during development something stops working we can immediately check if the communication modules also stopped working. |
165 |
\item It served as a minimal insurance that our project would integrate the communication modules, in case we could not implement multiplayer mode. |
166 |
\item It is a useful feature. |
167 |
\end{enumerate} |
168 |
\begin{figure}[H] \centering |
169 |
\includegraphics[scale=0.45]{chat01} |
170 |
\caption{Chat environment} |
171 |
\end{figure} |
172 |
The chat can be used for exchanging messages of up to 75 characters directly writable with the keyboard. The character limit was imposed to prevent strings from rendering as wider than the input box, and the fact they should be directly writable with the keyboard simplifies the process of capturing scancodes, having as downside not allowing to write characters that require more than one key press (like exclamation or question marks in a Portuguese keyboard). \par |
173 |
\begin{figure}[H] \centering |
174 |
\begin{subfigure}[b]{0.48\linewidth} |
175 |
\includegraphics[width=\linewidth]{chat02_01} |
176 |
\caption{Computer 1 chat} |
177 |
\end{subfigure} |
178 |
\begin{subfigure}[b]{0.48\linewidth} |
179 |
\includegraphics[width=\linewidth]{chat02_02} |
180 |
\caption{Computer 2 chat} |
181 |
\end{subfigure} |
182 |
\caption{Two users interacting via chat} |
183 |
\end{figure} |
184 |
The user can exit the chat mode by pressing \textbf{ESC}. |
185 |
\section{Project status} |
186 |
All functionalities previously presented were fully implemented, with the exception of: |
187 |
\begin{itemize} |
188 |
\item \textbf{Campaign}: autonomous opponents were supposed to follow a pre-programmed path and shoot on sight at the player. Currently, they don't do either of those. |
189 |
\item \textbf{Multiplayer}: still working on it. |
190 |
\end{itemize} |
191 |
The I/O devices used in the project are presented in the following table. |
192 |
\begin{center} \begin{tabular}{c || l | c} |
193 |
\textbf{Device} & \textbf{What for} & Method \\ \hline |
194 |
Timer & Frame rate, time since beginning of game & Interrupts \\ |
195 |
Keyboard & Player movement, writing chat messages & Interrupts \\ |
196 |
Mouse & Player orientation, shooting, selecting options in menus & Interrupts \\ |
197 |
Video card & In-game drawing, menus & None \\ |
198 |
RTC & Scoreboards & Interrupts \\ |
199 |
Serial port & Chat communication, multiplayer modes & Interrupts |
200 |
\end{tabular} \end{center} |
201 |
To manage all interrupt subscriptions, the general function \texttt{unsubscribe\_interrupt} was implemented and used. |
202 |
\subsection{Timer} |
203 |
Timer 0 is used to generate periodic interrupts at a rate of 60Hz, essentially controlling a large part of what the program does.\par |
204 |
Timer interrupts regulate screen refreshing, which happens at a rate of 60Hz. In all game modes, timer interrupts serve not only the purpose of refreshing the screen, but also to process all the game data: collisions, movement, path-finding algorithms, etc. \par |
205 |
To manage timer interrupt subscriptions, functions \texttt{subscribe\_timer\_interrupt}, \texttt{timer\_int\_handler} and \texttt{timer\_get\_no\_interrupts} were implemented and used. |
206 |
\subsection{Keyboard} |
207 |
The keyboard was configured to issue interrupts on key presses and releases. \par |
208 |
The keyboard is used to control some menus (namely using \textbf{ESC}), to input text in the chat, and to control player movement in the game and zoom in/out options.\par |
209 |
To manage keyboard interrupt subscription, function \texttt{subscribe\_kbc\_interrupt} was implemented. To manage keyboard interrupts, functions \texttt{kbc\_ih}, \texttt{keyboard\_get\_done}, \texttt{keyboard\_get\_scancode} and \texttt{update\_key\_presses} were implemented and used. |
210 |
\subsection{Mouse} |
211 |
The mouse was configured to issue interrupts on movement and button presses. \par |
212 |
The mouse is used to control all menus (position and buttons), as well as allowing the player to aim at the opponents (position) and shoot bullets (buttons). \par |
213 |
To manage mouse interrupt subscription, function \texttt{subscribe\_mouse\_interrupt} was implemented. To manage mouse interrupts, functions \texttt{mouse\_ih}, \texttt{mouse\_parse\_packet} and \texttt{update\_mouse}, among others, were implemented. |
214 |
\subsection{Video card} |
215 |
The video card was configured to work in graphic mode with direct color encoding, with resolution 1024x768 pixels and 8 bits for each color component Red, Green and Blue, yielding a total of approximately 16.8 million colors. \par |
216 |
Simple buffering was used to eliminate flickering. All graphics are first drawn to the scree buffer, and only after all graphical operations (that is, at the end of the processing of the timer interrupt) is the buffer information copied to the VRAM. \par |
217 |
The modules \texttt{rectangle\_t} and \texttt{menu\_t} were developed for displaying simple shapes and menus. The modules \texttt{basic\_sprite\_t} and \texttt{sprite\_t} were developed for displaying moving sprites, allowing for rotation around the "center" of the image, as well as scaling and movement. \par |
218 |
The modules \texttt{font\_t} and \texttt{text\_t} were developed to allow for dynamic rendering of text, using as default (and, for now, sole) font Consolas. The only type of supported fonts are bitmap fonts. \par |
219 |
Configuration of the UART was made using the functions \texttt{graph\_init} and \texttt{graph\_cleanup} developed during the project. Drawing to the buffer is made primarily through the functions \texttt{graph\_get\_XRes}, \linebreak \texttt{graph\_get\_YRes}, \texttt{graph\_set\_pixel}, \texttt{graph\_clear\_screen} and \texttt{graph\_draw}.\par |
220 |
Basic sprites are constructed from small XPM files, or loaded at runtime from XPM2 files. A XPM2 file is a XPM file stripped from all the C syntax, leaving only the strings. This format is easier to load at runtime than XPM. A XPM file can be easily converted to a XPM2 file using the function \texttt{xpm\_save\_as\_xpm2}. All XPM arrays of strings are loaded to bitmaps using the function \texttt{xpm\_load} provided by the LCF. |
221 |
\subsection{Real-Time Clock (RTC)} |
222 |
The RTC was configured to issue interrupts and if the interrupt source is an Update Event, in which the bit 4 of Register C will be set, then the values of time will be updated by reading their registers.\par |
223 |
The date isn't updated unless asked, this is, on the interrupt notification the date isn't updated, and the reading process is different from the time. If it's asked to read the date then the date registers will be read two consecutive times, and will repeat the process if the date values of consecutive readings isn't equal, this ensures the values read are correct in case any update occurs. These updates aren't as frequent as the time values so there's no need to update it every second.\par |
224 |
To manage RTC interrupt subscription, function \texttt{subscribe\_rtc\_interrupt} was implemented. In order to enable the Update Event Interrupts, the function \texttt{rtc\_set\_updates\_int} that enables or disables the Update Events interrupts by writing to bit 4 of register B. To manage mouse interrupts, function \texttt{rtc\_ih} was implemented, that verifies if the source if from an Update Event by reading register C and verifying bit 4 and if so updates the time values (seconds, minutes and hours). To ease the process of reading and writing values to the RTC, functions \texttt{rtc\_read\_register} and \texttt{rtc\_write\_register} were implemented as general functions, while having specific functions for reading the date and time values, such as \texttt{rtc\_read\_min} among others, that use the general functions cited above.\par |
225 |
The RTC is used to obtain date and time for the scoreboards on the game-modes. |
226 |
|
227 |
\subsection{Serial port} |
228 |
The UART was configured to issue interrupts for Receiver Ready and Transmitter Empty. Communication is processed with the same parameters at both ends, at a bit-rate of 9600bps, 8 bits per char, 2 stop bits. UART FIFOs are used, with trigger level of 4 bytes per interrupt. The protocols that were developed will be discussed in section \ref{sec:details}. \par |
229 |
In multiplayer mode, data is transferred from host to remote at a frequency of 60Hz, with each message having at least 24 bytes. Data transference from remote to host is made whenever needed, with each message having on average 9 bytes. |
230 |
\pagebreak |
231 |
\section{Code organization/structure} |
232 |
\subsection{\texttt{libs}} |
233 |
Collection of useful classes and functions. This module was developed with the goal of being as general and independent from \texttt{proj} as possible, although there are rare occasions where sub-modules of \texttt{libs} include sub-modules of \texttt{proj}. |
234 |
\subsubsection{\texttt{classes}} |
235 |
Provides classes \texttt{list\_t} and \texttt{queue\_t}. These classes achieve generality by storing pointers to \texttt{void}, which have as major disadvantage requiring the use of \texttt{free}. \par |
236 |
\subsubsection{\texttt{graph}} |
237 |
Provides basic elements for screen drawing, like drawing pixels, rectangles, text. Manages screen buffering and VRAM. |
238 |
\subsubsection{\texttt{uart}} |
239 |
Provides basic functions and the NCTP protocol for communication between computers. |
240 |
\subsubsection{\texttt{utils}} |
241 |
Common functions, as well as functions for handling XPMs and XPM2 file format. |
242 |
\subsubsection{others} |
243 |
\begin{itemize} |
244 |
\item \texttt{kbc}: Manage mouse and keyboard |
245 |
\item \texttt{rtc}: Manage RTC, get current time |
246 |
\item \texttt{timer}: Manage timer 0 interrupts and subscriptions |
247 |
\end{itemize} |
248 |
\subsection{\texttt{proj}} |
249 |
\subsubsection{\texttt{campaign}} |
250 |
Campaign mode module. |
251 |
\subsubsection{\texttt{chat}} |
252 |
Exchange messages with connected computer. |
253 |
\subsubsection{\texttt{ent}} |
254 |
One of the most important modules. Implements the most important entities, and controls their interactions. |
255 |
\subsubsection{\texttt{hltp}} |
256 |
Provides function to interpret and send information for the serial port. |
257 |
\subsubsection{\texttt{proj\_func}} |
258 |
Functions related to the game dynamics, such as updating the game state, updating movement, keys presses, game timer and building the information for serial port. |
259 |
\subsubsection{others} |
260 |
\begin{itemize} |
261 |
\item \texttt{interrupts\_func}: group some interrupt handling functions together |
262 |
\item \texttt{makecode\_map}: map makecodes to chars |
263 |
\item \texttt{proj\_macros}: macros used throughout the project |
264 |
\item \texttt{proj\_structures}: mostly structures for transmission via HLTP |
265 |
\item \texttt{singplayer}: simple menu |
266 |
\end{itemize} |
267 |
\begin{center} \begin{tabular}{c | c | c | c | p{45mm} | p{45mm}} |
268 |
& & \textbf{Weight} & \textbf{Resp.} & \textbf{Contribution DR} & \textbf{Contribution TB} \\ \hline |
269 |
\multirow{7}{*}{libs} |
270 |
& classes & 13\% & DR & Everything & - \\ \cline{2-6} |
271 |
& graph & 10\% & DR & Most part & Some contributions \\ \cline{2-6} |
272 |
& kbc & 2\% & TB & In labs & In labs, adapted to project \\ \cline{2-6} |
273 |
& rtc & 4\% & TB & - & Everything \\ \cline{2-6} |
274 |
& timer & 2\% & TB & In labs & In labs, adapted to project \\ \cline{2-6} |
275 |
& uart & 16\% & DR & Everything & - \\ \cline{2-6} |
276 |
& utils & 3\% & DR & In labs, most part & In labs, some contributions \\ \hline |
277 |
\multirow{12}{*}{proj} |
278 |
& campaign & 3\% & DR & Most part & Some contributions \\ \cline{2-6} |
279 |
& chat & 7\% & DR & Everything & - \\ \cline{2-6} |
280 |
& ent & 10\% & DR & Everything except TB & \texttt{bullet\_t} \\ \cline{2-6} |
281 |
& hltp & 4\% & DR & Most part & Some contributions \\ \cline{2-6} |
282 |
& interrupts\_func & 3\% & TB & Small contribution & Most part \\ \cline{2-6} |
283 |
& makecode\_map & 1\% & TB & - & Everything \\ \cline{2-6} |
284 |
& proj\_func & 9\% & TB & $1/2$ & $1/2$ \\ \cline{2-6} |
285 |
& proj\_macros & 1\% & TB & - & Everything \\ \cline{2-6} |
286 |
& proj\_structures & 1\% & TB & - & Everything \\ \cline{2-6} |
287 |
& scoreboards & 2\% & TB & - & Everything \\ \cline{2-6} |
288 |
& singleplayer & 1\% & DR & Everything & - \\ \cline{2-6} |
289 |
& zombies & 8\% & DR & Everything & - |
290 |
\end{tabular} \end{center} |
291 |
\section{Implementation details} \label{sec:details} |
292 |
\subsection{Object-oriented programming} |
293 |
Object-oriented programming was implemented to its greatest extent possible. Classes were declared using the \texttt{typedef struct} expression, and their public methods declared in the corresponding header file, always requiring as first argument a pointer/const pointer to that class. Classes were defined in the corresponding source file, and private member functions were defined as \texttt{static}.\par |
294 |
The majority of entities were encapsulated in classes. |
295 |
\subsection{XPM and XPM2} |
296 |
The extensive use of large XPMs by simply including them with an \texttt{\#include} directive gives rise to large executable files, besides making it harder to change the used XPMs without recompiling the project.\par |
297 |
The XPM2 file format is similar to XPM, except it is stripped from all the C syntax, making it a plain text file. This file format has the main advantage of being easy to load on runtime, unlike XPM that would require extensive parsing.\par |
298 |
To use the XPM2 file format, two functions \texttt{xpm\_save\_to\_xpm2} and \texttt{xpm\_load\_xpm2} were implemented; the first one to convert XPM files to XPM2 files, and the second one was used in the project to load the XPM data (as an array of C-strings) from the XPM2 file format. |
299 |
\subsection{Communication protocols} |
300 |
\subsubsection{Non-Critical Transmission Protocol} |
301 |
NCTP was designed to encapsulate the basic UART functions that were developed, by providing a set of middle-level functions that allow communication between computers.\par |
302 |
NCTP allows to send a message to another computer through the function \texttt{nctp\_send}, which sends the contents of a set of memory blocks as a single message. The first argument is the number of memory blocks, followed by an array of pointers to void denoting the beginning of each memory block, and an array of sizes, where \texttt{sz[i]} is the number of bytes of the block starting at \texttt{ptr[i]} that should be sent. On receiving, NCTP calls a user-provided function to interpret the message.\par |
303 |
NCTP uses queues on transmission and receiving. When a Tx interrupt occurs, NCTP tries to send as many chars of the transmission queue as possible, and on a Rx interrupts NCTP tries to extract from the receiving register as many chars as possible, putting them in the receiving queue.\par |
304 |
When a complete message is detected, the chars of the message are popped out of the receiving queue, and passed to the user-provided function for interpretation.\par |
305 |
A message is composed of a header, a body and trailling filler chars. The header is a pair of bytes denoting the size of the body. Then comes the body, and finally the trailling filler chars, whose purpose is to make sure the total size of the message is a multiple of 4, to prevent problems like a number of final message chars smaller than the trigger level causing a timeout. |
306 |
\subsubsection{High-Level Transmission Protocol} |
307 |
HLTP fills the gap left by NCTP, by providing a way to interpret messages. For that, HLTP keeps an enumeration of all the data types it knows, and for each deta type a pair of functions for coding and decoding a message containing that data type.\par |
308 |
HLTP inserts at the beginning of the body a byte indicating the data type, so the message can then be decoded on the other computer. |
309 |
\subsection{Path-finding} |
310 |
The implementation of the following behaviour of zombies was made using a modified version of Dijkstra's path-finding algorithm. Because running a regular Dijkstra every frame causes tremendous lag, a modification was made; for each pixel, it is known the zombie should go to another pixel. At the beginning, a complete Dijkstra is ran, and each time the position of the player changes Dijkstra is only ran in the vicinity of the player.\par |
311 |
This solution does not always provide the shortest path, but is significantly more efficient and returns a working path to the player. |
312 |
\end{document} |