papilot 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -2
- package/README.zh-CN.md +127 -0
- package/bin/papilot.mjs +113 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/Inconsolatazi4-Regular-Dbtq7t_r.otf +0 -0
- package/dist/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
- package/dist/assets/TeXGyrePagellaX-Italic-CSuYIstn.otf +0 -0
- package/dist/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
- package/dist/assets/cursor-editorTextHighlight-kG3uUOwL.svg +8 -0
- package/dist/assets/index-BhQFCMTx.js +131 -0
- package/dist/assets/index-CAV3-87U.css +1 -0
- package/dist/assets/pdf-view-DJo0X38w.js +598 -0
- package/dist/assets/pdf.worker.min-CjEcRF4W.mjs +28 -0
- package/dist/favicon.png +0 -0
- package/dist/index.html +4 -0
- package/examples/papilot-paper/.latex-review.json +4 -0
- package/examples/papilot-paper/appendix/proofs.tex +60 -0
- package/examples/papilot-paper/appendix/reference.tex +54 -0
- package/examples/papilot-paper/main.tex +57 -0
- package/examples/papilot-paper/preamble.tex +53 -0
- package/examples/papilot-paper/references.bib +158 -0
- package/examples/papilot-paper/sections/01-introduction.tex +66 -0
- package/examples/papilot-paper/sections/02-using.tex +134 -0
- package/examples/papilot-paper/sections/03-background.tex +97 -0
- package/examples/papilot-paper/sections/04-model.tex +185 -0
- package/examples/papilot-paper/sections/05-conflicts.tex +99 -0
- package/examples/papilot-paper/sections/06-building.tex +70 -0
- package/examples/papilot-paper/sections/07-geometry.tex +193 -0
- package/examples/papilot-paper/sections/08-notes.tex +104 -0
- package/examples/papilot-paper/sections/09-agents.tex +106 -0
- package/examples/papilot-paper/sections/10-usage.tex +56 -0
- package/examples/papilot-paper/sections/11-git.tex +62 -0
- package/examples/papilot-paper/sections/12-security.tex +41 -0
- package/examples/papilot-paper/sections/13-evaluation.tex +106 -0
- package/examples/papilot-paper/sections/14-related.tex +38 -0
- package/examples/papilot-paper/sections/15-conclusion.tex +27 -0
- package/package.json +85 -4
- package/scripts/demo.ts +21 -0
- package/scripts/service.ts +35 -0
- package/src/notes.ts +40 -0
- package/src/server/agents.ts +343 -0
- package/src/server/codex.ts +63 -0
- package/src/server/frontend.ts +15 -0
- package/src/server/main.ts +202 -0
- package/src/server/manuscript.ts +337 -0
- package/src/server/paths.ts +11 -0
- package/src/server/pdf.ts +27 -0
- package/src/server/review.ts +53 -0
- package/src/server/security.ts +15 -0
- package/src/server/store.ts +60 -0
- package/src/server/workspace.ts +530 -0
- package/src/shared.ts +28 -0
- package/vendor/latex-review/LICENSE +21 -0
- package/vendor/latex-review/dist/web/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
- package/vendor/latex-review/dist/web/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
- package/vendor/latex-review/dist/web/assets/index-DyzYnIGk.css +1 -0
- package/vendor/latex-review/dist/web/assets/index-HxBK-dls.js +19 -0
- package/vendor/latex-review/dist/web/index.html +18 -0
- package/vendor/latex-review/package-lock.json +3114 -0
- package/vendor/latex-review/package.json +49 -0
- package/vendor/latex-review/src/cli.ts +494 -0
- package/vendor/latex-review/src/client.ts +198 -0
- package/vendor/latex-review/src/core/build.ts +296 -0
- package/vendor/latex-review/src/core/changes.ts +126 -0
- package/vendor/latex-review/src/core/config.ts +270 -0
- package/vendor/latex-review/src/core/diff.ts +54 -0
- package/vendor/latex-review/src/core/files.ts +96 -0
- package/vendor/latex-review/src/core/git.ts +317 -0
- package/vendor/latex-review/src/core/outline.ts +314 -0
- package/vendor/latex-review/src/core/process.ts +111 -0
- package/vendor/latex-review/src/core/publish.ts +339 -0
- package/vendor/latex-review/src/core/review.ts +734 -0
- package/vendor/latex-review/src/core/store.ts +66 -0
- package/vendor/latex-review/src/core/tasks.ts +237 -0
- package/vendor/latex-review/src/render/inline.ts +805 -0
- package/vendor/latex-review/src/render/locate.ts +481 -0
- package/vendor/latex-review/src/render/pdf.ts +281 -0
- package/vendor/latex-review/src/render/pdfdiff.ts +272 -0
- package/vendor/latex-review/src/render/round.ts +236 -0
- package/vendor/latex-review/src/render/worker.ts +10 -0
- package/vendor/latex-review/src/runtime.ts +23 -0
- package/vendor/latex-review/src/server.ts +401 -0
- package/vendor/latex-review/src/types.ts +283 -0
- package/vendor/latex-review/src/web/ChangeMap.tsx +212 -0
- package/vendor/latex-review/src/web/PdfStrip.tsx +77 -0
- package/vendor/latex-review/src/web/fonts/GUST-FONT-LICENSE.txt +29 -0
- package/vendor/latex-review/src/web/fonts/LPPL.txt +415 -0
- package/vendor/latex-review/src/web/fonts/MANIFEST-newpx.txt +20 -0
- package/vendor/latex-review/src/web/fonts/README-newpx.txt +473 -0
- package/vendor/latex-review/src/web/fonts/README.md +12 -0
- package/vendor/latex-review/src/web/fonts/TeXGyrePagellaX-Bold.otf +0 -0
- package/vendor/latex-review/src/web/fonts/TeXGyrePagellaX-Regular.otf +0 -0
- package/vendor/latex-review/src/web/index.html +17 -0
- package/vendor/latex-review/src/web/main.tsx +10 -0
- package/vendor/latex-review/src/web/model.ts +153 -0
- package/vendor/latex-review/src/web/overview.tsx +146 -0
- package/vendor/latex-review/src/web/review.tsx +991 -0
- package/vendor/latex-review/src/web/style.css +1249 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
\section{Background}
|
|
2
|
+
\label{sec:background}
|
|
3
|
+
|
|
4
|
+
This section fixes notation for the four systems Papilot sits between: the
|
|
5
|
+
\TeX{} build, the PDF format, Git, and SyncTeX.
|
|
6
|
+
|
|
7
|
+
\subsection{The \LaTeX{} build as a fixed point}
|
|
8
|
+
|
|
9
|
+
\LaTeX{} resolves cross-references, citations and the table of contents by
|
|
10
|
+
reading auxiliary data written by the previous run~\cite{lamport1994}. Let
|
|
11
|
+
$x_k$ be the auxiliary state after run $k$ and $F$ the map computed by one run,
|
|
12
|
+
so that
|
|
13
|
+
\begin{equation}
|
|
14
|
+
x_{k+1} = F(x_k), \qquad x_0 = \varnothing .
|
|
15
|
+
\label{eq:fixed-point}
|
|
16
|
+
\end{equation}
|
|
17
|
+
A build tool such as \texttt{latexmk} repeats runs until a fixed point
|
|
18
|
+
$x^{\star} = F(x^{\star})$ is reached or a limit of $K$ runs is hit (five by
|
|
19
|
+
default). A single run suffices when $F(\varnothing) = \varnothing$, which is
|
|
20
|
+
the case only for documents without references. A typical paper converges in
|
|
21
|
+
two or three runs: the first records labels, the second resolves them, and a
|
|
22
|
+
third is needed only when resolving them moved a page break.
|
|
23
|
+
|
|
24
|
+
\begin{remark}
|
|
25
|
+
$F$ need not be a contraction. A reference whose resolution lengthens a line
|
|
26
|
+
can push a label onto the next page, whose page number shortens the line again;
|
|
27
|
+
the sequence $x_k$ then alternates between two states and the run limit ends
|
|
28
|
+
the build. Such documents are rare, but they are why a build key must include
|
|
29
|
+
the build settings and not only the sources (\cref{sec:build}).
|
|
30
|
+
\end{remark}
|
|
31
|
+
|
|
32
|
+
\subsection{Text in a PDF}
|
|
33
|
+
|
|
34
|
+
A PDF page places glyphs with a chain of matrices~\cite{iso32000}. The text
|
|
35
|
+
state contains the font size $T_{fs}$, the horizontal scaling $T_h$, the rise
|
|
36
|
+
$T_{\text{rise}}$, the character and word spacings $T_c$ and $T_w$, and the
|
|
37
|
+
text matrix $T_m$. A glyph is drawn in user space through the text rendering
|
|
38
|
+
matrix
|
|
39
|
+
\begin{equation}
|
|
40
|
+
T_{rm} =
|
|
41
|
+
\begin{pmatrix}
|
|
42
|
+
T_{fs} T_h & 0 & 0 \\
|
|
43
|
+
0 & T_{fs} & 0 \\
|
|
44
|
+
0 & T_{\text{rise}} & 1
|
|
45
|
+
\end{pmatrix}
|
|
46
|
+
\times T_m \times \mathrm{CTM},
|
|
47
|
+
\label{eq:trm}
|
|
48
|
+
\end{equation}
|
|
49
|
+
where $\mathrm{CTM}$ is the current transformation matrix. After each glyph
|
|
50
|
+
with horizontal advance $w_0$ (in thousandths of a text space unit) and a
|
|
51
|
+
positioning adjustment $T_j$, the text matrix moves by
|
|
52
|
+
\begin{equation}
|
|
53
|
+
t_x = \Bigl( \bigl(w_0 - \tfrac{T_j}{1000}\bigr) T_{fs} + T_c + T_w \Bigr) T_h ,
|
|
54
|
+
\qquad
|
|
55
|
+
T_m \leftarrow
|
|
56
|
+
\begin{pmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ t_x & 0 & 1 \end{pmatrix} T_m ,
|
|
57
|
+
\label{eq:advance}
|
|
58
|
+
\end{equation}
|
|
59
|
+
with $T_w$ applied only to the single-byte space character. \TeX{} rarely uses
|
|
60
|
+
$T_w$; it positions words with explicit adjustments $T_j$, which is why a PDF
|
|
61
|
+
produced from \TeX{} often contains no space characters at all.
|
|
62
|
+
|
|
63
|
+
\subsection{Git worktrees}
|
|
64
|
+
|
|
65
|
+
A Git repository is a store of immutable objects (blobs, trees and commits)
|
|
66
|
+
addressed by their hashes, together with a set of references~\cite{chacon2014}.
|
|
67
|
+
A \emph{worktree} is a checkout with its own working files $W_i$, index $I_i$
|
|
68
|
+
and head $\mathrm{HEAD}_i$, sharing the object store with every other worktree
|
|
69
|
+
of the same repository:
|
|
70
|
+
\begin{equation}
|
|
71
|
+
\mathcal{R} = \bigl(\mathcal{O},\ \mathrm{refs}\bigr), \qquad
|
|
72
|
+
w_i = \bigl(W_i,\ I_i,\ \mathrm{HEAD}_i\bigr), \quad i = 0, 1, \dots, t .
|
|
73
|
+
\end{equation}
|
|
74
|
+
The manuscript checkout is $w_0$; each task $i \ge 1$ gets its own $w_i$ on its
|
|
75
|
+
own branch. Because objects are shared, creating a worktree costs only the
|
|
76
|
+
working files, and because indexes are separate, a task can stage, diff and
|
|
77
|
+
build without touching $w_0$.
|
|
78
|
+
|
|
79
|
+
\subsection{SyncTeX}
|
|
80
|
+
|
|
81
|
+
SyncTeX records, while \TeX{} typesets, where each box, glue and kern came from:
|
|
82
|
+
an input file, a line and a column~\cite{laurens2008}. Positions are recorded
|
|
83
|
+
in \TeX{}'s scaled points. With
|
|
84
|
+
\begin{equation}
|
|
85
|
+
1\,\mathrm{pt} = \tfrac{1}{72.27}\,\mathrm{in}, \qquad
|
|
86
|
+
1\,\mathrm{bp} = \tfrac{1}{72}\,\mathrm{in}, \qquad
|
|
87
|
+
1\,\mathrm{sp} = 2^{-16}\,\mathrm{pt},
|
|
88
|
+
\end{equation}
|
|
89
|
+
a horizontal position $h$ in scaled points becomes PDF points as
|
|
90
|
+
\begin{equation}
|
|
91
|
+
x = \bigl(h + h_{\text{origin}}\bigr) \cdot 2^{-16} \cdot \frac{72}{72.27},
|
|
92
|
+
\label{eq:sp-to-bp}
|
|
93
|
+
\end{equation}
|
|
94
|
+
where $h_{\text{origin}}$ is the one-inch offset of \TeX{}'s origin from the
|
|
95
|
+
page edge, expressed in scaled points. A reverse query (``edit'') returns the
|
|
96
|
+
record nearest to a point on the page; a forward query (``view'') returns the
|
|
97
|
+
boxes produced by a source line. Papilot uses only reverse queries.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
\section{A model of review rounds}
|
|
2
|
+
\label{sec:model}
|
|
3
|
+
|
|
4
|
+
\subsection{Documents and edits}
|
|
5
|
+
|
|
6
|
+
We view a manuscript as a finite sequence of source characters over an
|
|
7
|
+
alphabet $\Sigma$, $\doc \in \Sigma^{*}$, of length $n = \abs{\doc}$. An
|
|
8
|
+
\emph{edit} replaces a half-open range of the source by new text.
|
|
9
|
+
|
|
10
|
+
\begin{definition}[Edit]
|
|
11
|
+
An edit is a triple $e = (a, b, w)$ with $0 \le a \le b \le n$ and
|
|
12
|
+
$w \in \Sigma^{*}$. Applying $e$ to $\doc$ gives
|
|
13
|
+
\begin{equation}
|
|
14
|
+
\doc \apply e = \doc_{[0,a)} \, w \, \doc_{[b,n)},
|
|
15
|
+
\label{eq:apply}
|
|
16
|
+
\end{equation}
|
|
17
|
+
whose length is $n + \delta(e)$ with \emph{shift} $\delta(e) = \abs{w} - (b - a)$.
|
|
18
|
+
\end{definition}
|
|
19
|
+
|
|
20
|
+
A task proposes a \emph{revision} $\edit = \{e_1, \dots, e_m\}$, a finite set
|
|
21
|
+
of edits against the same base document. Papilot groups nearby edits into
|
|
22
|
+
\emph{changes}, the units the author decides on.
|
|
23
|
+
|
|
24
|
+
\begin{definition}[Compatibility]
|
|
25
|
+
Two edits $e = (a, b, w)$ and $e' = (a', b', w')$ are \emph{compatible},
|
|
26
|
+
written $e \perp e'$, when their ranges are disjoint and not adjacent:
|
|
27
|
+
\begin{equation}
|
|
28
|
+
e \perp e' \iff b < a' \ \text{or}\ b' < a .
|
|
29
|
+
\label{eq:compatible}
|
|
30
|
+
\end{equation}
|
|
31
|
+
A revision is compatible when its edits are pairwise compatible.
|
|
32
|
+
\end{definition}
|
|
33
|
+
|
|
34
|
+
For a compatible revision we apply edits from right to left, so that earlier
|
|
35
|
+
offsets remain valid:
|
|
36
|
+
\begin{equation}
|
|
37
|
+
\doc \apply \edit
|
|
38
|
+
= \bigl(\cdots\bigl((\doc \apply e_{\sigma(1)}) \apply e_{\sigma(2)}\bigr)
|
|
39
|
+
\cdots\bigr) \apply e_{\sigma(m)},
|
|
40
|
+
\qquad a_{\sigma(1)} > a_{\sigma(2)} > \dots > a_{\sigma(m)} .
|
|
41
|
+
\label{eq:apply-revision}
|
|
42
|
+
\end{equation}
|
|
43
|
+
|
|
44
|
+
\begin{proposition}[Compatible edits commute]
|
|
45
|
+
\label{prop:commute}
|
|
46
|
+
If $e \perp e'$, then applying them right to left in either listing gives the
|
|
47
|
+
same document, and the result of \cref{eq:apply-revision} does not depend on
|
|
48
|
+
how the remaining edits of a compatible revision are ordered.
|
|
49
|
+
\end{proposition}
|
|
50
|
+
|
|
51
|
+
\begin{proof}
|
|
52
|
+
Assume without loss of generality $b < a'$. Applying $e'$ first changes only
|
|
53
|
+
positions at or after $a'$, so the range $[a, b)$ and every offset before it are
|
|
54
|
+
unchanged, and applying $e$ afterwards gives
|
|
55
|
+
$\doc_{[0,a)}\, w\, \doc_{[b,a')}\, w'\, \doc_{[b',n)}$. Applying $e$ first
|
|
56
|
+
shifts every later offset by $\delta(e)$; rewriting $e'$ as
|
|
57
|
+
$(a'+\delta(e), b'+\delta(e), w')$ yields the same string. Induction on the
|
|
58
|
+
number of edits, ordering them by start offset, gives the general statement.
|
|
59
|
+
\end{proof}
|
|
60
|
+
|
|
61
|
+
\subsection{Rebasing edits}
|
|
62
|
+
|
|
63
|
+
When one task's edit $e$ has been accepted, another task's edit $e'$, written
|
|
64
|
+
against the old document, must be moved onto the new one. The
|
|
65
|
+
\emph{transformation} of $e'$ past $e$ is
|
|
66
|
+
\begin{equation}
|
|
67
|
+
\tau(e', e) =
|
|
68
|
+
\begin{cases}
|
|
69
|
+
(a', b', w'), & b' < a,\\
|
|
70
|
+
\bigl(a' + \delta(e),\ b' + \delta(e),\ w'\bigr), & b < a',\\
|
|
71
|
+
\bot, & \text{otherwise,}
|
|
72
|
+
\end{cases}
|
|
73
|
+
\label{eq:transform}
|
|
74
|
+
\end{equation}
|
|
75
|
+
where $\bot$ signals a conflict that the author must resolve.
|
|
76
|
+
|
|
77
|
+
\begin{lemma}[Convergence of rebased edits]
|
|
78
|
+
\label{lem:tp1}
|
|
79
|
+
If $e \perp e'$, then
|
|
80
|
+
\begin{equation}
|
|
81
|
+
\doc \apply e \apply \tau(e', e) = \doc \apply e' \apply \tau(e, e') .
|
|
82
|
+
\label{eq:tp1}
|
|
83
|
+
\end{equation}
|
|
84
|
+
\end{lemma}
|
|
85
|
+
|
|
86
|
+
\begin{proof}
|
|
87
|
+
If $b < a'$, then $\tau(e, e') = e$ and $\tau(e', e)$ is $e'$ shifted by
|
|
88
|
+
$\delta(e)$, so the left side is the second computation in the proof of
|
|
89
|
+
\cref{prop:commute} and the right side the first. The case $b' < a$ is
|
|
90
|
+
symmetric.
|
|
91
|
+
\end{proof}
|
|
92
|
+
|
|
93
|
+
\Cref{eq:tp1} is the convergence property known from operational
|
|
94
|
+
transformation~\cite{ellis1989}. Papilot needs only the compatible case: an
|
|
95
|
+
incompatible pair is never merged automatically.
|
|
96
|
+
|
|
97
|
+
\subsection{Decisions and rounds}
|
|
98
|
+
|
|
99
|
+
Let $U$ be the set of changes in a round. A \emph{decision state} is a map
|
|
100
|
+
\begin{equation}
|
|
101
|
+
\theta : U \to \{\textsf{pending}, \textsf{accepted}, \textsf{rejected},
|
|
102
|
+
\textsf{commented}\},
|
|
103
|
+
\end{equation}
|
|
104
|
+
and the accepted part of a revision is
|
|
105
|
+
$\edit^{\theta} = \{ e \in \edit : \theta(u(e)) = \textsf{accepted} \}$.
|
|
106
|
+
The manuscript after round $t$ is
|
|
107
|
+
\begin{equation}
|
|
108
|
+
\doc_{t+1} =
|
|
109
|
+
\begin{cases}
|
|
110
|
+
\doc_t \apply \edit_t^{\theta_t}, & \text{if } \edit_t^{\theta_t}
|
|
111
|
+
\text{ applies cleanly to } \doc_t,\\[2pt]
|
|
112
|
+
\doc_t, & \text{otherwise, and the task is asked for a new proposal.}
|
|
113
|
+
\end{cases}
|
|
114
|
+
\label{eq:round}
|
|
115
|
+
\end{equation}
|
|
116
|
+
Rejected and commented changes return to the agent as feedback bound to their
|
|
117
|
+
round, file and lines; the agent must not reintroduce a rejected edit unless it
|
|
118
|
+
is explicitly asked to.
|
|
119
|
+
|
|
120
|
+
\paragraph{Actions and undo.}
|
|
121
|
+
An \emph{action} $\alpha = (\iota, u, d)$ carries an identifier $\iota$, a
|
|
122
|
+
change $u$ and a decision $d$. Applying it to a state gives
|
|
123
|
+
$\theta \cdot \alpha = \theta[u \mapsto d]$, and the history is a stack
|
|
124
|
+
$\alpha_1, \dots, \alpha_k$. Two properties make decisions safe over an
|
|
125
|
+
unreliable network:
|
|
126
|
+
\begin{align}
|
|
127
|
+
(\theta \cdot \alpha) \cdot \alpha &= \theta \cdot \alpha
|
|
128
|
+
&& \text{(idempotence: a retried write changes nothing)},
|
|
129
|
+
\label{eq:idempotent}\\
|
|
130
|
+
\operatorname{undo}(\theta_0 \cdot \alpha_1 \cdots \alpha_k)
|
|
131
|
+
&= \theta_0 \cdot \alpha_1 \cdots \alpha_{k-1}
|
|
132
|
+
&& \text{(undo reverses exactly the last action)}.
|
|
133
|
+
\label{eq:undo}
|
|
134
|
+
\end{align}
|
|
135
|
+
An undo request names the action it reverses, so a retried undo cannot reverse
|
|
136
|
+
a different one.
|
|
137
|
+
|
|
138
|
+
\subsection{How many rounds?}
|
|
139
|
+
|
|
140
|
+
Suppose that each change is accepted in a round independently with probability
|
|
141
|
+
$p \in (0,1]$, and otherwise revised and proposed again. The number of rounds
|
|
142
|
+
$R$ until a single change is accepted is geometric,
|
|
143
|
+
\begin{equation}
|
|
144
|
+
\Prob(R = r) = (1-p)^{r-1} p, \qquad
|
|
145
|
+
\E[R] = \frac{1}{p}, \qquad
|
|
146
|
+
\Var(R) = \frac{1-p}{p^{2}} .
|
|
147
|
+
\end{equation}
|
|
148
|
+
For $m$ independent changes the task ends when the last one is accepted, after
|
|
149
|
+
$R_{\max} = \max_i R_i$ rounds.
|
|
150
|
+
|
|
151
|
+
\begin{proposition}[Expected rounds]
|
|
152
|
+
\label{prop:rounds}
|
|
153
|
+
\begin{equation}
|
|
154
|
+
\E[R_{\max}]
|
|
155
|
+
= \sum_{k=1}^{m} (-1)^{k+1} \binom{m}{k} \frac{1}{1 - (1-p)^{k}} .
|
|
156
|
+
\label{eq:rounds}
|
|
157
|
+
\end{equation}
|
|
158
|
+
\end{proposition}
|
|
159
|
+
|
|
160
|
+
The proof is in \cref{app:proofs}. For small $p$ the sum behaves like
|
|
161
|
+
$\frac{1}{p} H_m$, where $H_m = \sum_{k=1}^{m} 1/k \approx \ln m + \gamma$ is
|
|
162
|
+
the $m$-th harmonic number: doubling the number of changes adds only about
|
|
163
|
+
$(\ln 2)/p$ rounds.
|
|
164
|
+
|
|
165
|
+
\begin{corollary}[Rounds with confidence]
|
|
166
|
+
\label{cor:rounds}
|
|
167
|
+
For every $\eta \in (0,1)$, the task ends within
|
|
168
|
+
\begin{equation}
|
|
169
|
+
r_{\eta} = \ceil*{ \frac{\ln(m/\eta)}{-\ln(1-p)} }
|
|
170
|
+
\label{eq:rounds-confidence}
|
|
171
|
+
\end{equation}
|
|
172
|
+
rounds with probability at least $1 - \eta$.
|
|
173
|
+
\end{corollary}
|
|
174
|
+
|
|
175
|
+
\begin{proof}
|
|
176
|
+
By the union bound, $\Prob(R_{\max} > r) \le m\, \Prob(R > r) = m (1-p)^{r}$,
|
|
177
|
+
which is at most $\eta$ as soon as $r \ge \ln(m/\eta) / (-\ln(1-p))$.
|
|
178
|
+
\end{proof}
|
|
179
|
+
|
|
180
|
+
\begin{example}
|
|
181
|
+
With $p = 0.8$ and $m = 5$, \cref{eq:rounds} gives
|
|
182
|
+
$\E[R_{\max}] \approx 1.91$ and \cref{eq:rounds-confidence} with $\eta = 0.05$
|
|
183
|
+
gives $r_{\eta} = 3$: most tasks finish in one or two rounds, and almost all
|
|
184
|
+
within three.
|
|
185
|
+
\end{example}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
\section{Concurrent tasks and conflicts}
|
|
2
|
+
\label{sec:conflicts}
|
|
3
|
+
|
|
4
|
+
Papilot runs up to $c = 2$ tasks at once, each against the manuscript as it was
|
|
5
|
+
when the task started. Accepted text from one task can therefore overlap a
|
|
6
|
+
later proposal from another, and \cref{eq:transform} then returns $\bot$.
|
|
7
|
+
This section estimates how often that happens.
|
|
8
|
+
|
|
9
|
+
\subsection{Uniformly placed edits}
|
|
10
|
+
|
|
11
|
+
\begin{assumption}
|
|
12
|
+
\label{ass:uniform}
|
|
13
|
+
Each task proposes edits of a common length $\ell$ whose start positions are
|
|
14
|
+
independent and uniformly distributed over a document of length $n \gg \ell$.
|
|
15
|
+
\end{assumption}
|
|
16
|
+
|
|
17
|
+
\begin{proposition}[Conflict probability]
|
|
18
|
+
\label{prop:conflict}
|
|
19
|
+
Under \cref{ass:uniform}, let two tasks propose $k_1$ and $k_2$ edits. The
|
|
20
|
+
number $X$ of incompatible cross pairs has mean
|
|
21
|
+
\begin{equation}
|
|
22
|
+
\E[X] = \frac{(2\ell + 1)\, k_1 k_2}{n},
|
|
23
|
+
\label{eq:conflict-mean}
|
|
24
|
+
\end{equation}
|
|
25
|
+
and the probability that the tasks conflict at all is
|
|
26
|
+
\begin{equation}
|
|
27
|
+
\Prob(X > 0)
|
|
28
|
+
= 1 - \Bigl(1 - \frac{2\ell + 1}{n}\Bigr)^{k_1 k_2}
|
|
29
|
+
\approx 1 - \exp\Bigl(-\frac{(2\ell+1)\, k_1 k_2}{n}\Bigr).
|
|
30
|
+
\label{eq:conflict}
|
|
31
|
+
\end{equation}
|
|
32
|
+
\end{proposition}
|
|
33
|
+
|
|
34
|
+
\begin{proof}
|
|
35
|
+
Two edits of length $\ell$ starting at $s$ and $s'$ violate
|
|
36
|
+
\cref{eq:compatible} exactly when $\abs{s - s'} \le \ell$, an event of
|
|
37
|
+
probability $(2\ell+1)/n$ up to boundary effects of order $\ell/n$. Summing over
|
|
38
|
+
the $k_1 k_2$ cross pairs gives \cref{eq:conflict-mean}. Treating the pairs as
|
|
39
|
+
independent gives the product in \cref{eq:conflict}, and
|
|
40
|
+
$\log(1-x) = -x + O(x^2)$ gives the approximation, which is the Poisson
|
|
41
|
+
approximation $\Prob(X = 0) \approx e^{-\E[X]}$~\cite{feller1968}.
|
|
42
|
+
\end{proof}
|
|
43
|
+
|
|
44
|
+
For a $40$-page paper with $n \approx 1.2 \times 10^{5}$ characters, two tasks
|
|
45
|
+
of $k_1 = k_2 = 10$ edits of $\ell = 200$ characters have $\E[X] \approx 0.33$
|
|
46
|
+
and conflict with probability about $1 - e^{-0.33} \approx 0.28$.
|
|
47
|
+
|
|
48
|
+
\subsection{How much overlaps}
|
|
49
|
+
|
|
50
|
+
When two edits do conflict, the overlap is usually partial.
|
|
51
|
+
|
|
52
|
+
\begin{proposition}[Expected overlap]
|
|
53
|
+
\label{prop:overlap}
|
|
54
|
+
Conditioned on a conflict, the offset $d = s' - s$ is uniform on
|
|
55
|
+
$\{-\ell, \dots, \ell\}$ and the overlap $\ell - \abs{d}$ has mean
|
|
56
|
+
\begin{equation}
|
|
57
|
+
\E\bigl[\ell - \abs{d} \bigm| \abs{d} \le \ell\bigr]
|
|
58
|
+
= \frac{1}{2\ell + 1} \sum_{d=-\ell}^{\ell} \bigl(\ell - \abs{d}\bigr)
|
|
59
|
+
= \frac{\ell^{2}}{2\ell + 1}
|
|
60
|
+
\approx \frac{\ell}{2} .
|
|
61
|
+
\label{eq:overlap}
|
|
62
|
+
\end{equation}
|
|
63
|
+
\end{proposition}
|
|
64
|
+
|
|
65
|
+
\begin{proof}
|
|
66
|
+
The sum is $\ell + 2\sum_{j=1}^{\ell} (\ell - j) = \ell + \ell(\ell - 1) = \ell^2$.
|
|
67
|
+
\end{proof}
|
|
68
|
+
|
|
69
|
+
A typical conflict therefore involves about half of an edit, which matches the
|
|
70
|
+
experience that conflicting proposals usually touch the same sentence rather
|
|
71
|
+
than the same word.
|
|
72
|
+
|
|
73
|
+
\subsection{Clustered edits}
|
|
74
|
+
|
|
75
|
+
Real revisions are not uniform: an author working on the introduction asks for
|
|
76
|
+
several changes there. We model this by letting both tasks place their edits
|
|
77
|
+
uniformly within a common region covering a fraction $\nu \in (0, 1]$ of the
|
|
78
|
+
document.
|
|
79
|
+
|
|
80
|
+
\begin{corollary}[Clustering raises conflicts]
|
|
81
|
+
\label{cor:clustered}
|
|
82
|
+
If both tasks edit only within a region of length $\nu n$,
|
|
83
|
+
\begin{equation}
|
|
84
|
+
\Prob(X > 0) \approx 1 - \exp\Bigl(-\frac{(2\ell+1)\, k_1 k_2}{\nu n}\Bigr),
|
|
85
|
+
\label{eq:clustered}
|
|
86
|
+
\end{equation}
|
|
87
|
+
so that confining two tasks to a tenth of the paper multiplies the expected
|
|
88
|
+
number of conflicts by ten.
|
|
89
|
+
\end{corollary}
|
|
90
|
+
|
|
91
|
+
\begin{proof}
|
|
92
|
+
Apply \cref{prop:conflict} with $n$ replaced by $\nu n$.
|
|
93
|
+
\end{proof}
|
|
94
|
+
|
|
95
|
+
\Cref{cor:clustered} argues for fewer, longer tasks over many short ones that
|
|
96
|
+
touch the same part of a paper, and for finishing the review of one task
|
|
97
|
+
before starting another in the same section. It also explains why Papilot
|
|
98
|
+
surfaces conflicts from Git rather than resolving them: a resolution would
|
|
99
|
+
have to choose between two texts the author has not yet compared.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
\section{Building the PDF}
|
|
2
|
+
\label{sec:build}
|
|
3
|
+
|
|
4
|
+
\subsection{Snapshots of the working tree}
|
|
5
|
+
|
|
6
|
+
The PDF view shows the manuscript exactly as it is on disk, including accepted
|
|
7
|
+
but uncommitted changes. Let $W$ be the working tree and $H$ the tree of
|
|
8
|
+
\texttt{HEAD}. A temporary index $I$ records the working tree without touching
|
|
9
|
+
the real index:
|
|
10
|
+
\begin{equation}
|
|
11
|
+
I \leftarrow \operatorname{read-tree}(H), \qquad
|
|
12
|
+
I \leftarrow I \cup \operatorname{add}(W), \qquad
|
|
13
|
+
T = \operatorname{write-tree}(I) .
|
|
14
|
+
\label{eq:snapshot}
|
|
15
|
+
\end{equation}
|
|
16
|
+
The tree $T$ is a content hash of every tracked and untracked, non-ignored
|
|
17
|
+
file. The build is cached under the key
|
|
18
|
+
\begin{equation}
|
|
19
|
+
\key(W) = h\bigl(T \,\|\, \text{build settings}\bigr),
|
|
20
|
+
\label{eq:key}
|
|
21
|
+
\end{equation}
|
|
22
|
+
where $h$ is a cryptographic hash and the settings name the engine and the
|
|
23
|
+
main file.
|
|
24
|
+
|
|
25
|
+
\begin{proposition}[Undo never recompiles]
|
|
26
|
+
\label{prop:undo-build}
|
|
27
|
+
Let $W_0$ be a working tree whose build has completed, and let a sequence of
|
|
28
|
+
decisions followed by their undos return the files to the same content. Then
|
|
29
|
+
the final working tree has the key of $W_0$ and its PDF is served from the
|
|
30
|
+
cache.
|
|
31
|
+
\end{proposition}
|
|
32
|
+
|
|
33
|
+
\begin{proof}
|
|
34
|
+
$T$ in \cref{eq:snapshot} depends only on file contents, so equal contents give
|
|
35
|
+
equal trees and, by \cref{eq:key}, equal keys.
|
|
36
|
+
\end{proof}
|
|
37
|
+
|
|
38
|
+
\subsection{When to build}
|
|
39
|
+
|
|
40
|
+
While the PDF view is open, Papilot checks the working tree every second and
|
|
41
|
+
a half during a build, every fifteen seconds otherwise, and whenever the window
|
|
42
|
+
regains focus. Let checks happen at rate at least $\varphi$, edits at rate
|
|
43
|
+
$\mu$ and builds take time $\beta$. A build that is running when the tree
|
|
44
|
+
changes is not cancelled; the first check after it finishes starts a build for
|
|
45
|
+
the newer key. An edit made just after a build started waits for that build,
|
|
46
|
+
then for the next check, then for its own build, so the PDF is never older than
|
|
47
|
+
\begin{equation}
|
|
48
|
+
\text{staleness} \le 2\beta + \frac{1}{\varphi} ,
|
|
49
|
+
\label{eq:staleness}
|
|
50
|
+
\end{equation}
|
|
51
|
+
and the number of builds per unit time is at most
|
|
52
|
+
$\min\bigl(\mu, \varphi, 1/\beta\bigr)$ in the long run: frequent edits do not
|
|
53
|
+
cause a backlog of builds, they only skip intermediate states.
|
|
54
|
+
|
|
55
|
+
\subsection{Reviewing changed pages}
|
|
56
|
+
|
|
57
|
+
A review compares the PDF of the round's base with the PDF after the task's
|
|
58
|
+
edits. Let the two documents have pages $P = (p_1, \dots, p_N)$ and
|
|
59
|
+
$P' = (p'_1, \dots, p'_M)$ with content hashes $h(p_i)$. Inserting a paragraph
|
|
60
|
+
shifts every later page, so comparing pages by index would mark almost
|
|
61
|
+
everything as changed. A robust comparison aligns the pages by a longest common
|
|
62
|
+
subsequence of their hashes,
|
|
63
|
+
\begin{equation}
|
|
64
|
+
\operatorname{LCS}(P, P') = \max\Bigl\{ \abs{S} : S \subseteq [N]\times[M]
|
|
65
|
+
\text{ increasing},\ h(p_i) = h(p'_j)\ \forall (i,j) \in S \Bigr\},
|
|
66
|
+
\end{equation}
|
|
67
|
+
computed in $O\bigl((N + M) D\bigr)$ time, where $D = N + M - 2\abs{S}$ is the
|
|
68
|
+
size of the edit script~\cite{myers1986}. Papilot's review goes further and
|
|
69
|
+
renders only the strips of each page around the changed source lines, so that
|
|
70
|
+
a one-word change is reviewed as one or two lines of typeset text.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
\section{Geometry of the PDF view}
|
|
2
|
+
\label{sec:geometry}
|
|
3
|
+
|
|
4
|
+
\subsection{From a glyph to a pixel}
|
|
5
|
+
|
|
6
|
+
A PDF page lives in user space, in points ($1\,\mathrm{bp} = 1/72$\,in), with
|
|
7
|
+
the origin at the bottom left of its view box $[x_0, y_0, x_1, y_1]$. Following
|
|
8
|
+
the PDF convention we use row vectors. A glyph's origin is placed by the text
|
|
9
|
+
rendering matrix of \cref{eq:trm},
|
|
10
|
+
\begin{equation}
|
|
11
|
+
\begin{pmatrix} x & y & 1 \end{pmatrix}
|
|
12
|
+
= \begin{pmatrix} 0 & 0 & 1 \end{pmatrix} T_{rm} ,
|
|
13
|
+
\end{equation}
|
|
14
|
+
and the viewer draws the page at scale $s$ (device pixels per point) with the
|
|
15
|
+
origin at the top left, so for an unrotated page the viewport matrix is
|
|
16
|
+
\begin{equation}
|
|
17
|
+
V_s =
|
|
18
|
+
\begin{pmatrix}
|
|
19
|
+
s & 0 & 0 \\
|
|
20
|
+
0 & -s & 0 \\
|
|
21
|
+
-s x_0 & s y_1 & 1
|
|
22
|
+
\end{pmatrix},
|
|
23
|
+
\qquad
|
|
24
|
+
\begin{pmatrix} u & v & 1 \end{pmatrix}
|
|
25
|
+
= \begin{pmatrix} x & y & 1 \end{pmatrix} V_s .
|
|
26
|
+
\label{eq:viewport}
|
|
27
|
+
\end{equation}
|
|
28
|
+
A page rotated by $\phi \in \{0, \tfrac{\pi}{2}, \pi, \tfrac{3\pi}{2}\}$ is
|
|
29
|
+
drawn with $V_s$ composed with a rotation about the page centre,
|
|
30
|
+
\begin{equation}
|
|
31
|
+
R_{\phi} =
|
|
32
|
+
\begin{pmatrix}
|
|
33
|
+
\cos\phi & \sin\phi & 0 \\
|
|
34
|
+
-\sin\phi & \cos\phi & 0 \\
|
|
35
|
+
0 & 0 & 1
|
|
36
|
+
\end{pmatrix},
|
|
37
|
+
\end{equation}
|
|
38
|
+
so that the complete chain from a glyph to a pixel is
|
|
39
|
+
$(0\ 0\ 1)\, T_{rm}\, V_s\, R_{\phi}$ up to a translation. Zooming changes only
|
|
40
|
+
$s$, which is why Papilot stores positions on the page in normalised
|
|
41
|
+
coordinates $r = (\xi_0, \eta_0, \xi_1, \eta_1) \in [0,1]^4$ relative to the
|
|
42
|
+
view box:
|
|
43
|
+
\begin{equation}
|
|
44
|
+
\xi = \frac{x - x_0}{x_1 - x_0}, \qquad
|
|
45
|
+
\eta = \frac{y_1 - y}{y_1 - y_0} .
|
|
46
|
+
\label{eq:normalised}
|
|
47
|
+
\end{equation}
|
|
48
|
+
|
|
49
|
+
\subsection{How \TeX{} spaces a line}
|
|
50
|
+
|
|
51
|
+
\TeX{} sets a line of natural width $L_0$ in a box of width $L$ by stretching or
|
|
52
|
+
shrinking the glue between words~\cite{knuth1984}. If the line's glue has total
|
|
53
|
+
stretchability $Y$ and shrinkability $Z$, the \emph{adjustment ratio} is
|
|
54
|
+
\begin{equation}
|
|
55
|
+
r =
|
|
56
|
+
\begin{cases}
|
|
57
|
+
(L - L_0)/Y, & L \ge L_0,\\
|
|
58
|
+
(L - L_0)/Z, & L < L_0,
|
|
59
|
+
\end{cases}
|
|
60
|
+
\label{eq:ratio}
|
|
61
|
+
\end{equation}
|
|
62
|
+
and an interword space with natural width $g$, stretch $y$ and shrink $z$ is
|
|
63
|
+
set at width $g + r y$ or $g + r z$. The \emph{badness} of the line is
|
|
64
|
+
approximately
|
|
65
|
+
\begin{equation}
|
|
66
|
+
b(r) = \min\bigl(10000,\ 100\,\abs{r}^{3}\bigr),
|
|
67
|
+
\label{eq:badness}
|
|
68
|
+
\end{equation}
|
|
69
|
+
with $10000$ meaning infinitely bad. The Knuth--Plass algorithm chooses breaks
|
|
70
|
+
that minimise the total \emph{demerits}~\cite{knuth1981}, where a line with
|
|
71
|
+
badness $b$ ending at a break with penalty $p$ costs
|
|
72
|
+
\begin{equation}
|
|
73
|
+
d =
|
|
74
|
+
\begin{cases}
|
|
75
|
+
(l + b)^{2} + p^{2}, & 0 \le p < 10000,\\
|
|
76
|
+
(l + b)^{2} - p^{2}, & -10000 < p < 0,\\
|
|
77
|
+
(l + b)^{2}, & p \le -10000,
|
|
78
|
+
\end{cases}
|
|
79
|
+
\label{eq:demerits}
|
|
80
|
+
\end{equation}
|
|
81
|
+
and $l$ is the line penalty (ten by default). Because \cref{eq:demerits}
|
|
82
|
+
trades badness between lines, interword spaces vary from line to line, and
|
|
83
|
+
that variation is what a text layer must follow.
|
|
84
|
+
|
|
85
|
+
\subsection{Why a line-wide text layer drifts}
|
|
86
|
+
|
|
87
|
+
To make text selectable, a viewer overlays invisible text on the rendered page.
|
|
88
|
+
A common construction places one run per line in a stand-in font and stretches
|
|
89
|
+
it horizontally so that the run's ends meet the line's ends~\cite{pdfjs}. Let a
|
|
90
|
+
line consist of glyphs with true advances $a_1, \dots, a_N$ and stand-in
|
|
91
|
+
advances $b_1, \dots, b_N$. The stretch factor is
|
|
92
|
+
\begin{equation}
|
|
93
|
+
\sigma = \frac{\sum_{i=1}^{N} a_i}{\sum_{i=1}^{N} b_i},
|
|
94
|
+
\end{equation}
|
|
95
|
+
and the selectable position of glyph $k$ is off by
|
|
96
|
+
\begin{equation}
|
|
97
|
+
\varepsilon_k = \sum_{i<k} \delta_i, \qquad
|
|
98
|
+
\delta_i = \sigma b_i - a_i, \qquad
|
|
99
|
+
\varepsilon_1 = \varepsilon_{N+1} = 0 .
|
|
100
|
+
\label{eq:drift}
|
|
101
|
+
\end{equation}
|
|
102
|
+
|
|
103
|
+
\begin{theorem}[Mid-line drift]
|
|
104
|
+
\label{thm:drift}
|
|
105
|
+
If the discrepancies $\delta_1, \dots, \delta_N$ are exchangeable with
|
|
106
|
+
variance $v$, then $\sum_i \delta_i = 0$ forces
|
|
107
|
+
$\Cov(\delta_i, \delta_j) = -v/(N-1)$ for $i \ne j$, and
|
|
108
|
+
\begin{equation}
|
|
109
|
+
\Var(\varepsilon_k) = v\, \frac{(k-1)(N-k+1)}{N-1},
|
|
110
|
+
\qquad
|
|
111
|
+
\max_k \Var(\varepsilon_k) \approx \frac{v N}{4} .
|
|
112
|
+
\label{eq:bridge}
|
|
113
|
+
\end{equation}
|
|
114
|
+
\end{theorem}
|
|
115
|
+
|
|
116
|
+
The proof is in \cref{app:proofs}. \Cref{eq:bridge} is the variance of a
|
|
117
|
+
discrete Brownian bridge~\cite{billingsley1999}: the error vanishes at both
|
|
118
|
+
ends of the line, where the run is anchored, and is largest in the middle.
|
|
119
|
+
|
|
120
|
+
Justified lines add a deterministic bias. Every space is printed $g + r y$
|
|
121
|
+
wide but drawn $\sigma b_{\text{space}}$ wide in the stand-in font, so that
|
|
122
|
+
\begin{equation}
|
|
123
|
+
\beta_k = \bigl(g + r y - \sigma b_{\text{space}}\bigr) \cdot
|
|
124
|
+
\#\{\text{spaces before glyph } k\} .
|
|
125
|
+
\label{eq:bias}
|
|
126
|
+
\end{equation}
|
|
127
|
+
With \TeX{}'s interword glue at ten points, $g = 3.33$\,pt and $y = 1.67$\,pt,
|
|
128
|
+
a line set with $r = 0.6$ has spaces of $4.33$\,pt; if the stand-in font's
|
|
129
|
+
spaces come out at $3.0$\,pt after stretching, the bias grows by $1.33$\,pt per
|
|
130
|
+
space and reaches $8$\,pt, more than a letter and a half, after six words. Together,
|
|
131
|
+
\cref{eq:bridge,eq:bias} explain drifts of several characters in the middle of
|
|
132
|
+
a line, enough for a drag to select a neighbouring word.
|
|
133
|
+
|
|
134
|
+
\subsection{Per-word runs}
|
|
135
|
+
|
|
136
|
+
Papilot instead reads every word's printed place from the PDF: its left edge
|
|
137
|
+
$\lambda_j$, baseline $y_j$, size $f_j$ and width $w_j$. It places one run per
|
|
138
|
+
word, stretched to end where the next word begins, so that the run for word
|
|
139
|
+
$j$ covers glyphs $J_j$ and the space after them with its own factor
|
|
140
|
+
\begin{equation}
|
|
141
|
+
\sigma_j = \frac{\lambda_{j+1} - \lambda_j}
|
|
142
|
+
{\sum_{i \in J_j} b_i + b_{\text{space}}} .
|
|
143
|
+
\end{equation}
|
|
144
|
+
|
|
145
|
+
\begin{theorem}[Per-word drift]
|
|
146
|
+
\label{thm:per-word}
|
|
147
|
+
With per-word runs, every word starts exactly where it is printed, and the
|
|
148
|
+
error at a glyph $k$ of word $j$ satisfies
|
|
149
|
+
\begin{equation}
|
|
150
|
+
\abs{\varepsilon_k} \le \sum_{i \in J_j} \abs{\sigma_j b_i - a_i}
|
|
151
|
+
\le N_j \max_{i \in J_j} \abs{\sigma_j b_i - a_i},
|
|
152
|
+
\qquad
|
|
153
|
+
\Var(\varepsilon_k) \le \frac{v N_j}{4} ,
|
|
154
|
+
\label{eq:per-word}
|
|
155
|
+
\end{equation}
|
|
156
|
+
where $N_j = \abs{J_j}$. The bias \cref{eq:bias} does not accumulate across
|
|
157
|
+
words.
|
|
158
|
+
\end{theorem}
|
|
159
|
+
|
|
160
|
+
\begin{proof}
|
|
161
|
+
The run for word $j$ starts at $\lambda_j$, so $\varepsilon$ is zero at the
|
|
162
|
+
first glyph of every word. Within the word, \cref{eq:drift} applies with $N$
|
|
163
|
+
replaced by $N_j$ and $\sigma$ by $\sigma_j$, which gives the first two bounds
|
|
164
|
+
by the triangle inequality and the third by \cref{thm:drift}. Since every word
|
|
165
|
+
restarts at its printed position, no space contributes to the error of a later
|
|
166
|
+
word.
|
|
167
|
+
\end{proof}
|
|
168
|
+
|
|
169
|
+
For a line of $N = 80$ glyphs and words of $N_j \approx 6$, the typical error
|
|
170
|
+
falls by a factor of about $\sqrt{N/N_j} \approx 3.7$ and the bias disappears.
|
|
171
|
+
\Cref{fig:drift} compares the two layouts.
|
|
172
|
+
|
|
173
|
+
\begin{figure}[t]
|
|
174
|
+
\centering
|
|
175
|
+
\begin{tikzpicture}
|
|
176
|
+
\begin{axis}[
|
|
177
|
+
width=0.92\linewidth, height=5.2cm,
|
|
178
|
+
xlabel={glyph position $k$ in the line}, ylabel={$\sqrt{\Var(\varepsilon_k)/v}$},
|
|
179
|
+
xmin=0, xmax=80, ymin=0, ymax=5,
|
|
180
|
+
legend style={at={(0.5,1.03)}, anchor=south, legend columns=2, draw=none, font=\small, /tikz/every even column/.append style={column sep=1.2em}},
|
|
181
|
+
grid=major, grid style={gray!20}, tick label style={font=\small}, label style={font=\small}]
|
|
182
|
+
\addplot[blue!60!black, thick, domain=0:80, samples=161] {sqrt(x*(80-x)/79)};
|
|
183
|
+
\addlegendentry{one run per line}
|
|
184
|
+
\addplot[orange!80!black, thick, domain=0:80, samples=561] {sqrt(mod(x,7)*(7-mod(x,7))/6)};
|
|
185
|
+
\addlegendentry{one run per word}
|
|
186
|
+
\end{axis}
|
|
187
|
+
\end{tikzpicture}
|
|
188
|
+
\caption{Standard deviation of the selection error along a line of $80$ glyphs,
|
|
189
|
+
from \cref{eq:bridge}, for a single stretched run and for per-word runs with
|
|
190
|
+
words of six letters and a space. The per-word error returns to zero at every
|
|
191
|
+
word.}
|
|
192
|
+
\label{fig:drift}
|
|
193
|
+
\end{figure}
|