Merge branch 'audit-fixes'

This commit is contained in:
Levi Neuwirth 2026-04-10 17:42:16 -04:00
commit 7e0b4c3a53
97 changed files with 2972 additions and 8009 deletions

View File

@ -1,9 +1,19 @@
# Copy this file to .env and fill in the values. # Copy this file to .env and fill in the values.
# .env is gitignored — never commit it. # .env is gitignored — never commit it.
# #
# Used by `make deploy` to push to GitHub before rsyncing to the VPS. # `make deploy` rsyncs the built _site/ to the VPS, then pushes the
# If either variable is unset, the push step is skipped (rsync still runs). # repository to GitHub. The Makefile aborts with a clear error if any
# of VPS_USER / VPS_HOST / VPS_PATH is unset.
# --- VPS deployment target -------------------------------------------------
# SSH user on the deployment VPS.
VPS_USER=
# Hostname or IP of the deployment VPS.
VPS_HOST=
# Absolute path to the document root on the VPS (e.g. /var/www/levineuwirth.org).
VPS_PATH=
# --- GitHub mirror push ----------------------------------------------------
# A GitHub fine-grained personal access token with Contents: read+write # A GitHub fine-grained personal access token with Contents: read+write
# on the levineuwirth.org repository. # on the levineuwirth.org repository.
# Generate at: https://github.com/settings/tokens # Generate at: https://github.com/settings/tokens

32
.gitignore vendored
View File

@ -9,6 +9,38 @@ _cache/
*.swp *.swp
*.swo *.swo
# Python bytecode caches
**/__pycache__/
*.pyc
*.pyo
# LaTeX build artifacts (sitewide — covers paper/, any future TeX sources)
*.aux
*.bbl
*.blg
*.brf
*.fdb_latexmk
*.fls
*.glo
*.gls
*.idx
*.ilg
*.ind
*.lof
*.lot
*.nav
*.out
*.snm
*.synctex.gz
*.toc
*.vrb
# PGF/TikZ scratch outputs
pgftest*.pdf
pgftest*.log
pgftest*.aux
# LaTeX run logs (scoped to paper/ — bare *.log would be too broad sitewide)
paper/*.log
# Data files that are generated at build time (not version-controlled) # Data files that are generated at build time (not version-controlled)
data/embeddings.json data/embeddings.json
data/similar-links.json data/similar-links.json

View File

@ -6,6 +6,12 @@
export export
build: build:
# Auto-snapshot any uncommitted content/ changes BEFORE the build
# so the stability heuristic in build/Stability.hs sees a stable
# git history. If a subsequent step fails, the snapshot remains in
# the history — that's intentional. The next successful build
# either reuses it (no new content/ changes) or appends another
# snapshot on top, so failures don't disappear from the log.
@git add content/ @git add content/
@git diff --cached --quiet || git commit -m "auto: $$(date -u +%Y-%m-%dT%H:%M:%SZ)" @git diff --cached --quiet || git commit -m "auto: $$(date -u +%Y-%m-%dT%H:%M:%SZ)"
@date +%s > data/build-start.txt @date +%s > data/build-start.txt
@ -54,15 +60,23 @@ pdf-thumbs:
fi fi
deploy: clean build sign deploy: clean build sign
git push -u origin main @test -n "$(VPS_USER)" || (echo "deploy: VPS_USER not set in .env" >&2; exit 1)
@test -n "$(VPS_HOST)" || (echo "deploy: VPS_HOST not set in .env" >&2; exit 1)
@test -n "$(VPS_PATH)" || (echo "deploy: VPS_PATH not set in .env" >&2; exit 1)
rsync -avz --delete _site/ $(VPS_USER)@$(VPS_HOST):$(VPS_PATH)/ rsync -avz --delete _site/ $(VPS_USER)@$(VPS_HOST):$(VPS_PATH)/
git push -u origin main
watch: export SITE_ENV = dev
watch: watch:
cabal run site -- watch cabal run site -- watch
clean: clean:
cabal run site -- clean cabal run site -- clean
# Dev build includes any in-progress drafts under content/drafts/essays/.
# SITE_ENV=dev is read by build/Site.hs; drafts are otherwise invisible to
# every build (make build / make deploy / cabal run site -- build directly).
dev: export SITE_ENV = dev
dev: dev:
cabal run site -- clean cabal run site -- clean
cabal run site -- build cabal run site -- build

View File

@ -1 +1,86 @@
# levineuwirth.org # levineuwirth.org
Personal site of Levi Neuwirth — essays, blog posts, poetry, fiction, and music.
Built with [Hakyll](https://jaspervdj.be/hakyll/) and [Pandoc](https://pandoc.org/),
with a custom build system in `build/` and a Haskell + JS + Python toolchain.
## Quickstart
```sh
make build # one-shot production build into _site/
make dev # dev build (drafts visible) + local server on :8000
make watch # cabal-watch rebuild (drafts visible)
make clean # cabal run site -- clean
make deploy # clean → build → sign → push → rsync to VPS
```
`make build` always runs `make clean` implicitly when invoked from `make deploy`.
For day-to-day work, prefer `make dev` (which serves the site on
`http://localhost:8000`) or `make watch` (rebuilds on save without a server).
**Run `make build` any time you add or replace binary assets** (JPEG/PNG
figures, PDFs, music assets). `make dev` and `make watch` skip the
`convert-images.sh` / `pdf-thumbs` preprocessing steps, so a fresh JPEG
will have no `.webp` companion and a fresh PDF will have no thumbnail
until a full `make build` regenerates them. Once the companions exist
they survive subsequent `make dev` runs.
## Optional features
- **Similar-links and embeddings.** `tools/embed.py` precomputes
page-level embeddings for the "Related" block. To enable:
```sh
uv sync # creates .venv with sentence-transformers, faiss-cpu
```
The build silently skips embedding when `.venv` is absent.
- **Client-side semantic search.** Downloads a quantized ONNX model
used by `static/js/semantic-search.js` (run once; files are gitignored):
```sh
make download-model
```
- **Image conversion.** `make build` calls `tools/convert-images.sh` to
produce `.webp` companions next to every JPEG/PNG. Requires `cwebp`
(`libwebp` on Arch, `webp` on Debian/Ubuntu).
- **PDF thumbnails.** `make pdf-thumbs` generates first-page thumbnails
for PDFs in `static/papers/` using `pdftoppm` (`poppler` on Arch,
`poppler-utils` on Debian/Ubuntu). Skipped silently when missing.
## Configuration
`.env` (gitignored, copy from `.env.example`) holds the GitHub PAT and
the VPS rsync target consumed by `make deploy`. Never commit it.
## Repository layout
- `build/` — Haskell build system (Hakyll rules, Pandoc filters, contexts).
See `build/Filters/` for the Pandoc AST transforms (sidenotes,
wikilinks, transclusion, score embedding, viz, …).
- `content/` — authored Markdown (essays, blog, poetry, fiction, music).
- `templates/` — Hakyll/Pandoc HTML templates.
- `static/` — CSS, JS, fonts, images, vendored PDF.js.
- `tools/` — Python tooling (embeddings, importers) and shell scripts.
- `data/` — generated and source data (commonplace.yaml, annotations.json,
bibliographies, similar-links.json).
- `paper/` — LaTeX source for in-progress academic papers.
- `spec.md` — full architectural notes and design intent.
## Architecture pointers
- `build/Site.hs` is the Hakyll rules entry point.
- `build/Patterns.hs` defines canonical content patterns shared by
Backlinks, Authors, Tags, and Site.
- `build/Compilers.hs` wires the Pandoc filter chain into Hakyll.
- `build/Filters/Images.hs` does WebP `<picture>` wrapping; requires
the `.webp` companions produced by `tools/convert-images.sh`.
For deeper architectural detail, see `spec.md`.
## License
See `LICENSE`.

View File

@ -12,32 +12,29 @@
module Authors module Authors
( buildAllAuthors ( buildAllAuthors
, applyAuthorRules , applyAuthorRules
, authorLinksField
) where ) where
import Data.Char (isAlphaNum, toLower)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Hakyll import Hakyll
import Hakyll.Core.Metadata (lookupStringList)
import Pagination (sortAndGroup) import Pagination (sortAndGroup)
import Tags (tagLinksField) import Patterns (authorIndexable)
import Contexts (abstractField, tagLinksField)
import Utils (authorSlugify, authorNameOf)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Slug helpers -- Slug helpers
--
-- The slugify and nameOf helpers used to live here in their own
-- definitions; they now defer to 'Utils' so that they cannot drift from
-- the 'Contexts' versions on Unicode edge cases.
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Lowercase, replace spaces with hyphens, strip anything else.
slugify :: String -> String slugify :: String -> String
slugify = map (\c -> if c == ' ' then '-' else c) slugify = authorSlugify
. filter (\c -> isAlphaNum c || c == ' ')
. map toLower
-- | Extract the author name from a "Name | url" entry, trimming whitespace.
nameOf :: String -> String nameOf :: String -> String
nameOf s = strip $ case break (== '|') s of { (n, _) -> n } nameOf = authorNameOf
where
strip = reverse . dropWhile (== ' ') . reverse . dropWhile (== ' ')
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -47,8 +44,10 @@ nameOf s = strip $ case break (== '|') s of { (n, _) -> n }
defaultAuthor :: String defaultAuthor :: String
defaultAuthor = "Levi Neuwirth" defaultAuthor = "Levi Neuwirth"
-- | Content patterns indexed by author. Sourced from 'Patterns.authorIndexable'
-- so this stays in lockstep with Tags.hs and Backlinks.hs.
allContent :: Pattern allContent :: Pattern
allContent = ("content/essays/*.md" .||. "content/blog/*.md") .&&. hasNoVersion allContent = authorIndexable
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -103,25 +102,7 @@ applyAuthorRules authors baseCtx = tagsRules authors $ \name pat -> do
where where
itemCtx = dateField "date" "%-d %B %Y" itemCtx = dateField "date" "%-d %B %Y"
<> tagLinksField "item-tags" <> tagLinksField "item-tags"
<> abstractField
<> defaultContext <> defaultContext
-- ---------------------------------------------------------------------------
-- Context field
-- ---------------------------------------------------------------------------
-- | Exposes each item's authors as @author-name@ / @author-url@ pairs.
-- All links point to /authors/{slug}/, regardless of any URL in frontmatter.
-- Defaults to Levi Neuwirth when no "authors" frontmatter key is present.
--
-- Usage in templates:
-- $for(author-links)$<a href="$author-url$">$author-name$</a>$sep$, $endfor$
authorLinksField :: Context a
authorLinksField = listFieldWith "author-links" ctx $ \item -> do
meta <- getMetadata (itemIdentifier item)
let entries = fromMaybe [] (lookupStringList "authors" meta)
names = if null entries then [defaultAuthor] else map nameOf entries
return $ map (\n -> Item (fromFilePath "") (n, "/authors/" ++ slugify n ++ "/")) names
where
ctx = field "author-name" (return . fst . itemBody)
<> field "author-url" (return . snd . itemBody)

View File

@ -32,10 +32,12 @@ import Data.Ord (comparing)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import qualified Data.ByteString as BS
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TLE import qualified Data.Text.Lazy.Encoding as TLE
import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TE
import qualified Data.Aeson as Aeson import qualified Data.Aeson as Aeson
import Data.Aeson ((.=)) import Data.Aeson ((.=))
import Text.Pandoc.Class (runPure) import Text.Pandoc.Class (runPure)
@ -47,6 +49,7 @@ import Text.Pandoc.Walk (query)
import Hakyll import Hakyll
import Compilers (readerOpts, writerOpts) import Compilers (readerOpts, writerOpts)
import Filters (preprocessSource) import Filters (preprocessSource)
import qualified Patterns as P
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Link-with-context entry (intermediate, saved by the "links" pass) -- Link-with-context entry (intermediate, saved by the "links" pass)
@ -184,28 +187,47 @@ linksCompiler = do
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Normalise an internal URL as a map key: strip query string, fragment, -- | Normalise an internal URL as a map key: strip query string, fragment,
-- and trailing @.html@; ensure a leading slash. -- and trailing @.html@; ensure a leading slash; percent-decode the path
-- so that @\/essays\/caf%C3%A9@ and @\/essays\/café@ collide on the same
-- key.
normaliseUrl :: String -> String normaliseUrl :: String -> String
normaliseUrl url = normaliseUrl url =
let t = T.pack url let t = T.pack url
t1 = fst (T.breakOn "?" (fst (T.breakOn "#" t))) t1 = fst (T.breakOn "?" (fst (T.breakOn "#" t)))
t2 = if T.isPrefixOf "/" t1 then t1 else "/" `T.append` t1 t2 = if T.isPrefixOf "/" t1 then t1 else "/" `T.append` t1
t3 = fromMaybe t2 (T.stripSuffix ".html" t2) t3 = fromMaybe t2 (T.stripSuffix ".html" t2)
in T.unpack t3 in percentDecode (T.unpack t3)
-- | Decode percent-escapes (@%XX@) into raw bytes, then re-interpret the
-- resulting bytestring as UTF-8. Invalid escapes are passed through
-- verbatim so this is safe to call on already-decoded input.
percentDecode :: String -> String
percentDecode = T.unpack . TE.decodeUtf8With lenientDecode . pack . go
where
go [] = []
go ('%':a:b:rest)
| Just hi <- hexDigit a
, Just lo <- hexDigit b
= fromIntegral (hi * 16 + lo) : go rest
go (c:rest) = fromIntegral (fromEnum c) : go rest
hexDigit c
| c >= '0' && c <= '9' = Just (fromEnum c - fromEnum '0')
| c >= 'a' && c <= 'f' = Just (fromEnum c - fromEnum 'a' + 10)
| c >= 'A' && c <= 'F' = Just (fromEnum c - fromEnum 'A' + 10)
| otherwise = Nothing
pack = BS.pack
lenientDecode = TE.lenientDecode
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Content patterns (must match the rules in Site.hs) -- Content patterns (must match the rules in Site.hs — sourced from
-- Patterns.allContent so additions to the canonical list automatically
-- propagate to backlinks).
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
allContent :: Pattern allContent :: Pattern
allContent = allContent = P.allContent
"content/essays/*.md"
.||. "content/essays/*/index.md"
.||. "content/blog/*.md"
.||. "content/poetry/*.md"
.||. "content/fiction/*.md"
.||. "content/music/*/index.md"
.||. "content/*.md"
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Hakyll rules -- Hakyll rules

View File

@ -7,7 +7,8 @@ module Catalog
( musicCatalogCtx ( musicCatalogCtx
) where ) where
import Data.List (groupBy, sortBy) import Data.Char (isSpace, toLower)
import Data.List (groupBy, isPrefixOf, sortBy)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Ord (comparing) import Data.Ord (comparing)
import Data.Aeson (Value (..)) import Data.Aeson (Value (..))
@ -15,7 +16,6 @@ import qualified Data.Aeson.KeyMap as KM
import qualified Data.Vector as V import qualified Data.Vector as V
import qualified Data.Text as T import qualified Data.Text as T
import Hakyll import Hakyll
import Hakyll.Core.Metadata (lookupStringList)
import Contexts (siteCtx) import Contexts (siteCtx)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -115,6 +115,40 @@ parseCatalogEntry item = do
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- HTML rendering -- HTML rendering
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
--
-- Trust model: per the site convention (see also Stats.hs:pageLink),
-- frontmatter @title@ values are author-controlled trusted HTML and may
-- contain inline markup such as @<em>...</em>@. They are emitted
-- pre-escaped — but we still escape every other interpolated frontmatter
-- value (year, duration, instrumentation) and sanitize hrefs through
-- 'safeHref', so a stray @<@ in those fields cannot break the markup.
-- | Defense-in-depth href sanitiser. Mirrors 'Stats.isSafeUrl'.
safeHref :: String -> String
safeHref u =
let norm = map toLower (dropWhile isSpace u)
in if not ("//" `isPrefixOf` norm)
&& any (`isPrefixOf` norm) ["/", "https://", "mailto:", "#"]
then escAttr u
else "#"
escAttr :: String -> String
escAttr = concatMap esc
where
esc '&' = "&amp;"
esc '<' = "&lt;"
esc '>' = "&gt;"
esc '"' = "&quot;"
esc '\'' = "&#39;"
esc c = [c]
escText :: String -> String
escText = concatMap esc
where
esc '&' = "&amp;"
esc '<' = "&lt;"
esc '>' = "&gt;"
esc c = [c]
renderIndicators :: CatalogEntry -> String renderIndicators :: CatalogEntry -> String
renderIndicators e = concatMap render renderIndicators e = concatMap render
@ -129,19 +163,21 @@ renderEntry :: CatalogEntry -> String
renderEntry e = concat renderEntry e = concat
[ "<li class=\"catalog-entry\">" [ "<li class=\"catalog-entry\">"
, "<div class=\"catalog-entry-main\">" , "<div class=\"catalog-entry-main\">"
, "<a class=\"catalog-title\" href=\"", ceUrl e, "\">", ceTitle e, "</a>" , "<a class=\"catalog-title\" href=\"", safeHref (ceUrl e), "\">"
, ceTitle e
, "</a>"
, renderIndicators e , renderIndicators e
, maybe "" (\y -> "<span class=\"catalog-year\">" ++ y ++ "</span>") (ceYear e) , maybe "" (\y -> "<span class=\"catalog-year\">" ++ escText y ++ "</span>") (ceYear e)
, maybe "" (\d -> "<span class=\"catalog-duration\">" ++ d ++ "</span>") (ceDuration e) , maybe "" (\d -> "<span class=\"catalog-duration\">" ++ escText d ++ "</span>") (ceDuration e)
, "</div>" , "</div>"
, maybe "" (\i -> "<div class=\"catalog-instrumentation\">" ++ i ++ "</div>") (ceInstrumentation e) , maybe "" (\i -> "<div class=\"catalog-instrumentation\">" ++ escText i ++ "</div>") (ceInstrumentation e)
, "</li>" , "</li>"
] ]
renderCategorySection :: String -> [CatalogEntry] -> String renderCategorySection :: String -> [CatalogEntry] -> String
renderCategorySection cat entries = concat renderCategorySection cat entries = concat
[ "<section class=\"catalog-section\">" [ "<section class=\"catalog-section\">"
, "<h2 class=\"catalog-section-title\">", categoryLabel cat, "</h2>" , "<h2 class=\"catalog-section-title\">", escText (categoryLabel cat), "</h2>"
, "<ul class=\"catalog-list\">" , "<ul class=\"catalog-list\">"
, concatMap renderEntry entries , concatMap renderEntry entries
, "</ul>" , "</ul>"
@ -191,7 +227,12 @@ catalogByCategoryField = field "catalog-by-category" $ \_ -> do
else do else do
let sorted = sortBy (comparing (categoryRank . ceCategory)) entries let sorted = sortBy (comparing (categoryRank . ceCategory)) entries
grouped = groupBy (\a b -> ceCategory a == ceCategory b) sorted grouped = groupBy (\a b -> ceCategory a == ceCategory b) sorted
return $ concatMap (\g -> renderCategorySection (ceCategory (head g)) g) grouped return $ concatMap renderGroup grouped
where
-- groupBy on a non-empty list yields non-empty sublists, but pattern
-- matching is total whereas 'head' is not.
renderGroup [] = ""
renderGroup g@(e : _) = renderCategorySection (ceCategory e) g
musicCatalogCtx :: Context String musicCatalogCtx :: Context String
musicCatalogCtx = musicCatalogCtx =

View File

@ -137,9 +137,14 @@ transformInline :: Map Text Int -> Inline -> Inline
transformInline keyNums (Cite citations _) = transformInline keyNums (Cite citations _) =
let keys = map citationId citations let keys = map citationId citations
nums = mapMaybe (`Map.lookup` keyNums) keys nums = mapMaybe (`Map.lookup` keyNums) keys
in if null nums in case (keys, nums) of
then Str "" -- Both lists are guaranteed non-empty by the @null nums@ check
else RawInline "html" (markerHtml keys (head keys) (head nums) nums) -- below, but pattern-match to keep this total instead of
-- relying on @head@.
(firstKey : _, firstNum : _) ->
RawInline "html" (markerHtml keys firstKey firstNum nums)
_ ->
Str ""
transformInline _ x = x transformInline _ x = x
markerHtml :: [Text] -> Text -> Int -> [Int] -> Text markerHtml :: [Text] -> Text -> Int -> [Int] -> Text

View File

@ -125,9 +125,9 @@ renderThemedView entries =
renderChronoView :: [CPEntry] -> String renderChronoView :: [CPEntry] -> String
renderChronoView entries = renderChronoView entries =
"<div class=\"cp-chrono\" id=\"cp-chrono\" hidden>" "<div class=\"cp-chrono\" id=\"cp-chrono\" hidden>"
++ if null sorted ++ (if null sorted
then "<p class=\"cp-empty\">No entries yet.</p>" then "<p class=\"cp-empty\">No entries yet.</p>"
else concatMap renderEntry sorted else concatMap renderEntry sorted)
++ "</div>" ++ "</div>"
where where
sorted = sortBy (comparing (Down . cpDateAdded)) entries sorted = sortBy (comparing (Down . cpDateAdded)) entries

View File

@ -12,7 +12,6 @@ module Compilers
) where ) where
import Hakyll import Hakyll
import Hakyll.Core.Metadata (lookupStringList, lookupString)
import Text.Pandoc.Definition (Pandoc (..), Block (..), import Text.Pandoc.Definition (Pandoc (..), Block (..),
Inline (..)) Inline (..))
import Text.Pandoc.Options (ReaderOptions (..), WriterOptions (..), import Text.Pandoc.Options (ReaderOptions (..), WriterOptions (..),
@ -158,7 +157,9 @@ essayCompilerWith rOpts = do
Viz.inlineViz srcDir pandocWithScores Viz.inlineViz srcDir pandocWithScores
-- Apply remaining AST-level filters (sidenotes, smallcaps, links, etc.). -- Apply remaining AST-level filters (sidenotes, smallcaps, links, etc.).
let pandocFiltered = applyAll pandocWithViz -- applyAll touches the filesystem via Images.apply (webp existence
-- check), so it runs through unsafeCompiler.
pandocFiltered <- unsafeCompiler $ applyAll srcDir pandocWithViz
let pandocItem' = itemSetBody pandocFiltered pandocItem let pandocItem' = itemSetBody pandocFiltered pandocItem
-- Build TOC from the filtered AST. -- Build TOC from the filtered AST.
@ -205,8 +206,12 @@ pageCompiler = do
body <- getResourceBody body <- getResourceBody
let src = itemBody body let src = itemBody body
body' = itemSetBody (preprocessSource src) body body' = itemSetBody (preprocessSource src) body
pandocItem <- fmap (fmap applyAll) (readPandocWith readerOpts body') filePath <- getResourceFilePath
let htmlItem = writePandocWith writerOpts pandocItem let srcDir = takeDirectory filePath
pandocItem <- readPandocWith readerOpts body'
pandocFiltered <- unsafeCompiler $ applyAll srcDir (itemBody pandocItem)
let pandocItem' = itemSetBody pandocFiltered pandocItem
let htmlItem = writePandocWith writerOpts pandocItem'
_ <- saveSnapshot "word-count" (itemSetBody (show (wordCount src)) htmlItem) _ <- saveSnapshot "word-count" (itemSetBody (show (wordCount src)) htmlItem)
_ <- saveSnapshot "reading-time" (itemSetBody (show (readingTime src)) htmlItem) _ <- saveSnapshot "reading-time" (itemSetBody (show (readingTime src)) htmlItem)
return htmlItem return htmlItem

View File

@ -9,13 +9,16 @@ module Contexts
, fictionCtx , fictionCtx
, compositionCtx , compositionCtx
, contentKindField , contentKindField
, abstractField
, tagLinksField
, authorLinksField
) where ) where
import Data.Aeson (Value (..)) import Data.Aeson (Value (..))
import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.KeyMap as KM
import qualified Data.Vector as V import qualified Data.Vector as V
import Data.List (isPrefixOf) import Data.List (intercalate, isPrefixOf)
import Data.Maybe (catMaybes, fromMaybe) import Data.Maybe (fromMaybe)
import Data.Time.Calendar (toGregorian) import Data.Time.Calendar (toGregorian)
import Data.Time.Clock (getCurrentTime, utctDay) import Data.Time.Clock (getCurrentTime, utctDay)
import Data.Time.Format (formatTime, defaultTimeLocale) import Data.Time.Format (formatTime, defaultTimeLocale)
@ -24,13 +27,11 @@ import Text.Read (readMaybe)
import qualified Data.Text as T import qualified Data.Text as T
import Text.Pandoc (runPure, readMarkdown, writeHtml5String, Pandoc(..), Block(..), Inline(..)) import Text.Pandoc (runPure, readMarkdown, writeHtml5String, Pandoc(..), Block(..), Inline(..))
import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..)) import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..))
import Hakyll import Hakyll hiding (trim)
import Hakyll.Core.Metadata (lookupStringList)
import Authors (authorLinksField)
import Backlinks (backlinksField) import Backlinks (backlinksField)
import SimilarLinks (similarLinksField) import SimilarLinks (similarLinksField)
import Stability (stabilityField, lastReviewedField, versionHistoryField) import Stability (stabilityField, lastReviewedField, versionHistoryField)
import Tags (tagLinksField) import Utils (authorSlugify, authorNameOf, trim)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Affiliation field -- Affiliation field
@ -96,13 +97,13 @@ contentKindField = field "item-kind" $ \item -> do
r <- getRoute (itemIdentifier item) r <- getRoute (itemIdentifier item)
return $ case r of return $ case r of
Nothing -> "Page" Nothing -> "Page"
Just route Just r'
| "essays/" `isPrefixOf` route -> "Essay" | "essays/" `isPrefixOf` r' -> "Essay"
| "blog/" `isPrefixOf` route -> "Post" | "blog/" `isPrefixOf` r' -> "Post"
| "poetry/" `isPrefixOf` route -> "Poem" | "poetry/" `isPrefixOf` r' -> "Poem"
| "fiction/" `isPrefixOf` route -> "Fiction" | "fiction/" `isPrefixOf` r' -> "Fiction"
| "music/" `isPrefixOf` route -> "Composition" | "music/" `isPrefixOf` r' -> "Composition"
| otherwise -> "Page" | otherwise -> "Page"
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Site-wide context -- Site-wide context
@ -112,22 +113,79 @@ contentKindField = field "item-kind" $ \item -> do
-- in the @js:@ frontmatter key (accepts a scalar string or a YAML list). -- in the @js:@ frontmatter key (accepts a scalar string or a YAML list).
-- Returns an empty list when absent; $for iterates zero times, emitting nothing. -- Returns an empty list when absent; $for iterates zero times, emitting nothing.
-- NOTE: do not use fail here — $for does not catch noResult the way $if does. -- NOTE: do not use fail here — $for does not catch noResult the way $if does.
--
-- Each child Item is keyed on @<parent-identifier>#js-<index>@ so that two
-- pages referencing the same script path (e.g. @shared.js@) do not collide
-- in Hakyll's item store.
pageScriptsField :: Context String pageScriptsField :: Context String
pageScriptsField = listFieldWith "page-scripts" ctx $ \item -> do pageScriptsField = listFieldWith "page-scripts" ctx $ \item -> do
meta <- getMetadata (itemIdentifier item) meta <- getMetadata (itemIdentifier item)
let scripts = case lookupStringList "js" meta of let scripts = case lookupStringList "js" meta of
Just xs -> xs Just xs -> xs
Nothing -> maybe [] (:[]) (lookupString "js" meta) Nothing -> maybe [] (:[]) (lookupString "js" meta)
return $ map (\s -> Item (fromFilePath s) s) scripts parent = toFilePath (itemIdentifier item)
return $ zipWith
(\i s -> Item (fromFilePath (parent ++ "#js-" ++ show (i :: Int))) s)
[0 ..]
scripts
where where
ctx = field "script-src" (return . itemBody) ctx = field "script-src" (return . itemBody)
-- ---------------------------------------------------------------------------
-- Tag links field
-- ---------------------------------------------------------------------------
-- | List context field exposing an item's own (non-expanded) tags as
-- @tag-name@ / @tag-url@ objects.
--
-- $for(essay-tags)$<a href="$tag-url$">$tag-name$</a>$endfor$
tagLinksField :: String -> Context a
tagLinksField fieldName = listFieldWith fieldName ctx $ \item ->
map toItem <$> getTags (itemIdentifier item)
where
toItem t = Item (fromFilePath (t ++ "/index.html")) t
ctx = field "tag-name" (return . itemBody)
<> field "tag-url" (\i -> return $ "/" ++ itemBody i ++ "/")
-- ---------------------------------------------------------------------------
-- Author links field
-- ---------------------------------------------------------------------------
--
-- 'authorSlugify' and 'authorNameOf' are imported from 'Utils' so that
-- they cannot drift from the copies in 'Authors'.
-- | Exposes each item's authors as @author-name@ / @author-url@ pairs.
-- Defaults to Levi Neuwirth when no "authors" frontmatter key is present.
--
-- Entries that produce an empty name (e.g. @"| https://url"@) or an empty
-- slug (e.g. all-punctuation names) are dropped, so the field never emits
-- a @/authors//@ link.
--
-- $for(author-links)$<a href="$author-url$">$author-name$</a>$sep$, $endfor$
authorLinksField :: Context a
authorLinksField = listFieldWith "author-links" ctx $ \item -> do
meta <- getMetadata (itemIdentifier item)
let entries = fromMaybe [] (lookupStringList "authors" meta)
rawNames = if null entries then ["Levi Neuwirth"] else map authorNameOf entries
validNames = filter (\n -> not (null n) && not (null (authorSlugify n))) rawNames
names = if null validNames then ["Levi Neuwirth"] else validNames
return $ map (\n -> Item (fromFilePath "") (n, "/authors/" ++ authorSlugify n ++ "/")) names
where
ctx = field "author-name" (return . fst . itemBody)
<> field "author-url" (return . snd . itemBody)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Abstract field -- Abstract field
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Renders the abstract using Pandoc to support Markdown and LaTeX math. -- | Renders the abstract using Pandoc to support Markdown and LaTeX math.
-- Strips the outer <p> tag if the abstract is a single paragraph. -- Strips the outer @<p>@ wrapping. A single-paragraph abstract becomes a
-- bare @Plain@ so the rendered HTML is unwrapped inlines. A multi-paragraph
-- abstract (author used a blank line in the YAML literal block) is flattened
-- to a single @Plain@ with @LineBreak@ separators between what were
-- originally paragraph boundaries — the visual break is preserved without
-- emitting stray @<p>@ tags inside the metadata block. Mixed block content
-- (e.g. an abstract containing a blockquote) falls through unchanged.
abstractField :: Context String abstractField :: Context String
abstractField = field "abstract" $ \item -> do abstractField = field "abstract" $ \item -> do
meta <- getMetadata (itemIdentifier item) meta <- getMetadata (itemIdentifier item)
@ -138,12 +196,20 @@ abstractField = field "abstract" $ \item -> do
doc <- readMarkdown defaultHakyllReaderOptions (T.pack src) doc <- readMarkdown defaultHakyllReaderOptions (T.pack src)
let doc' = case doc of let doc' = case doc of
Pandoc m [Para ils] -> Pandoc m [Plain ils] Pandoc m [Para ils] -> Pandoc m [Plain ils]
_ -> doc Pandoc m blocks
| all isPara blocks && not (null blocks) ->
let joined = intercalate [LineBreak]
[ils | Para ils <- blocks]
in Pandoc m [Plain joined]
_ -> doc
let wOpts = defaultHakyllWriterOptions { writerHTMLMathMethod = MathML } let wOpts = defaultHakyllWriterOptions { writerHTMLMathMethod = MathML }
writeHtml5String wOpts doc' writeHtml5String wOpts doc'
case pandocResult of case pandocResult of
Left err -> fail $ "Pandoc error rendering abstract: " ++ show err Left err -> fail $ "Pandoc error rendering abstract: " ++ show err
Right html -> return (T.unpack html) Right html -> return (T.unpack html)
where
isPara (Para _) = True
isPara _ = False
siteCtx :: Context String siteCtx :: Context String
siteCtx = siteCtx =
@ -208,21 +274,37 @@ dotsField ctxKey metaKey = field ctxKey $ \item -> do
-- | @$confidence-trend$@: ↑, ↓, or → derived from the last two entries -- | @$confidence-trend$@: ↑, ↓, or → derived from the last two entries
-- in the @confidence-history@ frontmatter list. Returns @noResult@ when -- in the @confidence-history@ frontmatter list. Returns @noResult@ when
-- there is no history or only a single entry. -- there is no history or only a single entry.
--
-- The arrow flips when the absolute change crosses 'trendThreshold'
-- (currently 5 percentage points). Smaller swings count as flat.
confidenceTrendField :: Context String confidenceTrendField :: Context String
confidenceTrendField = field "confidence-trend" $ \item -> do confidenceTrendField = field "confidence-trend" $ \item -> do
meta <- getMetadata (itemIdentifier item) meta <- getMetadata (itemIdentifier item)
case lookupStringList "confidence-history" meta of case lookupStringList "confidence-history" meta of
Nothing -> fail "no confidence history" Nothing -> fail "no confidence history"
Just xs | length xs < 2 -> fail "no confidence history" Just xs -> case lastTwo xs of
Just xs -> Nothing -> fail "no confidence history"
let prev = readMaybe (xs !! (length xs - 2)) :: Maybe Int Just (prevS, curS) ->
cur = readMaybe (last xs) :: Maybe Int let prev = readMaybe prevS :: Maybe Int
in case (prev, cur) of cur = readMaybe curS :: Maybe Int
(Just p, Just c) in case (prev, cur) of
| c - p > 5 -> return "\x2191" -- ↑ (Just p, Just c)
| p - c > 5 -> return "\x2193" -- ↓ | c - p > trendThreshold -> return "\x2191" -- ↑
| otherwise -> return "\x2192" -- → | p - c > trendThreshold -> return "\x2193" -- ↓
_ -> return "\x2192" | otherwise -> return "\x2192" -- →
_ -> return "\x2192"
where
trendThreshold :: Int
trendThreshold = 5
-- Total replacement for @(xs !! (length xs - 2), last xs)@: returns
-- the last two elements of a list, in order, or 'Nothing' when the
-- list has fewer than two entries.
lastTwo :: [a] -> Maybe (a, a)
lastTwo [] = Nothing
lastTwo [_] = Nothing
lastTwo [a, b] = Just (a, b)
lastTwo (_ : rest) = lastTwo rest
-- | @$overall-score$@: weighted composite of confidence (50 %), -- | @$overall-score$@: weighted composite of confidence (50 %),
-- evidence quality (30 %), and importance (20 %), expressed as an -- evidence quality (30 %), and importance (20 %), expressed as an
@ -332,12 +414,27 @@ data Movement = Movement
, movAudio :: Maybe String , movAudio :: Maybe String
} }
parseMovements :: Metadata -> [Movement] -- | Parse the @movements@ frontmatter key. Returns parsed movements and a
parseMovements meta = -- list of human-readable warnings for any entries that failed to parse.
-- Callers can surface the warnings via 'unsafeCompiler' so silent typos
-- don't strip movements without diagnostic.
parseMovementsWithWarnings :: Metadata -> ([Movement], [String])
parseMovementsWithWarnings meta =
case KM.lookup "movements" meta of case KM.lookup "movements" meta of
Just (Array v) -> catMaybes $ map parseOne (V.toList v) Just (Array v) ->
_ -> [] let results = zipWith parseIndexed [1 :: Int ..] (V.toList v)
in ( [m | Right m <- results]
, [w | Left w <- results]
)
_ -> ([], [])
where where
parseIndexed i value =
case parseOne value of
Just m -> Right m
Nothing -> Left $
"movement #" ++ show i ++ " is missing a required field "
++ "(name, page, or duration) — entry skipped"
parseOne (Object o) = Movement parseOne (Object o) = Movement
<$> (getString =<< KM.lookup "name" o) <$> (getString =<< KM.lookup "name" o)
<*> (getInt =<< KM.lookup "page" o) <*> (getInt =<< KM.lookup "page" o)
@ -351,6 +448,9 @@ parseMovements meta =
getInt (Number n) = Just (floor (fromRational (toRational n) :: Double)) getInt (Number n) = Just (floor (fromRational (toRational n) :: Double))
getInt _ = Nothing getInt _ = Nothing
parseMovements :: Metadata -> [Movement]
parseMovements = fst . parseMovementsWithWarnings
-- | Extract the composition slug from an item's identifier. -- | Extract the composition slug from an item's identifier.
-- "content/music/symphonic-dances/index.md" → "symphonic-dances" -- "content/music/symphonic-dances/index.md" → "symphonic-dances"
compSlug :: Item a -> String compSlug :: Item a -> String
@ -410,7 +510,11 @@ compositionCtx =
movementsListField = listFieldWith "movements" movCtx $ \item -> do movementsListField = listFieldWith "movements" movCtx $ \item -> do
meta <- getMetadata (itemIdentifier item) meta <- getMetadata (itemIdentifier item)
let mvs = parseMovements meta let (mvs, warnings) = parseMovementsWithWarnings meta
ident = toFilePath (itemIdentifier item)
unsafeCompiler $ mapM_
(\w -> putStrLn $ "[Movements] " ++ ident ++ ": " ++ w)
warnings
return $ zipWith return $ zipWith
(\idx mv -> Item (fromFilePath ("mv" ++ show (idx :: Int))) mv) (\idx mv -> Item (fromFilePath ("mv" ++ show (idx :: Int))) mv)
[1..] mvs [1..] mvs

View File

@ -22,16 +22,25 @@ import qualified Filters.Images as Images
-- | Apply all AST-level filters in pipeline order. -- | Apply all AST-level filters in pipeline order.
-- Run on the Pandoc document after reading, before writing. -- Run on the Pandoc document after reading, before writing.
applyAll :: Pandoc -> Pandoc --
applyAll -- 'Filters.Images.apply' is the only IO-performing filter (it probes the
= Sidenotes.apply -- filesystem for @.webp@ companions before deciding whether to emit
. Typography.apply -- @<picture>@). It runs first — i.e. innermost in the composition — and
. Links.apply -- every downstream filter stays pure. @srcDir@ is the directory of the
. Smallcaps.apply -- source Markdown file, passed through to Images for relative-path
. Dropcaps.apply -- resolution of co-located assets.
. Math.apply applyAll :: FilePath -> Pandoc -> IO Pandoc
. Code.apply applyAll srcDir doc = do
. Images.apply imagesDone <- Images.apply srcDir doc
pure
. Sidenotes.apply
. Typography.apply
. Links.apply
. Smallcaps.apply
. Dropcaps.apply
. Math.apply
. Code.apply
$ imagesDone
-- | Apply source-level preprocessors to the raw Markdown string. -- | Apply source-level preprocessors to the raw Markdown string.
-- Order matters: EmbedPdf must run before Transclusion, because the -- Order matters: EmbedPdf must run before Transclusion, because the

View File

@ -16,6 +16,7 @@ module Filters.EmbedPdf (preprocess) where
import Data.Char (isDigit) import Data.Char (isDigit)
import Data.List (isPrefixOf, isSuffixOf) import Data.List (isPrefixOf, isSuffixOf)
import qualified Utils as U
-- | Apply PDF-embed substitution to the raw Markdown source string. -- | Apply PDF-embed substitution to the raw Markdown source string.
preprocess :: String -> String preprocess :: String -> String
@ -23,7 +24,7 @@ preprocess = unlines . map processLine . lines
processLine :: String -> String processLine :: String -> String
processLine line = processLine line =
case parseDirective (trim line) of case parseDirective (U.trim line) of
Nothing -> line Nothing -> line
Just (filePath, pageHash) -> renderEmbed filePath pageHash Just (filePath, pageHash) -> renderEmbed filePath pageHash
@ -64,7 +65,9 @@ renderEmbed filePath pageHash =
-- | Percent-encode characters that would break a query-string value. -- | Percent-encode characters that would break a query-string value.
-- Slashes are left unencoded so root-relative paths remain readable and -- Slashes are left unencoded so root-relative paths remain readable and
-- work correctly with PDF.js's internal fetch. -- work correctly with PDF.js's internal fetch. @#@ is encoded for
-- defense-in-depth even though the directive parser already splits on it
-- before this function is called.
encodeQueryValue :: String -> String encodeQueryValue :: String -> String
encodeQueryValue = concatMap enc encodeQueryValue = concatMap enc
where where
@ -73,9 +76,6 @@ encodeQueryValue = concatMap enc
enc '?' = "%3F" enc '?' = "%3F"
enc '+' = "%2B" enc '+' = "%2B"
enc '"' = "%22" enc '"' = "%22"
enc '#' = "%23"
enc c = [c] enc c = [c]
-- | Strip leading and trailing spaces.
trim :: String -> String
trim = f . f
where f = reverse . dropWhile (== ' ')

View File

@ -2,53 +2,93 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
-- | Image filter: lazy loading, lightbox markers, and WebP <picture> wrappers. -- | Image filter: lazy loading, lightbox markers, and WebP <picture> wrappers.
-- --
-- For local raster images (JPG, JPEG, PNG, GIF), emits a @<picture>@ element -- For local raster images (JPG, JPEG, PNG, GIF) whose @.webp@ companion
-- with a WebP @<source>@ and the original format as the @<img>@ fallback. -- exists on disk at build time, emits a @<picture>@ element with a WebP
-- tools/convert-images.sh produces the companion .webp files at build time. -- @<source>@ and the original format as the @<img>@ fallback. When the
-- webp companion is absent (cwebp not installed, @convert-images.sh@ not
-- yet run, or a single file missed), the filter emits a plain @<img>@ so
-- the image still renders. This matters because browsers do NOT fall back
-- from a 404'd @<source>@ inside @<picture>@ to the nested @<img>@ — the
-- source is selected up front and a broken one leaves the area blank.
--
-- @tools/convert-images.sh@ produces the companion .webp files at build
-- time. When cwebp is not installed the script is a no-op, and this
-- filter degrades gracefully to plain @<img>@.
-- --
-- SVG files and external URLs are passed through with only lazy loading -- SVG files and external URLs are passed through with only lazy loading
-- (and lightbox markers for standalone images). -- (and lightbox markers for standalone images).
module Filters.Images (apply) where module Filters.Images (apply) where
import Data.Char (toLower) import Data.Char (toLower)
import Data.List (isPrefixOf)
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import System.FilePath (replaceExtension) import System.Directory (doesFileExist)
import System.FilePath (replaceExtension, takeExtension, (</>))
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Walk (walk) import Text.Pandoc.Walk (walkM)
import qualified Utils as U
-- | Apply image attribute injection and WebP wrapping to the entire document. -- | Apply image attribute injection and WebP wrapping to the entire document.
apply :: Pandoc -> Pandoc --
apply = walk transformInline -- @srcDir@ is the directory of the source Markdown file, used to resolve
-- relative image paths when probing for the corresponding @.webp@
-- companion file. Absolute paths (leading @/@) are resolved against
-- @static/@ instead, matching the layout @convert-images.sh@ writes to.
apply :: FilePath -> Pandoc -> IO Pandoc
apply srcDir = walkM (transformInline srcDir)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Core transformation -- Core transformation
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
transformInline :: Inline -> Inline transformInline :: FilePath -> Inline -> IO Inline
transformInline (Link lAttr ils lTarget) = transformInline srcDir (Link lAttr ils lTarget) = do
-- Recurse into link contents; images inside a link get no lightbox marker. -- Recurse into link contents; images inside a link get no lightbox marker.
Link lAttr (map wrapLinkedImg ils) lTarget ils' <- mapM (wrapLinkedImg srcDir) ils
where pure (Link lAttr ils' lTarget)
wrapLinkedImg (Image iAttr alt iTarget) = renderImg iAttr alt iTarget False transformInline srcDir (Image attr alt target) =
wrapLinkedImg x = x renderImg srcDir attr alt target True
transformInline (Image attr alt target) = transformInline _ x = pure x
renderImg attr alt target True
transformInline x = x wrapLinkedImg :: FilePath -> Inline -> IO Inline
wrapLinkedImg srcDir (Image iAttr alt iTarget) =
renderImg srcDir iAttr alt iTarget False
wrapLinkedImg _ x = pure x
-- | Dispatch on image type: -- | Dispatch on image type:
-- * Local raster → @<picture>@ with WebP @<source>@ -- * Local raster with webp companion on disk → @<picture>@ with WebP @<source>@
-- * Everything else → plain @<img>@ with loading/lightbox attrs -- * Local raster without companion → plain @<img>@ (graceful degradation)
renderImg :: Attr -> [Inline] -> Target -> Bool -> Inline -- * Everything else (SVG, URL) → plain @<img>@ with loading/lightbox attrs
renderImg attr alt target@(src, _) lightbox renderImg :: FilePath -> Attr -> [Inline] -> Target -> Bool -> IO Inline
| isLocalRaster (T.unpack src) = renderImg srcDir attr alt target@(src, _) lightbox
RawInline (Format "html") (renderPicture attr alt target lightbox) | isLocalRaster (T.unpack src) = do
hasWebp <- doesFileExist (webpPhysicalPath srcDir src)
if hasWebp
then pure $ RawInline (Format "html")
(renderPicture attr alt target lightbox)
else pure $ Image (addLightbox lightbox (addAttr "loading" "lazy" attr))
alt target
| otherwise = | otherwise =
Image (addLightbox lightbox (addAttr "loading" "lazy" attr)) alt target pure $ Image (addLightbox lightbox (addAttr "loading" "lazy" attr)) alt target
where where
addLightbox True a = addAttr "data-lightbox" "true" a addLightbox True a = addAttr "data-lightbox" "true" a
addLightbox False a = a addLightbox False a = a
-- | Physical on-disk path of the @.webp@ companion for a Markdown image src.
--
-- Absolute paths (@/images/foo.jpg@) resolve under @static/@ because that
-- is where Hakyll's static-asset rule writes them from. Relative paths
-- resolve against the source file's directory, where Pandoc already
-- expects co-located assets to live.
webpPhysicalPath :: FilePath -> Text -> FilePath
webpPhysicalPath srcDir src =
let s = T.unpack src
physical = if "/" `isPrefixOf` s
then "static" ++ s
else srcDir </> s
in replaceExtension physical ".webp"
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- <picture> rendering -- <picture> rendering
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -73,8 +113,13 @@ renderPicture (ident, classes, kvs) alt (src, title) lightbox =
] ]
where where
webpSrc = replaceExtension (T.unpack src) ".webp" webpSrc = replaceExtension (T.unpack src) ".webp"
-- Strip attrs we handle explicitly so they don't appear twice. -- Strip attrs we handle explicitly above (id/class/alt/title) and the
passedKvs = filter (\(k, _) -> k `notElem` ["loading", "data-lightbox"]) kvs -- attrs we always emit ourselves (loading, data-lightbox), so they don't
-- appear twice on the <img>.
passedKvs = filter
(\(k, _) -> k `notElem`
["loading", "data-lightbox", "id", "class", "alt", "title", "src"])
kvs
attrId :: Text -> Text attrId :: Text -> Text
attrId t = if T.null t then "" else " id=\"" <> esc t <> "\"" attrId t = if T.null t then "" else " id=\"" <> esc t <> "\""
@ -103,11 +148,11 @@ isLocalRaster src = not (isUrl src) && lowerExt src `elem` [".jpg", ".jpeg", ".p
isUrl :: String -> Bool isUrl :: String -> Bool
isUrl s = any (`isPrefixOf` s) ["http://", "https://", "//", "data:"] isUrl s = any (`isPrefixOf` s) ["http://", "https://", "//", "data:"]
where isPrefixOf pfx str = take (length pfx) str == pfx
-- | Extension of a path, lowercased (e.g. ".JPG" → ".jpg"). -- | Extension of a path, lowercased (e.g. ".JPG" → ".jpg").
-- Returns the empty string for paths with no extension.
lowerExt :: FilePath -> String lowerExt :: FilePath -> String
lowerExt = map toLower . reverse . ('.' :) . takeWhile (/= '.') . tail . dropWhile (/= '.') . reverse lowerExt = map toLower . takeExtension
-- | Prepend a key=value pair if not already present. -- | Prepend a key=value pair if not already present.
addAttr :: Text -> Text -> Attr -> Attr addAttr :: Text -> Text -> Attr -> Attr
@ -119,24 +164,28 @@ addAttr k v (i, cs, kvs)
stringify :: [Inline] -> Text stringify :: [Inline] -> Text
stringify = T.concat . map go stringify = T.concat . map go
where where
go (Str t) = t go (Str t) = t
go Space = " " go Space = " "
go SoftBreak = " " go SoftBreak = " "
go LineBreak = " " go LineBreak = " "
go (Emph ils) = stringify ils go (Emph ils) = stringify ils
go (Strong ils) = stringify ils go (Strong ils) = stringify ils
go (Code _ t) = t go (Strikeout ils) = stringify ils
go (Link _ ils _) = stringify ils go (Superscript ils) = stringify ils
go (Image _ ils _) = stringify ils go (Subscript ils) = stringify ils
go (Span _ ils) = stringify ils go (SmallCaps ils) = stringify ils
go _ = "" go (Underline ils) = stringify ils
go (Quoted _ ils) = stringify ils
go (Cite _ ils) = stringify ils
go (Code _ t) = t
go (Math _ t) = t
go (RawInline _ _) = ""
go (Link _ ils _) = stringify ils
go (Image _ ils _) = stringify ils
go (Span _ ils) = stringify ils
go (Note _) = ""
-- | HTML-escape a text value for use in attribute values. -- | HTML-escape a text value for use in attribute values.
-- Defers to the canonical 'Utils.escapeHtmlText'.
esc :: Text -> Text esc :: Text -> Text
esc = T.concatMap escChar esc = U.escapeHtmlText
where
escChar '&' = "&amp;"
escChar '<' = "&lt;"
escChar '>' = "&gt;"
escChar '"' = "&quot;"
escChar c = T.singleton c

View File

@ -25,14 +25,20 @@ apply = walk classifyLink . walk classifyPdfLink
-- Preserves the original path in @data-pdf-src@ so the popup thumbnail -- Preserves the original path in @data-pdf-src@ so the popup thumbnail
-- provider can locate the corresponding @.thumb.png@ file. -- provider can locate the corresponding @.thumb.png@ file.
-- Skips links that are already pointing at the viewer (idempotent). -- Skips links that are already pointing at the viewer (idempotent).
--
-- Handles fragment identifiers (e.g. @/papers/foo.pdf#page=5@): the
-- fragment is stripped before the @.pdf@ suffix check and re-attached
-- after the viewer URL so PDF.js's anchor handling works.
classifyPdfLink :: Inline -> Inline classifyPdfLink :: Inline -> Inline
classifyPdfLink (Link (ident, classes, kvs) ils (url, title)) classifyPdfLink (Link (ident, classes, kvs) ils (url, title))
| "/" `T.isPrefixOf` url | "/" `T.isPrefixOf` url
, ".pdf" `T.isSuffixOf` T.toLower url , let (path, fragment) = T.break (== '#') url
, ".pdf" `T.isSuffixOf` T.toLower path
, "pdf-link" `notElem` classes = , "pdf-link" `notElem` classes =
let viewerUrl = "/pdfjs/web/viewer.html?file=" <> encodeQueryValue url let viewerUrl = "/pdfjs/web/viewer.html?file="
<> encodeQueryValue path <> fragment
classes' = classes ++ ["pdf-link"] classes' = classes ++ ["pdf-link"]
kvs' = kvs ++ [("data-pdf-src", url)] kvs' = kvs ++ [("data-pdf-src", path)]
in Link (ident, classes', kvs') ils (viewerUrl, title) in Link (ident, classes', kvs') ils (viewerUrl, title)
classifyPdfLink x = x classifyPdfLink x = x
@ -53,10 +59,33 @@ classifyLink x = x
-- Helpers -- Helpers
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | True if the URL points outside the site's domain.
--
-- Uses a strict hostname comparison rather than substring matching, so
-- that a hostile lookalike like @evil-levineuwirth.org.attacker.com@ is
-- correctly classified as external (and gets @rel=noopener noreferrer@
-- plus @target=_blank@ applied).
isExternal :: Text -> Bool isExternal :: Text -> Bool
isExternal url = isExternal url =
("http://" `T.isPrefixOf` url || "https://" `T.isPrefixOf` url) case extractHost url of
&& not ("levineuwirth.org" `T.isInfixOf` url) Nothing -> False
Just host ->
not (host == siteHost || ("." <> siteHost) `T.isSuffixOf` host)
where
siteHost = "levineuwirth.org"
-- | Extract the lowercased hostname from an absolute http(s) URL.
-- Returns 'Nothing' for non-http(s) URLs (relative paths, mailto:, etc.).
extractHost :: Text -> Maybe Text
extractHost url
| Just rest <- T.stripPrefix "https://" url = Just (hostOf rest)
| Just rest <- T.stripPrefix "http://" url = Just (hostOf rest)
| otherwise = Nothing
where
hostOf rest =
let withPort = T.takeWhile (\c -> c /= '/' && c /= '?' && c /= '#') rest
host = T.takeWhile (/= ':') withPort
in T.toLower host
-- | Icon name for the link, matching a file in /images/link-icons/<name>.svg. -- | Icon name for the link, matching a file in /images/link-icons/<name>.svg.
domainIcon :: Text -> Text domainIcon :: Text -> Text

View File

@ -14,12 +14,16 @@
-- the appropriate exhibit attributes for gallery.js TOC integration. -- the appropriate exhibit attributes for gallery.js TOC integration.
module Filters.Score (inlineScores) where module Filters.Score (inlineScores) where
import Control.Exception (IOException, try)
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as TIO import qualified Data.Text.IO as TIO
import System.Directory (doesFileExist)
import System.FilePath ((</>)) import System.FilePath ((</>))
import System.IO (hPutStrLn, stderr)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Walk (walkM) import Text.Pandoc.Walk (walkM)
import qualified Utils as U
-- | Walk the Pandoc AST and inline all score-fragment divs. -- | Walk the Pandoc AST and inline all score-fragment divs.
-- @baseDir@ is the directory of the source file; image paths in the -- @baseDir@ is the directory of the source file; image paths in the
@ -37,11 +41,40 @@ inlineScore baseDir (Div (_, cls, attrs) blocks)
Nothing -> return $ Div ("", cls, attrs) blocks Nothing -> return $ Div ("", cls, attrs) blocks
Just path -> do Just path -> do
let fullPath = baseDir </> T.unpack path let fullPath = baseDir </> T.unpack path
svgRaw <- TIO.readFile fullPath exists <- doesFileExist fullPath
let html = buildHtml mName mCaption (processColors svgRaw) if not exists
return $ RawBlock (Format "html") html then do
hPutStrLn stderr $
"[Score] missing SVG: " ++ fullPath
++ " (referenced from a score-fragment in " ++ baseDir ++ ")"
return (errorBlock mName ("Missing score: " <> path))
else do
result <- try (TIO.readFile fullPath) :: IO (Either IOException T.Text)
case result of
Left e -> do
hPutStrLn stderr $
"[Score] read error on " ++ fullPath ++ ": " ++ show e
return (errorBlock mName ("Could not read score: " <> path))
Right svgRaw -> do
let html = buildHtml mName mCaption (processColors svgRaw)
return $ RawBlock (Format "html") html
inlineScore _ block = return block inlineScore _ block = return block
-- | Render an inline error block in place of a missing or unreadable score.
-- Mirrors the convention in 'Filters.Viz.errorBlock' so build failures are
-- visible to the author without aborting the entire site build.
errorBlock :: Maybe T.Text -> T.Text -> Block
errorBlock mName message =
RawBlock (Format "html") $ T.concat
[ "<figure class=\"score-fragment score-fragment--error\""
, maybe "" (\n -> " data-exhibit-name=\"" <> escHtml n <> "\"") mName
, ">"
, "<div class=\"score-fragment-error\">"
, escHtml message
, "</div>"
, "</figure>"
]
-- | Extract the image src from the first Para that contains an Image inline. -- | Extract the image src from the first Para that contains an Image inline.
findImagePath :: [Block] -> Maybe T.Text findImagePath :: [Block] -> Maybe T.Text
findImagePath blocks = listToMaybe findImagePath blocks = listToMaybe
@ -86,7 +119,4 @@ buildHtml mName mCaption svgContent = T.concat
] ]
escHtml :: T.Text -> T.Text escHtml :: T.Text -> T.Text
escHtml = T.replace "\"" "&quot;" escHtml = U.escapeHtmlText
. T.replace ">" "&gt;"
. T.replace "<" "&lt;"
. T.replace "&" "&amp;"

View File

@ -33,13 +33,24 @@ convertNote (Note blocks) = do
return $ RawInline "html" (renderNote n blocks) return $ RawInline "html" (renderNote n blocks)
convertNote x = return x convertNote x = return x
-- | Convert a 1-based counter to a letter label: 1→a, 2→b, … 26→z. -- | Convert a 1-based counter to a letter label using base-26 expansion
-- (Excel-column style): 1→a, 2→b, … 26→z, 27→aa, 28→ab, … 52→az,
-- 53→ba, … 702→zz, 703→aaa. Guarantees a unique label per counter so
-- no two sidenotes in a single document collide on @id="sn-…"@.
toLabel :: Int -> Text toLabel :: Int -> Text
toLabel n = T.singleton (toEnum (fromEnum 'a' + (n - 1) `mod` 26)) toLabel n
| n <= 0 = "?"
| otherwise = T.pack (go n)
where
go k
| k <= 0 = ""
| otherwise =
let (q, r) = (k - 1) `divMod` 26
in go q ++ [toEnum (fromEnum 'a' + r)]
renderNote :: Int -> [Block] -> Text renderNote :: Int -> [Block] -> Text
renderNote n blocks = renderNote n blocks =
let inner = replacePTags (blocksToHtml blocks) let inner = blocksToInlineHtml blocks
lbl = toLabel n lbl = toLabel n
in T.concat in T.concat
[ "<sup class=\"sidenote-ref\" id=\"snref-", lbl, "\">" [ "<sup class=\"sidenote-ref\" id=\"snref-", lbl, "\">"
@ -51,13 +62,34 @@ renderNote n blocks =
, "</span>" , "</span>"
] ]
-- | Replace <p> / </p> with inline-block spans so that sidenote content -- | Render a list of Pandoc blocks for inclusion inside an inline @<span
-- stays valid inside the outer <span class="sidenote">. A bare <p> inside -- class="sidenote">@. Each top-level @Para@ is wrapped in a
-- a <span> is invalid HTML and causes browsers to implicitly close the span. -- @<span class="sidenote-para">@ instead of a @<p>@ (which would be
replacePTags :: Text -> Text -- invalid inside a @<span>@); other block types are rendered with the
replacePTags = -- regular Pandoc HTML writer.
T.replace "<p>" "<span class=\"sidenote-para\">" --
. T.replace "</p>" "</span>" -- Operating on the AST is preferred over post-rendered string
-- substitution because the latter mangles content that legitimately
-- contains the literal text @<p>@ (e.g. code samples discussing HTML).
blocksToInlineHtml :: [Block] -> Text
blocksToInlineHtml = T.concat . map renderOne
where
renderOne :: Block -> Text
renderOne (Para inlines) =
"<span class=\"sidenote-para\">"
<> inlinesToHtml inlines
<> "</span>"
renderOne (Plain inlines) =
inlinesToHtml inlines
renderOne b =
blocksToHtml [b]
-- | Render a list of inlines to HTML (no surrounding @<p>@).
inlinesToHtml :: [Inline] -> Text
inlinesToHtml inlines =
case runPure (writeHtml5String (def :: WriterOptions) (Pandoc mempty [Plain inlines])) of
Left _ -> T.empty
Right t -> t
-- | Render a list of Pandoc blocks to an HTML fragment via a pure writer run. -- | Render a list of Pandoc blocks to an HTML fragment via a pure writer run.
blocksToHtml :: [Block] -> Text blocksToHtml :: [Block] -> Text

View File

@ -22,6 +22,7 @@ import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Walk (walk) import Text.Pandoc.Walk (walk)
import qualified Utils as U
-- | Apply smallcaps detection to paragraph-level content. -- | Apply smallcaps detection to paragraph-level content.
-- Skips heading blocks to avoid false positives. -- Skips heading blocks to avoid false positives.
@ -62,10 +63,4 @@ isAbbreviation t =
&& T.any isAlpha t && T.any isAlpha t
escHtml :: Text -> Text escHtml :: Text -> Text
escHtml = T.concatMap esc escHtml = U.escapeHtmlText
where
esc '<' = "&lt;"
esc '>' = "&gt;"
esc '&' = "&amp;"
esc '"' = "&quot;"
esc c = T.singleton c

View File

@ -14,6 +14,7 @@
module Filters.Transclusion (preprocess) where module Filters.Transclusion (preprocess) where
import Data.List (isSuffixOf, isPrefixOf, stripPrefix) import Data.List (isSuffixOf, isPrefixOf, stripPrefix)
import qualified Utils as U
-- | Apply transclusion substitution to the raw Markdown source string. -- | Apply transclusion substitution to the raw Markdown source string.
preprocess :: String -> String preprocess :: String -> String
@ -21,14 +22,18 @@ preprocess = unlines . map processLine . lines
processLine :: String -> String processLine :: String -> String
processLine line = processLine line =
case parseDirective (trim line) of case parseDirective (U.trim line) of
Nothing -> line Nothing -> line
Just (url, secAttr) -> Just (url, secAttr) ->
"<div class=\"transclude\" data-src=\"" ++ url ++ "\"" "<div class=\"transclude\" data-src=\"" ++ escAttr url ++ "\""
++ secAttr ++ "></div>" ++ secAttr ++ "></div>"
-- | Parse a {{slug}} or {{slug#section}} directive. -- | Parse a {{slug}} or {{slug#section}} directive.
-- Returns (absolute-url, section-attribute-string) or Nothing. -- Returns (absolute-url, section-attribute-string) or Nothing.
--
-- The section name is HTML-escaped before being interpolated into the
-- @data-section@ attribute, so a stray @\"@, @&@, @<@, or @>@ in a
-- section name cannot break the surrounding markup.
parseDirective :: String -> Maybe (String, String) parseDirective :: String -> Maybe (String, String)
parseDirective s = do parseDirective s = do
inner <- stripPrefix "{{" s >>= stripSuffix "}}" inner <- stripPrefix "{{" s >>= stripSuffix "}}"
@ -38,15 +43,29 @@ parseDirective s = do
(slug, '#' : sec) (slug, '#' : sec)
| null sec -> Just (slugToUrl slug, "") | null sec -> Just (slugToUrl slug, "")
| otherwise -> Just (slugToUrl slug, | otherwise -> Just (slugToUrl slug,
" data-section=\"" ++ sec ++ "\"") " data-section=\"" ++ escAttr sec ++ "\"")
_ -> Nothing _ -> Nothing
-- | Convert a slug (possibly with leading slash, possibly with path segments) -- | Convert a slug (possibly with leading slash, possibly with path segments)
-- to a root-relative .html URL. -- to a root-relative .html URL. Idempotent for slugs that already end in
-- @.html@ so callers can safely pass either form.
slugToUrl :: String -> String slugToUrl :: String -> String
slugToUrl slug slugToUrl slug
| "/" `isPrefixOf` slug = slug ++ ".html" | ".html" `isSuffixOf` slug, "/" `isPrefixOf` slug = slug
| otherwise = "/" ++ slug ++ ".html" | ".html" `isSuffixOf` slug = "/" ++ slug
| "/" `isPrefixOf` slug = slug ++ ".html"
| otherwise = "/" ++ slug ++ ".html"
-- | Minimal HTML attribute-value escape.
escAttr :: String -> String
escAttr = concatMap esc
where
esc '&' = "&amp;"
esc '<' = "&lt;"
esc '>' = "&gt;"
esc '"' = "&quot;"
esc '\'' = "&#39;"
esc c = [c]
-- | Strip a suffix from a string, returning Nothing if not present. -- | Strip a suffix from a string, returning Nothing if not present.
stripSuffix :: String -> String -> Maybe String stripSuffix :: String -> String -> Maybe String
@ -54,7 +73,3 @@ stripSuffix suf str
| suf `isSuffixOf` str = Just (take (length str - length suf) str) | suf `isSuffixOf` str = Just (take (length str - length suf) str)
| otherwise = Nothing | otherwise = Nothing
-- | Strip leading and trailing spaces.
trim :: String -> String
trim = f . f
where f = reverse . dropWhile (== ' ')

View File

@ -9,9 +9,9 @@
module Filters.Typography (apply) where module Filters.Typography (apply) where
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Walk (walk) import Text.Pandoc.Walk (walk)
import Utils (escapeHtmlText)
-- | Apply all typographic transformations to the document. -- | Apply all typographic transformations to the document.
apply :: Pandoc -> Pandoc apply :: Pandoc -> Pandoc
@ -38,21 +38,17 @@ abbrevMap =
-- | If the Str token exactly matches a known abbreviation, replace it with -- | If the Str token exactly matches a known abbreviation, replace it with
-- a @RawInline "html"@ @<abbr>@ element; otherwise leave it unchanged. -- a @RawInline "html"@ @<abbr>@ element; otherwise leave it unchanged.
--
-- Both the @title@ attribute and the visible body pass through
-- 'escapeHtmlText' for consistency with every other raw-HTML emitter
-- in the filter pipeline. The abbreviations themselves are ASCII-safe
-- so this is defense-in-depth rather than a live hazard.
expandAbbrev :: Inline -> Inline expandAbbrev :: Inline -> Inline
expandAbbrev (Str t) = expandAbbrev (Str t) =
case lookup t abbrevMap of case lookup t abbrevMap of
Just title -> Just title ->
RawInline "html" $ RawInline "html" $
"<abbr title=\"" <> title <> "\">" <> escHtml t <> "</abbr>" "<abbr title=\"" <> escapeHtmlText title <> "\">"
<> escapeHtmlText t <> "</abbr>"
Nothing -> Str t Nothing -> Str t
expandAbbrev x = x expandAbbrev x = x
-- | Minimal HTML escaping for the abbr content (should be plain text).
escHtml :: Text -> Text
escHtml = T.concatMap esc
where
esc '<' = "&lt;"
esc '>' = "&gt;"
esc '&' = "&amp;"
esc '"' = "&quot;"
esc c = T.singleton c

View File

@ -39,11 +39,14 @@ module Filters.Viz (inlineViz) where
import Control.Exception (IOException, catch) import Control.Exception (IOException, catch)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import qualified Data.Text as T import qualified Data.Text as T
import System.Directory (doesFileExist)
import System.Exit (ExitCode (..)) import System.Exit (ExitCode (..))
import System.FilePath ((</>)) import System.FilePath ((</>))
import System.IO (hPutStrLn, stderr)
import System.Process (readProcessWithExitCode) import System.Process (readProcessWithExitCode)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Walk (walkM) import Text.Pandoc.Walk (walkM)
import qualified Utils as U
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Public entry point -- Public entry point
@ -87,19 +90,26 @@ transformBlock _ b = return b
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Run @python3 <script>@. Returns the script\'s stdout on success, or an -- | Run @python3 <script>@. Returns the script\'s stdout on success, or an
-- error message on failure (non-zero exit or missing @script=@ attribute). -- error message on failure (non-zero exit, missing @script=@ attribute, or
-- missing script file).
runScript :: FilePath -> [(T.Text, T.Text)] -> IO (Either String T.Text) runScript :: FilePath -> [(T.Text, T.Text)] -> IO (Either String T.Text)
runScript baseDir attrs = runScript baseDir attrs =
case lookup "script" attrs of case lookup "script" attrs of
Nothing -> return (Left "missing script= attribute") Nothing -> return (Left "missing script= attribute")
Just p -> do Just p -> do
let fullPath = baseDir </> T.unpack p let fullPath = baseDir </> T.unpack p
(ec, out, err) <- exists <- doesFileExist fullPath
readProcessWithExitCode "python3" [fullPath] "" if not exists
`catch` (\e -> return (ExitFailure 1, "", show (e :: IOException))) then return (Left ("script not found: " ++ fullPath))
return $ case ec of else do
ExitSuccess -> Right (T.pack out) (ec, out, err) <-
ExitFailure _ -> Left (if null err then "non-zero exit" else err) readProcessWithExitCode "python3" [fullPath] ""
`catch` (\e -> return (ExitFailure 1, "", show (e :: IOException)))
return $ case ec of
ExitSuccess -> Right (T.pack out)
ExitFailure _ -> Left $
"in " ++ fullPath ++ ": "
++ (if null err then "non-zero exit" else err)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- SVG colour post-processing (mirrors Filters.Score.processColors) -- SVG colour post-processing (mirrors Filters.Score.processColors)
@ -173,10 +183,7 @@ attr :: T.Text -> [(T.Text, T.Text)] -> T.Text
attr key kvs = fromMaybe "" (lookup key kvs) attr key kvs = fromMaybe "" (lookup key kvs)
warn :: String -> String -> IO () warn :: String -> String -> IO ()
warn kind msg = putStrLn $ "[Viz] " ++ kind ++ " error: " ++ msg warn kind msg = hPutStrLn stderr $ "[Viz] " ++ kind ++ " error: " ++ msg
escHtml :: T.Text -> T.Text escHtml :: T.Text -> T.Text
escHtml = T.replace "&" "&amp;" escHtml = U.escapeHtmlText
. T.replace "<" "&lt;"
. T.replace ">" "&gt;"
. T.replace "\"" "&quot;"

View File

@ -15,6 +15,7 @@ module Filters.Wikilinks (preprocess) where
import Data.Char (isAlphaNum, toLower, isSpace) import Data.Char (isAlphaNum, toLower, isSpace)
import Data.List (intercalate) import Data.List (intercalate)
import qualified Utils as U
-- | Scan the raw Markdown source for @[[…]]@ wikilinks and replace them -- | Scan the raw Markdown source for @[[…]]@ wikilinks and replace them
-- with standard Markdown link syntax. -- with standard Markdown link syntax.
@ -29,21 +30,49 @@ preprocess ('[':'[':rest) =
preprocess (c:rest) = c : preprocess rest preprocess (c:rest) = c : preprocess rest
-- | Convert the inner content of @[[…]]@ to a Markdown link. -- | Convert the inner content of @[[…]]@ to a Markdown link.
--
-- Display text is escaped via 'escMdLinkText' so that a literal @]@, @[@,
-- or backslash in the display does not break the surrounding Markdown
-- link syntax. The URL itself is produced by 'slugify' and therefore only
-- ever contains @[a-z0-9-]@, so no URL-side encoding is needed — adding
-- one would be defense against a character set we can't produce.
toMarkdownLink :: String -> String toMarkdownLink :: String -> String
toMarkdownLink inner = toMarkdownLink inner =
let (title, display) = splitOnPipe inner let (title, display) = splitOnPipe inner
url = "/" ++ slugify title url = "/" ++ slugify title
in "[" ++ display ++ "](" ++ url ++ ")" in "[" ++ escMdLinkText display ++ "](" ++ url ++ ")"
-- | Escape the minimum set of characters that would prematurely terminate
-- a Markdown link's display-text segment: backslash (escape char), @[@,
-- and @]@. Backslash MUST be escaped first so the escapes we introduce
-- for @[@ and @]@ are not themselves re-escaped.
--
-- Deliberately NOT escaped: @_@, @*@, @\`@, @<@. Those are inline
-- formatting markers in Markdown and escaping them would strip the
-- author's ability to put emphasis, code, or inline HTML in a wikilink's
-- display text.
escMdLinkText :: String -> String
escMdLinkText = concatMap esc
where
esc '\\' = "\\\\"
esc '[' = "\\["
esc ']' = "\\]"
esc c = [c]
-- | Split on the first @|@; if none, display = title. -- | Split on the first @|@; if none, display = title.
splitOnPipe :: String -> (String, String) splitOnPipe :: String -> (String, String)
splitOnPipe s = splitOnPipe s =
case break (== '|') s of case break (== '|') s of
(title, '|':display) -> (trim title, trim display) (title, '|':display) -> (U.trim title, U.trim display)
_ -> (trim s, trim s) _ -> (U.trim s, U.trim s)
-- | Produce a URL slug: lowercase, words joined by hyphens, -- | Produce a URL slug: lowercase, words joined by hyphens,
-- non-alphanumeric characters removed. -- non-alphanumeric characters removed.
--
-- Trailing punctuation is dropped rather than preserved as a dangling
-- hyphen — @slugify "end." == "end"@, not @"end-"@. This is intentional:
-- author-authored wikilinks tend to end sentences with a period and the
-- desired URL is almost always the terminal-punctuation-free form.
slugify :: String -> String slugify :: String -> String
slugify = intercalate "-" . words . map toLowerAlnum slugify = intercalate "-" . words . map toLowerAlnum
where where
@ -55,5 +84,3 @@ slugify = intercalate "-" . words . map toLowerAlnum
-- split correctly and double-hyphens are -- split correctly and double-hyphens are
-- collapsed by 'words' -- collapsed by 'words'
trim :: String -> String
trim = reverse . dropWhile (== ' ') . reverse . dropWhile (== ' ')

View File

@ -1,2 +0,0 @@
-- | Metadata utilities (Phase 2+).
module Metadata where

100
build/Patterns.hs Normal file
View File

@ -0,0 +1,100 @@
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Canonical content-pattern definitions, shared across modules.
--
-- Several modules need to enumerate "all author-written content" or
-- "all essays". Historically each module hard-coded its own slightly
-- different list, which produced silent omissions (e.g. directory-form
-- essays not appearing on author pages). This module is the single source
-- of truth — every place that needs a content pattern should import from
-- here, not write its own.
module Patterns
( -- * Per-section patterns
essayPattern
, draftEssayPattern
, blogPattern
, poetryPattern
, fictionPattern
, musicPattern
, standalonePagesPattern
-- * Aggregated patterns
, allWritings -- essays + blog + poetry + fiction
, allContent -- everything that backlinks should index
, authorIndexable -- everything that should appear on /authors/{slug}/
, tagIndexable -- everything that should appear on /<tag>/
) where
import Hakyll
-- ---------------------------------------------------------------------------
-- Per-section
-- ---------------------------------------------------------------------------
-- | All published essays — flat files and directory-based (with co-located
-- assets). Drafts under @content/drafts/essays/**@ are intentionally NOT
-- included; 'Site.rules' unions them in conditionally when @SITE_ENV=dev@.
essayPattern :: Pattern
essayPattern = "content/essays/*.md" .||. "content/essays/*/index.md"
-- | In-progress essay drafts. Matches the flat and directory forms under
-- @content/drafts/essays/@. Only 'Site.rules' consumes this, gated on
-- @SITE_ENV=dev@ — every other module that enumerates content (Authors,
-- Tags, Backlinks, Stats, feeds) sees only 'essayPattern', so drafts are
-- automatically invisible to listings, tags, authors, backlinks, and stats.
draftEssayPattern :: Pattern
draftEssayPattern =
"content/drafts/essays/*.md"
.||. "content/drafts/essays/*/index.md"
-- | All blog posts. Currently flat-only; co-located blog assets would
-- require a directory variant analogous to 'essayPattern'.
blogPattern :: Pattern
blogPattern = "content/blog/*.md"
-- | All poetry: flat poems plus collection poems, excluding collection
-- index pages (which are landing pages, not poems).
poetryPattern :: Pattern
poetryPattern =
"content/poetry/*.md"
.||. ("content/poetry/*/*.md" .&&. complement "content/poetry/*/index.md")
-- | All fiction. Currently flat-only.
fictionPattern :: Pattern
fictionPattern = "content/fiction/*.md"
-- | Music compositions (landing pages live at @content/music/<slug>/index.md@).
musicPattern :: Pattern
musicPattern = "content/music/*/index.md"
-- | Top-level standalone pages (about, colophon, current, gpg, …).
standalonePagesPattern :: Pattern
standalonePagesPattern = "content/*.md"
-- ---------------------------------------------------------------------------
-- Aggregations
-- ---------------------------------------------------------------------------
-- | All long-form authored writings.
allWritings :: Pattern
allWritings = essayPattern .||. blogPattern .||. poetryPattern .||. fictionPattern
-- | Every content file the backlinks pass should index. Includes music
-- landing pages and top-level standalone pages, in addition to writings.
allContent :: Pattern
allContent =
essayPattern
.||. blogPattern
.||. poetryPattern
.||. fictionPattern
.||. musicPattern
.||. standalonePagesPattern
-- | Content shown on author index pages — essays + blog posts.
-- (Poetry and fiction have their own dedicated indexes and are not
-- aggregated by author.)
authorIndexable :: Pattern
authorIndexable = (essayPattern .||. blogPattern) .&&. hasNoVersion
-- | Content shown on tag index pages — essays + blog posts.
tagIndexable :: Pattern
tagIndexable = (essayPattern .||. blogPattern) .&&. hasNoVersion

View File

@ -18,10 +18,12 @@
module SimilarLinks (similarLinksField) where module SimilarLinks (similarLinksField) where
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import qualified Data.ByteString as BS
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TE
import qualified Data.Aeson as Aeson import qualified Data.Aeson as Aeson
import Hakyll import Hakyll
@ -83,7 +85,29 @@ normaliseUrl url =
t3 = fromMaybe t2 (T.stripSuffix "index.html" t2) t3 = fromMaybe t2 (T.stripSuffix "index.html" t2)
-- strip bare .html extension only for non-index pages -- strip bare .html extension only for non-index pages
t4 = fromMaybe t3 (T.stripSuffix ".html" t3) t4 = fromMaybe t3 (T.stripSuffix ".html" t3)
in T.unpack t4 in percentDecode (T.unpack t4)
-- | Percent-decode @%XX@ escapes (UTF-8) so percent-encoded paths
-- collide with their decoded form on map lookup. Mirrors
-- 'Backlinks.percentDecode'; the two implementations are intentionally
-- duplicated because they apply different normalisations *before*
-- decoding (Backlinks strips @.html@ unconditionally; SimilarLinks
-- preserves the trailing-slash form for index pages).
percentDecode :: String -> String
percentDecode = T.unpack . TE.decodeUtf8With TE.lenientDecode . BS.pack . go
where
go [] = []
go ('%':a:b:rest)
| Just hi <- hexDigit a
, Just lo <- hexDigit b
= fromIntegral (hi * 16 + lo) : go rest
go (c:rest) = fromIntegral (fromEnum c) : go rest
hexDigit c
| c >= '0' && c <= '9' = Just (fromEnum c - fromEnum '0')
| c >= 'a' && c <= 'f' = Just (fromEnum c - fromEnum 'a' + 10)
| c >= 'A' && c <= 'F' = Just (fromEnum c - fromEnum 'A' + 10)
| otherwise = Nothing
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- HTML rendering -- HTML rendering

View File

@ -2,10 +2,13 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Site (rules) where module Site (rules) where
import Control.Monad (filterM) import Control.Monad (filterM, when)
import Data.List (intercalate, isPrefixOf) import Data.List (isPrefixOf)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import System.Environment (lookupEnv)
import System.FilePath (takeDirectory, takeFileName, replaceExtension) import System.FilePath (takeDirectory, takeFileName, replaceExtension)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as LBS
import Hakyll import Hakyll
import Authors (buildAllAuthors, applyAuthorRules) import Authors (buildAllAuthors, applyAuthorRules)
import Backlinks (backlinkRules) import Backlinks (backlinkRules)
@ -15,14 +18,11 @@ import Catalog (musicCatalogCtx)
import Commonplace (commonplaceCtx) import Commonplace (commonplaceCtx)
import Contexts (siteCtx, essayCtx, postCtx, pageCtx, poetryCtx, fictionCtx, compositionCtx, import Contexts (siteCtx, essayCtx, postCtx, pageCtx, poetryCtx, fictionCtx, compositionCtx,
contentKindField) contentKindField)
import qualified Patterns as P
import Tags (buildAllTags, applyTagRules) import Tags (buildAllTags, applyTagRules)
import Pagination (blogPaginateRules) import Pagination (blogPaginateRules)
import Stats (statsRules) import Stats (statsRules)
-- All essays: flat files and directory-based (with co-located assets).
allEssays :: Pattern
allEssays = "content/essays/*.md" .||. "content/essays/*/index.md"
-- Poems inside collection subdirectories, excluding their index pages. -- Poems inside collection subdirectories, excluding their index pages.
collectionPoems :: Pattern collectionPoems :: Pattern
collectionPoems = "content/poetry/*/*.md" .&&. complement "content/poetry/*/index.md" collectionPoems = "content/poetry/*/*.md" .&&. complement "content/poetry/*/index.md"
@ -51,6 +51,16 @@ musicFeedConfig = FeedConfiguration
rules :: Rules () rules :: Rules ()
rules = do rules = do
-- ---------------------------------------------------------------------------
-- Build mode. SITE_ENV=dev (set by `make dev` / `make watch`) includes
-- drafts under content/drafts/**; anything else (unset, "deploy", "build")
-- excludes them entirely from every match, listing, and asset rule below.
-- ---------------------------------------------------------------------------
isDev <- preprocess $ (== Just "dev") <$> lookupEnv "SITE_ENV"
let allEssays = if isDev
then P.essayPattern .||. P.draftEssayPattern
else P.essayPattern
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Backlinks (pass 1: link extraction; pass 2: JSON generation) -- Backlinks (pass 1: link extraction; pass 2: JSON generation)
-- Must run before content rules so dependencies resolve correctly. -- Must run before content rules so dependencies resolve correctly.
@ -70,8 +80,14 @@ rules = do
applyTagRules tags siteCtx applyTagRules tags siteCtx
statsRules tags statsRules tags
-- Per-page JS files — authored alongside content in content/**/*.js -- Per-page JS files — authored alongside content in content/**/*.js.
match "content/**/*.js" $ do -- Draft JS is handled by a separate dev-only rule below.
match ("content/**/*.js" .&&. complement "content/drafts/**") $ do
route $ gsubRoute "content/" (const "")
compile copyFileCompiler
-- Per-page JS co-located with draft essays (dev-only).
when isDev $ match "content/drafts/**/*.js" $ do
route $ gsubRoute "content/" (const "") route $ gsubRoute "content/" (const "")
compile copyFileCompiler compile copyFileCompiler
@ -177,14 +193,25 @@ rules = do
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Essays — flat (content/essays/foo.md → essays/foo.html) and -- Essays — flat (content/essays/foo.md → essays/foo.html) and
-- directory-based (content/essays/slug/index.md → essays/slug/index.html) -- directory-based (content/essays/slug/index.md → essays/slug/index.html).
-- In dev mode, drafts under content/drafts/essays/ route to
-- drafts/essays/foo.html (flat) or drafts/essays/slug/index.html (dir).
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
match allEssays $ do match allEssays $ do
route $ customRoute $ \ident -> route $ customRoute $ \ident ->
let fp = toFilePath ident let fp = toFilePath ident
in if takeFileName fp == "index.md" fname = takeFileName fp
then replaceExtension (drop 8 fp) "html" isIndex = fname == "index.md"
else "essays/" ++ replaceExtension (takeFileName fp) "html" isDraft = "content/drafts/essays/" `isPrefixOf` fp
in case (isDraft, isIndex) of
-- content/drafts/essays/slug/index.md → drafts/essays/slug/index.html
(True, True) -> replaceExtension (drop 8 fp) "html"
-- content/drafts/essays/foo.md → drafts/essays/foo.html
(True, False) -> "drafts/essays/" ++ replaceExtension fname "html"
-- content/essays/slug/index.md → essays/slug/index.html
(False, True) -> replaceExtension (drop 8 fp) "html"
-- content/essays/foo.md → essays/foo.html
(False, False) -> "essays/" ++ replaceExtension fname "html"
compile $ essayCompiler compile $ essayCompiler
>>= saveSnapshot "content" >>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/essay.html" essayCtx >>= loadAndApplyTemplate "templates/essay.html" essayCtx
@ -198,6 +225,13 @@ rules = do
route $ gsubRoute "content/" (const "") route $ gsubRoute "content/" (const "")
compile copyFileCompiler compile copyFileCompiler
-- Static assets co-located with draft essays (dev-only).
when isDev $ match ("content/drafts/essays/**"
.&&. complement "content/drafts/essays/*.md"
.&&. complement "content/drafts/essays/*/index.md") $ do
route $ gsubRoute "content/" (const "")
compile copyFileCompiler
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Blog posts -- Blog posts
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -400,7 +434,7 @@ rules = do
poetry <- loadAll ("content/poetry/*.md" .&&. hasNoVersion) :: Compiler [Item String] poetry <- loadAll ("content/poetry/*.md" .&&. hasNoVersion) :: Compiler [Item String]
routes <- mapM (getRoute . itemIdentifier) (essays ++ posts ++ fiction ++ poetry) routes <- mapM (getRoute . itemIdentifier) (essays ++ posts ++ fiction ++ poetry)
let urls = [ "/" ++ r | Just r <- routes ] let urls = [ "/" ++ r | Just r <- routes ]
makeItem $ "[" ++ intercalate "," (map show urls) ++ "]" makeItem $ LBS.unpack (Aeson.encode urls)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Atom feed — all content sorted by date -- Atom feed — all content sorted by date

View File

@ -29,7 +29,9 @@ import Data.Maybe (catMaybes, fromMaybe, listToMaybe)
import Data.Time.Calendar (Day, diffDays) import Data.Time.Calendar (Day, diffDays)
import Data.Time.Format (parseTimeM, formatTime, defaultTimeLocale) import Data.Time.Format (parseTimeM, formatTime, defaultTimeLocale)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import System.Exit (ExitCode (..)) import System.Exit (ExitCode (..))
import System.IO (hPutStrLn, stderr)
import System.Process (readProcessWithExitCode) import System.Process (readProcessWithExitCode)
import Hakyll import Hakyll
@ -39,9 +41,12 @@ import Hakyll
-- | Read @IGNORE.txt@ (paths relative to project root, one per line). -- | Read @IGNORE.txt@ (paths relative to project root, one per line).
-- Returns an empty list when the file is absent or empty. -- Returns an empty list when the file is absent or empty.
--
-- Uses strict text IO so the file handle is released immediately rather
-- than left dangling on the lazy spine of 'readFile'.
readIgnore :: IO [FilePath] readIgnore :: IO [FilePath]
readIgnore = readIgnore =
(filter (not . null) . lines <$> readFile "IGNORE.txt") (filter (not . null) . map T.unpack . T.lines <$> TIO.readFile "IGNORE.txt")
`catch` \(_ :: IOException) -> return [] `catch` \(_ :: IOException) -> return []
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -49,13 +54,24 @@ readIgnore =
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Return commit dates (ISO "YYYY-MM-DD", newest-first) for @fp@. -- | Return commit dates (ISO "YYYY-MM-DD", newest-first) for @fp@.
--
-- Logs git's stderr to the build's stderr when present so the author
-- isn't left in the dark when a file isn't tracked yet (the warning
-- otherwise vanishes silently).
gitDates :: FilePath -> IO [String] gitDates :: FilePath -> IO [String]
gitDates fp = do gitDates fp = do
(ec, out, _) <- readProcessWithExitCode (ec, out, err) <- readProcessWithExitCode
"git" ["log", "--follow", "--format=%ad", "--date=short", "--", fp] "" "git" ["log", "--follow", "--format=%ad", "--date=short", "--", fp] ""
case ec of case ec of
ExitFailure _ -> return [] ExitFailure _ -> do
ExitSuccess -> return $ filter (not . null) (lines out) let msg = if null err then "git log failed" else err
hPutStrLn stderr $ "[Stability] " ++ fp ++ ": " ++ msg
return []
ExitSuccess -> do
case err of
"" -> return ()
_ -> hPutStrLn stderr $ "[Stability] " ++ fp ++ ": " ++ err
return $ filter (not . null) (lines out)
-- | Parse an ISO "YYYY-MM-DD" string to a 'Day'. -- | Parse an ISO "YYYY-MM-DD" string to a 'Day'.
parseIso :: String -> Maybe Day parseIso :: String -> Maybe Day
@ -69,17 +85,38 @@ daySpan oldest newest =
_ -> 0 _ -> 0
-- | Derive stability label from commit dates (newest-first). -- | Derive stability label from commit dates (newest-first).
--
-- Thresholds (commit count + age in days since first commit):
--
-- * @volatile@ — solo commit OR less than two weeks old.
-- * @revising@ — under six commits AND under three months old.
-- * @fairly stable@ — under sixteen commits OR under one year old.
-- * @stable@ — under thirty-one commits OR under two years old.
-- * @established@ — anything beyond.
--
-- These cliffs are deliberately conservative: a fast burst of commits
-- early in a piece's life looks volatile until enough time has passed
-- to demonstrate it has settled.
stabilityFromDates :: [String] -> String stabilityFromDates :: [String] -> String
stabilityFromDates [] = "volatile" stabilityFromDates [] = "volatile"
stabilityFromDates dates = stabilityFromDates dates@(newest : _) =
classify (length dates) (daySpan (last dates) (head dates)) let oldest = case reverse dates of
(x : _) -> x
[] -> newest -- unreachable; matched above
in classify (length dates) (daySpan oldest newest)
where where
classify n age classify n age
| n <= 1 || age < 14 = "volatile" | n <= 1 || age < volatileAge = "volatile"
| n <= 5 && age < 90 = "revising" | n <= 5 && age < revisingAge = "revising"
| n <= 15 || age < 365 = "fairly stable" | n <= 15 || age < fairlyStableAge = "fairly stable"
| n <= 30 || age < 730 = "stable" | n <= 30 || age < stableAge = "stable"
| otherwise = "established" | otherwise = "established"
volatileAge, revisingAge, fairlyStableAge, stableAge :: Int
volatileAge = 14
revisingAge = 90
fairlyStableAge = 365
stableAge = 730
-- | Format an ISO date as "%-d %B %Y" (e.g. "16 March 2026"). -- | Format an ISO date as "%-d %B %Y" (e.g. "16 March 2026").
fmtIso :: String -> String fmtIso :: String -> String

View File

@ -8,15 +8,18 @@ module Stats (statsRules) where
import Control.Exception (IOException, catch) import Control.Exception (IOException, catch)
import Control.Monad (forM) import Control.Monad (forM)
import Data.List (find, isSuffixOf, sort, sortBy) import Data.Char (isSpace, toLower)
import Data.List (find, isPrefixOf, isSuffixOf, sort, sortBy)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe) import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe)
import Data.Ord (comparing, Down (..)) import Data.Ord (comparing, Down (..))
import qualified Data.Set as Set import qualified Data.Set as Set
import Data.String (fromString)
import Data.Time (getCurrentTime, formatTime, defaultTimeLocale, import Data.Time (getCurrentTime, formatTime, defaultTimeLocale,
Day, parseTimeM, utctDay, addDays, diffDays) Day, parseTimeM, utctDay, addDays, diffDays)
import Data.Time.Calendar (toGregorian, dayOfWeek) import Data.Time.Calendar (toGregorian, dayOfWeek)
import System.Directory (doesDirectoryExist, getFileSize, listDirectory) import System.Directory (doesDirectoryExist, getFileSize, listDirectory,
pathIsSymbolicLink)
import System.Exit (ExitCode (..)) import System.Exit (ExitCode (..))
import System.FilePath (takeExtension, (</>)) import System.FilePath (takeExtension, (</>))
import System.Process (readProcessWithExitCode) import System.Process (readProcessWithExitCode)
@ -26,10 +29,15 @@ import qualified Data.Aeson.Key as AK
import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.KeyMap as KM
import qualified Data.Vector as V import qualified Data.Vector as V
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding as TE
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.Blaze.Html.Renderer.String (renderHtml)
import qualified Text.Blaze.Internal as BI
import Hakyll import Hakyll
import Authors (authorLinksField) import Contexts (siteCtx, authorLinksField)
import Contexts (siteCtx) import qualified Patterns as P
import Utils (readingTime) import Utils (readingTime)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -100,10 +108,49 @@ pctStr _ 0 = "—"
pctStr n total = show (n * 100 `div` total) ++ "%" pctStr n total = show (n * 100 `div` total) ++ "%"
-- | Strip HTML tags for plain-text word counting. -- | Strip HTML tags for plain-text word counting.
--
-- Handles:
-- * Tag bodies, including @>@ inside double-quoted attribute values
-- (so @\<img alt=\"a > b\"\>@ doesn't slice the surrounding text).
-- * HTML comments @\<!-- ... --\>@ as a unit.
-- * @\<![CDATA[ ... ]]\>@ sections.
--
-- This is still a heuristic — it does not validate the HTML — but it
-- closes the most common ways for "tag stripping" to leak content.
stripHtmlTags :: String -> String stripHtmlTags :: String -> String
stripHtmlTags [] = [] stripHtmlTags = go
stripHtmlTags ('<':rest) = stripHtmlTags (drop 1 (dropWhile (/= '>') rest)) where
stripHtmlTags (c:rest) = c : stripHtmlTags rest go [] = []
go ('<':'!':'-':'-':rest) = go (dropComment rest)
go ('<':'!':'[':'C':'D':'A':'T':'A':'[':rest)
= go (dropCdata rest)
go ('<':rest) = go (dropTag rest)
go (c:rest) = c : go rest
-- Drop everything up to and including "-->".
dropComment ('-':'-':'>':rs) = rs
dropComment (_:rs) = dropComment rs
dropComment [] = []
-- Drop everything up to and including "]]>".
dropCdata (']':']':'>':rs) = rs
dropCdata (_:rs) = dropCdata rs
dropCdata [] = []
-- Drop a tag body, respecting double-quoted attribute values.
dropTag ('"':rs) = dropTag (skipQuoted rs)
dropTag ('\'':rs) = dropTag (skipApos rs)
dropTag ('>':rs) = rs
dropTag (_:rs) = dropTag rs
dropTag [] = []
skipQuoted ('"':rs) = rs
skipQuoted (_:rs) = skipQuoted rs
skipQuoted [] = []
skipApos ('\'':rs) = rs
skipApos (_:rs) = skipApos rs
skipApos [] = []
-- | Normalise a page URL for backlink map lookup (strip trailing .html). -- | Normalise a page URL for backlink map lookup (strip trailing .html).
normUrl :: String -> String normUrl :: String -> String
@ -114,10 +161,16 @@ normUrl u
pad2 :: (Show a, Integral a) => a -> String pad2 :: (Show a, Integral a) => a -> String
pad2 n = if n < 10 then "0" ++ show n else show n pad2 n = if n < 10 then "0" ++ show n else show n
-- | Median of a non-empty list; returns 0 for empty. -- | Median of a non-empty list; returns 0 for empty. Uses 'drop' +
-- pattern match instead of @(!!)@ so the function is total in its
-- own implementation, not just by external invariant.
median :: [Int] -> Int median :: [Int] -> Int
median [] = 0 median [] = 0
median xs = let s = sort xs in s !! (length s `div` 2) median xs =
case drop (length xs `div` 2) (sort xs) of
(m : _) -> m
[] -> 0 -- unreachable: length xs >= 1 above
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Date helpers (for /stats/ page) -- Date helpers (for /stats/ page)
@ -145,83 +198,169 @@ shortMonth m = case m of
9 -> "Sep"; 10 -> "Oct"; 11 -> "Nov"; 12 -> "Dec" 9 -> "Sep"; 10 -> "Oct"; 11 -> "Nov"; 12 -> "Dec"
_ -> "" _ -> ""
-- ---------------------------------------------------------------------------
-- URL sanitization and core HTML combinators
-- ---------------------------------------------------------------------------
-- | Defense-in-depth URL allowlist: reject anything that isn't an internal
-- path, a fragment, or an explicit safe scheme. Case-insensitive and
-- whitespace-tolerant to block @JavaScript:@, @\tjavascript:@, @data:@, etc.
-- @http://@ is intentionally excluded to avoid mixed-content warnings.
--
-- Protocol-relative URLs (@//evil.com@) are rejected because the leading
-- slash would otherwise admit them through the @\"\/\"@ prefix check.
isSafeUrl :: String -> Bool
isSafeUrl u =
let norm = map toLower (dropWhile isSpace u)
in not ("//" `isPrefixOf` norm)
&& any (`isPrefixOf` norm) ["/", "https://", "mailto:", "#"]
safeHref :: String -> H.AttributeValue
safeHref u
| isSafeUrl u = H.stringValue u
| otherwise = H.stringValue "#"
-- | Shorthand for 'H.toHtml' over a 'String'.
txt :: String -> H.Html
txt = H.toHtml
-- | Anchor element with escaped title text and URL sanitized via 'safeHref'.
-- Use for trusted plain-text labels such as tag slugs.
link :: String -> String -> H.Html
link url title = H.a H.! A.href (safeHref url) $ H.toHtml title
-- | Anchor for a content page, where the title comes from frontmatter and
-- may contain author-authored inline HTML (e.g. @<em>Book Title</em>@).
-- The URL is still sanitized via 'safeHref'; the title is emitted
-- pre-escaped, matching site convention that metadata titles are
-- author-controlled trusted HTML.
pageLink :: String -> String -> H.Html
pageLink url title = H.a H.! A.href (safeHref url) $ H.preEscapedToHtml title
-- | Typed section header followed by its body content.
section :: String -> String -> H.Html -> H.Html
section id_ title body = do
H.h2 H.! A.id (H.stringValue id_) $ H.toHtml title
body
-- | Build-telemetry table with header row, body rows, and an optional total
-- row. Cell contents are pre-rendered 'H.Html' so callers may embed links or
-- emphasis inside cells without risking double-escaping.
table :: [String] -> [[H.Html]] -> Maybe [H.Html] -> H.Html
table headers rows mFoot =
H.table H.! A.class_ "build-table" $ do
H.thead $ H.tr $ mapM_ (H.th . H.toHtml) headers
H.tbody $ mapM_ renderRow rows
maybe (return ()) renderFoot mFoot
where
renderRow cells = H.tr $ mapM_ H.td cells
renderFoot cells = H.tfoot $
H.tr H.! A.class_ "build-total" $ mapM_ H.td cells
-- | Two-column metadata block: each pair becomes a @<dt>/<dd>@ entry. Values
-- are 'H.Html' to allow mixing links and plain text.
dl :: [(String, H.Html)] -> H.Html
dl pairs = H.dl H.! A.class_ "build-meta" $
mapM_ (\(k, v) -> do H.dt (H.toHtml k); H.dd v) pairs
-- ---------------------------------------------------------------------------
-- SVG / custom element helpers (no blaze-svg dependency)
-- ---------------------------------------------------------------------------
svgTag, rectTag, textTag, titleTag :: H.Html -> H.Html
svgTag = BI.customParent "svg"
rectTag = BI.customParent "rect"
textTag = BI.customParent "text"
titleTag = BI.customParent "title"
-- | Attach an attribute that isn't in 'Text.Blaze.Html5.Attributes' (e.g.
-- SVG @viewBox@, @x@, @y@, or @data-target@).
customAttr :: String -> String -> H.Attribute
customAttr name val = BI.customAttribute (fromString name) (fromString val)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Heatmap SVG -- Heatmap SVG
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | 52-week writing activity heatmap (inline SVG, CSS-variable colors). -- | 52-week writing activity heatmap. Styled via @.heatmap-svg@ rules in
renderHeatmap :: Map.Map Day Int -> Day -> String -- static/css/build.css (no inline @<style>@).
renderHeatmap :: Map.Map Day Int -> Day -> H.Html
renderHeatmap wordsByDay today = renderHeatmap wordsByDay today =
let cellSz = 10 :: Int let cellSz = 10 :: Int
gap = 2 :: Int gap = 2 :: Int
step = cellSz + gap step = cellSz + gap
hdrH = 22 :: Int -- vertical space for month labels hdrH = 22 :: Int -- vertical space for month labels
nWeeks = 52 nWeeks = 52
-- First Monday of the 52-week window -- First Monday of the 52-week window
startDay = addDays (fromIntegral (-(nWeeks - 1)) * 7) (weekStart today) startDay = addDays (fromIntegral (-(nWeeks - 1)) * 7) (weekStart today)
nDays = diffDays today startDay + 1 nDays = diffDays today startDay + 1
allDays = [addDays i startDay | i <- [0 .. nDays - 1]] allDays = [addDays i startDay | i <- [0 .. nDays - 1]]
weekOf d = fromIntegral (diffDays d startDay `div` 7) :: Int weekOf d = fromIntegral (diffDays d startDay `div` 7) :: Int
dowOf d = fromEnum (dayOfWeek d) -- Mon=0..Sun=6 dowOf d = fromEnum (dayOfWeek d) -- Mon=0..Sun=6
svgW = (nWeeks - 1) * step + cellSz svgW = (nWeeks - 1) * step + cellSz
svgH = 6 * step + cellSz + hdrH svgH = 6 * step + cellSz + hdrH
-- Month labels: one per first-of-month day monthLabel d =
monthLbls = concatMap (\d ->
let (_, mo, da) = toGregorian d let (_, mo, da) = toGregorian d
in if da == 1 in if da == 1
then "<text class=\"hm-lbl\" x=\"" ++ show (weekOf d * step) then textTag H.! A.class_ "hm-lbl"
++ "\" y=\"14\">" ++ shortMonth mo ++ "</text>" H.! customAttr "x" (show (weekOf d * step))
else "") allDays H.! customAttr "y" "14"
$ txt (shortMonth mo)
else mempty
-- One rect per day dayCell d =
cells = concatMap (\d -> let wc = fromMaybe 0 (Map.lookup d wordsByDay)
let wc = fromMaybe 0 (Map.lookup d wordsByDay)
(yr, mo, da) = toGregorian d (yr, mo, da) = toGregorian d
x = weekOf d * step x = weekOf d * step
y = dowOf d * step + hdrH y = dowOf d * step + hdrH
tip = show yr ++ "-" ++ pad2 mo ++ "-" ++ pad2 da tip = show yr ++ "-" ++ pad2 mo ++ "-" ++ pad2 da
++ if wc > 0 then ": " ++ commaInt wc ++ " words" else "" ++ if wc > 0 then ": " ++ commaInt wc ++ " words" else ""
in "<rect class=\"" ++ heatClass wc ++ "\"" in rectTag H.! A.class_ (H.stringValue (heatClass wc))
++ " x=\"" ++ show x ++ "\" y=\"" ++ show y ++ "\"" H.! customAttr "x" (show x)
++ " width=\"" ++ show cellSz ++ "\" height=\"" ++ show cellSz ++ "\"" H.! customAttr "y" (show y)
++ " rx=\"2\"><title>" ++ tip ++ "</title></rect>") allDays H.! customAttr "width" (show cellSz)
H.! customAttr "height" (show cellSz)
H.! customAttr "rx" "2"
$ titleTag (txt tip)
-- Inline legend (five sample rects)
legendW = 5 * step - gap legendW = 5 * step - gap
legendSvg = legendCell i =
"<svg width=\"" ++ show legendW ++ "\" height=\"" ++ show cellSz ++ "\"" rectTag H.! A.class_ (H.stringValue ("hm" ++ show i))
++ " viewBox=\"0 0 " ++ show legendW ++ " " ++ show cellSz ++ "\"" H.! customAttr "x" (show (i * step))
++ " style=\"display:inline;vertical-align:middle\">" H.! customAttr "y" "0"
++ concatMap (\i -> H.! customAttr "width" (show cellSz)
"<rect class=\"hm" ++ show i ++ "\"" H.! customAttr "height" (show cellSz)
++ " x=\"" ++ show (i * step) ++ "\" y=\"0\"" H.! customAttr "rx" "2"
++ " width=\"" ++ show cellSz ++ "\" height=\"" ++ show cellSz ++ "\"" $ mempty
++ " rx=\"2\"/>") [0..4]
++ "</svg>"
in "<figure class=\"stats-heatmap\">" legendSvg =
++ "<svg width=\"" ++ show svgW ++ "\" height=\"" ++ show svgH ++ "\"" svgTag H.! customAttr "width" (show legendW)
++ " viewBox=\"0 0 " ++ show svgW ++ " " ++ show svgH ++ "\"" H.! customAttr "height" (show cellSz)
++ " class=\"heatmap-svg\" role=\"img\"" H.! customAttr "viewBox" ("0 0 " ++ show legendW ++ " " ++ show cellSz)
++ " aria-label=\"52-week writing activity heatmap\">" H.! customAttr "style" "display:inline;vertical-align:middle"
++ "<style>" $ mapM_ legendCell [0 .. 4 :: Int]
++ ".hm0{fill:var(--hm-0)}.hm1{fill:var(--hm-1)}.hm2{fill:var(--hm-2)}"
++ ".hm3{fill:var(--hm-3)}.hm4{fill:var(--hm-4)}" in H.figure H.! A.class_ "stats-heatmap" $ do
++ ".hm-lbl{font-size:9px;fill:var(--text-faint);font-family:sans-serif}" svgTag H.! customAttr "width" (show svgW)
++ "</style>" H.! customAttr "height" (show svgH)
++ monthLbls ++ cells H.! customAttr "viewBox" ("0 0 " ++ show svgW ++ " " ++ show svgH)
++ "</svg>" H.! A.class_ "heatmap-svg"
++ "<figcaption class=\"heatmap-legend\">" H.! customAttr "role" "img"
++ "Less\xA0" ++ legendSvg ++ "\xA0More" H.! customAttr "aria-label" "52-week writing activity heatmap"
++ "</figcaption>" $ do
++ "</figure>" mapM_ monthLabel allDays
mapM_ dayCell allDays
H.figcaption H.! A.class_ "heatmap-legend" $ do
"Less\xA0"
legendSvg
"\xA0More"
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Stats page sections -- Stats page sections
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
renderMonthlyVolume :: Map.Map Day Int -> String renderMonthlyVolume :: Map.Map Day Int -> H.Html
renderMonthlyVolume wordsByDay = renderMonthlyVolume wordsByDay =
section "volume" "Monthly volume" $ section "volume" "Monthly volume" $
let byMonth = Map.fromListWith (+) let byMonth = Map.fromListWith (+)
@ -230,71 +369,80 @@ renderMonthlyVolume wordsByDay =
, let (y, m, _) = toGregorian day , let (y, m, _) = toGregorian day
] ]
in if Map.null byMonth in if Map.null byMonth
then "<p><em>No dated content yet.</em></p>" then H.p (H.em "No dated content yet.")
else else
let maxWC = max 1 $ maximum $ Map.elems byMonth let maxWC = max 1 $ maximum $ Map.elems byMonth
bar (y, m) = bar (y, m) =
let wc = fromMaybe 0 (Map.lookup (y, m) byMonth) let wc = fromMaybe 0 (Map.lookup (y, m) byMonth)
pct = if wc == 0 then 0 else max 2 (wc * 100 `div` maxWC) pct = if wc == 0 then 0 else max 2 (wc * 100 `div` maxWC)
lbl = shortMonth m ++ " \x2019" ++ drop 2 (show y) lbl = shortMonth m ++ " \x2019" ++ drop 2 (show y)
in "<div class=\"build-bar-row\">" in H.div H.! A.class_ "build-bar-row" $ do
++ "<span class=\"build-bar-label\">" ++ lbl ++ "</span>" H.span H.! A.class_ "build-bar-label" $ txt lbl
++ "<span class=\"build-bar-wrap\"><span class=\"build-bar\" style=\"width:" H.span H.! A.class_ "build-bar-wrap" $
++ show pct ++ "%\"></span></span>" H.span H.! A.class_ "build-bar"
++ "<span class=\"build-bar-count\">" H.! A.style (H.stringValue ("width:" ++ show pct ++ "%"))
++ (if wc > 0 then commaInt wc else "") ++ "</span>" $ mempty
++ "</div>" H.span H.! A.class_ "build-bar-count" $
in "<div class=\"build-bars\">" ++ concatMap bar (Map.keys byMonth) ++ "</div>" if wc > 0 then txt (commaInt wc) else mempty
in H.div H.! A.class_ "build-bars" $
mapM_ bar (Map.keys byMonth)
renderCorpus :: [TypeRow] -> [PageInfo] -> String renderCorpus :: [TypeRow] -> [PageInfo] -> H.Html
renderCorpus typeRows allPIs = renderCorpus typeRows allPIs =
section "corpus" "Corpus" $ concat section "corpus" "Corpus" $ do
[ dl [ ("Total words", commaInt totalWords) dl [ ("Total words", txt (commaInt totalWords))
, ("Total pages", commaInt (length allPIs)) , ("Total pages", txt (commaInt (length allPIs)))
, ("Total reading time", rtStr totalWords) , ("Total reading time", txt (rtStr totalWords))
, ("Average length", commaInt avgWC ++ " words") , ("Average length", txt (commaInt avgWC ++ " words"))
, ("Median length", commaInt medWC ++ " words") , ("Median length", txt (commaInt medWC ++ " words"))
] ]
, table ["Type", "Pages", "Words", "Reading time"] table ["Type", "Pages", "Words", "Reading time"]
(map row typeRows) (map row typeRows)
(Just ["Total", commaInt (sum (map trCount typeRows)) (Just [ "Total"
, commaInt totalWords, rtStr totalWords]) , txt (commaInt (sum (map trCount typeRows)))
] , txt (commaInt totalWords)
, txt (rtStr totalWords)
])
where where
hasSomeWC = filter (\p -> piWC p > 0) allPIs hasSomeWC = filter (\p -> piWC p > 0) allPIs
totalWords = sum (map trWords typeRows) totalWords = sum (map trWords typeRows)
avgWC = if null hasSomeWC then 0 else totalWords `div` length hasSomeWC avgWC = if null hasSomeWC then 0 else totalWords `div` length hasSomeWC
medWC = median (map piWC hasSomeWC) medWC = median (map piWC hasSomeWC)
row r = [trLabel r, commaInt (trCount r), commaInt (trWords r), rtStr (trWords r)] row r = [ txt (trLabel r)
, txt (commaInt (trCount r))
, txt (commaInt (trWords r))
, txt (rtStr (trWords r))
]
renderNotable :: [PageInfo] -> String renderNotable :: [PageInfo] -> H.Html
renderNotable allPIs = renderNotable allPIs =
section "notable" "Notable" $ concat section "notable" "Notable" $ do
[ "<p><strong>Longest</strong></p>" H.p (H.strong "Longest")
, pageList (take 5 (sortBy (comparing (Down . piWC)) hasSomeWC)) pageList (take 5 (sortBy (comparing (Down . piWC)) hasSomeWC))
, "<p><strong>Shortest</strong></p>" H.p (H.strong "Shortest")
, pageList (take 5 (sortBy (comparing piWC) hasSomeWC)) pageList (take 5 (sortBy (comparing piWC) hasSomeWC))
]
where where
hasSomeWC = filter (\p -> piWC p > 50) allPIs hasSomeWC = filter (\p -> piWC p > 50) allPIs
pageList ps = "<ol class=\"build-page-list\">" pageList ps = H.ol H.! A.class_ "build-page-list" $
++ concatMap (\p -> "<li>" ++ link (piUrl p) (piTitle p) mapM_ (\p -> H.li $ do
++ " \x2014 " ++ commaInt (piWC p) ++ " words</li>") ps pageLink (piUrl p) (piTitle p)
++ "</ol>" txt (" \x2014 " ++ commaInt (piWC p) ++ " words")
) ps
renderStatsTags :: [(String, Int)] -> Int -> String -- | Renamed/aliased to 'renderTagsSection' below — kept as a name for
renderStatsTags topTags uniqueCount = -- legacy call sites until they are migrated. Defining it as the same
section "tags" "Tags" $ concat -- function (instead of an independent copy) prevents the two from
[ dl [("Unique tags", commaInt uniqueCount)] -- drifting silently.
, table ["Tag", "Items"] (map row topTags) Nothing renderStatsTags :: [(String, Int)] -> Int -> H.Html
] renderStatsTags = renderTagsSection
where row (t, n) = [link ("/" ++ t ++ "/") t, show n]
statsTOC :: String statsTOC :: H.Html
statsTOC = "<ol>\n" ++ concatMap item entries ++ "</ol>\n" statsTOC = H.ol $ mapM_ item entries
where where
item (i, t) = "<li><a href=\"#" ++ i ++ "\" data-target=\"" ++ i ++ "\">" item (i, t) =
++ t ++ "</a></li>\n" H.li $ H.a H.! A.href (H.stringValue ("#" ++ i))
H.! customAttr "data-target" i
$ txt t
entries = [ ("activity", "Writing activity") entries = [ ("activity", "Writing activity")
, ("volume", "Monthly volume") , ("volume", "Monthly volume")
, ("corpus", "Corpus") , ("corpus", "Corpus")
@ -306,17 +454,28 @@ statsTOC = "<ol>\n" ++ concatMap item entries ++ "</ol>\n"
-- IO: output directory walk -- IO: output directory walk
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- | Recursively walk a directory, returning @(file, size)@ tuples for every
-- regular file beneath it.
--
-- Symlinks (both files and directories) are skipped, so a stray
-- @_site\/a -> _site@ doesn't trigger an infinite loop.
walkDir :: FilePath -> IO [(FilePath, Integer)] walkDir :: FilePath -> IO [(FilePath, Integer)]
walkDir dir = do walkDir dir = do
entries <- listDirectory dir `catch` (\(_ :: IOException) -> return []) entries <- listDirectory dir `catch` (\(_ :: IOException) -> return [])
fmap concat $ forM entries $ \e -> do fmap concat $ forM entries $ \e -> do
let path = dir </> e let path = dir </> e
isDir <- doesDirectoryExist path isLink <- pathIsSymbolicLink path
if isDir `catch` (\(_ :: IOException) -> return False)
then walkDir path if isLink
then return []
else do else do
sz <- getFileSize path `catch` (\(_ :: IOException) -> return 0) isDir <- doesDirectoryExist path
return [(path, sz)] if isDir
then walkDir path
else do
sz <- getFileSize path
`catch` (\(_ :: IOException) -> return 0)
return [(path, sz)]
displayExt :: FilePath -> String displayExt :: FilePath -> String
displayExt path = case takeExtension path of displayExt path = case takeExtension path of
@ -354,9 +513,13 @@ countLinesDir :: FilePath -> String -> (FilePath -> Bool) -> IO (Int, Int)
countLinesDir dir ext skipPred = do countLinesDir dir ext skipPred = do
entries <- listDirectory dir `catch` (\(_ :: IOException) -> return []) entries <- listDirectory dir `catch` (\(_ :: IOException) -> return [])
let files = filter (\e -> takeExtension e == ext && not (skipPred e)) entries let files = filter (\e -> takeExtension e == ext && not (skipPred e)) entries
-- Use strict text IO so the file handle is released as soon as the
-- contents are read; the prior 'readFile' chained lazy IO under
-- 'forM', leaving every handle open until the loop forced 'lines'.
ls <- fmap sum $ forM files $ \e -> do ls <- fmap sum $ forM files $ \e -> do
content <- readFile (dir </> e) `catch` (\(_ :: IOException) -> return "") content <- TIO.readFile (dir </> e)
return (length (lines content)) `catch` (\(_ :: IOException) -> return T.empty)
return (length (T.lines content))
return (length files, ls) return (length files, ls)
getLocStats :: IO (Int, Int, Int, Int, Int, Int) getLocStats :: IO (Int, Int, Int, Int, Int, Int)
@ -385,125 +548,116 @@ getGitStats = do
return (commits, firstDate) return (commits, firstDate)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- HTML rendering: section helpers -- HTML rendering: build page sections
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
section :: String -> String -> String -> String renderContent :: [TypeRow] -> H.Html
section id_ title body = concat
[ "<h2 id=\"", id_, "\">", title, "</h2>\n"
, body
]
table :: [String] -> [[String]] -> Maybe [String] -> String
table headers rows mFoot = concat
[ "<table class=\"build-table\">"
, "<thead><tr>", concatMap (\h -> "<th>" ++ h ++ "</th>") headers, "</tr></thead>"
, "<tbody>", concatMap renderRow rows, "</tbody>"
, maybe "" renderFoot mFoot
, "</table>"
]
where
renderRow cells = "<tr>" ++ concatMap (\c -> "<td>" ++ c ++ "</td>") cells ++ "</tr>"
renderFoot cells = "<tfoot><tr class=\"build-total\">"
++ concatMap (\c -> "<td>" ++ c ++ "</td>") cells
++ "</tr></tfoot>"
dl :: [(String, String)] -> String
dl pairs = "<dl class=\"build-meta\">"
++ concatMap (\(k, v) -> "<dt>" ++ k ++ "</dt><dd>" ++ v ++ "</dd>") pairs
++ "</dl>"
link :: String -> String -> String
link url title = "<a href=\"" ++ url ++ "\">" ++ title ++ "</a>"
-- ---------------------------------------------------------------------------
-- HTML rendering: sections
-- ---------------------------------------------------------------------------
renderContent :: [TypeRow] -> String
renderContent rows = renderContent rows =
section "content" "Content" $ section "content" "Content" $
table table ["Type", "Count", "Words", "Reading time"]
["Type", "Count", "Words", "Reading time"] (map row rows)
(map row rows) (Just [ "Total"
(Just ["Total", commaInt totalCount, commaInt totalWords, rtStr totalWords]) , txt (commaInt totalCount)
, txt (commaInt totalWords)
, txt (rtStr totalWords)
])
where where
totalCount = sum (map trCount rows) totalCount = sum (map trCount rows)
totalWords = sum (map trWords rows) totalWords = sum (map trWords rows)
row r = [ trLabel r row r = [ txt (trLabel r)
, commaInt (trCount r) , txt (commaInt (trCount r))
, commaInt (trWords r) , txt (commaInt (trWords r))
, rtStr (trWords r) , txt (rtStr (trWords r))
] ]
renderPages :: [PageInfo] -> Maybe (String,String,String) -> Maybe (String,String,String) -> String renderPages :: [PageInfo]
-> Maybe (String, String, String)
-> Maybe (String, String, String)
-> H.Html
renderPages allPIs mOldest mNewest = renderPages allPIs mOldest mNewest =
section "pages" "Pages" $ concat section "pages" "Pages" $ do
[ dl $ dl $
[ ("Total pages", commaInt (length allPIs)) [ ("Total pages", txt (commaInt (length allPIs)))
, ("Average length", commaInt avgWC ++ " words") , ("Average length", txt (commaInt avgWC ++ " words"))
] ++ ] ++
maybe [] (\(d,t,u) -> [("Oldest content", d ++ " \x2014 " ++ link u t)]) mOldest ++ maybe [] (\(d,t,u) -> [("Oldest content", datedLink d t u)]) mOldest ++
maybe [] (\(d,t,u) -> [("Newest content", d ++ " \x2014 " ++ link u t)]) mNewest maybe [] (\(d,t,u) -> [("Newest content", datedLink d t u)]) mNewest
, "<p><strong>Longest</strong></p>" H.p (H.strong "Longest")
, pageList (take 3 (sortBy (comparing (Down . piWC)) hasSomeWC)) pageList (take 3 (sortBy (comparing (Down . piWC)) hasSomeWC))
, "<p><strong>Shortest</strong></p>" H.p (H.strong "Shortest")
, pageList (take 3 (sortBy (comparing piWC) hasSomeWC)) pageList (take 3 (sortBy (comparing piWC) hasSomeWC))
]
where where
hasSomeWC = filter (\p -> piWC p > 0) allPIs hasSomeWC = filter (\p -> piWC p > 0) allPIs
avgWC = if null hasSomeWC then 0 avgWC = if null hasSomeWC then 0
else sum (map piWC hasSomeWC) `div` length hasSomeWC else sum (map piWC hasSomeWC) `div` length hasSomeWC
pageList ps = "<ol class=\"build-page-list\">" datedLink d t u = do
++ concatMap (\p -> "<li>" ++ link (piUrl p) (piTitle p) txt (d ++ " \x2014 ")
++ " \x2014 " ++ commaInt (piWC p) ++ " words</li>") ps pageLink u t
++ "</ol>" pageList ps = H.ol H.! A.class_ "build-page-list" $
mapM_ (\p -> H.li $ do
pageLink (piUrl p) (piTitle p)
txt (" \x2014 " ++ commaInt (piWC p) ++ " words")
) ps
renderDistribution :: [Int] -> String renderDistribution :: [Int] -> H.Html
renderDistribution wcs = renderDistribution wcs =
section "distribution" "Word-length distribution" $ section "distribution" "Word-length distribution" $
"<div class=\"build-bars\">" ++ concatMap bar buckets ++ "</div>" H.div H.! A.class_ "build-bars" $ mapM_ bar buckets
where where
bucketOf w bucketOf w
| w < 500 = 0 | w < 1000 = 1 | w < 2000 = 2 | w < 5000 = 3 | otherwise = 4 | w < 500 = 0
labels = ["&lt; 500", "500 \x2013 1k", "1k \x2013 2k", "2k \x2013 5k", "\x2265 5k"] | w < 1000 = 1
| w < 2000 = 2
| w < 5000 = 3
| otherwise = 4
labels :: [H.Html]
labels = [ "< 500"
, "500 \x2013 1k"
, "1k \x2013 2k"
, "2k \x2013 5k"
, "\x2265 5k"
]
counts = foldr (\w acc -> Map.insertWith (+) (bucketOf w) (1 :: Int) acc) counts = foldr (\w acc -> Map.insertWith (+) (bucketOf w) (1 :: Int) acc)
(Map.fromList [(i, 0 :: Int) | i <- [0..4]]) wcs (Map.fromList [(i, 0 :: Int) | i <- [0 .. 4]]) wcs
buckets = [(labels !! i, fromMaybe 0 (Map.lookup i counts)) | i <- [0..4]] buckets = [(labels !! i, fromMaybe 0 (Map.lookup i counts)) | i <- [0 .. 4]]
maxCount = max 1 (maximum (map snd buckets)) maxCount = max 1 (maximum (map snd buckets))
bar (lbl, n) = bar (lbl, n) =
let pct = n * 100 `div` maxCount let pct = n * 100 `div` maxCount
in concat in H.div H.! A.class_ "build-bar-row" $ do
[ "<div class=\"build-bar-row\">" H.span H.! A.class_ "build-bar-label" $ lbl
, "<span class=\"build-bar-label\">", lbl, "</span>" H.span H.! A.class_ "build-bar-wrap" $
, "<span class=\"build-bar-wrap\"><span class=\"build-bar\" style=\"width:" H.span H.! A.class_ "build-bar"
, show pct, "%\"></span></span>" H.! A.style (H.stringValue ("width:" ++ show pct ++ "%"))
, "<span class=\"build-bar-count\">", show n, "</span>" $ mempty
, "</div>" H.span H.! A.class_ "build-bar-count" $ txt (show n)
]
renderTagsSection :: [(String, Int)] -> Int -> String renderTagsSection :: [(String, Int)] -> Int -> H.Html
renderTagsSection topTags uniqueCount = renderTagsSection topTags uniqueCount =
section "tags" "Tags" $ concat section "tags" "Tags" $ do
[ dl [("Unique tags", commaInt uniqueCount)] dl [("Unique tags", txt (commaInt uniqueCount))]
, table ["Tag", "Items"] (map row topTags) Nothing table ["Tag", "Items"] (map row topTags) Nothing
]
where where
row (t, n) = [link ("/" ++ t ++ "/") t, show n] row (t, n) = [link ("/" ++ t ++ "/") t, txt (show n)]
renderLinks :: Maybe (String, Int, String) -> Int -> Int -> String renderLinks :: Maybe (String, Int, String) -> Int -> Int -> H.Html
renderLinks mMostLinked orphanCount total = renderLinks mMostLinked orphanCount total =
section "links" "Links" $ section "links" "Links" $
dl $ dl
(case mMostLinked of [ case mMostLinked of
Nothing -> [("Most-linked page", "\x2014")] Nothing -> ("Most-linked page", "\x2014")
Just (u, n, t) -> [("Most-linked page", Just (u, n, t) ->
link u t ++ " (" ++ show n ++ " inbound links)")]) ++ ( "Most-linked page"
[ ("Orphan pages", commaInt orphanCount , do pageLink u t
++ " of " ++ commaInt total txt (" (" ++ show n ++ " inbound links)")
++ " (" ++ pctStr orphanCount total ++ ")") ] )
, ( "Orphan pages"
, txt (commaInt orphanCount
++ " of " ++ commaInt total
++ " (" ++ pctStr orphanCount total ++ ")")
)
]
renderEpistemic :: Int -> Int -> Int -> Int -> Int -> String renderEpistemic :: Int -> Int -> Int -> Int -> Int -> H.Html
renderEpistemic total ws wc wi we = renderEpistemic total ws wc wi we =
section "epistemic" "Epistemic coverage" $ section "epistemic" "Epistemic coverage" $
table table
@ -515,47 +669,54 @@ renderEpistemic total ws wc wi we =
] ]
Nothing Nothing
where where
row label n = [label, show n ++ " / " ++ show total, pctStr n total] row label n = [ txt label
, txt (show n ++ " / " ++ show total)
, txt (pctStr n total)
]
renderOutput :: Map.Map String (Int, Integer) -> Int -> Integer -> String renderOutput :: Map.Map String (Int, Integer) -> Int -> Integer -> H.Html
renderOutput grouped totalFiles totalSize = renderOutput grouped totalFiles totalSize =
section "output" "Output" $ section "output" "Output" $
table table
["Type", "Files", "Size"] ["Type", "Files", "Size"]
(map row (sortBy (comparing (Down . snd . snd)) (Map.toList grouped))) (map row (sortBy (comparing (Down . snd . snd)) (Map.toList grouped)))
(Just ["Total", commaInt totalFiles, formatBytes totalSize]) (Just [ "Total"
, txt (commaInt totalFiles)
, txt (formatBytes totalSize)
])
where where
row (ext, (n, sz)) = [ext, commaInt n, formatBytes sz] row (ext, (n, sz)) = [txt ext, txt (commaInt n), txt (formatBytes sz)]
renderRepository :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> String -> String renderRepository :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> String -> H.Html
renderRepository hf hl cf cl jf jl commits firstDate = renderRepository hf hl cf cl jf jl commits firstDate =
section "repository" "Repository" $ section "repository" "Repository" $
dl dl
[ ("Haskell", commaInt hl ++ " lines across " ++ show hf ++ " files") [ ("Haskell", txt (commaInt hl ++ " lines across " ++ show hf ++ " files"))
, ("CSS", commaInt cl ++ " lines across " ++ show cf ++ " files") , ("CSS", txt (commaInt cl ++ " lines across " ++ show cf ++ " files"))
, ("JavaScript", commaInt jl ++ " lines across " ++ show jf ++ " files (excl. minified)") , ("JavaScript", txt (commaInt jl ++ " lines across " ++ show jf ++ " files (excl. minified)"))
, ("Total git commits", commaInt commits) , ("Total git commits", txt (commaInt commits))
, ("Repository started", firstDate) , ("Repository started", txt firstDate)
] ]
renderBuild :: String -> String -> String renderBuild :: String -> String -> H.Html
renderBuild ts dur = renderBuild ts dur =
section "build" "Build" $ section "build" "Build" $
dl dl
[ ("Generated", ts) [ ("Generated", txt ts)
, ("Last build duration", dur) , ("Last build duration", txt dur)
] ]
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Static TOC (matches the nine h2 sections above) -- Static TOC (matches the nine h2 sections above)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
pageTOC :: String pageTOC :: H.Html
pageTOC = "<ol>\n" ++ concatMap item sections ++ "</ol>\n" pageTOC = H.ol $ mapM_ item sections
where where
item (id_, title) = item (id_, title) =
"<li><a href=\"#" ++ id_ ++ "\" data-target=\"" ++ id_ ++ "\">" H.li $ H.a H.! A.href (H.stringValue ("#" ++ id_))
++ title ++ "</a></li>\n" H.! customAttr "data-target" id_
$ txt title
sections = sections =
[ ("content", "Content") [ ("content", "Content")
, ("pages", "Pages") , ("pages", "Pages")
@ -584,7 +745,7 @@ statsRules tags = do
-- ---------------------------------------------------------------- -- ----------------------------------------------------------------
-- Load all content items -- Load all content items
-- ---------------------------------------------------------------- -- ----------------------------------------------------------------
essays <- loadAll ("content/essays/*.md" .&&. hasNoVersion) essays <- loadAll (P.essayPattern .&&. hasNoVersion)
posts <- loadAll ("content/blog/*.md" .&&. hasNoVersion) posts <- loadAll ("content/blog/*.md" .&&. hasNoVersion)
poems <- loadAll ("content/poetry/*.md" .&&. hasNoVersion) poems <- loadAll ("content/poetry/*.md" .&&. hasNoVersion)
fiction <- loadAll ("content/fiction/*.md" .&&. hasNoVersion) fiction <- loadAll ("content/fiction/*.md" .&&. hasNoVersion)
@ -664,9 +825,9 @@ statsRules tags = do
-- ---------------------------------------------------------------- -- ----------------------------------------------------------------
essayMetas <- mapM (getMetadata . itemIdentifier) essays essayMetas <- mapM (getMetadata . itemIdentifier) essays
postMetas <- mapM (getMetadata . itemIdentifier) posts postMetas <- mapM (getMetadata . itemIdentifier) posts
let epMetas = essayMetas ++ postMetas let epMetas = essayMetas ++ postMetas
epTotal = length epMetas epTotal = length epMetas
ep f = length (filter (isJust . f) epMetas) ep f = length (filter (isJust . f) epMetas)
withStatus = ep (lookupString "status") withStatus = ep (lookupString "status")
withConf = ep (lookupString "confidence") withConf = ep (lookupString "confidence")
withImp = ep (lookupString "importance") withImp = ep (lookupString "importance")
@ -698,32 +859,33 @@ statsRules tags = do
-- ---------------------------------------------------------------- -- ----------------------------------------------------------------
-- Assemble page -- Assemble page
-- ---------------------------------------------------------------- -- ----------------------------------------------------------------
let content = concat let htmlContent :: H.Html
[ renderContent rows htmlContent = do
, renderPages allPIs oldestDate newestDate renderContent rows
, renderDistribution allWCs renderPages allPIs oldestDate newestDate
, renderTagsSection topTags uniqueTags renderDistribution allWCs
, renderLinks mostLinkedInfo orphanCount (length allPIs) renderTagsSection topTags uniqueTags
, renderEpistemic epTotal withStatus withConf withImp withEv renderLinks mostLinkedInfo orphanCount (length allPIs)
, renderOutput outputGrouped totalFiles totalSize renderEpistemic epTotal withStatus withConf withImp withEv
, renderRepository hf hl cf cl jf jl commits firstDate renderOutput outputGrouped totalFiles totalSize
, renderBuild buildTimestamp lastBuildDur renderRepository hf hl cf cl jf jl commits firstDate
] renderBuild buildTimestamp lastBuildDur
plainText = stripHtmlTags content contentString = renderHtml htmlContent
wc = length (words plainText) plainText = stripHtmlTags contentString
rt = readingTime plainText wc = length (words plainText)
ctx = constField "toc" pageTOC rt = readingTime plainText
<> constField "word-count" (show wc) ctx = constField "toc" (renderHtml pageTOC)
<> constField "reading-time" (show rt) <> constField "word-count" (show wc)
<> constField "title" "Build Telemetry" <> constField "reading-time" (show rt)
<> constField "abstract" "Per-build corpus statistics, tag distribution, \ <> constField "title" "Build Telemetry"
\link analysis, epistemic coverage, output metrics, \ <> constField "abstract" "Per-build corpus statistics, tag distribution, \
\repository overview, and build timing." \link analysis, epistemic coverage, output metrics, \
<> constField "build" "true" \repository overview, and build timing."
<> authorLinksField <> constField "build" "true"
<> siteCtx <> authorLinksField
<> siteCtx
makeItem content makeItem contentString
>>= loadAndApplyTemplate "templates/essay.html" ctx >>= loadAndApplyTemplate "templates/essay.html" ctx
>>= loadAndApplyTemplate "templates/default.html" ctx >>= loadAndApplyTemplate "templates/default.html" ctx
>>= relativizeUrls >>= relativizeUrls
@ -734,7 +896,7 @@ statsRules tags = do
create ["stats/index.html"] $ do create ["stats/index.html"] $ do
route idRoute route idRoute
compile $ do compile $ do
essays <- loadAll ("content/essays/*.md" .&&. hasNoVersion) essays <- loadAll (P.essayPattern .&&. hasNoVersion)
posts <- loadAll ("content/blog/*.md" .&&. hasNoVersion) posts <- loadAll ("content/blog/*.md" .&&. hasNoVersion)
poems <- loadAll ("content/poetry/*.md" .&&. hasNoVersion) poems <- loadAll ("content/poetry/*.md" .&&. hasNoVersion)
fiction <- loadAll ("content/fiction/*.md" .&&. hasNoVersion) fiction <- loadAll ("content/fiction/*.md" .&&. hasNoVersion)
@ -773,27 +935,28 @@ statsRules tags = do
today <- unsafeCompiler (utctDay <$> getCurrentTime) today <- unsafeCompiler (utctDay <$> getCurrentTime)
let content = concat let htmlContent :: H.Html
[ section "activity" "Writing activity" (renderHeatmap wordsByDay today) htmlContent = do
, renderMonthlyVolume wordsByDay section "activity" "Writing activity" (renderHeatmap wordsByDay today)
, renderCorpus typeRows allPIs renderMonthlyVolume wordsByDay
, renderNotable allPIs renderCorpus typeRows allPIs
, renderStatsTags topTags uniqueTags renderNotable allPIs
] renderStatsTags topTags uniqueTags
plainText = stripHtmlTags content contentString = renderHtml htmlContent
wc = length (words plainText) plainText = stripHtmlTags contentString
rt = readingTime plainText wc = length (words plainText)
ctx = constField "toc" statsTOC rt = readingTime plainText
<> constField "word-count" (show wc) ctx = constField "toc" (renderHtml statsTOC)
<> constField "reading-time" (show rt) <> constField "word-count" (show wc)
<> constField "title" "Writing Statistics" <> constField "reading-time" (show rt)
<> constField "abstract" "Writing activity, corpus breakdown, \ <> constField "title" "Writing Statistics"
\and tag distribution computed at build time." <> constField "abstract" "Writing activity, corpus breakdown, \
<> constField "build" "true" \and tag distribution computed at build time."
<> authorLinksField <> constField "build" "true"
<> siteCtx <> authorLinksField
<> siteCtx
makeItem content makeItem contentString
>>= loadAndApplyTemplate "templates/essay.html" ctx >>= loadAndApplyTemplate "templates/essay.html" ctx
>>= loadAndApplyTemplate "templates/default.html" ctx >>= loadAndApplyTemplate "templates/default.html" ctx
>>= relativizeUrls >>= relativizeUrls

View File

@ -15,12 +15,13 @@
module Tags module Tags
( buildAllTags ( buildAllTags
, applyTagRules , applyTagRules
, tagLinksField
) where ) where
import Data.List (intercalate, nub) import Data.List (intercalate, nub)
import Hakyll import Hakyll
import Pagination (pageSize, sortAndGroup) import Pagination (sortAndGroup)
import Patterns (tagIndexable)
import Contexts (abstractField, tagLinksField)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -64,9 +65,7 @@ tagIdentifier = fromFilePath . tagFilePath
-- | Scan all essays and blog posts and build the Tags index. -- | Scan all essays and blog posts and build the Tags index.
buildAllTags :: Rules Tags buildAllTags :: Rules Tags
buildAllTags = buildAllTags =
buildTagsWith getExpandedTags allContent tagIdentifier buildTagsWith getExpandedTags tagIndexable tagIdentifier
where
allContent = ("content/essays/*.md" .||. "content/essays/*/index.md" .||. "content/blog/*.md") .&&. hasNoVersion
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -77,6 +76,7 @@ tagItemCtx :: Context String
tagItemCtx = tagItemCtx =
dateField "date" "%-d %B %Y" dateField "date" "%-d %B %Y"
<> tagLinksField "item-tags" <> tagLinksField "item-tags"
<> abstractField
<> defaultContext <> defaultContext
-- | Page identifier for a tag index page. -- | Page identifier for a tag index page.
@ -106,18 +106,3 @@ applyTagRules tags baseCtx = tagsRules tags $ \tag pat -> do
>>= relativizeUrls >>= relativizeUrls
-- ---------------------------------------------------------------------------
-- Tag links context field
-- ---------------------------------------------------------------------------
-- | List context field exposing an item's own (non-expanded) tags as
-- @tag-name@ / @tag-url@ objects.
--
-- $for(essay-tags)$<a href="$tag-url$">$tag-name$</a>$endfor$
tagLinksField :: String -> Context a
tagLinksField fieldName = listFieldWith fieldName itemCtx $ \item ->
map toItem <$> getTags (itemIdentifier item)
where
toItem t = Item (tagIdentifier t) t
itemCtx = field "tag-name" (return . itemBody)
<> field "tag-url" (\i -> return $ "/" ++ itemBody i ++ "/")

View File

@ -1,10 +1,26 @@
{-# LANGUAGE GHC2021 #-} {-# LANGUAGE GHC2021 #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Shared utilities used across the build system.
--
-- The HTML escapers (one for 'String', one for 'Text') live here so that
-- every filter, context, and renderer goes through the same definition.
-- The expansion order matters: @&@ MUST be replaced first, otherwise the
-- @&amp;@ injected by other rules gets re-escaped to @&amp;amp;@. The
-- pure-character-by-character implementation used here avoids that hazard
-- entirely (each character is mapped exactly once).
module Utils module Utils
( wordCount ( wordCount
, readingTime , readingTime
, escapeHtml , escapeHtml
, escapeHtmlText
, trim
, authorSlugify
, authorNameOf
) where ) where
import Data.Char (isAlphaNum, isSpace, toLower)
import qualified Data.Text as T
-- | Count the number of words in a string (split on whitespace). -- | Count the number of words in a string (split on whitespace).
wordCount :: String -> Int wordCount :: String -> Int
wordCount = length . words wordCount = length . words
@ -14,13 +30,49 @@ wordCount = length . words
readingTime :: String -> Int readingTime :: String -> Int
readingTime s = max 1 (wordCount s `div` 200) readingTime s = max 1 (wordCount s `div` 200)
-- | Escape HTML special characters: <, >, &, ", '. -- | Escape HTML special characters: @&@, @<@, @>@, @\"@, @\'@.
--
-- Safe for use in attribute values and text content. The order of the
-- @case@ branches is irrelevant — each input character maps to exactly
-- one output sequence.
escapeHtml :: String -> String escapeHtml :: String -> String
escapeHtml = concatMap escChar escapeHtml = concatMap escChar
where where
escChar '&' = "&amp;"
escChar '<' = "&lt;" escChar '<' = "&lt;"
escChar '>' = "&gt;" escChar '>' = "&gt;"
escChar '&' = "&amp;"
escChar '"' = "&quot;" escChar '"' = "&quot;"
escChar '\'' = "&#39;" escChar '\'' = "&#39;"
escChar c = [c] escChar c = [c]
-- | 'Text' counterpart of 'escapeHtml'.
escapeHtmlText :: T.Text -> T.Text
escapeHtmlText = T.concatMap escChar
where
escChar '&' = "&amp;"
escChar '<' = "&lt;"
escChar '>' = "&gt;"
escChar '"' = "&quot;"
escChar '\'' = "&#39;"
escChar c = T.singleton c
-- | Strip leading and trailing whitespace.
trim :: String -> String
trim = dropWhile isSpace . reverse . dropWhile isSpace . reverse
-- | Lowercase a string, drop everything that isn't alphanumeric or
-- space, then replace runs of spaces with single hyphens.
--
-- Used for author URL slugs (e.g. @"Levi Neuwirth" → "levi-neuwirth"@).
-- Centralised here so 'Authors' and 'Contexts' cannot drift on Unicode
-- edge cases.
authorSlugify :: String -> String
authorSlugify = map (\c -> if c == ' ' then '-' else c)
. filter (\c -> isAlphaNum c || c == ' ')
. map toLower
-- | Extract the author name from a "Name | url" frontmatter entry.
-- The URL portion is dropped (it's no longer used by the author system,
-- which routes everything through @/authors/{slug}/@).
authorNameOf :: String -> String
authorNameOf s = trim (takeWhile (/= '|') s)

View File

@ -2,7 +2,7 @@ active-repositories: hackage.haskell.org:merge
constraints: any.Glob ==0.10.2, constraints: any.Glob ==0.10.2,
any.HUnit ==1.6.2.0, any.HUnit ==1.6.2.0,
any.JuicyPixels ==3.3.9, any.JuicyPixels ==3.3.9,
any.OneTuple ==0.4.2, any.OneTuple ==0.4.2.1,
any.Only ==0.1, any.Only ==0.1,
any.QuickCheck ==2.15.0.1, any.QuickCheck ==2.15.0.1,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
@ -182,7 +182,7 @@ constraints: any.Glob ==0.10.2,
any.text-conversions ==0.3.1.1, any.text-conversions ==0.3.1.1,
any.text-icu ==0.8.0.5, any.text-icu ==0.8.0.5,
any.text-iso8601 ==0.1.1, any.text-iso8601 ==0.1.1,
any.text-short ==0.1.6, any.text-short ==0.1.6.1,
any.th-abstraction ==0.6.0.0, any.th-abstraction ==0.6.0.0,
any.th-compat ==0.1.7, any.th-compat ==0.1.7,
any.th-expand-syns ==0.4.12.0, any.th-expand-syns ==0.4.12.0,

View File

@ -60,7 +60,7 @@ A full accounting of what this build process has actually produced is available
## The Computing Environment ## The Computing Environment
::: dropcap ::: dropcap
Every tool I use was chosen rather than accepted. This distinction matters: the default path through the software ecosystem, particularly for developers, routes through choices made by convenience, employer mandate, or inertia rather than deliberation. I have consistently refused that path — not out of contrarianism, but because I find that the tools one uses become constitutive of the work one produces. This site is downstream of those choices in every particular. I am, like many passionate nerds within the realm of computing, obsessive over my technological choices. They are the subject of constant critique, review, revision, etc. I believe in the value of putting deep thought into the systems that one interacts with, rather than accepting the first showing of convenience and going with the flow. A system interacted with is an experience moreso than a mere tool.
::: :::
### Desktop ### Desktop
@ -72,23 +72,21 @@ My primary desktop is a rig I built myself, running **Gentoo Linux** with **Hypr
- It is, in my experience, the best-maintained Linux distribution I have ever used. - It is, in my experience, the best-maintained Linux distribution I have ever used.
- The community is phenomenal. - The community is phenomenal.
I have strong hardware preferences to match: [AMD]{.smallcaps} processors and graphics throughout. This is not tribal loyalty — I have used multiple distinct products from each major vendor and formed the preference empirically. I have strong hardware preferences to match; [AMD]{.smallcaps} hardware is used in favor of Intel and NVIDIA. I have used at least 2 distinct chips from each manufacturer and consistently find that, as far as x86-64 is concerned, AMD is the clear winner. As for my preference for AMD gpus, not only do I quite disagree with the business direction of NVIDIA, but the VRAM offerings are simply superior from AMD.
### Laptop ### Laptop
For mobile computing, I use a [P]{.smallcaps}-series ThinkPad running **Arch Linux** — the same Hyprland environment, the same *Levshell*, the same muscle memory, but without the compilation overhead. Gentoo is impractical on battery-constrained hardware: compilation times are simply too long, and running the compiler continuously on battery is not a reasonable tradeoff. Arch is a sound alternative. Portage is better than pacman, but pacman on a laptop is entirely correct. For mobile computing, I use a [P]{.smallcaps}-series ThinkPad running **Arch Linux** — the same Hyprland environment, the same *Levshell*, the same muscle memory down to the config file. Gentoo is impractical on battery-constrained hardware, as compilation times are simply too long and require active power connection. Arch is a sound alternative, configurable enough to pass. Portage is better than pacman in my opinion, but pacman is still far better than horrid package managers like apt.
The Hyprland environment runs identically on both machines. This is deliberate — I do not want to maintain two workflows. Every keybinding, every visual detail, every tool behaves the same. The continuity is itself a productivity commitment.
### Editor ### Editor
Everything on this site — every word of prose, every line of Haskell, every [CSS]{.smallcaps} rule — was written in **Emacs**. I use Emacs for the same reason I use Gentoo: it is configurable to a degree that other editors treat as pathological. Running `make watch` alongside an Emacs buffer gives something close to a [WYSIWYG]{.smallcaps} experience for this workflow: save the file, the Hakyll watcher recompiles, the browser updates. The loop is tight enough that the editor and the rendered output feel nearly continuous. Everything on this site — every word of prose, every line of Haskell, every [CSS]{.smallcaps} rule — was written in **Emacs**. I have used most of the major editors, and consistently experiment with others, but have yet to find any which come close to the power of Emacs. I intend to complete a project called "Pmacs," which will introduce much moderner parallelization, among other features, to Emacs. This is a project I intend to tackle in the Summer of 2026 at high intensity.
### Privacy-First Computing ### Privacy-First Computing
My email and [VPN]{.smallcaps} are self-hosted; I use Thunderbird as a client for the former. For browsing I use [LibreWolf](https://librewolf.net/), not Firefox: the Chromium monopoly and Mozilla's evident incompetence at browser development are, to me, equally concerning developments, and LibreWolf is the most coherent response to both. My phone runs [GrapheneOS](https://grapheneos.org/) — the only reasonably secure and private option for a mobile device, and one whose restrictions are, frankly, a feature rather than a limitation. My email and [VPN]{.smallcaps} are self-hosted; I use Thunderbird as a client for the former. For browsing I use [LibreWolf](https://librewolf.net/), not Firefox: the Chromium monopoly and Mozilla's evident incompetence at browser development are, to me, equally concerning developments, and LibreWolf is the most coherent response to both. My phone runs [GrapheneOS](https://grapheneos.org/) — the only reasonably secure and private option for a mobile device, and one whose restrictions are, frankly, a feature rather than a limitation.
The principle underlying all of these choices is the same one underlying the site's **No Tracking** policy: privacy is an architectural decision, not a settings toggle. You build the infrastructure first. Bolting on privacy after the fact, whether in a browser or on a website, is not privacy — it is the appearance of privacy. The principle underlying all of these choices is the same one underlying the site's **No Tracking** policy: privacy is an architectural decision, not a settings toggle. Bolting on privacy after the fact, whether in a browser or on a website, is not privacy — it is the appearance of privacy.
--- ---
@ -161,5 +159,4 @@ The tradition of the personal website is one that is built on a sense of communi
This site is unfinished. Several portals have no content yet. The annotated bibliography This site is unfinished. Several portals have no content yet. The annotated bibliography
is sparse. I am in the progress of migrating content, so stay tuned! is sparse. I am in the progress of migrating content, so stay tuned!
The colophon itself is a living document. When the site changes substantially, this The colophon itself is a living document. When the site changes substantially, this page will change with it. The git repository on Forgejo (hosted on the git subdomain here) should always be considered to take precedence.
page will change with it. The git history functions as an authoritative record; this page is my personal annotated summary.

View File

@ -0,0 +1,41 @@
---
title: "The Modern Idolatry"
date: 2026-04-06
abstract: >
Thoughts on idolizing notions of success, whether extrinsic or intrinsic, prompted by my upcoming graduation from Brown University and a recent week spent in Paris.
tags:
- miscellany
- philosophy
- personal
- personal/travel
authors:
- "Levi Neuwirth | /me.html"
status: "Draft"
history:
- date: "2026-04-06"
---
Travel affects me profoundly, and the effect is strangely uniform. There is a hierarchical structure of dichotomies that seems to define most aspects of my life, and my interactions with place are no exception to this rule. One of the dichotomies is as follows: I am rather accustomed to moving around in my adult life to date, never spending more than 4 months in a place before spending at least a few weeks somewhere else, and yet I rapidly develop a sense of "home" wherever I am - a stagnation of sorts, an acceptance of the region in which I reside and an abstraction away of the remainder of the world to some vast, estoeric TERRA INCOGNITA. Perhaps the most profound, persistent personal effect of travel on me is that it knocks me out of this mental state of spatial hibernation, reminding me that there is an entire world beyond that which I consistently perceive, and that I have the means to do something to have a positive impact on it. This has been a profoundly important sensation for me to have for many years now, and is thus one basis by which travel is consistently a high priority for me.
This is often combined with a sense of grand melancholy, the sort that for me is nearly ubiquitous in the presence of grandeur and beauty. It is a different incarnation of the same melancholy^[I should emphasize here that while "melancholy" may in general invoke a negative connotation, I do not feel that this is a negative emotion whatsoever. To me, the primary effect of melancholy, or at least melancholy of this sort, is an amplification of the imposing impetus, usually some sense of grandeur. The melancholy is like delicate cinnamon powder added to the top of a pristine flat white.] that I feel when I listen to a profound piece of music, view a painting that I enjoy, or reach the summit of a mountain that I have been embracing for hours. In this case the strength is perhaps yielded by the confluence of grandeur of the natural world - the vastness of space, the mystery of distinct regions that I have yet to know and the warm embrace of returning to those which I know but not well - and that of the human world - the various cultures, languages, beliefs, institutions, and above all people that are present in various places.
This grand, amplified melancholy typically has three causes in my life, two of which I have already mentioned. The third is instances of outward-facing "success" - I typically feel melancholic and pensive when I have done something or crossed some milestone^["Milestones" are not terms that I would use nor guidelines or aspects of some personal timeline or plan, but rather things that society imposes. They don't mean much to me on a personal level, but do unavoidably impact how I feel, since I cannot avoid societial influences as much as I sometimes wish I could.] that many folks see as an indicator of success (or the potential for it). One might imagine, then, that I felt quite a sensation as I was travelling in Paris during my most recent spring break, on the verge of graduating from Brown University after four years of work and extreme personal growth, and such an imagination would be highly warranted. As I took endless walks on the [Champ de Mars](https://en.wikipedia.org/wiki/Champ_de_Mars) and along the [Seine](https://en.wikipedia.org/wiki/Seine) many thoughts and musings were prompted by the grand sensations of emotion, grandeur, and wonder that I felt. They are largely concentrated around the theme of modern idolatry in the name of "success" and the impliciations of this, on both a personal and broader philosophical and societal level. My attempts to collect them into a format that I can share follow.
## Dichotomies
<figure class="prose-excerpt">
<blockquote>
"Everything is a dichotomy; that is perhaps the grandeur of life, of the Universe itself."
</blockquote>
<figcaption>Levi's personal journal, 29 January 2026</figcaption>
</figure>
::: dropcap
What of "success" do I understand, and what of it have I cumulatively failed to understand? Of course, this question depends on one's chosen definition of "success," so perhaps the most interesting approach is to parameterize our choice of definition. Indeed, SUCCESS is a concept that means different things to different people, so perhaps such parameterization is implicitly necessary. Yet such parameterization unsettles me greatly on a personal level. It is the first example of dichotomy that we, together, may explore.
:::
Society widely seems to view success as the fulfillment of goals rooted in extrinsic motivations. The credentialist nature of our society seems to conflate one's ability to earn a title with competence, experience, and, in some cases, worthiness - and who, exactly, is worthy of success, or, rather, is it success that deems one worthy in the eyes of the world? In more ways than one, it seems that we have been conditioned somehow through our institutions, both explicit and implicit, to conflate worthiness with success, and this conflation is perhaps grounded in the idea that success will be transitative; that is, one's continued association with successful people leads to more successful outcomes. This seems to imply that "success" is somehow a communal thing, inherently extrinsic that it diffuses and saturates, so long as those who have it^[For the sake of illustration here we are assuming that "success" is something to be had, a notion that will be debunked later.] are willing to continue associating with those who have less of it.
Yet this is in direct contrast to what is arguably the foundation of our^[I use "our" here to refer to citizens of the United States, my country of birth and the culture that largely influenced my perception of success.] success. The extrinsic nature of such success is not problematic, but the communal aspect is. The ethos of the [American Dream](https://en.wikipedia.org/wiki/American_Dream) is largely that of individualism - the promise that dense individual effort leads to success.

View File

@ -1,216 +0,0 @@
---
title: "Beyond Comorbidity Indices"
date: 2026-03-28
abstract: >
A permutation-invariant deep-learning model that learns ICD-10-CM code representations improved 30-day readmission prediction (AUC 0.7496 vs 0.6553 for CCI) and postdischarge in-hospital mortality prediction (AUC 0.8557 vs 0.7844 for age-adjusted CCI) compared with standard comorbidity-index baselines in a national claims database of over 51 million adult hospitalizations.
tags:
- research
- research/machine-learning
authors:
- "Levi Neuwirth | /me.html"
- "Liqi Shu"
- "Xilin Wang"
- "Henry Zheng"
affiliation:
- "Department of Neurology, Warren Alpert Medical School, Brown University"
- "Department of Computer Science, Brown University | https://cs.brown.edu"
- "Department of Mathematics, Brown University | https://mathematics.brown.edu/"
- "Department of Computer Science, Northeastern University"
status: "Durable"
confidence: 80
importance: 3
evidence: 5
scope: average
novelty: moderate
practicality: moderate
history:
- date: "2026-03-28"
note: Preprint auto-formatted for levineuwirth.org
---
::: {.annotation .annotation--collapsible}
**KEY POINTS**
**Question.** Among adult hospitalizations in a national claims database, does a permutation-invariant model that learns ICD-10-CM representations improve prediction of 30-day outcomes compared with Charlson and Elixhauser comorbidity indices?
**Findings.** In a temporally held-out national cohort (2021--2022), the ICD-10-CM embedding model achieved higher discrimination than comorbidity-index models for 30-day unplanned readmission (AUC, 0.7496) and 30-day postdischarge in-hospital mortality (AUC, 0.8557). The highest-performing comorbidity-index comparators achieved AUCs of 0.6553 and 0.7844, respectively.
**Meaning.** Learning representations directly from ICD-10-CM codes may improve claims-based risk adjustment and help prioritize transitional-care resources at discharge.
:::
## Introduction
::: dropcap
Accurate prediction and risk adjustment for short-term clinical outcomes, such as 30-day hospital mortality and readmission, are critical for enhancing healthcare research quality, allowing fair assessment of healthcare outcomes and quality metrics [@cms_hrrp]. Most claims-based risk adjustment continues to rely on comorbidity indices such as the Charlson Comorbidity Index (CCI) and Elixhauser Comorbidity Index (ECI), which map diagnosis codes to a limited set of conditions [@charlson1987; @elixhauser1998]. While these indices are interpretable and widely deployed, they inevitably discard granularity and may miss clinically meaningful comorbidity patterns and interactions among diagnoses.
:::
Recent machine-learning approaches increasingly use high-dimensional ICD code inputs and have demonstrated improved prediction for a range of outcomes [@deschepper2020; @lelay2022; @qiao2022]. However, many approaches simplify or truncate ICD codes, aggregate diagnosis lists in ways that depend on code order, or are trained and evaluated in settings where coding practices differ across sites---each of which can limit robustness and transportability. In addition, many claims-based studies focus on in-hospital mortality and do not evaluate postdischarge mortality as a discharge-time outcome [@qiao2022; @davis2022; @harerimana2021; @matsui2022; @nguyen2017].
We developed and temporally validated a claims-based prediction model that uses trainable ICD-10-CM embeddings and permutation-invariant aggregation of diagnosis lists. Using the Nationwide Readmissions Database (2016--2022), we assessed discrimination, calibration, and recall-weighted performance for 30-day unplanned readmission and 30-day postdischarge in-hospital mortality and compared results with Charlson and Elixhauser comorbidity-index models; we also generated diagnosis-level attributions using Integrated Gradients.
## Methods
### Study Design, Data Source, and Oversight
We conducted a retrospective cohort study using the Healthcare Cost and Utilization Project (HCUP) Nationwide Readmissions Database (NRD), 2016--2022. Adult discharges from 2016--2020 were used for model development, and discharges from 2021--2022 were reserved for temporal external testing. Discharges in December of each year were excluded to allow complete 30-day follow-up within the same calendar year.
Use of the NRD was governed by the HCUP data use agreement. Because the NRD contains deidentified data, the institutional review board determined the study was not human participants research and that informed consent was not required.
### Cohort Definition
We included hospitalizations for patients aged 18 years or older with a valid patient linkage identifier within each calendar year. For mortality analyses, index hospitalizations with in-hospital death were excluded from the primary outcome definition (postdischarge in-hospital mortality) and examined in a prespecified secondary analysis.
### Outcomes
The coprimary outcomes were (1) 30-day unplanned readmission and (2) 30-day postdischarge in-hospital mortality within 30 days of discharge. Readmissions were classified as unplanned using the HCUP algorithm. Postdischarge mortality was defined as inpatient death occurring during a subsequent hospitalization within 30 days after discharge; deaths outside the hospital are not captured in the NRD.
### Predictors
For each index hospitalization, we used up to 40 ICD-10-CM diagnosis codes (principal and secondary diagnoses). Diagnosis codes were label-encoded into integer identifiers for model input. Demographic and socioeconomic covariates included age, sex, primary payer, and ZIP-code median income quartile. Age was standardized; categorical variables (sex, payer, income quartile) were one-hot encoded with explicit handling of missing values. No diagnosis codes were filtered, simplified, or reordered.
### Model Development
#### Embedding Model Architecture
The embedding model mapped each ICD-10-CM code to a trainable embedding vector and used a Deep Sets architecture [@zaheer2017] for permutation-invariant aggregation. The Deep Sets encoder processed individual diagnosis embeddings independently through shared multilayer perceptrons (MLPs); outputs were summed (permutation-invariant pooling) and passed to a decoder MLP. Demographic and socioeconomic covariates were processed through a separate 2-layer MLP and concatenated with the Deep Sets output before the final predictor MLP. The model was implemented in TensorFlow [@abadi2016] and trained with binary cross-entropy loss, the Adam optimizer, and early stopping on validation loss.
#### Training Strategy
Models were trained separately for each outcome. To address outcome imbalance, we randomly downsampled majority-class encounters in the training set to a 1:1 case-control ratio (validation and temporal test sets were not downsampled). Predicted probabilities were recalibrated on the validation set using logistic calibration. Hyperparameters were tuned via random search (32 trials per outcome), prioritizing validation AUC with $F_2$ as a secondary criterion.[^hparams]
[^hparams]: Hyperparameter search details and final selected configurations are provided in the supplementary eMethods 1 and eTable 1.
### Comparator Models
We implemented logistic regression models based on the Charlson and Elixhauser comorbidity indices. For the Charlson model, we used both the unweighted comorbidity count and the traditional age-adjusted score [@ahrq_elixhauser; @fernando2019; @quan2005; @deyo1992; @quan2011; @stagg2006]. For the Elixhauser model, we used the unweighted comorbidity count; the refined AHRQ algorithm was applied for ICD-10-CM mapping [@ahrq_elixhauser]. All comparator models included age, sex, primary payer, and income quartile as covariates.
### Statistical Analysis and Performance Evaluation
Primary performance evaluation used a prespecified stratified random subsample of 2021--2022 discharges (n = 3,226,831; sampling fraction 0.10). Discrimination was assessed with AUC-ROC and 95% CIs (DeLong method); pairwise comparisons used DeLong tests [@youden1950; @delong1988]. Calibration was evaluated with calibration plots and the Brier score. Binary classification thresholds were selected on the validation set by maximizing the Youden index and applied unchanged to the temporal test set. Threshold-dependent metrics included sensitivity, specificity, precision, $F_1$, and $F_2$ (which weights recall twice as heavily as precision).
### Interpretability Analysis
We used Integrated Gradients [@sundararajan2017] to generate code-level attributions, quantifying each ICD-10-CM code's contribution to predicted risk. Attributions were averaged across occurrences for the 10 most positively and negatively influential codes per outcome (minimum 50 occurrences in the interpretability cohort).[^ig]
[^ig]: Additional details on training, testing, and interpretability methods are provided in supplementary eMethods 1--3.
### Ablation Studies
We conducted three prespecified ablation studies to evaluate the contribution of key architectural choices: (1) addition of transformer blocks for attention-based contextualization, (2) replacement of Deep Sets with a permutation-variant flattening comparator, and (3) removal of demographic and socioeconomic covariates.[^ablation]
[^ablation]: Full ablation details and results are in supplementary eMethods 4 and eTable 3.
## Results
### Cohort Characteristics
The study included 19,120,000 adult hospitalizations from 2016--2020 (development cohort) and 32,268,308 from 2021--2022 (temporal test cohort). The 30-day unplanned readmission rate was 11.0% in the development cohort and 10.7% in the temporal test cohort. The 30-day postdischarge mortality rate was 0.6% in both cohorts. Additional cohort characteristics are reported in Table 1.
### Primary Performance Comparison
For 30-day readmission, the embedding model achieved an AUC of 0.7496 (95% CI, 0.7488--0.7504), outperforming the Charlson model (AUC, 0.6553; 95% CI, 0.6544--0.6562; P < .001) and the Elixhauser model (AUC, 0.6363; 95% CI, 0.6353--0.6372; P < .001). For 30-day postdischarge in-hospital mortality, the embedding model achieved an AUC of 0.8557 (95% CI, 0.8532--0.8581) vs the best-performing comparator (age-adjusted Charlson: 0.7844; 95% CI, 0.7813--0.7874; P < .001). Performance metrics are summarized in Table 2.
The embedding model also showed superior recall-weighted performance. For 30-day readmission, $F_2$ was 0.4848 vs 0.4066 for the best comparator; for postdischarge mortality, $F_2$ was 0.0530 vs 0.0480. Calibration plots demonstrated good agreement between predicted and observed risks across the probability range.[^calibration]
[^calibration]: Calibration reliability plots are provided in supplementary eFigure 2.
::: {.annotation .annotation--static}
**Figure 1 --- [placeholder]** Receiver operating characteristic curves for 30-day readmission (left) and 30-day postdischarge in-hospital mortality (right), comparing the embedding model against Charlson and Elixhauser comparators on the temporal test set.
:::
::: {.annotation .annotation--static}
**Figure 2 --- [placeholder]** Calibration reliability plots for the embedding model and comparators on the temporal test set.
:::
### Interpretability Analysis
Integrated Gradients attributions identified clinically relevant patterns. For readmission, codes indicating prior readmissions, chronic conditions requiring ongoing management, and social determinants (e.g., housing issues) showed high positive attributions. For postdischarge mortality, codes reflecting severe illness (e.g., sepsis, respiratory failure) and palliative care had high positive attributions, while codes for routine procedures (e.g., colonoscopy) had negative attributions.
::: {.annotation .annotation--static}
**Figure 3 --- [placeholder]** Top 10 positively and negatively attributed ICD-10-CM codes by mean Integrated Gradients attribution for 30-day readmission.
:::
::: {.annotation .annotation--static}
**Figure 4 --- [placeholder]** Top 10 positively and negatively attributed ICD-10-CM codes by mean Integrated Gradients attribution for 30-day postdischarge in-hospital mortality.
:::
### Ablation Studies
Addition of transformer blocks did not improve discrimination or $F_2$ score for either outcome.[^transformer] Replacing Deep Sets with a permutation-variant flattening comparator reduced AUC (P < .001 for readmission; P = .014 for mortality) and $F_2$ score.[^flattening] Removing demographic and socioeconomic covariates slightly reduced performance (P = .020 for readmission; P < .001 for mortality).[^demographics]
[^transformer]: Reported P values for AUROC differences were P < .001 for readmission and P = 0.57 for postdischarge mortality. Full results in supplementary eTable 3A.
[^flattening]: Full results in supplementary eTable 3B.
[^demographics]: Full results in supplementary eTable 3C.
::: {.annotation .annotation--static}
**Figure 5 --- [placeholder]** Ablation study results comparing the base embedding model against transformer-augmented, permutation-variant, and ICD-only variants for both outcomes.
:::
## Discussion
In a large national claims database, a permutation-invariant embedding model that learned ICD-10-CM representations achieved higher discrimination than Charlson and Elixhauser comorbidity-index models for predicting 30-day unplanned readmission and 30-day postdischarge in-hospital mortality. The embedding model also showed better calibration and recall-weighted performance, supporting its potential utility for discharge-time risk stratification and claims-based risk adjustment.
### Comparison With Prior Work
Prior studies have demonstrated the value of machine learning for readmission and mortality prediction [@deschepper2020; @lelay2022; @qiao2022; @davis2022; @harerimana2021; @matsui2022; @nguyen2017]. However, many approaches rely on permutation-variant aggregation (e.g., recurrent networks or attention over ordered sequences), which can be sensitive to code ordering---a dimension that varies across sites and coders. Our permutation-invariant approach addresses this limitation and may improve transportability. In addition, many claims-based studies focus on in-hospital mortality and do not evaluate postdischarge mortality as a discharge-time outcome. Our focus on postdischarge mortality provides a more clinically relevant endpoint for discharge planning and transitional care.
### Implications for Practice and Policy
Readmission reduction is a key quality and policy goal, with financial penalties under the Hospital Readmissions Reduction Program [@cms_hrrp; @joynt2013; @desai2016; @zuckerman2017]. However, current risk adjustment often relies on comorbidity indices that may under-adjust for complexity, leading to unfair comparisons across hospitals serving different populations [@obermeyer2019; @kind2014; @joynt2011]. Our findings suggest that learning representations directly from ICD-10-CM codes could improve fairness and precision in risk adjustment.
At the patient level, improved risk stratification at discharge could help prioritize transitional-care resources (e.g., care coordination, medication reconciliation, follow-up calls) to high-risk patients [@hansen2011; @coleman2006]. The interpretability analysis also highlights specific diagnoses that drive risk, which may inform discharge planning conversations.
### Limitations
Our study has several limitations. First, the NRD captures only readmissions to acute-care hospitals within the same state and does not include deaths outside the hospital; both outcomes are therefore underestimated. Second, the model was trained and evaluated in a single national database; external validation in other claims databases and prospective evaluation are needed to assess generalizability and clinical utility. Third, while Integrated Gradients provides code-level attributions, the embedding model remains less interpretable than simple comorbidity indices [@rudin2019]. Fourth, we did not incorporate additional predictors (e.g., laboratory values, vital signs, discharge disposition) that may be available in some settings and could further improve performance. Finally, residual confounding by unmeasured factors (e.g., social determinants, functional status) may affect model predictions and limit clinical deployment without further validation.
### Conclusions
In a large national claims database, a permutation-invariant model that learned ICD-10-CM representations improved prediction of 30-day readmission and postdischarge in-hospital mortality compared with Charlson and Elixhauser index models. These findings support the use of high-dimensional diagnosis information for claims-based risk adjustment and discharge-time risk stratification, with prospective evaluation needed before clinical deployment.
## Tables
### Table 1: Cohort Characteristics {#table-1}
| Characteristic | Development (2016--2020) | Temporal Test (2021--2022) |
|:---|---:|---:|
| Total hospitalizations | 19,120,000 | 32,268,308 |
| 30-day readmission rate (%) | 11.0 | 10.7 |
| 30-day postdischarge mortality rate (%) | 0.6 | 0.6 |
### Table 2: Model Performance Comparison (Temporal Test Set) {#table-2}
| Outcome | Model | AUC-ROC | 95% CI | Precision | Recall | $F_1$ | $F_2$ |
|:---|:---|---:|:---:|---:|---:|---:|---:|
| 30-day readmission | Embedding | 0.7496 | 0.7488--0.7504 | 0.1881 | 0.8006 | 0.3046 | 0.4848 |
| | CCI | 0.6553 | 0.6544--0.6562 | 0.1493 | 0.6819 | 0.2453 | 0.4066 |
| | CCI (age-adj) | 0.6483 | 0.6474--0.6491 | 0.1469 | 0.6794 | 0.2416 | 0.4016 |
| | ECI | 0.6363 | 0.6353--0.6372 | 0.1426 | 0.6750 | 0.2357 | 0.3925 |
| 30-day postdischarge mortality | Embedding | 0.8557 | 0.8532--0.8581 | 0.0111 | 0.8756 | 0.0220 | 0.0530 |
| | CCI | 0.7217 | 0.7180--0.7253 | 0.0075 | 0.8026 | 0.0149 | 0.0371 |
| | CCI (age-adj) | 0.7844 | 0.7813--0.7874 | 0.0093 | 0.7846 | 0.0185 | 0.0480 |
| | ECI | 0.6686 | 0.6645--0.6728 | 0.0068 | 0.7901 | 0.0135 | 0.0336 |
::: {.annotation .annotation--static}
**Full title:** Development of an ICD-10-CM Embedding Model for Predicting 30-Day Readmission and Postdischarge In-Hospital Mortality in the Nationwide Readmissions Database
**Keywords:** ICD-10-CM; readmission; mortality; claims data; risk adjustment; deep learning; model interpretability
:::
::: {.annotation .annotation--collapsible}
**Structured Abstract**
**Importance.** Comorbidity indices are widely used for claims-based risk adjustment but compress diagnostic information and may under-adjust for clinical complexity.
**Objective.** To develop and temporally validate a permutation-invariant model that learns ICD-10-CM representations to predict 30-day unplanned readmission and 30-day postdischarge mortality and to compare performance with Charlson and Elixhauser comorbidity-index models.
**Design, Setting, and Participants.** Retrospective cohort study of adult hospitalizations in the Healthcare Cost and Utilization Project Nationwide Readmissions Database (NRD), 2016--2022. Models were developed using discharges from 2016--2020 and temporally tested using 2021--2022 discharges. Primary performance evaluation was conducted in a prespecified stratified random subsample of 2021--2022 discharges (n = 3,226,831).
**Exposure.** Up to 40 discharge diagnosis codes (ICD-10-CM) were mapped to trainable embeddings and aggregated with a permutation-invariant Deep Sets architecture; age, sex, primary payer, and ZIP code income quartile were also included as covariates.
**Main Outcomes and Measures.** Outcomes were 30-day unplanned readmission and 30-day postdischarge in-hospital mortality. Discrimination was assessed with the area under the receiver operating characteristic curve (AUC) and 95% CIs; calibration and threshold-dependent metrics (including $F_2$) were evaluated. Performance was compared with optimized logistic regression models based on Charlson and Elixhauser indices.
**Results.** In temporal testing, the embedding model showed higher discrimination for 30-day readmission (AUC, 0.7496 [95% CI, 0.7488--0.7504]) than Charlson (0.6553 [95% CI, 0.6544--0.6562]) and Elixhauser (0.6363 [95% CI, 0.6353--0.6372]). For 30-day postdischarge in-hospital mortality, the embedding model achieved an AUC of 0.8557 (95% CI, 0.8532--0.8581) vs the best-performing comparator (age-adjusted Charlson: 0.7844 [95% CI, 0.7813--0.7874]); DeLong tests were significant for each comparison (P < .001). Recall-weighted performance similarly favored the embedding model ($F_2$: 0.4848 vs 0.4066 for readmission; 0.0530 vs 0.0480 for postdischarge mortality).
**Conclusions and Relevance.** In a large national claims database, a permutation-invariant model that learned ICD-10-CM representations improved prediction of 30-day readmission and postdischarge in-hospital mortality compared with Charlson and Elixhauser index models. These findings support the use of high-dimensional diagnosis information for claims-based risk adjustment and discharge-time risk stratification, with prospective evaluation needed before clinical deployment.
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

View File

@ -0,0 +1,367 @@
---
title: "Beyond Comorbidity Indices"
date: 2026-04-09
abstract: >
A deep learning model using ICD-10-CM diagnosis codes with a permutation-invariant Deep Sets aggregator improved 30-day unplanned readmission (AUC 0.7496 vs 0.6553 for CCI) and 30-day postdischarge in-hospital mortality (AUC 0.8557 vs 0.7844 for age-adjusted CCI) compared with Charlson and Elixhauser comorbidity-index benchmarks in a national claims database of over 113 million adult hospitalizations.
tags:
- research
- research/machine-learning
authors:
- "Levi Neuwirth | /me.html"
- "Liqi Shu"
- "Xilin Wang"
- "Henry Zheng"
affiliation:
- "Department of Neurology, Warren Alpert Medical School, Brown University"
- "Department of Computer Science, Brown University | https://cs.brown.edu"
- "Department of Mathematics, Brown University | https://mathematics.brown.edu/"
- "Department of Computer Science, Northeastern University"
status: "Durable"
confidence: 80
importance: 3
evidence: 5
scope: average
novelty: moderate
practicality: moderate
bibliography: data/bci-paper.bib
repository: "https://git.levineuwirth.org/neuwirth/beyond_comorbidity_indices"
history:
- date: "2026-03-28"
note: Preprint auto-formatted for levineuwirth.org
---
::: {.annotation .annotation--collapsible}
**KEY POINTS**
**Question.** Among adult hospitalizations in a national claims database, does a deep learning model using ICD-10-CM diagnosis codes improve prediction of 30-day unplanned readmission and 30-day postdischarge in-hospital mortality compared with benchmark models based on Charlson and Elixhauser comorbidity indices?
**Findings.** In this cohort study of 3,226,831 temporally held-out discharges, the ICD-10-CM--based model showed better discrimination than benchmark comorbidity-index models for both outcomes.
**Meaning.** Using the full set of discharge diagnosis codes may improve short-term claims-based outcome prediction beyond summary comorbidity indices.
:::
## Introduction (Background and Significance)
::: dropcap
Accurate prediction and risk adjustment for short-term clinical outcomes, such as 30-day mortality and readmission, are critical for enhancing healthcare research quality, allowing fair assessment of healthcare outcomes and quality metrics [@cms_hrrp]. Most claims-based risk adjustment continues to rely on comorbidity indices such as the Charlson Comorbidity Index (CCI) and Elixhauser Comorbidity Index (ECI), which map diagnosis codes to a limited set of conditions [@charlson1987; @elixhauser1998]. While these indices are interpretable and widely deployed, they inevitably discard granularity and may miss clinically meaningful comorbidity patterns and interactions among diagnoses.
:::
Recent machine-learning approaches increasingly use a set of ICD-10-CM diagnosis codes and have demonstrated improved prediction for a range of outcomes [@deschepper2020; @lelay2022; @qiao2022]. However, many approaches simplify or truncate ICD codes, aggregate diagnosis lists in ways that depend on code order, or are trained and evaluated in settings where coding practices differ across sites---each of which can limit generalizability across settings. In addition, many claims-based studies focus on in-hospital mortality and do not evaluate postdischarge mortality among outcomes relevant at the time of discharge [@qiao2022; @davis2022; @harerimana2021; @matsui2022; @nguyen2017].
In this study, we developed and temporally validated a claims-based deep learning model using ICD-10-CM diagnosis codes to predict 30-day unplanned readmission and 30-day postdischarge mortality in the Nationwide Readmissions Database. We compared its performance with benchmark models based on the Charlson and Elixhauser comorbidity indices, which are widely used for claims-based risk adjustment but were not originally designed for these specific outcomes. We also evaluated the model with different architectural design and examined diagnosis-level contributions to model predictions.
## Materials and Methods
### Study Design, Data Source, and Oversight
We conducted a retrospective cohort study using the Healthcare Cost and Utilization Project (HCUP) Nationwide Readmissions Database (NRD), 2016--2022. Adult discharges from 2016 through 2020 were used for model development, and a later temporally separated cohort from 2021 through 2022 was reserved for temporal validation. Discharges in December of each year were excluded to allow complete 30-day follow-up within the same calendar year.
Use of the NRD was governed by the HCUP data use agreement. Because the NRD contains deidentified data, the institutional review board determined the study was not human participants research and that informed consent was not required.
### Cohort Definition
We included hospitalizations for patients aged 18 years or older with a valid patient linkage identifier within each calendar year. For both the readmission and mortality analyses, index hospitalizations ending in in-hospital death were excluded because patients were not at risk for postdischarge outcomes. In-hospital death during the index hospitalization was examined in a prespecified secondary mortality analysis (eResults 1).
### Outcomes
The coprimary outcomes were (1) 30-day unplanned readmission and (2) 30-day postdischarge in-hospital mortality (hereafter, postdischarge mortality). Readmissions were classified as unplanned if they were coded as nonelective admissions in the HCUP database. Postdischarge mortality was defined as inpatient death occurring during a subsequent hospitalization within 30 days after discharge. Deaths outside the hospital are not captured in the NRD.
### Predictors
For each index hospitalization, we used up to 40 ICD-10-CM diagnosis codes (principal and secondary) and patient-level covariates (age, sex, primary payer, and ZIP-code median income quartile). Age was standardized, and categorical variables were represented using one-hot encoding. Analyses were restricted to records with nonmissing outcome ascertainment and complete covariates.
### Comparator Models
For benchmarking, we computed the Elixhauser Comorbidity Index (ECI) and Charlson Comorbidity Index (CCI) for each index hospitalization and treated each index as a continuous risk score [@charlson1987; @elixhauser1998]. The ECI identifies 30+ distinct conditions from administrative data, serving as a critical tool for risk adjustment in studies evaluating in-hospital mortality and short-term readmissions [@elixhauser1998; @ahrq_elixhauser; @fernando2019; @quan2005]. The CCI consolidates up to 19 comorbid conditions into a weighted numeric score, including variants that adjust for age, primarily predicting long-term mortality and readmissions [@charlson1987; @fernando2019; @quan2005; @deyo1992; @quan2011]. The ECI was computed using an ICD-10-CM--adapted AHRQ approach that identifies chronic comorbidities primarily from secondary diagnoses [@quan2005]. The CCI was computed using ICD-10-CM mappings to 17 comorbidity categories; both raw CCI and age-adjusted CCI were evaluated [@stagg2006]. These benchmark models used the index score alone as the predictor; age, sex, primary payer, and ZIP-code income quartile were not added separately. Discrimination and threshold-dependent classification metrics were derived directly from the score distributions, with operating thresholds selected on the validation set and then applied unchanged to the temporal test evaluation subsample.
### Model Architecture
We developed a deep learning framework that embeds each patient's diagnosis list along with demographic and socioeconomic information to predict the outcome. Each ICD-10-CM code was mapped into a dense vector representation through a learned numerical transformation. To obtain a single representation for each patient while avoiding reliance on diagnosis ordering, we used an aggregation approach that does not depend on code order:
$$f(x) = \rho\!\left(\sum_{x \in X} \phi(x)\right)$$
where $X$ denotes the set of embedded diagnosis vectors. Functions $\phi$ and $\rho$ were implemented as multilayer perceptrons with ReLU activations [@zaheer2017].
Demographic and socioeconomic variables were processed via a separate 2-layer multilayer perceptron. The resulting vector was concatenated with the aggregated diagnosis representation and passed through fully connected layers with ReLU activations and dropout regularization. A sigmoid output layer finally produced a predicted probability for each outcome.
### Model Development and Temporal Validation
Data from 2016--2020 were split into training (90%) and validation (10%) sets. For each outcome, models were trained to minimize binary cross-entropy loss. To address class imbalance, majority-class downsampling was applied during training (see Supplementary eMethods 1). Because majority-class downsampling altered the effective outcome prevalence in the training data, predicted probabilities were corrected using the original training-set prevalence before reporting calibration, temporal-test probabilities, and web-calculator outputs [@pozzolo2015]. This deterministic correction affects probability scaling but not rank-based discrimination. Hyperparameters (embedding dimension, Deep Sets depth/width, demographic tower width, predictor multilayer perceptron configuration, and dropout rate) were tuned using random search; the configuration with best validation AUROC (with recall-weighted metrics used as secondary criteria) was selected (see Supplementary eTable 1).
Temporal validation was based on eligible 2021--2022 discharges. To support computational feasibility while preserve outcome prevalence, primary performance evaluation was conducted in a prespecified stratified random subsample of the eligible 2021--2022 temporal test cohort, with 10% of outcome-positive and 10% of outcome-negative discharges sampled for each outcome (Supplementary eMethods 2). Models were implemented in Python using TensorFlow [@abadi2016].
### Performance Metrics
For threshold-dependent metrics, binary classification thresholds were selected on the validation set by maximizing the Youden index (sensitivity + specificity 1) and then applied unchanged to the temporal test evaluation subsample for each model [@youden1950].
Because outcomes were imbalanced, we emphasized discrimination and precision--recall performance. Primary metrics included AUROC with 95% confidence intervals (CIs), average precision, precision, recall, $F_1$ score, and $F_2$ score (placing greater weight on recall).
### Statistical Analysis
AUROCs and their 95% CIs were estimated using DeLong's nonparametric method [@delong1988]. Pairwise comparisons in AUROC between the embedding model and each comorbidity-index comparator were performed using DeLong tests for correlated ROC curves [@delong1988]. Resulting $P$ values are unadjusted and interpreted alongside effect sizes and 95% CIs.
We conducted prespecified ablation analyses to estimate the incremental contribution of key model components, including addition of transformer blocks, replacement of the order-invariant Deep Sets aggregator with a permutation-variant flattening comparator, and removal of demographic and socioeconomic inputs; details are provided in Supplementary eMethods 4.
### Model Interpretation
We used Integrated Gradients (IG) to estimate code-level contributions to model predictions for each outcome [@sundararajan2017; @placido2023]. Attribution values were summarized at the ICD-10-CM code level, with positive values indicating higher predicted risk and negative values indicating lower predicted risk. To reduce instability from rare codes, ranked summaries were restricted to codes with at least 50 occurrences in the temporal test evaluation subsample. Additional implementation details are provided in Supplementary eMethods 3.
### Web Application
A public, read-only web calculator accepts discharge diagnosis lists and returns risk estimates with code-level explanations; inputs are not stored. The tool is intended for research and demonstration purposes rather than clinical decision-making. The calculator is available at: <https://levineuwirth.github.io/icd_embeddings/>. Implementation details are provided in Supplementary eFigure 4.
## Results
### Cohort size and event prevalence
The NRD included 80,217,696 discharges in 2016--2020 for model development and 33,322,761 discharges in 2021--2022 for temporal testing (Figure 1). After application of outcome-specific eligibility criteria, the validation cohort included 7,828,015 discharges. Primary performance evaluation was conducted in a prespecified stratified random subsample of 3,226,831 discharges from the eligible 2021--2022 temporal test cohort, and this evaluation subsample was not downsampled. For model training, majority-class downsampling was applied to address class imbalance, yielding analytic training samples of 17,200,994 discharges for the readmission analysis and 544,138 discharges for the postdischarge mortality analysis. In the temporal test evaluation subsample, 30-day unplanned readmission occurred in 362,696 discharges (11.2%), and 30-day postdischarge in-hospital mortality occurred in 13,071 discharges (0.4%).
### Model Performance
Detailed performance metrics for the ICD-10-CM--based model and benchmark comorbidity-index models are shown in Table 1. In the temporal test evaluation subsample, the ICD-10-CM--based model showed higher discrimination than comparator models for both 30-day unplanned readmission and 30-day postdischarge mortality (Figure 2). For readmission, the AUROC was 0.750 (95% CI, 0.749--0.750) for the ICD-10-CM--based model, compared with 0.655 (95% CI, 0.654--0.656) for the CCI model, 0.644 (95% CI, 0.644--0.645) for the age-adjusted CCI model, and 0.636 (95% CI, 0.635--0.637) for the ECI model. For postdischarge mortality, the AUROC was 0.856 (95% CI, 0.853--0.858) for the ICD-10-CM--based model, compared with 0.784 (95% CI, 0.781--0.787) for the best-performing comparator, the age-adjusted CCI model; the AUROC for the ECI model was 0.641 (95% CI, 0.636--0.647). DeLong tests comparing the ICD-10-CM--based model with each comorbidity-index comparator were significant for all pairwise comparisons ($P < .001$). Calibration curves showed overall agreement between predicted and observed risk for both outcomes, with greater deviation at higher predicted-risk ranges; this deviation was less pronounced for postdischarge mortality than for readmission (Figure 3).
At the prespecified threshold selected on the validation set, the ICD-10-CM--based model showed higher recall-weighted performance than comparator models, with $F_2$ scores of 0.485 vs 0.407 for 30-day readmission and 0.053 vs 0.048 for postdischarge mortality. Threshold-dependent metrics, including precision, recall, and specificity, are shown in Table 1. Because the classification threshold was selected using the Youden index, these metrics reflect a balance of sensitivity and specificity rather than optimization for a specific clinical use case. For readmission, these gains were accompanied by only modest precision, consistent with the difficulty of predicting this heterogeneous outcome.
In the prespecified secondary analysis expanding mortality to include in-hospital death during the index hospitalization, the ICD-10-CM--based model achieved an AUROC of 0.965 (95% CI, 0.965--0.966), exceeding that of the best-performing comparator model (age-adjusted CCI: AUROC, 0.750 [95% CI, 0.749--0.751]) (eTable 2).
### Ablation Studies
We evaluated a set of prespecified model variants that removed or augmented architectural components (eg, ICD-only inputs and insertion of transformer blocks) to estimate the incremental contribution of each element. We also compared the order-invariant aggregation approach with an order-dependent flattening-based aggregator to quantify any performance tradeoff attributable to enforcing invariance. In covariate ablation, removing demographic and socioeconomic inputs (age, sex, payer, and ZIP-income quartile) caused modest attenuation in performance (readmission AUROC, 0.750 vs 0.748; postdischarge mortality AUROC, 0.856 vs 0.848; similar $F_2$ scores), suggesting that diagnosis patterns captured most, but not all, of the predictive signal. Implementation details are provided in Supplementary eMethods 4; results are summarized in eTable 3.
### Feature Importance
ICD-10-CM codes with the 10 highest positive and negative contributions to both prediction outcomes are shown in Figure 4. For 30-day readmission, acute myeloblastic leukemia, in relapse (C9202), had the greatest positive contribution, whereas encounter for care and examination of mother immediately after delivery (Z390) had the greatest negative contribution. For 30-day postdischarge mortality, C9202 also had the greatest positive contribution, whereas assault by unspecified sharp object, initial encounter (X999XXA) had the greatest negative contribution. The most influential diagnosis codes for 30-day mortality prediction including inpatient death are shown in Supplementary eFigure 2.
## Discussion
In this national claims-based cohort study, a deep learning model using the full set of discharge diagnosis codes showed better discrimination than benchmark models based on Charlson and Elixhauser comorbidity indices for both 30-day unplanned readmission and 30-day postdischarge in-hospital mortality. The performance gain was larger for postdischarge mortality than for readmission. Performance remained favorable in a later, temporally separated NRD cohort, supporting robustness across subsequent years of the same database [@davis2020; @collins2024]. At the same time, these comparisons should be interpreted as benchmarking against widely used summary comorbidity approaches rather than as head-to-head comparisons with models purpose-built for these exact outcomes.
### Comparison with prior work
This pattern is consistent with the structure of the compared methods. Charlson and Elixhauser indices compress diagnosis information into a limited set of predefined conditions and were designed primarily for broad case-mix adjustment rather than high-resolution outcome prediction [@charlson1987; @elixhauser1998]. By contrast, the present model learns from the full diagnosis-code set and can represent co-occurrence patterns that are not captured by summary indices [@morgan2019; @beam2018]. Unlike many prior deep-learning approaches that depend on richer electronic health record inputs and site-specific preprocessing, this framework was designed for portability within claims-based settings by using routinely available diagnosis, demographic, and payer-related variables [@rajkomar2018]. This design also preserves the broader diagnostic context of each hospitalization rather than reducing diagnoses to fixed summary weights as ECI and CCI.
### Interpretability
Interpretability in this setting should not be viewed as an afterthought to an otherwise opaque model. Using Integrated Gradients, the model provided code-level attributions that were generally clinically plausible and helped explain why predicted risk increased or decreased for a given patient. For example, diagnoses associated with high treatment burden or advanced systemic illness, such as relapsed acute myeloid leukemia and alcoholic cirrhosis with ascites, tended to increase predicted risk, whereas postpartum encounters and some assault-related injuries tended to decrease it. These findings suggest that learning-based models can yield clinically meaningful information rather than functioning only as "black boxes," even when they are more flexible than traditional summary indices [@sundararajan2017; @placido2023; @rudin2019].
One plausible explanation for the performance gap between the present model and the Charlson and Elixhauser indices is that the prognostic contribution of a diagnosis is not fixed across patients. Summary comorbidity indices assign prespecified, static weights to diagnosis groups, effectively assuming that a given condition contributes similarly regardless of the broader diagnostic context. By contrast, in the present model, the contribution of a diagnosis could vary according to the full set of co-occurring diagnoses, which is more consistent with how risk is often understood clinically. We did not directly test this mechanism, so it should be interpreted as a hypothesis supported by the attribution patterns rather than as a proven explanation for the observed performance differences. Nevertheless, this dynamic view of diagnosis contribution may help explain why retaining the full diagnosis-code context improved prediction beyond summary comorbidity scores. As with other attribution methods, these explanations improve transparency but do not establish causality.
### Clinical and policy implications
These findings have two potential implications. First, in discharge-facing workflows, a claims-compatible model could be evaluated in read-only settings to identify patients who may warrant closer follow-up, medication reconciliation, or transitional-care outreach [@hansen2011; @coleman2006]. Second, in research and quality measurement, more granular use of diagnosis data may improve outcome prediction when summary comorbidity indices underrepresent diagnostic complexity [@joynt2013; @desai2016; @zuckerman2017]. This tool is intended for clinical prioritization and equitable quality measurement, not for coverage denial or utilization gatekeeping [@obermeyer2019].
Because demographic and socioeconomic factors are known to influence postdischarge outcomes, we assessed their incremental contribution beyond diagnosis patterns using ablation [@kind2014; @joynt2011]. Removing age, sex, payer, and neighborhood income produced minimal changes in performance, suggesting that much of the predictive signal available to this model was already captured by diagnosis patterns. This finding should not be interpreted to mean that demographic or socioeconomic factors are unimportant. Rather, within this claims-based framework, coded diagnoses may already capture part of the risk signal associated with demographic and socioeconomic differences, whether through differences in disease burden, comorbidity clustering, or patterns of healthcare use.
The public read-only calculator is intended for research and demonstration rather than clinical deployment. Future work should focus on external validation, prospective evaluation in read-only workflows, monitoring for coding and case-mix drift, and recalibration when needed [@davis2020; @collins2024; @collins2015].
### Limitations
This study has several limitations. First, the NRD captures deaths only during inpatient encounters; therefore, the mortality outcome reflects postdischarge in-hospital mortality rather than all-cause 30-day mortality. Second, claims data are subject to coding error and variation and do not directly capture functional status, physiologic severity, or many social risk factors. Third, although temporal validation in later NRD years reduces optimism, it is not a substitute for external validation, and performance may differ in other health systems or data sources with different coding practices, case mix, and discharge workflows. Fourth, this study evaluated predictive performance rather than downstream improvement in confounding control, hospital profiling, or other risk-adjustment applications; thus, better discrimination does not by itself establish superior risk adjustment, and because the model was trained specifically for 30-day unplanned readmission and postdischarge in-hospital mortality, performance may not generalize to other outcomes without separate validation. Fifth, Charlson and Elixhauser indices were included as benchmark comparators because of their widespread use in claims-based analyses, but they were not originally developed for these specific outcomes; accordingly, these comparisons should be interpreted as benchmarking rather than definitive head-to-head testing. Finally, attribution methods may improve transparency but do not establish causality [@rudin2019].
## Conclusions
A deep learning model using ICD-10-CM diagnosis codes improved prediction of 30-day unplanned readmission and 30-day postdischarge mortality compared with Charlson and Elixhauser comorbidity-index models in the Nationwide Readmissions Database. Prospective validation, drift monitoring, and attention to intended use will be essential before implementation for clinical decision support or policy applications.
## Data Sharing Statement
The study used de-identified data from the Healthcare Cost and Utilization Project (HCUP) Nationwide Readmissions Database under a data-use agreement. Data are available from HCUP to qualified researchers.
## Code Availability
The analytic code (including the non-elective readmission implementation, model training, and evaluation) will be made publicly available at publication in a GitHub repository (<https://github.com/Rice-wxl/icd-10-embedding>), with a versioned release tag/commit to support reproducibility. HCUP NRD data cannot be shared by the authors under the data-use agreement.
## Conflict of Interest Disclosures
The authors report no conflicts of interest related to this work.
## Tables
### Table 1: Performance metrics for the ICD model vs. CCI and ECI {#table-1}
Performance metrics for the ICD model vs. CCI and ECI for 30-day readmission (a) and 30-day postdischarge mortality (b) in the temporal test evaluation subsample.
**(a) 30-day readmission**
| Methods | AUC-ROC | Accuracy | Precision | Recall | $F_1$ | $F_2$ |
|:-------------------------------|:-----------------------------|---------:|-----------:|-----------:|-----------:|-----------:|
| ICD Model (Threshold: 0.5022) | **0.7496** [0.7488, 0.7504] | 0.5892 | **0.1881** | **0.8006** | **0.3046** | **0.4848** |
| CCI | 0.6553 [0.6544, 0.6562] | **0.6962** | 0.1844 | 0.4973 | 0.2690 | 0.3713 |
| CCI Age-Adjusted | 0.6444 [0.6435, 0.6453] | 0.6479 | 0.1673 | 0.5360 | 0.2550 | 0.3720 |
| ECI | 0.6363 [0.6353, 0.6372] | 0.5708 | 0.1598 | 0.6622 | 0.2575 | 0.4066 |
**(b) 30-day postdischarge mortality**
| Methods | AUC-ROC | Accuracy | Precision | Recall | $F_1$ | $F_2$ |
|:-------------------------------|:-----------------------------|-----------:|-----------:|-----------:|-----------:|-----------:|
| ICD Model (Threshold: 0.4644) | **0.8557** [0.8532, 0.8581] | 0.6848 | **0.0111** | **0.8756** | **0.0220** | **0.0530** |
| CCI | 0.7621 [0.7585, 0.7657] | 0.6987 | 0.0093 | 0.6963 | 0.0184 | 0.0442 |
| CCI Age-Adjusted | 0.7844 [0.7813, 0.7874] | 0.7352 | 0.0102 | 0.6700 | 0.0201 | 0.0480 |
| ECI | 0.6414 [0.6358, 0.6469] | **0.7763** | 0.0089 | 0.4915 | 0.0175 | 0.0415 |
*Primary performance evaluation used a prespecified stratified random subsample of eligible 2021--2022 discharges. Classification thresholds were selected on the validation set by maximizing the Youden index and then applied unchanged to the temporal test evaluation subsample.*
## Figures
::: {.annotation .annotation--static}
**Figure 1 --- [placeholder]** Flow chart of discharge records in NRD for training, validation, and testing cohorts. Primary performance evaluation used a prespecified stratified random subsample of eligible 2021--2022 discharges. This temporal-test evaluation subsample was not downsampled. Classification thresholds were selected on the validation set by maximizing the Youden index and then applied unchanged to the temporal test subsample.
:::
**Figure 2.** Receiver operating characteristic (ROC) curves and area under the curve (AUC) for 30-day readmission and postdischarge mortality in the temporal test evaluation subsample. Each curve depicts the trade-off between sensitivity and specificity across different thresholds.
![(a) 30-day readmission](figures/fig2a-roc-readmission.png)
![(b) 30-day postdischarge mortality](figures/fig2b-roc-mortality.png)
**Figure 3.** Calibration curves on temporal test evaluation subsample for 30-day readmission and postdischarge mortality.
![(a) 30-day readmission](figures/fig3a-calibration-readmission.png)
![(b) 30-day postdischarge mortality](figures/fig3b-calibration-mortality.png)
**Figure 4.** Top influential ICD codes for model prediction. Mean Integrated Gradients attribution per occurrence (positive values indicate higher predicted risk; negative values indicate lower predicted risk).
![(a) 30-day readmission](figures/fig4a-ig-readmission.png)
![(b) 30-day postdischarge mortality](figures/fig4b-ig-mortality.png)
## Supplement
### eMethods
1. **Training and Hyperparameter Tuning**
2. **Temporal Test Set Construction and Threshold Selection**
3. **Integrated Gradients for Code-Level Attribution**
4. **Ablation Analyses**
1. Transformer Blocks
2. Deep Sets vs Permutation-Variant Flattening Comparator
3. Demographic and Socioeconomic Inputs
### eTables
1. **Hyperparameter Configurations Used in Models**
2. **Performance Comparison for 30-Day Mortality Including Index-Hospital Death**
3. **Ablation Study Results**
- (A) Addition of Transformer Blocks
- (B) Replacement of Deep Sets With Flattening Comparator
- (C) Removal of Demographic and Socioeconomic Inputs (ICD-Only)
### eFigures
1. **Permutation-invariant ICD Embedding Model**
2. **Top ICD-10-CM Codes by Integrated Gradients for 30-Day Mortality Including Index-Hospital Death**
3. **Calibration Reliability Plots for Temporal Test Set Predictions**
- 30-Day Readmission
- 30-Day Postdischarge Mortality
4. **Web Calculator Interface Examples**
### eMethods 1. Training and Hyperparameter Tuning
Models were trained using batch size 128 for 10 epochs with the Adam optimizer at a learning rate of 2e-5. Early stopping was applied with patience of 2 epochs, retaining the checkpoint with the best validation performance. To address outcome imbalance, we randomly downsampled majority-class encounters in the training set to achieve a target case-control ratio of 1:1 (validation data and temporal test evaluation subsample were not downsampled). Because majority-class downsampling changes the effective outcome prevalence and can bias predicted probabilities, we performed adjustment with the original downsampling ratio of the training set and use the readjusted model outputs in all metrics/plots and for the web calculator. Random seeds for the train/validation split, downsampling, and model initialization were set to ensure reproducibility.
Hyperparameters were tuned via random search (32 trials per outcome). eTable 1 reports the selected configurations. Hyperparameter definitions: $d_{\text{embed}}$ (ICD embedding dimension); $d_{\text{hidden}}$ (Deep Sets hidden dimension); $r_{\text{deepset}} \times d_{\text{hidden}}$ (Deep Sets output dimension); $n_{\text{encode}}$ and $n_{\text{decode}}$ (numbers of Deep Sets encoding/decoding layers); $d_{\text{demo}}$ (first-layer width of the demographic/socioeconomic MLP; second layer set to $d_{\text{demo}}/2$); $d_{\text{mlp}}$ (first-layer width of the predictor MLP, halving each layer to a minimum of 32 over 4 layers); $r_{\text{dropout}}$ (dropout rate). Model selection prioritized validation AUROC; recall-weighted metrics (including $F_2$) were used as secondary criteria.
### eMethods 2. Temporal Test Set Construction and Threshold Selection
Temporal testing used eligible 2021--2022 data. To enable computationally feasible evaluation while preserving outcome prevalence, we created the temporal test evaluation subsample by stratified random sampling of the combined eligible 2021--2022 cohort, sampling 10% of outcome-positive and 10% of outcome-negative discharges for each outcome. This yielded approximately 3.2 million discharges for primary performance evaluation. Interpretability analyses used the same temporal test evaluation subsample.
Binary classification thresholds for each model were selected on the validation set by maximizing the Youden index (sensitivity + specificity 1) and then applied unchanged to the temporal test evaluation subsample.
### eMethods 3. Integrated Gradients for Code-Level Attribution
Integrated Gradients (IG) was used to quantify code-level influence on model predictions. The baseline input was defined as a neutral input corresponding to an empty diagnosis list (ie, no diagnosis codes). The straight-line interpolation path from baseline to the observed input was discretized into 32 steps. At each step, gradients of the model logit were computed with respect to diagnosis embeddings; gradients were accumulated across steps and summed across embedding dimensions to yield a scalar attribution per code occurrence. Attribution values retained sign, with positive values indicating higher predicted risk and negative values indicating lower predicted risk. To reduce instability from rare codes, ICD-10-CM codes with fewer than 50 total occurrences in the temporal test evaluation subsample were excluded from ranked summaries. For each outcome, we reported the 10 codes with the largest mean positive attributions and the 10 codes with the largest mean negative attributions.
### eMethods 4. Ablation Analyses
#### eMethods 4.1 Transformer blocks
To evaluate whether attention-based contextualization improves performance, we added three multi-head transformer blocks operating over individual ICD embeddings. Each block followed a standard transformer design with multi-head attention, residual connections, normalization, and feed-forward sublayers. We used 3 attention heads, dropout 0.3, embedding dimension $d_{\text{embed}}$, and feed-forward dimension $4 \times d_{\text{embed}}$.
Comparative results are shown in eTable 3, Panel A. Across both outcomes, transformer blocks did not materially improve AUROC and $F_2$ score relative to the base model. Reported $P$ values for AUROC differences were $P < .001$ for readmission and $P = 0.57$ for postdischarge mortality.
#### eMethods 4.2 Deep Sets vs permutation-variant flattening comparator
To test whether permutation invariance via Deep Sets reduced predictive performance, we compared the base model with a permutation-variant alternative: a flattening layer that converts the 2-dimensional ICD embedding matrix into a 1-dimensional vector, followed by two MLP layers with $d_{\text{hidden}}$ and $r_{\text{deepset}} \times d_{\text{hidden}}$ units to mirror the Deep Sets hidden/output sizes.
Results are shown in eTable 3, Panel B. The base Deep Sets models outperformed the flattening comparators on AUROC and $F_2$ score. Reported $P$ values for AUROC differences were $P < .001$ for readmission and $P = 0.014$ for postdischarge mortality.
#### eMethods 4.3 Demographic and socioeconomic inputs
To evaluate the incremental value of non-diagnosis covariates, we removed the 2-layer demographic/socioeconomic MLP and trained ICD-only variants.
Results are shown in eTable 3, Panel C. ICD-only variants had slightly worse AUROC and $F_2$ score. Reported $P$ values for AUROC differences were $P = 0.020$ for readmission and $P < .001$ for postdischarge mortality.
### eTable 1: Hyperparameter configurations used in models
| Outcome | $d_{\text{embed}}$ | $d_{\text{hidden}}$ | $r_{\text{deepset}}$ | $n_{\text{encode}}$ | $n_{\text{decode}}$ | $d_{\text{demo}}$ | $d_{\text{mlp}}$ | $r_{\text{dropout}}$ |
|:--------------------------------------------------|---:|----:|----:|---:|---:|---:|----:|----:|
| 30-day readmission | 32 | 416 | 0.5 | 1 | 3 | 64 | 480 | 0.1 |
| 30-day postdischarge mortality | 64 | 320 | 0.6 | 2 | 1 | 64 | 384 | 0.1 |
| 30-day mortality including index-hospital death | 64 | 416 | 0.8 | 3 | 3 | 64 | 448 | 0.4 |
*The model configurations are determined through hyperparameter tuning for each outcome variable, respectively. All models share the same batch size and learning rate.*
### eTable 2. Performance comparison for 30-day mortality including index-hospital death
| Methods | AUC-ROC | Precision | Recall | $F_1$ | $F_2$ |
|:-----------------|:-------------------------|----------:|-------:|-------:|-------:|
| ICD Model | 0.9651 [0.9647, 0.9656] | 0.2107 | 0.9165 | 0.3427 | 0.5489 |
| CCI | 0.7217 [0.7203, 0.7231] | 0.0663 | 0.6270 | 0.1200 | 0.2331 |
| CCI Age-Adjusted | 0.7501 [0.7489, 0.7513] | 0.0724 | 0.6043 | 0.1294 | 0.2448 |
| ECI | 0.6158 [0.6139, 0.6177] | 0.0637 | 0.4427 | 0.1114 | 0.2022 |
### eTable 3. Ablation study results
**(A) Addition of transformer blocks**
| Outcome Variable | Model Variant | AUC-ROC | AUC-ROC CI | Precision | Recall | $F_1$ | $F_2$ |
|:-------------------------------|:---------------------|--------:|:-----------------|----------:|-------:|-------:|-------:|
| 30-day readmission | Full Model | 0.7496 | [0.7488, 0.7504] | 0.1881 | 0.8006 | 0.3046 | 0.4848 |
| | 3 Transformer Blocks | 0.7472 | [0.7464, 0.7479] | 0.1974 | 0.7565 | 0.3131 | 0.4829 |
| 30-day postdischarge mortality | Full Model | 0.8557 | [0.8532, 0.8581] | 0.0111 | 0.8756 | 0.0220 | 0.0530 |
| | 3 Transformer Blocks | 0.8547 | [0.8523, 0.8572] | 0.0114 | 0.8662 | 0.0225 | 0.0542 |
**(B) Replacement of Deep Sets with flattening comparator**
| Outcome Variable | Model Variant | AUC-ROC | AUC-ROC CI | Precision | Recall | $F_1$ | $F_2$ |
|:-------------------------------|:----------------|--------:|:-----------------|----------:|-------:|-------:|-------:|
| 30-day readmission | Full Model | 0.7496 | [0.7488, 0.7504] | 0.1881 | 0.8006 | 0.3046 | 0.4848 |
| | Without DeepSet | 0.7474 | [0.7466, 0.7482] | 0.1933 | 0.7724 | 0.3092 | 0.4829 |
| 30-day postdischarge mortality | Full Model | 0.8557 | [0.8532, 0.8581] | 0.0111 | 0.8756 | 0.0220 | 0.0530 |
| | Without DeepSet | 0.8513 | [0.8488, 0.8538] | 0.0108 | 0.8777 | 0.0214 | 0.0515 |
**(C) Removal of demographic and socioeconomic inputs (ICD-only)**
| Outcome Variable | Model Variant | AUC-ROC | AUC-ROC CI | Precision | Recall | $F_1$ | $F_2$ |
|:-------------------------------|:----------------|--------:|:-----------------|----------:|-------:|-------:|-------:|
| 30-day readmission | Full Model | 0.7496 | [0.7488, 0.7504] | 0.1881 | 0.8006 | 0.3046 | 0.4848 |
| | ICD Inputs Only | 0.7483 | [0.7475, 0.7490] | 0.1907 | 0.7868 | 0.3070 | 0.4842 |
| 30-day postdischarge mortality | Full Model | 0.8557 | [0.8532, 0.8581] | 0.0111 | 0.8756 | 0.0220 | 0.0530 |
| | ICD Inputs Only | 0.8483 | [0.8457, 0.8509] | 0.0110 | 0.8627 | 0.0218 | 0.0525 |
### eFigure 1: Permutation-invariant ICD Embedding Model
![Given a patient's ICD-10-CM diagnosis codes together with demographic and socioeconomic features, the model estimates risk of 30-day readmission and 30-day postdischarge mortality. Diagnosis codes are embedded and aggregated with a Deep Sets module, then combined with other inputs to generate the final prediction.](figures/efig1-model-architecture.png)
### eFigure 2: Top ICD-10-CM codes by Integrated Gradients for 30-day mortality including index-hospital death
![Top ICD-10-CM codes by Integrated Gradients for 30-day mortality including index-hospital death.](figures/efig2-ig-mortality-with-inhospital.png)
### eFigure 3. Calibration reliability plots for temporal test evaluation subsample predictions
*(A) 30-day unplanned readmission and (B) 30-day postdischarge mortality.*
::: {.annotation .annotation--static}
**eFigure 3 --- [placeholder]** Calibration reliability plots for temporal test evaluation subsample predictions.
:::
### eFigure 4. Web Calculator Interface Examples
*(A) With demographics and (B) Without demographics.*
**(A)**
![Web calculator interface with demographics.](figures/efig4a-calculator-with-demographics.png)
**(B)**
![Web calculator interface without demographics.](figures/efig4b-calculator-without-demographics.png)

359
data/bci-paper.bib Normal file
View File

@ -0,0 +1,359 @@
% bci-paper.bib — Beyond Comorbidity Indices
% BibLaTeX format. References for the "Deep Learning of Diagnosis Codes
% for Readmission and Postdischarge Mortality Prediction" manuscript.
% Numbered order matches the docx reference list.
@misc{cms_hrrp,
author = {{Centers for Medicare \& Medicaid Services}},
title = {Hospital Readmissions Reduction Program ({HRRP})},
url = {https://www.cms.gov/medicare/payment/prospective-payment-systems/acute-inpatient-pps/hospital-readmissions-reduction-program-hrrp}
}
@article{charlson1987,
author = {Charlson, M. E. and Pompei, P. and Ales, K. L. and MacKenzie, C. R.},
title = {A new method of classifying prognostic comorbidity in longitudinal studies: development and validation},
journal = {Journal of Chronic Diseases},
year = {1987},
volume = {40},
number = {5},
pages = {373--383}
}
@article{elixhauser1998,
author = {Elixhauser, A. and Steiner, C. and Harris, D. R. and Coffey, R. M.},
title = {Comorbidity measures for use with administrative data},
journal = {Medical Care},
year = {1998},
volume = {36},
number = {1},
pages = {8--27}
}
@article{deschepper2020,
author = {Deschepper, Mathieu and Eeckloo, Kristof and Vogelaers, Dominique and Waegeman, Willem},
title = {Using structured pathology data to predict hospital-wide mortality at admission},
journal = {PLoS One},
year = {2020},
volume = {15},
number = {6},
pages = {e0235117}
}
@article{lelay2022,
author = {Le Lay, Jonathan and Martin, Sylvain and Mosnier, Arnaud and others},
title = {Prediction of hospital readmission of multimorbid patients using machine learning models},
journal = {PLoS One},
year = {2022},
volume = {17},
number = {12},
pages = {e0279433}
}
@article{qiao2022,
author = {Qiao, Edmund M. and Deng, Jie and Kluger, Harvey M. and Yu, James B. and Gross, Cary P.},
title = {Evaluating High-Dimensional Machine Learning Models to Predict Hospital Mortality Among Older Patients With Cancer},
journal = {JCO Clinical Cancer Informatics},
year = {2022},
volume = {6},
pages = {e2100186}
}
@article{davis2022,
author = {Davis, Steve and Borah, Michael F. and Hansen, Justin and Malin, Bradley and Robinson, Jonathan and Turchin, Alexander},
title = {Effective hospital readmission prediction models using machine-learned features},
journal = {BMC Health Services Research},
year = {2022},
volume = {22},
number = {1},
pages = {1415}
}
@article{harerimana2021,
author = {Harerimana, Gaspard and Kim, Jong Wook and Jang, Beakcheol},
title = {A deep attention model to forecast the Length Of Stay and the in-hospital mortality right on admission from {ICD} codes and demographic data},
journal = {Journal of Biomedical Informatics},
year = {2021},
volume = {118},
pages = {103778}
}
@article{matsui2022,
author = {Matsui, Hiroki and Yasunaga, Hideo and Fushimi, Kiyohide and Homma, Yuichi},
title = {Development of Deep Learning Models for Predicting In-Hospital Mortality Using an Administrative Claims Database: Retrospective Cohort Study},
journal = {JMIR Medical Informatics},
year = {2022},
volume = {10},
number = {2},
pages = {e27936}
}
@article{nguyen2017,
author = {Nguyen, Phuoc and Tran, Truyen and Wickramasinghe, Nilmini and Venkatesh, Svetha},
title = {{Deepr}: A Convolutional Net for Medical Records},
journal = {IEEE Journal of Biomedical and Health Informatics},
year = {2017},
volume = {21},
number = {1},
pages = {22--30}
}
@misc{ahrq_elixhauser,
author = {{Agency for Healthcare Research and Quality}},
title = {Elixhauser Comorbidity Software Refined for {ICD-10-CM}},
url = {https://www.hcup-us.ahrq.gov/toolssoftware/comorbidityicd10/comorbidity_icd10.jsp}
}
@article{fernando2019,
author = {Fernando, Dulith T. and Berecki-Gisolf, Janneke and Newstead, Stuart and Ansari, Zul},
title = {Effect of comorbidity on injury outcomes: a review of existing indices},
journal = {Annals of Epidemiology},
year = {2019},
volume = {36},
pages = {5--14}
}
@article{quan2005,
author = {Quan, Hude and Sundararajan, Vijaya and Halfon, Patricia and others},
title = {Coding algorithms for defining comorbidities in {ICD-9-CM} and {ICD-10} administrative data},
journal = {Medical Care},
year = {2005},
volume = {43},
number = {11},
pages = {1130--1139}
}
@article{deyo1992,
author = {Deyo, Richard A. and Cherkin, Daniel C. and Ciol, Marcia A.},
title = {Adapting a clinical comorbidity index for use with {ICD-9-CM} administrative databases},
journal = {Journal of Clinical Epidemiology},
year = {1992},
volume = {45},
number = {6},
pages = {613--619}
}
@article{quan2011,
author = {Quan, Hude and Li, Bing and Couris, Chantal M. and others},
title = {Updating and validating the {Charlson} comorbidity index and score for risk adjustment in hospital discharge abstracts using data from 6 countries},
journal = {American Journal of Epidemiology},
year = {2011},
volume = {173},
number = {6},
pages = {676--682}
}
@misc{stagg2006,
author = {Stagg, Vince},
title = {{CHARLSON}: {Stata} module to calculate {Charlson} index of comorbidity},
year = {2006},
publisher = {Boston College Department of Economics}
}
@inproceedings{zaheer2017,
author = {Zaheer, Manzil and Kottur, Satwik and Ravanbakhsh, Siamak and Poczos, Barnabas and Salakhutdinov, Ruslan and Smola, Alexander},
title = {Deep Sets},
booktitle = {Advances in Neural Information Processing Systems},
year = {2017},
volume = {30}
}
@inproceedings{pozzolo2015,
author = {Dal Pozzolo, Andrea and Caelen, Olivier and Johnson, Reid A. and Bontempi, Gianluca},
title = {Calibrating Probability with Undersampling for Unbalanced Classification},
booktitle = {2015 {IEEE} Symposium Series on Computational Intelligence},
year = {2015},
pages = {159--166}
}
@inproceedings{abadi2016,
author = {Abadi, Mart{\'i}n and Barham, Paul and Chen, Jianmin and Chen, Zhifeng and Davis, Andy and Dean, Jeffrey and Devin, Matthieu and Ghemawat, Sanjay and Irving, Geoffrey and Isard, Michael and Kudlur, Manjunath and Levenberg, Josh and Monga, Rajat and Moore, Sherry and Murray, Derek G. and Steiner, Benoit and Tucker, Paul and Vasudevan, Vijay and Warden, Pete and Wicke, Martin and Yu, Yuan and Zheng, Xiaoqiang},
title = {{TensorFlow}: A System for Large-Scale Machine Learning},
booktitle = {Proceedings of the 12th {USENIX} Symposium on Operating Systems Design and Implementation},
year = {2016},
pages = {265--283}
}
@article{youden1950,
author = {Youden, W. J.},
title = {Index for rating diagnostic tests},
journal = {Cancer},
year = {1950},
volume = {3},
number = {1},
pages = {32--35}
}
@article{delong1988,
author = {DeLong, Elizabeth R. and DeLong, David M. and Clarke-Pearson, Daniel L.},
title = {Comparing the areas under two or more correlated receiver operating characteristic curves: a nonparametric approach},
journal = {Biometrics},
year = {1988},
volume = {44},
number = {3},
pages = {837--845}
}
@inproceedings{sundararajan2017,
author = {Sundararajan, Mukund and Taly, Ankur and Yan, Qiqi},
title = {Axiomatic Attribution for Deep Networks},
booktitle = {Proceedings of the 34th International Conference on Machine Learning},
year = {2017},
pages = {3319--3328}
}
@article{placido2023,
author = {Placido, Davide and Yuan, Bo and Hjaltelin, Jessica X. and Zheng, Chunlei and Haue, Amalie D. and Chmura, Piotr J. and Yuan, Chen and Kim, Jihye and Umeton, Renato and Antell, Gregory and Chowdhury, Alexander and Franz, Alexandra and Brais, Lauren and Andrews, Elizabeth and Marks, Debora S. and Regev, Aviv and Ayandeh, Siamack and Brophy, Mary T. and Do, Nhan V. and Kraft, Peter and Wolpin, Brian M. and Rosenthal, Michael H. and Fillmore, Nathanael R. and Brunak, S{\o}ren and Sander, Chris},
title = {A deep learning algorithm to predict risk of pancreatic cancer from disease trajectories},
journal = {Nature Medicine},
year = {2023},
volume = {29},
number = {5},
pages = {1113--1122}
}
@article{davis2020,
author = {Davis, Sharon E. and Greevy, Robert A. and Fonnesbeck, Christopher and Lasko, Thomas A. and Walsh, Colin G. and Matheny, Michael E.},
title = {Detection of calibration drift in clinical prediction models to inform model updating},
journal = {Journal of Biomedical Informatics},
year = {2020},
volume = {112},
pages = {103611}
}
@article{collins2024,
author = {Collins, Gary S. and Dhiman, Paula and Ma, Jie and Schlussel, Michael M. and Archer, Lucinda and Van Calster, Ben and Harrell, Frank E. and Martin, Glen P. and Moons, Karel G. M. and van Smeden, Maarten and Sperrin, Matthew and Bullock, Garrett S. and Riley, Richard D.},
title = {Evaluation of clinical prediction models (part 1): from development to external validation},
journal = {BMJ},
year = {2024},
volume = {384},
pages = {e074819}
}
@article{morgan2019,
author = {Morgan, Daniel J. and Bame, Bill and Zimand, Paul and Dooley, Patricia and Thom, Kerri A. and Harris, Anthony D. and Bentzen, S{\o}ren and Ettinger, Walter and Garrett-Ray, Stacy D. and Tracy, J. Kathleen and Liang, Yuanyuan},
title = {Assessment of Machine Learning vs Standard Prediction Rules for Predicting Hospital Readmissions},
journal = {JAMA Network Open},
year = {2019},
volume = {2},
number = {3},
pages = {e190348}
}
@article{beam2018,
author = {Beam, Andrew L. and Kohane, Isaac S.},
title = {Big Data and Machine Learning in Health Care},
journal = {JAMA},
year = {2018},
volume = {319},
number = {13},
pages = {1317--1318}
}
@article{rajkomar2018,
author = {Rajkomar, Alvin and Oren, Eyal and Chen, Kai and Dai, Andrew M. and Hajaj, Nissan and Hardt, Michaela and Liu, Peter J. and Liu, Xiaobing and Marcus, Jake and Sun, Mimi and Sundberg, Patrik and Yee, Hector and Zhang, Kun and Zhang, Yi and Flores, Gerardo and Duggan, Gavin E. and Irvine, Jamie and Le, Quoc and Litsch, Kurt and Mossin, Alexander and Tansuwan, Justin and Wang, De and Wexler, James and Wilson, Jimbo and Ludwig, Dana and Volchenboum, Samuel L. and Chou, Katherine and Pearson, Michael and Madabushi, Srinivasan and Shah, Nigam H. and Butte, Atul J. and Howell, Michael D. and Cui, Claire and Corrado, Greg S. and Dean, Jeffrey},
title = {Scalable and accurate deep learning with electronic health records},
journal = {npj Digital Medicine},
year = {2018},
volume = {1},
pages = {18}
}
@article{rudin2019,
author = {Rudin, Cynthia},
title = {Stop Explaining Black Box Machine Learning Models for High Stakes Decisions and Use Interpretable Models Instead},
journal = {Nature Machine Intelligence},
year = {2019},
volume = {1},
number = {5},
pages = {206--215}
}
@article{hansen2011,
author = {Hansen, Luke O. and Young, Robert S. and Hinami, Keiki and Leung, Albert and Williams, Mark V.},
title = {Interventions to reduce 30-day rehospitalization: a systematic review},
journal = {Annals of Internal Medicine},
year = {2011},
volume = {155},
number = {8},
pages = {520--528}
}
@article{coleman2006,
author = {Coleman, Eric A. and Parry, Carla and Chalmers, Sandra and Min, Sung-joon},
title = {The care transitions intervention: results of a randomized controlled trial},
journal = {Archives of Internal Medicine},
year = {2006},
volume = {166},
number = {17},
pages = {1822--1828}
}
@article{joynt2013,
author = {Joynt, Karen E. and Jha, Ashish K.},
title = {Characteristics of hospitals receiving penalties under the {Hospital Readmissions Reduction Program}},
journal = {JAMA},
year = {2013},
volume = {309},
number = {4},
pages = {342--343}
}
@article{desai2016,
author = {Desai, Nihar R. and Ross, Joseph S. and Kwon, Jin Yul and others},
title = {Association Between Hospital Penalty Status Under the {Hospital Readmission Reduction Program} and Readmission Rates for Target and Nontarget Conditions},
journal = {JAMA},
year = {2016},
volume = {316},
number = {24},
pages = {2647--2656}
}
@article{zuckerman2017,
author = {Zuckerman, Rachael B. and Sheingold, Steven H. and Orav, E. John and Ruhter, Jordan and Epstein, Arnold M.},
title = {Effect of a Hospital-wide Measure on the Readmissions Reduction Program},
journal = {New England Journal of Medicine},
year = {2017},
volume = {377},
number = {16},
pages = {1551--1558}
}
@article{obermeyer2019,
author = {Obermeyer, Ziad and Powers, Brian and Vogeli, Christine and Mullainathan, Sendhil},
title = {Dissecting racial bias in an algorithm used to manage the health of populations},
journal = {Science},
year = {2019},
volume = {366},
number = {6464},
pages = {447--453}
}
@article{kind2014,
author = {Kind, Amy J. H. and Jencks, Stephen and Brock, Jim and others},
title = {Neighborhood socioeconomic disadvantage and 30-day rehospitalization: a retrospective cohort study},
journal = {Annals of Internal Medicine},
year = {2014},
volume = {161},
number = {11},
pages = {765--774}
}
@article{joynt2011,
author = {Joynt, Karen E. and Orav, E. John and Jha, Ashish K.},
title = {Thirty-day readmission rates for {Medicare} beneficiaries by race and site of care},
journal = {JAMA},
year = {2011},
volume = {305},
number = {7},
pages = {675--681}
}
@article{collins2015,
author = {Collins, Gary S. and Reitsma, Johannes B. and Altman, Douglas G. and Moons, Karel G. M.},
title = {Transparent Reporting of a multivariable prediction model for Individual Prognosis or Diagnosis ({TRIPOD}): the {TRIPOD} statement},
journal = {Annals of Internal Medicine},
year = {2015},
volume = {162},
number = {1},
pages = {55--63}
}

View File

@ -20,9 +20,9 @@ executable site
SimilarLinks SimilarLinks
Compilers Compilers
Contexts Contexts
Patterns
Stats Stats
Stability Stability
Metadata
Tags Tags
Pagination Pagination
Citations Citations
@ -33,6 +33,7 @@ executable site
Filters.Smallcaps Filters.Smallcaps
Filters.Wikilinks Filters.Wikilinks
Filters.Transclusion Filters.Transclusion
Filters.EmbedPdf
Filters.Links Filters.Links
Filters.Math Filters.Math
Filters.Code Filters.Code
@ -56,9 +57,10 @@ executable site
bytestring >= 0.11 && < 0.13, bytestring >= 0.11 && < 0.13,
process >= 1.6 && < 1.7, process >= 1.6 && < 1.7,
data-default >= 0.7 && < 0.8, data-default >= 0.7 && < 0.8,
mtl >= 2.3 && < 2.4 mtl >= 2.3 && < 2.4,
blaze-html >= 0.9 && < 0.10,
blaze-markup >= 0.8 && < 0.9
default-language: GHC2021 default-language: GHC2021
ghc-options: ghc-options:
-threaded -threaded
-Wall -Wall
-Wno-unused-imports

View File

@ -1,173 +0,0 @@
\relax
\providecommand\hyper@newdestlabel[2]{}
\providecommand\HyField@AuxAddToFields[1]{}
\providecommand\HyField@AuxAddToCoFields[2]{}
\citation{fips203,fips204,fips205}
\citation{bettini2024}
\citation{kyber-avx2}
\citation{fips203}
\citation{ntt-survey}
\@writefile{toc}{\contentsline {section}{Abstract}{1}{section*.1}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}{section.1}\protected@file@percent }
\newlabel{sec:intro}{{1}{1}{Introduction}{section.1}{}}
\@writefile{toc}{\contentsline {section}{\numberline {2}Background}{1}{section.2}\protected@file@percent }
\newlabel{sec:background}{{2}{1}{Background}{section.2}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}ML-KEM and the Number Theoretic Transform}{1}{subsection.2.1}\protected@file@percent }
\citation{kyber-avx2}
\citation{papi}
\citation{rapl}
\citation{kyber-avx2}
\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}AVX2 SIMD on x86-64}{2}{subsection.2.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Compilation Variants}{2}{subsection.2.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Hardware Performance Counters and Energy}{2}{subsection.2.4}\protected@file@percent }
\newlabel{sec:bg:papi}{{2.4}{2}{Hardware Performance Counters and Energy}{subsection.2.4}{}}
\@writefile{tdo}{\contentsline {todo}{Phase 2: Expand with PAPI and RAPL background once data is collected.}{2}{section*.6}\protected@file@percent }
\pgfsyspdfmark {pgfid1}{20915651}{45096352}
\pgfsyspdfmark {pgfid4}{38210436}{45099302}
\pgfsyspdfmark {pgfid5}{38980483}{44906577}
\@writefile{toc}{\contentsline {section}{\numberline {3}Methodology}{2}{section.3}\protected@file@percent }
\newlabel{sec:methodology}{{3}{2}{Methodology}{section.3}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}Implementation Source}{2}{subsection.3.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Compilation Variants}{2}{subsection.3.2}\protected@file@percent }
\newlabel{sec:meth:variants}{{3.2}{2}{Compilation Variants}{subsection.3.2}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Benchmark Harness}{2}{subsection.3.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Hardware Platform}{2}{subsection.3.4}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 2: Hardware counter collection via PAPI.}{3}{section*.7}\protected@file@percent }
\pgfsyspdfmark {pgfid6}{12703613}{37681124}
\pgfsyspdfmark {pgfid7}{2015231}{37684074}
\pgfsyspdfmark {pgfid8}{2785278}{37491349}
\@writefile{toc}{\contentsline {subsection}{\numberline {3.5}Statistical Methodology}{3}{subsection.3.5}\protected@file@percent }
\newlabel{sec:meth:stats}{{3.5}{3}{Statistical Methodology}{subsection.3.5}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {3.6}Energy Measurement}{3}{subsection.3.6}\protected@file@percent }
\newlabel{sec:meth:energy}{{3.6}{3}{Energy Measurement}{subsection.3.6}{}}
\@writefile{tdo}{\contentsline {todo}{Phase 2: Intel RAPL (pkg + DRAM domains), EDP computation, per-operation joules.}{3}{section*.8}\protected@file@percent }
\pgfsyspdfmark {pgfid11}{3538944}{24335452}
\pgfsyspdfmark {pgfid12}{2015231}{24338402}
\pgfsyspdfmark {pgfid13}{2785278}{24145677}
\@writefile{toc}{\contentsline {section}{\numberline {4}Results}{3}{section.4}\protected@file@percent }
\newlabel{sec:results}{{4}{3}{Results}{section.4}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Cycle Count Distributions}{3}{subsection.4.1}\protected@file@percent }
\newlabel{sec:results:distributions}{{4.1}{3}{Cycle Count Distributions}{subsection.4.1}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Speedup Decomposition}{3}{subsection.4.2}\protected@file@percent }
\newlabel{sec:results:decomp}{{4.2}{3}{Speedup Decomposition}{subsection.4.2}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Cycle count distributions for three representative ML-KEM-512 operations. Log $x$-axis. Dashed lines mark medians. Right-skew and outlier structure motivate nonparametric statistics.}}{3}{figure.caption.9}\protected@file@percent }
\providecommand*\caption@xref[2]{\@setref\relax\@undefined{#1}}
\newlabel{fig:distributions}{{1}{3}{Cycle count distributions for three representative \mlkemk {512} operations. Log $x$-axis. Dashed lines mark medians. Right-skew and outlier structure motivate nonparametric statistics}{figure.caption.9}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}Hand-Written SIMD Speedup}{3}{subsection.4.3}\protected@file@percent }
\newlabel{sec:results:simd}{{4.3}{3}{Hand-Written SIMD Speedup}{subsection.4.3}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.4}Statistical Significance}{3}{subsection.4.4}\protected@file@percent }
\newlabel{sec:results:stats}{{4.4}{3}{Statistical Significance}{subsection.4.4}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Cumulative speedup at each optimization stage, normalized to \texttt {refo0}{} (1×). Three bars per operation: \textcolor {colRefnv}{$\blacksquare $}\nonbreakingspace O3 no auto-vec, \textcolor {colRef}{$\blacksquare $}\nonbreakingspace O3 + auto-vec, \textcolor {colAvx}{$\blacksquare $}\nonbreakingspace O3 + hand SIMD (AVX2). Log $y$-axis; 95\% bootstrap CI shown on \texttt {avx2}{} bars. Sorted by \texttt {avx2}{} speedup.}}{4}{figure.caption.10}\protected@file@percent }
\newlabel{fig:decomp}{{2}{4}{Cumulative speedup at each optimization stage, normalized to \varrefo {} (1×). Three bars per operation: \textcolor {colRefnv}{$\blacksquare $}~O3 no auto-vec, \textcolor {colRef}{$\blacksquare $}~O3 + auto-vec, \textcolor {colAvx}{$\blacksquare $}~O3 + hand SIMD (AVX2). Log $y$-axis; 95\% bootstrap CI shown on \varavx {} bars. Sorted by \varavx {} speedup}{figure.caption.10}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Hand-written SIMD speedup (\texttt {ref}{} $\to $ \texttt {avx2}{}) per operation, across all three ML-KEM{} parameter sets. Log $y$-axis. 95\% bootstrap CI error bars (often sub-pixel). Sorted by ML-KEM-512 speedup.}}{4}{figure.caption.11}\protected@file@percent }
\newlabel{fig:handsimd}{{3}{4}{Hand-written SIMD speedup (\varref {} $\to $ \varavx {}) per operation, across all three \mlkem {} parameter sets. Log $y$-axis. 95\% bootstrap CI error bars (often sub-pixel). Sorted by \mlkemk {512} speedup}{figure.caption.11}{}}
\@writefile{lot}{\contentsline {table}{\numberline {1}{\ignorespaces Hand-written SIMD speedup (\texttt {ref}{} $\to $ \texttt {avx2}{}), median ratio with 95\% bootstrap CI. All Cliff's $\delta = +1.000$, $p < 10^{-300}$.}}{4}{table.caption.12}\protected@file@percent }
\newlabel{tab:simd}{{1}{4}{Hand-written SIMD speedup (\varref {} $\to $ \varavx {}), median ratio with 95\% bootstrap CI. All Cliff's $\delta = +1.000$, $p < 10^{-300}$}{table.caption.12}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces Cliff's $\delta $ (\texttt {ref}{} vs.\ \texttt {avx2}{}) for all operations and parameter sets. $\delta = +1$: AVX2 is faster in every observation pair. Nearly all cells are at $+1.000$.}}{4}{figure.caption.13}\protected@file@percent }
\newlabel{fig:cliffs}{{4}{4}{Cliff's $\delta $ (\varref {} vs.\ \varavx {}) for all operations and parameter sets. $\delta = +1$: AVX2 is faster in every observation pair. Nearly all cells are at $+1.000$}{figure.caption.13}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.5}Cross-Parameter Consistency}{4}{subsection.4.5}\protected@file@percent }
\newlabel{sec:results:crossparams}{{4.5}{4}{Cross-Parameter Consistency}{subsection.4.5}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces Per-polynomial operation speedup (\texttt {ref}{} $\to $ \texttt {avx2}{}) across security parameters. Polynomial dimension is 256 for all; variation reflects cache-state differences in the calling context.}}{5}{figure.caption.14}\protected@file@percent }
\newlabel{fig:crossparams}{{5}{5}{Per-polynomial operation speedup (\varref {} $\to $ \varavx {}) across security parameters. Polynomial dimension is 256 for all; variation reflects cache-state differences in the calling context}{figure.caption.14}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.6}Hardware Counter Breakdown}{5}{subsection.4.6}\protected@file@percent }
\newlabel{sec:results:papi}{{4.6}{5}{Hardware Counter Breakdown}{subsection.4.6}{}}
\@writefile{tdo}{\contentsline {todo}{Phase 2: IPC, L1/L2/L3 cache miss rates, branch mispredictions via PAPI. This section will contain bar charts of per-counter values comparing ref and avx2 for each operation, explaining the mechanistic origins of the speedup.}{5}{section*.15}\protected@file@percent }
\pgfsyspdfmark {pgfid264}{3538944}{21389118}
\pgfsyspdfmark {pgfid265}{2015231}{21392068}
\pgfsyspdfmark {pgfid266}{2785278}{21199343}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.7}Energy Efficiency}{5}{subsection.4.7}\protected@file@percent }
\newlabel{sec:results:energy}{{4.7}{5}{Energy Efficiency}{subsection.4.7}{}}
\@writefile{tdo}{\contentsline {todo}{Phase 2: Intel RAPL pkg + DRAM energy readings per operation. EDP (energy-delay product) comparison. Energy per KEM operation.}{5}{section*.16}\protected@file@percent }
\pgfsyspdfmark {pgfid269}{3538944}{19496559}
\pgfsyspdfmark {pgfid270}{2015231}{-14840343}
\pgfsyspdfmark {pgfid271}{2785278}{-15033068}
\@writefile{toc}{\contentsline {section}{\numberline {5}Discussion}{5}{section.5}\protected@file@percent }
\newlabel{sec:discussion}{{5}{5}{Discussion}{section.5}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.1}Why Arithmetic Operations Benefit Most}{5}{subsection.5.1}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 2: Confirm with IPC and port utilisation counters.}{5}{section*.17}\protected@file@percent }
\pgfsyspdfmark {pgfid274}{13184317}{5758368}
\pgfsyspdfmark {pgfid275}{2015231}{-36522418}
\pgfsyspdfmark {pgfid276}{2785278}{-36715143}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.2}Why the Compiler Cannot Auto-Vectorise NTT}{5}{subsection.5.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.3}Why SHAKE Operations Benefit Less}{5}{subsection.5.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.4}Why Noise Sampling Barely Benefits}{5}{subsection.5.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.5}NTT Cache-State Variation Across Parameter Sets}{5}{subsection.5.5}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 2: Verify with L1/L2 miss counters split by scalar vs AVX2.}{5}{section*.18}\protected@file@percent }
\pgfsyspdfmark {pgfid279}{25927376}{9612704}
\pgfsyspdfmark {pgfid282}{38210436}{9615654}
\pgfsyspdfmark {pgfid283}{38980483}{9422929}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.6}Implications for Deployment}{5}{subsection.5.6}\protected@file@percent }
\citation{kyber-avx2}
\citation{pqclean}
\citation{kyber2018}
\citation{fips203}
\citation{pqm4}
\citation{supercop}
\citation{pqm4}
\citation{gueron2014}
\citation{bernstein2006}
\citation{ntt-survey}
\citation{cachetime}
\citation{papi}
\bibstyle{ACM-Reference-Format}
\bibdata{refs}
\bibcite{bernstein2006}{{1}{2006}{{Bernstein}}{{}}}
\bibcite{supercop}{{2}{[n.\,d.]}{{Bernstein and Lange}}{{}}}
\bibcite{cachetime}{{3}{2008}{{Bernstein and Schwabe}}{{}}}
\bibcite{kyber2018}{{4}{2018}{{Bos et~al\mbox {.}}}{{}}}
\bibcite{rapl}{{5}{2010}{{David et~al\mbox {.}}}{{}}}
\bibcite{bettini2024}{{6}{2023}{{Google Security Blog}}{{}}}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.7}Limitations}{6}{subsection.5.7}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{No hardware counter data (Phase\nonbreakingspace 1).}{6}{section*.19}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 2: PAPI counters: IPC, cache miss rates.}{6}{section*.20}\protected@file@percent }
\pgfsyspdfmark {pgfid284}{16379392}{38731168}
\pgfsyspdfmark {pgfid285}{2015231}{38734118}
\pgfsyspdfmark {pgfid286}{2785278}{38541393}
\@writefile{toc}{\contentsline {paragraph}{Single microarchitecture.}{6}{section*.21}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 3: Repeat on AMD Zen, ARM Graviton3, RISC-V.}{6}{section*.22}\protected@file@percent }
\pgfsyspdfmark {pgfid289}{6791818}{34708896}
\pgfsyspdfmark {pgfid290}{2015231}{32453210}
\pgfsyspdfmark {pgfid291}{2785278}{32260485}
\@writefile{toc}{\contentsline {paragraph}{Frequency scaling.}{6}{section*.23}\protected@file@percent }
\@writefile{tdo}{\contentsline {todo}{Phase 2: Characterize frequency during benchmarks; consider RAPL-normalized cycle counts.}{6}{section*.24}\protected@file@percent }
\pgfsyspdfmark {pgfid294}{6161296}{30686624}
\pgfsyspdfmark {pgfid295}{2015231}{24009614}
\pgfsyspdfmark {pgfid296}{2785278}{23816889}
\@writefile{toc}{\contentsline {section}{\numberline {6}Related Work}{6}{section.6}\protected@file@percent }
\newlabel{sec:related}{{6}{6}{Related Work}{section.6}{}}
\@writefile{toc}{\contentsline {paragraph}{ML-KEM / Kyber implementations.}{6}{section*.25}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{PQC benchmarking.}{6}{section*.26}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{SIMD in cryptography.}{6}{section*.27}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{NTT optimization.}{6}{section*.28}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Hardware counter profiling.}{6}{section*.29}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {7}Conclusion}{6}{section.7}\protected@file@percent }
\newlabel{sec:conclusion}{{7}{6}{Conclusion}{section.7}{}}
\@writefile{toc}{\contentsline {paragraph}{Future work.}{6}{section*.30}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Artifact.}{6}{section*.31}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{References}{6}{section*.33}\protected@file@percent }
\bibcite{gueron2014}{{7}{2013}{{Gueron and Krasnov}}{{}}}
\bibcite{papi}{{8}{[n.\,d.]}{{Innovative Computing Laboratory, University of Tennessee}}{{}}}
\bibcite{pqm4}{{9}{[n.\,d.]}{{Kannwischer et~al\mbox {.}}}{{}}}
\bibcite{ntt-survey}{{10}{2016}{{Longa and Naehrig}}{{}}}
\bibcite{fips204}{{11}{2024a}{{National Institute of Standards and Technology}}{{}}}
\bibcite{fips203}{{12}{2024b}{{National Institute of Standards and Technology}}{{}}}
\bibcite{fips205}{{13}{2024c}{{National Institute of Standards and Technology}}{{}}}
\bibcite{pqclean}{{14}{[n.\,d.]}{{PQClean Contributors}}{{}}}
\bibcite{kyber-avx2}{{15}{[n.\,d.]}{{Schwabe and Seiler}}{{}}}
\newlabel{tocindent-1}{0pt}
\newlabel{tocindent0}{0pt}
\newlabel{tocindent1}{6.25499pt}
\newlabel{tocindent2}{10.34999pt}
\newlabel{tocindent3}{0pt}
\newlabel{tocindent4}{0pt}
\newlabel{tocindent5}{0pt}
\@writefile{toc}{\contentsline {section}{\numberline {A}End-to-End KEM Speedup}{7}{appendix.A}\protected@file@percent }
\newlabel{sec:supp:kem}{{A}{7}{End-to-End KEM Speedup}{appendix.A}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces End-to-end KEM speedup (\texttt {ref}{} $\to $ \texttt {avx2}{}) for \texttt {kyber\_keypair}, \texttt {kyber\_encaps}, and \texttt {kyber\_decaps}. Intel Xeon Platinum 8268; 95\% bootstrap CI.}}{7}{figure.caption.34}\protected@file@percent }
\newlabel{fig:kemlevel}{{6}{7}{End-to-end KEM speedup (\varref {} $\to $ \varavx {}) for \op {kyber\_keypair}, \op {kyber\_encaps}, and \op {kyber\_decaps}. Intel Xeon Platinum 8268; 95\% bootstrap CI}{figure.caption.34}{}}
\@writefile{toc}{\contentsline {section}{\numberline {B}Full Operation Set}{7}{appendix.B}\protected@file@percent }
\newlabel{sec:supp:fullops}{{B}{7}{Full Operation Set}{appendix.B}{}}
\@writefile{tdo}{\contentsline {todo}{Full operation speedup table for all 20 benchmarked operations, including \texttt {poly\_compress}, \texttt {poly\_decompress}, \texttt {polyvec\_compress}, \texttt {poly\_tomsg}, and the \texttt {*\_derand} KEM variants.}{7}{section*.35}\protected@file@percent }
\pgfsyspdfmark {pgfid319}{28801187}{27830541}
\newlabel{TotPages}{{7}{7}{}{page.7}{}}
\gdef \@abspage@last{7}

View File

@ -1,237 +0,0 @@
%%% -*-BibTeX-*-
%%% Do NOT edit. File created by BibTeX with style
%%% ACM-Reference-Format-Journals [18-Jan-2012].
\begin{thebibliography}{15}
%%% ====================================================================
%%% NOTE TO THE USER: you can override these defaults by providing
%%% customized versions of any of these macros before the \bibliography
%%% command. Each of them MUST provide its own final punctuation,
%%% except for \shownote{} and \showURL{}. The latter two
%%% do not use final punctuation, in order to avoid confusing it with
%%% the Web address.
%%%
%%% To suppress output of a particular field, define its macro to expand
%%% to an empty string, or better, \unskip, like this:
%%%
%%% \newcommand{\showURL}[1]{\unskip} % LaTeX syntax
%%%
%%% \def \showURL #1{\unskip} % plain TeX syntax
%%%
%%% ====================================================================
\ifx \showCODEN \undefined \def \showCODEN #1{\unskip} \fi
\ifx \showISBNx \undefined \def \showISBNx #1{\unskip} \fi
\ifx \showISBNxiii \undefined \def \showISBNxiii #1{\unskip} \fi
\ifx \showISSN \undefined \def \showISSN #1{\unskip} \fi
\ifx \showLCCN \undefined \def \showLCCN #1{\unskip} \fi
\ifx \shownote \undefined \def \shownote #1{#1} \fi
\ifx \showarticletitle \undefined \def \showarticletitle #1{#1} \fi
\ifx \showURL \undefined \def \showURL {\relax} \fi
% The following commands are used for tagged output and should be
% invisible to TeX
\providecommand\bibfield[2]{#2}
\providecommand\bibinfo[2]{#2}
\providecommand\natexlab[1]{#1}
\providecommand\showeprint[2][]{arXiv:#2}
\bibitem[Bernstein(2006)]%
{bernstein2006}
\bibfield{author}{\bibinfo{person}{Daniel~J. Bernstein}.}
\bibinfo{year}{2006}\natexlab{}.
\newblock \bibinfo{title}{{Curve25519: new Diffie-Hellman speed records}}.
\newblock
\urldef\tempurl%
\url{https://cr.yp.to/ecdh.html}
\showURL{%
\tempurl}
\bibitem[Bernstein and Lange({[n.\,d.]})]%
{supercop}
\bibfield{author}{\bibinfo{person}{Daniel~J. Bernstein} {and}
\bibinfo{person}{Tanja Lange}.} \bibinfo{year}{[n.\,d.]}\natexlab{}.
\newblock \bibinfo{title}{{SUPERCOP: System for Unified Performance Evaluation
Related to Cryptographic Operations and Primitives}}.
\newblock
\urldef\tempurl%
\url{https://bench.cr.yp.to/supercop.html}
\showURL{%
\tempurl}
\bibitem[Bernstein and Schwabe(2008)]%
{cachetime}
\bibfield{author}{\bibinfo{person}{Daniel~J. Bernstein} {and}
\bibinfo{person}{Peter Schwabe}.} \bibinfo{year}{2008}\natexlab{}.
\newblock \bibinfo{title}{{New AES Software Speed Records}}.
\newblock
\urldef\tempurl%
\url{https://cr.yp.to/aes-speed.html}
\showURL{%
\tempurl}
\bibitem[Bos et~al\mbox{.}(2018)]%
{kyber2018}
\bibfield{author}{\bibinfo{person}{Joppe~W. Bos}, \bibinfo{person}{Léo Ducas},
\bibinfo{person}{Eike Kiltz}, \bibinfo{person}{Tancrède Lepoint},
\bibinfo{person}{Vadim Lyubashevsky}, \bibinfo{person}{John~M. Schanck},
\bibinfo{person}{Peter Schwabe}, \bibinfo{person}{Gregor Seiler}, {and}
\bibinfo{person}{Damien Stehlé}.} \bibinfo{year}{2018}\natexlab{}.
\newblock \showarticletitle{{CRYSTALS -- Kyber: A CCA-Secure
Module-Lattice-Based KEM}}. In \bibinfo{booktitle}{\emph{IEEE European
Symposium on Security and Privacy (EuroS\&P)}}. \bibinfo{pages}{353--367}.
\newblock
\href{https://doi.org/10.1109/EuroSP.2018.00032}{doi:\nolinkurl{10.1109/EuroSP.2018.00032}}
\bibitem[David et~al\mbox{.}(2010)]%
{rapl}
\bibfield{author}{\bibinfo{person}{Howard David}, \bibinfo{person}{Eugene
Gorbatov}, \bibinfo{person}{Ulf~R. Hanebutte}, \bibinfo{person}{Rahul
Khanna}, {and} \bibinfo{person}{Christian Le}.}
\bibinfo{year}{2010}\natexlab{}.
\newblock \showarticletitle{{RAPL: Memory Power Estimation and Capping}}. In
\bibinfo{booktitle}{\emph{ISLPED}}.
\newblock
\href{https://doi.org/10.1145/1840845.1840883}{doi:\nolinkurl{10.1145/1840845.1840883}}
\bibitem[{Google Security Blog}(2023)]%
{bettini2024}
\bibfield{author}{\bibinfo{person}{{Google Security Blog}}.}
\bibinfo{year}{2023}\natexlab{}.
\newblock \bibinfo{title}{{Protecting Chrome Traffic with Hybrid Kyber KEM}}.
\newblock
\urldef\tempurl%
\url{https://security.googleblog.com/2023/08/protecting-chrome-traffic-with-hybrid.html}
\showURL{%
\tempurl}
\bibitem[Gueron and Krasnov(2013)]%
{gueron2014}
\bibfield{author}{\bibinfo{person}{Shay Gueron} {and} \bibinfo{person}{Vlad
Krasnov}.} \bibinfo{year}{2013}\natexlab{}.
\newblock \showarticletitle{{Fast Garbling of Circuits Under Standard
Assumptions}}. In \bibinfo{booktitle}{\emph{ACM CCS}}.
\newblock
\newblock
\shownote{See also: Intel white paper on AES-GCM with AVX2}.
\bibitem[{Innovative Computing Laboratory, University of
Tennessee}({[n.\,d.]})]%
{papi}
\bibfield{author}{\bibinfo{person}{{Innovative Computing Laboratory, University
of Tennessee}}.} \bibinfo{year}{[n.\,d.]}\natexlab{}.
\newblock \bibinfo{title}{{PAPI: Performance Application Programming
Interface}}.
\newblock
\urldef\tempurl%
\url{https://icl.utk.edu/papi/}
\showURL{%
\tempurl}
\bibitem[Kannwischer et~al\mbox{.}({[n.\,d.]})]%
{pqm4}
\bibfield{author}{\bibinfo{person}{Matthias~J. Kannwischer},
\bibinfo{person}{Joost Rijneveld}, \bibinfo{person}{Peter Schwabe}, {and}
\bibinfo{person}{Ko Stoffelen}.} \bibinfo{year}{[n.\,d.]}\natexlab{}.
\newblock \bibinfo{title}{{pqm4: Post-quantum crypto library for the ARM
Cortex-M4}}.
\newblock
\urldef\tempurl%
\url{https://github.com/mupq/pqm4}
\showURL{%
\tempurl}
\bibitem[Longa and Naehrig(2016)]%
{ntt-survey}
\bibfield{author}{\bibinfo{person}{Patrick Longa} {and}
\bibinfo{person}{Michael Naehrig}.} \bibinfo{year}{2016}\natexlab{}.
\newblock \showarticletitle{{Speeding Up the Number Theoretic Transform for
Faster Ideal Lattice-Based Cryptography}}. In
\bibinfo{booktitle}{\emph{CANS}}.
\newblock
\href{https://doi.org/10.1007/978-3-319-48965-0_8}{doi:\nolinkurl{10.1007/978-3-319-48965-0_8}}
\bibitem[{National Institute of Standards and Technology}(2024a)]%
{fips204}
\bibfield{author}{\bibinfo{person}{{National Institute of Standards and
Technology}}.} \bibinfo{year}{2024}\natexlab{a}.
\newblock \bibinfo{booktitle}{\emph{{Module-Lattice-Based Digital Signature
Standard}}}.
\newblock \bibinfo{type}{{T}echnical {R}eport} FIPS 204.
\bibinfo{institution}{NIST}.
\newblock
\urldef\tempurl%
\url{https://doi.org/10.6028/NIST.FIPS.204}
\showURL{%
\tempurl}
\bibitem[{National Institute of Standards and Technology}(2024b)]%
{fips203}
\bibfield{author}{\bibinfo{person}{{National Institute of Standards and
Technology}}.} \bibinfo{year}{2024}\natexlab{b}.
\newblock \bibinfo{booktitle}{\emph{{Module-Lattice-Based Key-Encapsulation
Mechanism Standard}}}.
\newblock \bibinfo{type}{{T}echnical {R}eport} FIPS 203.
\bibinfo{institution}{NIST}.
\newblock
\urldef\tempurl%
\url{https://doi.org/10.6028/NIST.FIPS.203}
\showURL{%
\tempurl}
\bibitem[{National Institute of Standards and Technology}(2024c)]%
{fips205}
\bibfield{author}{\bibinfo{person}{{National Institute of Standards and
Technology}}.} \bibinfo{year}{2024}\natexlab{c}.
\newblock \bibinfo{booktitle}{\emph{{Stateless Hash-Based Digital Signature
Standard}}}.
\newblock \bibinfo{type}{{T}echnical {R}eport} FIPS 205.
\bibinfo{institution}{NIST}.
\newblock
\urldef\tempurl%
\url{https://doi.org/10.6028/NIST.FIPS.205}
\showURL{%
\tempurl}
\bibitem[{PQClean Contributors}({[n.\,d.]})]%
{pqclean}
\bibfield{author}{\bibinfo{person}{{PQClean Contributors}}.}
\bibinfo{year}{[n.\,d.]}\natexlab{}.
\newblock \bibinfo{title}{{PQClean: Clean, portable, tested implementations of
post-quantum cryptography}}.
\newblock
\urldef\tempurl%
\url{https://github.com/PQClean/PQClean}
\showURL{%
\tempurl}
\bibitem[Schwabe and Seiler({[n.\,d.]})]%
{kyber-avx2}
\bibfield{author}{\bibinfo{person}{Peter Schwabe} {and} \bibinfo{person}{Gregor
Seiler}.} \bibinfo{year}{[n.\,d.]}\natexlab{}.
\newblock \bibinfo{title}{{Better Bootstrapping in Fully Homomorphic
Encryption}}.
\newblock
\urldef\tempurl%
\url{https://github.com/pq-crystals/kyber}
\showURL{%
\tempurl}
\newblock
\shownote{AVX2 implementation in the pqclean project}.
\end{thebibliography}

View File

@ -1,77 +0,0 @@
This is BibTeX, Version 0.99e (TeX Live 2026/Arch Linux)
Capacity: max_strings=200000, hash_size=200000, hash_prime=170003
The top-level auxiliary file: main.aux
The style file: ACM-Reference-Format.bst
Reallocated singl_function (elt_size=8) to 100 items from 50.
Reallocated singl_function (elt_size=8) to 100 items from 50.
Reallocated wiz_functions (elt_size=8) to 6000 items from 3000.
Database file #1: refs.bib
Reallocated singl_function (elt_size=8) to 100 items from 50.
Reallocated wiz_functions (elt_size=8) to 9000 items from 6000.
Reallocated glb_str_ptr (elt_size=8) to 20 items from 10.
Reallocated global_strs (elt_size=200001) to 20 items from 10.
Reallocated glb_str_end (elt_size=8) to 20 items from 10.
Reallocated singl_function (elt_size=8) to 100 items from 50.
Warning--empty year in supercop
Warning--using n.d. in supercop
Warning--empty publisher in kyber2018
Warning--empty address in kyber2018
Warning--empty publisher in rapl
Warning--empty address in rapl
Warning--page numbers missing in both pages and numpages fields in rapl
Warning--empty publisher in gueron2014
Warning--empty address in gueron2014
Warning--page numbers missing in both pages and numpages fields in gueron2014
Warning--empty year in papi
Warning--using n.d. in papi
Warning--empty year in pqm4
Warning--using n.d. in pqm4
Warning--empty publisher in ntt-survey
Warning--empty address in ntt-survey
Warning--page numbers missing in both pages and numpages fields in ntt-survey
Warning--empty year in pqclean
Warning--using n.d. in pqclean
Warning--empty year in kyber-avx2
Warning--using n.d. in kyber-avx2
You've used 15 entries,
6273 wiz_defined-function locations,
1592 strings with 20522 characters,
and the built_in function-call counts, 12963 in all, are:
= -- 1307
> -- 435
< -- 3
+ -- 142
- -- 171
* -- 881
:= -- 1393
add.period$ -- 69
call.type$ -- 15
change.case$ -- 95
chr.to.int$ -- 13
cite$ -- 36
duplicate$ -- 1181
empty$ -- 859
format.name$ -- 203
if$ -- 2909
int.to.chr$ -- 4
int.to.str$ -- 1
missing$ -- 20
newline$ -- 198
num.names$ -- 103
pop$ -- 587
preamble$ -- 1
purify$ -- 186
quote$ -- 0
skip$ -- 444
stack$ -- 0
substring$ -- 727
swap$ -- 81
text.length$ -- 3
text.prefix$ -- 0
top$ -- 0
type$ -- 438
warning$ -- 21
while$ -- 94
width$ -- 0
write$ -- 343
(There were 21 warnings)

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
\BOOKMARK [1][-]{section*.1}{\376\377\000A\000b\000s\000t\000r\000a\000c\000t}{}% 1
\BOOKMARK [1][-]{section.1}{\376\377\0001\000\040\000I\000n\000t\000r\000o\000d\000u\000c\000t\000i\000o\000n}{}% 2
\BOOKMARK [1][-]{section.2}{\376\377\0002\000\040\000B\000a\000c\000k\000g\000r\000o\000u\000n\000d}{}% 3
\BOOKMARK [2][-]{subsection.2.1}{\376\377\0002\000.\0001\000\040\000M\000L\000-\000K\000E\000M\000\040\000a\000n\000d\000\040\000t\000h\000e\000\040\000N\000u\000m\000b\000e\000r\000\040\000T\000h\000e\000o\000r\000e\000t\000i\000c\000\040\000T\000r\000a\000n\000s\000f\000o\000r\000m}{section.2}% 4
\BOOKMARK [2][-]{subsection.2.2}{\376\377\0002\000.\0002\000\040\000A\000V\000X\0002\000\040\000S\000I\000M\000D\000\040\000o\000n\000\040\000x\0008\0006\000-\0006\0004}{section.2}% 5
\BOOKMARK [2][-]{subsection.2.3}{\376\377\0002\000.\0003\000\040\000C\000o\000m\000p\000i\000l\000a\000t\000i\000o\000n\000\040\000V\000a\000r\000i\000a\000n\000t\000s}{section.2}% 6
\BOOKMARK [2][-]{subsection.2.4}{\376\377\0002\000.\0004\000\040\000H\000a\000r\000d\000w\000a\000r\000e\000\040\000P\000e\000r\000f\000o\000r\000m\000a\000n\000c\000e\000\040\000C\000o\000u\000n\000t\000e\000r\000s\000\040\000a\000n\000d\000\040\000E\000n\000e\000r\000g\000y}{section.2}% 7
\BOOKMARK [1][-]{section.3}{\376\377\0003\000\040\000M\000e\000t\000h\000o\000d\000o\000l\000o\000g\000y}{}% 8
\BOOKMARK [2][-]{subsection.3.1}{\376\377\0003\000.\0001\000\040\000I\000m\000p\000l\000e\000m\000e\000n\000t\000a\000t\000i\000o\000n\000\040\000S\000o\000u\000r\000c\000e}{section.3}% 9
\BOOKMARK [2][-]{subsection.3.2}{\376\377\0003\000.\0002\000\040\000C\000o\000m\000p\000i\000l\000a\000t\000i\000o\000n\000\040\000V\000a\000r\000i\000a\000n\000t\000s}{section.3}% 10
\BOOKMARK [2][-]{subsection.3.3}{\376\377\0003\000.\0003\000\040\000B\000e\000n\000c\000h\000m\000a\000r\000k\000\040\000H\000a\000r\000n\000e\000s\000s}{section.3}% 11
\BOOKMARK [2][-]{subsection.3.4}{\376\377\0003\000.\0004\000\040\000H\000a\000r\000d\000w\000a\000r\000e\000\040\000P\000l\000a\000t\000f\000o\000r\000m}{section.3}% 12
\BOOKMARK [2][-]{subsection.3.5}{\376\377\0003\000.\0005\000\040\000S\000t\000a\000t\000i\000s\000t\000i\000c\000a\000l\000\040\000M\000e\000t\000h\000o\000d\000o\000l\000o\000g\000y}{section.3}% 13
\BOOKMARK [2][-]{subsection.3.6}{\376\377\0003\000.\0006\000\040\000E\000n\000e\000r\000g\000y\000\040\000M\000e\000a\000s\000u\000r\000e\000m\000e\000n\000t}{section.3}% 14
\BOOKMARK [1][-]{section.4}{\376\377\0004\000\040\000R\000e\000s\000u\000l\000t\000s}{}% 15
\BOOKMARK [2][-]{subsection.4.1}{\376\377\0004\000.\0001\000\040\000C\000y\000c\000l\000e\000\040\000C\000o\000u\000n\000t\000\040\000D\000i\000s\000t\000r\000i\000b\000u\000t\000i\000o\000n\000s}{section.4}% 16
\BOOKMARK [2][-]{subsection.4.2}{\376\377\0004\000.\0002\000\040\000S\000p\000e\000e\000d\000u\000p\000\040\000D\000e\000c\000o\000m\000p\000o\000s\000i\000t\000i\000o\000n}{section.4}% 17
\BOOKMARK [2][-]{subsection.4.3}{\376\377\0004\000.\0003\000\040\000H\000a\000n\000d\000-\000W\000r\000i\000t\000t\000e\000n\000\040\000S\000I\000M\000D\000\040\000S\000p\000e\000e\000d\000u\000p}{section.4}% 18
\BOOKMARK [2][-]{subsection.4.4}{\376\377\0004\000.\0004\000\040\000S\000t\000a\000t\000i\000s\000t\000i\000c\000a\000l\000\040\000S\000i\000g\000n\000i\000f\000i\000c\000a\000n\000c\000e}{section.4}% 19
\BOOKMARK [2][-]{subsection.4.5}{\376\377\0004\000.\0005\000\040\000C\000r\000o\000s\000s\000-\000P\000a\000r\000a\000m\000e\000t\000e\000r\000\040\000C\000o\000n\000s\000i\000s\000t\000e\000n\000c\000y}{section.4}% 20
\BOOKMARK [2][-]{subsection.4.6}{\376\377\0004\000.\0006\000\040\000H\000a\000r\000d\000w\000a\000r\000e\000\040\000C\000o\000u\000n\000t\000e\000r\000\040\000B\000r\000e\000a\000k\000d\000o\000w\000n}{section.4}% 21
\BOOKMARK [2][-]{subsection.4.7}{\376\377\0004\000.\0007\000\040\000E\000n\000e\000r\000g\000y\000\040\000E\000f\000f\000i\000c\000i\000e\000n\000c\000y}{section.4}% 22
\BOOKMARK [1][-]{section.5}{\376\377\0005\000\040\000D\000i\000s\000c\000u\000s\000s\000i\000o\000n}{}% 23
\BOOKMARK [2][-]{subsection.5.1}{\376\377\0005\000.\0001\000\040\000W\000h\000y\000\040\000A\000r\000i\000t\000h\000m\000e\000t\000i\000c\000\040\000O\000p\000e\000r\000a\000t\000i\000o\000n\000s\000\040\000B\000e\000n\000e\000f\000i\000t\000\040\000M\000o\000s\000t}{section.5}% 24
\BOOKMARK [2][-]{subsection.5.2}{\376\377\0005\000.\0002\000\040\000W\000h\000y\000\040\000t\000h\000e\000\040\000C\000o\000m\000p\000i\000l\000e\000r\000\040\000C\000a\000n\000n\000o\000t\000\040\000A\000u\000t\000o\000-\000V\000e\000c\000t\000o\000r\000i\000s\000e\000\040\000N\000T\000T}{section.5}% 25
\BOOKMARK [2][-]{subsection.5.3}{\376\377\0005\000.\0003\000\040\000W\000h\000y\000\040\000S\000H\000A\000K\000E\000\040\000O\000p\000e\000r\000a\000t\000i\000o\000n\000s\000\040\000B\000e\000n\000e\000f\000i\000t\000\040\000L\000e\000s\000s}{section.5}% 26
\BOOKMARK [2][-]{subsection.5.4}{\376\377\0005\000.\0004\000\040\000W\000h\000y\000\040\000N\000o\000i\000s\000e\000\040\000S\000a\000m\000p\000l\000i\000n\000g\000\040\000B\000a\000r\000e\000l\000y\000\040\000B\000e\000n\000e\000f\000i\000t\000s}{section.5}% 27
\BOOKMARK [2][-]{subsection.5.5}{\376\377\0005\000.\0005\000\040\000N\000T\000T\000\040\000C\000a\000c\000h\000e\000-\000S\000t\000a\000t\000e\000\040\000V\000a\000r\000i\000a\000t\000i\000o\000n\000\040\000A\000c\000r\000o\000s\000s\000\040\000P\000a\000r\000a\000m\000e\000t\000e\000r\000\040\000S\000e\000t\000s}{section.5}% 28
\BOOKMARK [2][-]{subsection.5.6}{\376\377\0005\000.\0006\000\040\000I\000m\000p\000l\000i\000c\000a\000t\000i\000o\000n\000s\000\040\000f\000o\000r\000\040\000D\000e\000p\000l\000o\000y\000m\000e\000n\000t}{section.5}% 29
\BOOKMARK [2][-]{subsection.5.7}{\376\377\0005\000.\0007\000\040\000L\000i\000m\000i\000t\000a\000t\000i\000o\000n\000s}{section.5}% 30
\BOOKMARK [1][-]{section.6}{\376\377\0006\000\040\000R\000e\000l\000a\000t\000e\000d\000\040\000W\000o\000r\000k}{}% 31
\BOOKMARK [1][-]{section.7}{\376\377\0007\000\040\000C\000o\000n\000c\000l\000u\000s\000i\000o\000n}{}% 32
\BOOKMARK [1][-]{section*.33}{\376\377\000R\000e\000f\000e\000r\000e\000n\000c\000e\000s}{}% 33
\BOOKMARK [1][-]{appendix.A}{\376\377\000A\000\040\000E\000n\000d\000-\000t\000o\000-\000E\000n\000d\000\040\000K\000E\000M\000\040\000S\000p\000e\000e\000d\000u\000p}{}% 34
\BOOKMARK [1][-]{appendix.B}{\376\377\000B\000\040\000F\000u\000l\000l\000\040\000O\000p\000e\000r\000a\000t\000i\000o\000n\000\040\000S\000e\000t}{}% 35

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,494 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:31
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest.tex
(/tmp/pgftest.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.groupplots
.code.tex
\pgfplots@group@current@plot=\count311
\pgfplots@group@current@row=\count312
\pgfplots@group@current@column=\count313
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
calc.code.tex
File: tikzlibrarycalc.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count314
)
No file pgftest.aux.
\openout1 = `pgftest.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 5.
LaTeX Font Info: ... okay on input line 5.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count315
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count316
\nofMParguments=\count317
\everyMPshowfont=\toks34
\MPscratchCnt=\count318
\MPscratchDim=\dimen272
\MPnumerator=\count319
\makeMPintoPDFobject=\count320
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 7: Using 'lua backend=false' for axis: ymod
e=log unsupported (yet).
Package pgfplots info on input line 7: Using 'lua backend=false' for axis: x co
ord trafo unsupported.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 9.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 9.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22374 strings out of 469515
603098 string characters out of 5470808
1118991 words of memory out of 5000000
50722 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,739b,2054s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb>
Output written on pgftest.pdf (1 page, 20045 bytes).
PDF statistics:
21 PDF objects out of 1000 (max. 8388607)
13 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,513 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:31
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest2.tex
(/tmp/pgftest2.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.groupplots
.code.tex
\pgfplots@group@current@plot=\count311
\pgfplots@group@current@row=\count312
\pgfplots@group@current@column=\count313
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
calc.code.tex
File: tikzlibrarycalc.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count314
)
No file pgftest2.aux.
\openout1 = `pgftest2.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 11.
LaTeX Font Info: ... okay on input line 11.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count315
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count316
\nofMParguments=\count317
\everyMPshowfont=\toks34
\MPscratchCnt=\count318
\MPscratchDim=\dimen272
\MPnumerator=\count319
\makeMPintoPDFobject=\count320
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
! Package pgfkeys Error: I do not know the key '/pgfplots/bar width', to which
you passed '5pt', and I am going to ignore it. Perhaps you misspelled it.
See the pgfkeys package documentation for explanation.
Type H <return> for immediate help.
...
l.13 ...bar, bar width=5pt, width=5cm, height=4cm]
This error message was generated by an \errmessage
command, so I can't give any explicit help.
Pretend that you're Hercule Poirot: Examine all clues,
and deduce the truth by order and method.
Package pgfplots info on input line 15: Using 'lua backend=false' for axis: ymo
de=log unsupported (yet).
Package pgfplots info on input line 15: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 17.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 17.
Package pgfplots info on input line 17: Using 'lua backend=false' for axis: ymo
de=log unsupported (yet).
Package pgfplots info on input line 17: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest2.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22386 strings out of 469515
603412 string characters out of 5470808
1122992 words of memory out of 5000000
50734 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,740b,2308s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb>
Output written on pgftest2.pdf (1 page, 20631 bytes).
PDF statistics:
21 PDF objects out of 1000 (max. 8388607)
13 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,498 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:32
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest3.tex
(/tmp/pgftest3.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.groupplots
.code.tex
\pgfplots@group@current@plot=\count311
\pgfplots@group@current@row=\count312
\pgfplots@group@current@column=\count313
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
calc.code.tex
File: tikzlibrarycalc.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count314
)
No file pgftest3.aux.
\openout1 = `pgftest3.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count315
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count316
\nofMParguments=\count317
\everyMPshowfont=\toks34
\MPscratchCnt=\count318
\MPscratchDim=\dimen272
\MPnumerator=\count319
\makeMPintoPDFobject=\count320
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 11: Using 'lua backend=false' for axis: ymo
de=log unsupported (yet).
Package pgfplots info on input line 11: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 13.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 13.
Package pgfplots info on input line 13: Using 'lua backend=false' for axis: ymo
de=log unsupported (yet).
Package pgfplots info on input line 13: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest3.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22386 strings out of 469515
603412 string characters out of 5470808
1122992 words of memory out of 5000000
50734 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,740b,2310s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb>
Output written on pgftest3.pdf (1 page, 20634 bytes).
PDF statistics:
21 PDF objects out of 1000 (max. 8388607)
13 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,496 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:45
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest4.tex
(/tmp/pgftest4.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count311
)
No file pgftest4.aux.
\openout1 = `pgftest4.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 8.
LaTeX Font Info: ... okay on input line 8.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count312
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count313
\nofMParguments=\count314
\everyMPshowfont=\toks34
\MPscratchCnt=\count315
\MPscratchDim=\dimen272
\MPnumerator=\count316
\makeMPintoPDFobject=\count317
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 11: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
! Package pgfkeys Error: I do not know the key '/tikz/ymode', to which you pass
ed 'log', and I am going to ignore it. Perhaps you misspelled it.
See the pgfkeys package documentation for explanation.
Type H <return> for immediate help.
...
l.13 \end{axis}
This error message was generated by an \errmessage
command, so I can't give any explicit help.
Pretend that you're Hercule Poirot: Examine all clues,
and deduce the truth by order and method.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 13.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 13.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest4.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22165 strings out of 469515
595598 string characters out of 5470808
1118992 words of memory out of 5000000
50519 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,734b,2073s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on pgftest4.pdf (1 page, 11971 bytes).
PDF statistics:
16 PDF objects out of 1000 (max. 8388607)
10 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,497 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:45
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest5.tex
(/tmp/pgftest5.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count311
)
No file pgftest5.aux.
\openout1 = `pgftest5.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 13.
LaTeX Font Info: ... okay on input line 13.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count312
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count313
\nofMParguments=\count314
\everyMPshowfont=\toks34
\MPscratchCnt=\count315
\MPscratchDim=\dimen272
\MPnumerator=\count316
\makeMPintoPDFobject=\count317
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 16: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
! Package pgfkeys Error: I do not know the key '/tikz/ymode', to which you pass
ed 'log', and I am going to ignore it. Perhaps you misspelled it.
See the pgfkeys package documentation for explanation.
Type H <return> for immediate help.
...
l.18 \end{axis}
This error message was generated by an \errmessage
command, so I can't give any explicit help.
Pretend that you're Hercule Poirot: Examine all clues,
and deduce the truth by order and method.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 18.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 18.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest5.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22168 strings out of 469515
595556 string characters out of 5470808
1118992 words of memory out of 5000000
50521 multiletter control sequences out of 15000+600000
628020 words of font info for 41 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,734b,2077s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr9.pfb>
Output written on pgftest5.pdf (1 page, 19832 bytes).
PDF statistics:
21 PDF objects out of 1000 (max. 8388607)
13 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,496 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:46
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest6.tex
(/tmp/pgftest6.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count311
)
No file pgftest6.aux.
\openout1 = `pgftest6.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 7.
LaTeX Font Info: ... okay on input line 7.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count312
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count313
\nofMParguments=\count314
\everyMPshowfont=\toks34
\MPscratchCnt=\count315
\MPscratchDim=\dimen272
\MPnumerator=\count316
\makeMPintoPDFobject=\count317
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 10: Using 'lua backend=false' for axis: x c
oord trafo unsupported.
! Package pgfkeys Error: I do not know the key '/tikz/ymode', to which you pass
ed 'log', and I am going to ignore it. Perhaps you misspelled it.
See the pgfkeys package documentation for explanation.
Type H <return> for immediate help.
...
l.12 \end{axis}
This error message was generated by an \errmessage
command, so I can't give any explicit help.
Pretend that you're Hercule Poirot: Examine all clues,
and deduce the truth by order and method.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 12.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 12.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest6.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22165 strings out of 469515
595570 string characters out of 5470808
1119992 words of memory out of 5000000
50519 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,734b,2075s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on pgftest6.pdf (1 page, 11993 bytes).
PDF statistics:
16 PDF objects out of 1000 (max. 8388607)
10 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,484 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:46
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgftest7.tex
(/tmp/pgftest7.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count311
)
No file pgftest7.aux.
\openout1 = `pgftest7.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 4.
LaTeX Font Info: ... okay on input line 4.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count312
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count313
\nofMParguments=\count314
\everyMPshowfont=\toks34
\MPscratchCnt=\count315
\MPscratchDim=\dimen272
\MPnumerator=\count316
\makeMPintoPDFobject=\count317
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots notification 'compat/show suggested version=true': document ha
s been generated with the most recent feature set (\pgfplotsset{compat=1.18}).
Package pgfplots info on input line 7: Using 'lua backend=false' for axis: ymod
e=log unsupported (yet).
Package pgfplots info on input line 7: Using 'lua backend=false' for axis: x co
ord trafo unsupported.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <7> on input line 9.
LaTeX Font Info: External font `cmex10' loaded for size
(Font) <5> on input line 9.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgftest7.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
22155 strings out of 469515
595347 string characters out of 5470808
1118992 words of memory out of 5000000
50509 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,9n,118p,734b,2054s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb>
Output written on pgftest7.pdf (1 page, 20071 bytes).
PDF statistics:
21 PDF objects out of 1000 (max. 8388607)
13 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -1,2 +0,0 @@
\relax
\gdef \@abspage@last{1}

View File

@ -1,478 +0,0 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.29 (TeX Live 2026/Arch Linux) (preloaded format=pdflatex 2026.3.6) 4 APR 2026 13:30
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**/tmp/pgfver.tex
(/tmp/pgfver.tex
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2025/01/22 v1.4n Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo
File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option)
)
\c@part=\count275
\c@section=\count276
\c@subsection=\count277
\c@subsubsection=\count278
\c@paragraph=\count279
\c@subparagraph=\count280
\c@figure=\count281
\c@table=\count282
\abovecaptionskip=\skip49
\belowcaptionskip=\skip50
\bibindent=\dimen148
)
(/usr/share/texmf-dist/tex/latex/pgfplots/pgfplots.sty
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.revision.tex)
Package: pgfplots 2025/08/14 v1.18.2 Data Visualization (1.18.2)
(/usr/share/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2024/12/31 v1.2e Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
\KV@toks@=\toks17
)
(/usr/share/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2024/08/06 v1.4g Standard LaTeX Graphics (DPC,SPQR)
(/usr/share/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2023/12/02 v1.11 sin cos tan (DPC)
)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
)
Package graphics Info: Driver file: pdftex.def on input line 106.
(/usr/share/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2025/09/29 v1.2d Graphics/color driver for pdftex
))
\Gin@req@height=\dimen149
\Gin@req@width=\dimen150
)
(/usr/share/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
\pgfutil@everybye=\toks18
\pgfutil@tempdima=\dimen151
\pgfutil@tempdimb=\dimen152
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
\pgfutil@abb=\box53
)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/pgf.revision.tex)
Package: pgfrcs 2025-08-29 v3.1.11a (3.1.11a)
))
Package: pgf 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
(/usr/share/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
Package: pgfsys 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
\pgfkeys@pathtoks=\toks19
\pgfkeys@temptoks=\toks20
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.te
x
\pgfkeys@tmptoks=\toks21
))
\pgf@x=\dimen153
\pgf@y=\dimen154
\pgf@xa=\dimen155
\pgf@ya=\dimen156
\pgf@xb=\dimen157
\pgf@yb=\dimen158
\pgf@xc=\dimen159
\pgf@yc=\dimen160
\pgf@xd=\dimen161
\pgf@yd=\dimen162
\w@pgf@writea=\write3
\r@pgf@reada=\read2
\c@pgf@counta=\count283
\c@pgf@countb=\count284
\c@pgf@countc=\count285
\c@pgf@countd=\count286
\t@pgf@toka=\toks22
\t@pgf@tokb=\toks23
\t@pgf@tokc=\toks24
\pgf@sys@id@count=\count287
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
File: pgf.cfg 2025-08-29 v3.1.11a (3.1.11a)
)
Driver file for pgf: pgfsys-pdftex.def
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
File: pgfsys-pdftex.def 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
File: pgfsys-common-pdf.def 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
File: pgfsyssoftpath.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfsyssoftpath@smallbuffer@items=\count288
\pgfsyssoftpath@bigbuffer@items=\count289
)
(/usr/share/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
File: pgfsysprotocol.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/xcolor/xcolor.sty
Package: xcolor 2024/09/29 v3.02 LaTeX color extensions (UK)
(/usr/share/texmf-dist/tex/latex/graphics-cfg/color.cfg
File: color.cfg 2016/01/02 v1.6 sample color configuration
)
Package xcolor Info: Driver file: pdftex.def on input line 274.
(/usr/share/texmf-dist/tex/latex/graphics/mathcolor.ltx)
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1349.
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1353.
Package xcolor Info: Model `RGB' extended on input line 1365.
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1367.
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1368.
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1369.
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1370.
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1371.
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1372.
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
Package: pgfcore 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
\pgfmath@dimen=\dimen163
\pgfmath@count=\count290
\pgfmath@box=\box54
\pgfmath@toks=\toks25
\pgfmath@stack@operand=\toks26
\pgfmath@stack@operation=\toks27
)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code
.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.te
x) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics
.code.tex) (/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
\c@pgfmathroundto@lastzeros=\count291
))
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
File: pgfcorepoints.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@picminx=\dimen164
\pgf@picmaxx=\dimen165
\pgf@picminy=\dimen166
\pgf@picmaxy=\dimen167
\pgf@pathminx=\dimen168
\pgf@pathmaxx=\dimen169
\pgf@pathminy=\dimen170
\pgf@pathmaxy=\dimen171
\pgf@xx=\dimen172
\pgf@xy=\dimen173
\pgf@yx=\dimen174
\pgf@yy=\dimen175
\pgf@zx=\dimen176
\pgf@zy=\dimen177
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
File: pgfcorepathconstruct.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@path@lastx=\dimen178
\pgf@path@lasty=\dimen179
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
File: pgfcorepathusage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@shorten@end@additional=\dimen180
\pgf@shorten@start@additional=\dimen181
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
File: pgfcorescopes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfpic=\box55
\pgf@hbox=\box56
\pgf@layerbox@main=\box57
\pgf@picture@serial@count=\count292
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
File: pgfcoregraphicstate.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgflinewidth=\dimen182
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.t
ex
File: pgfcoretransformations.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@pt@x=\dimen183
\pgf@pt@y=\dimen184
\pgf@pt@temp=\dimen185
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
File: pgfcorequick.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
File: pgfcoreobjects.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.te
x
File: pgfcorepathprocessing.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
File: pgfcorearrows.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfarrowsep=\dimen186
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
File: pgfcoreshade.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@max=\dimen187
\pgf@sys@shading@range@num=\count293
\pgf@shadingcount=\count294
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
File: pgfcoreimage.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
File: pgfcoreexternal.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfexternal@startupbox=\box58
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
File: pgfcorelayers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
File: pgfcoretransparency.code.tex 2025-08-29 v3.1.11a (3.1.11a)
) (/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
File: pgfcorepatterns.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
File: pgfcorerdf.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
File: pgfmoduleshapes.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfnodeparttextbox=\box59
)
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
File: pgfmoduleplot.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
Package: pgfcomp-version-0-65 2025-08-29 v3.1.11a (3.1.11a)
\pgf@nodesepstart=\dimen188
\pgf@nodesepend=\dimen189
)
(/usr/share/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
Package: pgfcomp-version-1-18 2025-08-29 v3.1.11a (3.1.11a)
))
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
(/usr/share/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
(/usr/share/texmf-dist/tex/latex/pgf/math/pgfmath.sty
(/usr/share/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
Package: pgffor 2025-08-29 v3.1.11a (3.1.11a)
\pgffor@iter=\dimen190
\pgffor@skip=\dimen191
\pgffor@stack=\toks28
\pgffor@toks=\toks29
))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
Package: tikz 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.te
x
File: pgflibraryplothandlers.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgf@plot@mark@count=\count295
\pgfplotmarksize=\dimen192
)
\tikz@lastx=\dimen193
\tikz@lasty=\dimen194
\tikz@lastxsaved=\dimen195
\tikz@lastysaved=\dimen196
\tikz@lastmovetox=\dimen197
\tikz@lastmovetoy=\dimen198
\tikzleveldistance=\dimen199
\tikzsiblingdistance=\dimen256
\tikz@figbox=\box60
\tikz@figbox@bg=\box61
\tikz@tempbox=\box62
\tikz@tempbox@bg=\box63
\tikztreelevel=\count296
\tikznumberofchildren=\count297
\tikznumberofcurrentchild=\count298
\tikz@fig@count=\count299
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
File: pgfmodulematrix.code.tex 2025-08-29 v3.1.11a (3.1.11a)
\pgfmatrixcurrentrow=\count300
\pgfmatrixcurrentcolumn=\count301
\pgf@matrix@numberofcolumns=\count302
)
\tikz@expandcount=\count303
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
topaths.code.tex
File: tikzlibrarytopaths.code.tex 2025-08-29 v3.1.11a (3.1.11a)
)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscore.code.tex
\t@pgfplots@toka=\toks30
\t@pgfplots@tokb=\toks31
\t@pgfplots@tokc=\toks32
\pgfplots@tmpa=\dimen257
\c@pgfplots@coordindex=\count304
\c@pgfplots@scanlineindex=\count305
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgfplotssysgeneric.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgfplotslibrary.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_l
oader.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryfpu.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/oldpgfcompatib/pgfplotsoldpgfsupp_p
gfutil-common-lists.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsliststructure
ext.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsarray.code.te
x
\c@pgfplotsarray@tmp=\count306
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsmatrix.code.t
ex)
(/usr/share/texmf-dist/tex/generic/pgfplots/numtable/pgfplotstableshared.code.t
ex
\c@pgfplotstable@counta=\count307
\t@pgfplotstable@a=\toks33
)
(/usr/share/texmf-dist/tex/generic/pgfplots/liststructure/pgfplotsdeque.code.te
x) (/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsbinary.data.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotsutil.verb.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/pgflibrarypgfplots.surfshading
.code.tex
\c@pgfplotslibrarysurf@no=\count308
(/usr/share/texmf-dist/tex/generic/pgfplots/sys/pgflibrarypgfplots.surfshading.
pgfsys-pdftex.def)))
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolormap.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/util/pgfplotscolor.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsstackedplots.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsplothandlers.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplothandler.code.tex
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsmeshplotimage.code.tex)))
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.scaling.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotscoordprocessing.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.errorbars.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.markers.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplotsticks.code.tex)
(/usr/share/texmf-dist/tex/generic/pgfplots/pgfplots.paths.code.tex)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
\pgfdecoratedcompleteddistance=\dimen258
\pgfdecoratedremainingdistance=\dimen259
\pgfdecoratedinputsegmentcompleteddistance=\dimen260
\pgfdecoratedinputsegmentremainingdistance=\dimen261
\pgf@decorate@distancetomove=\dimen262
\pgf@decorate@repeatstate=\count309
\pgfdecorationsegmentamplitude=\dimen263
\pgfdecorationsegmentlength=\dimen264
)
\tikz@lib@dec@box=\box64
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathmorphing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathmorphing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
decorations.pathreplacing.code.tex
(/usr/share/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorati
ons.pathreplacing.code.tex))
(/usr/share/texmf-dist/tex/generic/pgfplots/libs/tikzlibrarypgfplots.contourlua
.code.tex)
\pgfplots@numplots=\count310
\pgfplots@xmin@reg=\dimen265
\pgfplots@xmax@reg=\dimen266
\pgfplots@ymin@reg=\dimen267
\pgfplots@ymax@reg=\dimen268
\pgfplots@zmin@reg=\dimen269
\pgfplots@zmax@reg=\dimen270
)
(/usr/share/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrary
plotmarks.code.tex
File: tikzlibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
(/usr/share/texmf-dist/tex/generic/pgf/libraries/pgflibraryplotmarks.code.tex
File: pgflibraryplotmarks.code.tex 2025-08-29 v3.1.11a (3.1.11a)
))) (/usr/share/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2025-10-09 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count311
)
No file pgfver.aux.
\openout1 = `pgfver.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 3.
LaTeX Font Info: ... okay on input line 3.
(/usr/share/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count312
\scratchdimen=\dimen271
\scratchbox=\box65
\nofMPsegments=\count313
\nofMParguments=\count314
\everyMPshowfont=\toks34
\MPscratchCnt=\count315
\MPscratchDim=\dimen272
\MPnumerator=\count316
\makeMPintoPDFobject=\count317
\everyMPtoPDFconversion=\toks35
) (/usr/share/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
85.
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
Package pgfplots Warning: running in backwards compatibility mode (unsuitable t
ick labels; missing features). Consider writing \pgfplotsset{compat=1.18} into
your preamble.
on input line 3.
[1
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./pgfver.aux)
***********
LaTeX2e <2025-11-01>
L3 programming layer <2026-01-19>
***********
)
Here is how much of TeX's memory you used:
21675 strings out of 469515
581684 string characters out of 5470808
1115990 words of memory out of 5000000
50029 multiletter control sequences out of 15000+600000
627721 words of font info for 40 fonts, out of 8000000 for 9000
14 hyphenation exceptions out of 8191
99i,5n,118p,723b,273s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/share/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on pgfver.pdf (1 page, 14227 bytes).
PDF statistics:
16 PDF objects out of 1000 (max. 8388607)
10 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
13 words of extra memory for PDF output out of 10000 (max. 10000000)

View File

@ -5,16 +5,19 @@ description = "Build-time tooling for levineuwirth.org"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
# Visualization # Visualization
"matplotlib>=3.9", "matplotlib>=3.9,<4",
"altair>=5.4", "altair>=5.4,<6",
# Embedding pipeline # Embedding pipeline
"sentence-transformers>=3.4", # Upper bounds are intentionally generous (next major) but always
"faiss-cpu>=1.9", # present so that an unrelated `uv sync` upgrade can't silently pull
"numpy>=2.0", # an API-breaking 4.x release. Bump deliberately when validating.
"beautifulsoup4>=4.12", "sentence-transformers>=3.4,<4",
"faiss-cpu>=1.9,<2",
"numpy>=2.0,<3",
"beautifulsoup4>=4.12,<5",
# CPU-only torch — avoids pulling ~3 GB of CUDA libraries # CPU-only torch — avoids pulling ~3 GB of CUDA libraries
"torch>=2.5", "torch>=2.5,<3",
] ]
[[tool.uv.index]] [[tool.uv.index]]

View File

@ -119,7 +119,9 @@
--page-padding: 1.5rem; --page-padding: 1.5rem;
/* Transitions */ /* Transitions */
--transition-fast: 0.15s ease; --transition-fast: 0.15s ease;
--transition-medium: 0.28s ease;
--transition-slow: 0.5s ease;
/* Writing activity heatmap (light mode) */ /* Writing activity heatmap (light mode) */
--hm-0: #e8e8e4; /* empty cell */ --hm-0: #e8e8e4; /* empty cell */
@ -127,6 +129,29 @@
--hm-2: #787874; /* 5001999 words */ --hm-2: #787874; /* 5001999 words */
--hm-3: #424240; /* 20004999 words */ --hm-3: #424240; /* 20004999 words */
--hm-4: #1a1a1a; /* 5000+ words */ --hm-4: #1a1a1a; /* 5000+ words */
/* Aliases (introduced for build.css, components.css, and the
annotation system, all of which referenced custom properties that
were never defined). Browsers silently fall back to the property's
initial value when var(--undefined) is used, so without these
aliases the build/annotation pages would degrade to default
greys/serif fonts.
Defining them here keeps a single source of truth change the
primitive token (--border-muted, --font-sans, --bg-offset) and
every consumer follows. */
--rule: var(--border-muted);
--font-ui: var(--font-sans);
--bg-subtle: var(--bg-offset);
/* Layout breakpoints referenced from JS via getComputedStyle and
documented here for grep. CSS @media queries cannot use custom
properties, so the @media values throughout components.css and
layout.css must be kept in lockstep with these. */
--bp-phone: 540px;
--bp-tablet: 680px;
--bp-desktop: 900px;
--bp-wide: 1500px;
} }
@ -139,14 +164,17 @@
--bg: #121212; --bg: #121212;
--bg-nav: #181818; --bg-nav: #181818;
--bg-offset: #1a1a1a; --bg-offset: #1a1a1a;
/* --text-faint was previously #6a6660, which yields ~2.8:1 contrast
on the #121212 background and fails WCAG AA. Bumped to #8b8680 for
a contrast of ~3.5:1, the minimum for non-text UI elements. */
--text: #d4d0c8; --text: #d4d0c8;
--text-muted: #8c8881; --text-muted: #8c8881;
--text-faint: #6a6660; --text-faint: #8b8680;
--border: #333333; --border: #333333;
--border-muted: #444444; --border-muted: #444444;
--link: #d4d0c8; --link: #d4d0c8;
--link-underline: #6a6660; --link-underline: #8b8680;
--link-hover: #ffffff; --link-hover: #ffffff;
--link-hover-underline: #ffffff; --link-hover-underline: #ffffff;
--link-visited: #a39f98; --link-visited: #a39f98;
@ -154,6 +182,9 @@
--selection-bg: #d4d0c8; --selection-bg: #d4d0c8;
--selection-text: #121212; --selection-text: #121212;
/* Aliases — kept in sync with the light-mode definitions above. */
--bg-subtle: var(--bg-offset);
/* Writing activity heatmap (dark mode) */ /* Writing activity heatmap (dark mode) */
--hm-0: #252524; --hm-0: #252524;
--hm-1: #484844; --hm-1: #484844;
@ -170,12 +201,12 @@
--bg-offset: #1a1a1a; --bg-offset: #1a1a1a;
--text: #d4d0c8; --text: #d4d0c8;
--text-muted: #8c8881; --text-muted: #8c8881;
--text-faint: #6a6660; --text-faint: #8b8680;
--border: #333333; --border: #333333;
--border-muted: #444444; --border-muted: #444444;
--link: #d4d0c8; --link: #d4d0c8;
--link-underline: #6a6660; --link-underline: #8b8680;
--link-hover: #ffffff; --link-hover: #ffffff;
--link-hover-underline: #ffffff; --link-hover-underline: #ffffff;
--link-visited: #a39f98; --link-visited: #a39f98;
@ -183,6 +214,8 @@
--selection-bg: #d4d0c8; --selection-bg: #d4d0c8;
--selection-text: #121212; --selection-text: #121212;
--bg-subtle: var(--bg-offset);
--hm-0: #252524; --hm-0: #252524;
--hm-1: #484844; --hm-1: #484844;
--hm-2: #6e6e6a; --hm-2: #6e6e6a;
@ -228,6 +261,38 @@ body {
color: var(--selection-text); color: var(--selection-text);
} }
/* Global keyboard-focus indicator. Applies only when the user navigates
with the keyboard (`:focus-visible`), not on mouse click, so it does
not interfere with normal click feedback. Buttons, anchors, summaries
and form controls all share a single 2px outline so the focus path is
visible regardless of the surrounding component styling.
Individual components may override this with a tighter or differently
positioned ring, but the default is always present. */
:focus-visible {
outline: 2px solid var(--text);
outline-offset: 2px;
border-radius: 2px;
}
button:focus,
a:focus,
summary:focus,
[role="button"]:focus {
outline: none; /* fall back to :focus-visible above */
}
button:focus-visible,
a:focus-visible,
summary:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 2px solid var(--text);
outline-offset: 2px;
}
img, video, svg { img, video, svg {
max-width: 100%; max-width: 100%;
height: auto; height: auto;

View File

@ -123,6 +123,24 @@
max-width: 100%; max-width: 100%;
} }
/* Heatmap intensity classes (driven by CSS vars in base.css) */
.heatmap-svg .hm0,
.heatmap-legend .hm0 { fill: var(--hm-0); }
.heatmap-svg .hm1,
.heatmap-legend .hm1 { fill: var(--hm-1); }
.heatmap-svg .hm2,
.heatmap-legend .hm2 { fill: var(--hm-2); }
.heatmap-svg .hm3,
.heatmap-legend .hm3 { fill: var(--hm-3); }
.heatmap-svg .hm4,
.heatmap-legend .hm4 { fill: var(--hm-4); }
.heatmap-svg .hm-lbl {
font-size: 9px;
fill: var(--text-faint);
font-family: sans-serif;
}
/* Legend row below heatmap */ /* Legend row below heatmap */
.heatmap-legend { .heatmap-legend {
display: flex; display: flex;

View File

@ -424,7 +424,10 @@ nav.site-nav {
transform: rotate(-90deg); transform: rotate(-90deg);
} }
/* Nav: animates open/closed via max-height */ /* Nav: animates open/closed via max-height. The collapsed state hides
the nav from the keyboard tab order *and* the accessibility tree
using `aria-hidden="true"` (set by toc.js). The transition still
works because we keep `max-height: 0` for the visual collapse. */
.toc-nav { .toc-nav {
overflow: hidden; overflow: hidden;
max-height: 80vh; max-height: 80vh;
@ -432,7 +435,15 @@ nav.site-nav {
} }
#toc.is-collapsed .toc-nav { #toc.is-collapsed .toc-nav {
max-height: 0; max-height: 0;
visibility: hidden; }
#toc.is-collapsed .toc-nav a,
#toc.is-collapsed .toc-nav button {
/* Belt-and-suspenders: even if aria-hidden is somehow stripped,
the collapsed nav cannot receive focus. The earlier rule used
`visibility: hidden` which already removed elements from the
focus order, but `visibility` interacts poorly with the
max-height transition; tabindex=-1 set by toc.js is preferred. */
pointer-events: none;
} }
/* Nav list */ /* Nav list */

View File

@ -5,17 +5,21 @@
@media print { @media print {
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
Force light on paper Force light on paper. The custom-property overrides drive the
rest of the cascade use them consistently below instead of
reaching for hardcoded #fff/#000 again.
---------------------------------------------------------------- */ ---------------------------------------------------------------- */
:root, :root,
[data-theme="dark"] { [data-theme="dark"] {
--bg: #ffffff; --bg: #ffffff;
--bg-offset: #f5f5f5; --bg-offset: #f5f5f5;
--bg-subtle: #f9f9f9;
--text: #000000; --text: #000000;
--text-muted: #333333; --text-muted: #333333;
--text-faint: #555555; --text-faint: #555555;
--border: #cccccc; --border: #cccccc;
--border-muted: #aaaaaa; --border-muted: #aaaaaa;
--rule: #cccccc;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -41,8 +45,8 @@
body { body {
font-size: 11pt; font-size: 11pt;
line-height: 1.6; line-height: 1.6;
background: #fff; background: var(--bg);
color: #000; color: var(--text);
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@ -72,9 +76,9 @@
width: auto !important; width: auto !important;
margin: 0.5em 2em; margin: 0.5em 2em;
padding: 0.4em 0.8em; padding: 0.4em 0.8em;
border-left: 2px solid #ccc; border-left: 2px solid var(--border);
font-size: 9pt; font-size: 9pt;
color: #555; color: var(--text-faint);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
@ -109,7 +113,7 @@
a[href^="http"]::after { a[href^="http"]::after {
content: " (" attr(href) ")"; content: " (" attr(href) ")";
font-size: 0.8em; font-size: 0.8em;
color: #555; color: var(--text-faint);
word-break: break-all; word-break: break-all;
} }
/* But not for nav or obvious UI links */ /* But not for nav or obvious UI links */
@ -123,8 +127,8 @@
Code blocks strip background, border only Code blocks strip background, border only
---------------------------------------------------------------- */ ---------------------------------------------------------------- */
pre, code { pre, code {
background: #f9f9f9 !important; background: var(--bg-subtle) !important;
border: 1px solid #ddd !important; border: 1px solid var(--border-muted) !important;
box-shadow: none !important; box-shadow: none !important;
} }
@ -134,7 +138,7 @@
.page-meta-footer { .page-meta-footer {
margin-top: 1.5em; margin-top: 1.5em;
padding-top: 1em; padding-top: 1em;
border-top: 1px solid #ccc; border-top: 1px solid var(--border);
} }
.meta-footer-full, .meta-footer-full,

View File

@ -463,9 +463,22 @@ pre code {
border: 1px solid var(--border-muted); /* Inner bounding box for the image */ border: 1px solid var(--border-muted); /* Inner bounding box for the image */
} }
/* Image figures: size the box to the image and constrain the caption to the
same width. `display: table` + `caption-side: bottom` makes the figure's
intrinsic width depend only on its table-cell content (the image), so long
captions wrap to the image width instead of stretching the figure off-screen. */
#markdownBody figure:has(> img) {
display: table;
}
#markdownBody figure:has(> img) > figcaption {
display: table-caption;
caption-side: bottom;
}
#markdownBody figcaption { #markdownBody figcaption {
font-family: var(--font-sans); font-family: var(--font-sans);
font-size: var(--text-size-small); font-size: 0.92em;
color: var(--text-muted); color: var(--text-muted);
text-align: right; /* Editorial, museum-placard feel */ text-align: right; /* Editorial, museum-placard feel */
margin-top: 1rem; margin-top: 1rem;

View File

@ -150,12 +150,11 @@
tooltip.addEventListener('mouseleave', function () { hideTooltip(false); }); tooltip.addEventListener('mouseleave', function () { hideTooltip(false); });
} }
/* Defer to the shared utility (loaded synchronously from
templates/partials/head.html) so this file cannot drift from
popups.js, semantic-search.js, or build/Utils.hs. */
function escHtml(s) { function escHtml(s) {
return String(s) return window.lnUtils.escapeHtml(s);
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
} }
function showTooltip(mark, ann) { function showTooltip(mark, ann) {

View File

@ -225,9 +225,37 @@
if (e.key === 'Escape') { closeOverlay(); return; } if (e.key === 'Escape') { closeOverlay(); return; }
if (e.key === 'ArrowLeft') { navigate(-1); return; } if (e.key === 'ArrowLeft') { navigate(-1); return; }
if (e.key === 'ArrowRight') { navigate(+1); return; } if (e.key === 'ArrowRight') { navigate(+1); return; }
if (e.key === 'Tab') { trapTab(e); return; }
}); });
} }
/* Focus trap for the overlay: cycle Tab/Shift+Tab through the
focusable controls inside #gallery-overlay so keyboard users
cannot tab out into the (currently inert) page background. */
function trapTab(e) {
var focusable = Array.from(overlay.querySelectorAll(
'button:not([disabled]), [tabindex]:not([tabindex="-1"])'
));
if (focusable.length === 0) {
e.preventDefault();
return;
}
var first = focusable[0];
var last = focusable[focusable.length - 1];
var active = document.activeElement;
if (e.shiftKey) {
if (active === first || !overlay.contains(active)) {
e.preventDefault();
last.focus();
}
} else {
if (active === last || !overlay.contains(active)) {
e.preventDefault();
first.focus();
}
}
}
function openOverlay(idx) { function openOverlay(idx) {
currentIdx = idx; currentIdx = idx;
/* Show before rendering measurements (scrollWidth etc.) return 0 /* Show before rendering measurements (scrollWidth etc.) return 0

View File

@ -0,0 +1,40 @@
/* katex-bootstrap.js — Render every <span class="math"> / <div class="math">
block once KaTeX has finished loading.
Pandoc emits math blocks with the `math` class and the LaTeX source as
the element's text content. KaTeX is loaded with `defer` so this
bootstrap can simply run on DOMContentLoaded KaTeX guarantees its
own definitions are available by then.
Used to live as an inline `onload="..."` attribute on the KaTeX
<script> tag in templates/default.html, which blocked any future
strict CSP. Externalized here so the entire site can run with
`script-src 'self'` plus a single CDN allowance.
*/
(function () {
'use strict';
function renderAll() {
if (typeof katex === 'undefined') return;
var nodes = Array.from(document.getElementsByClassName('math'));
nodes.forEach(function (el) {
if (el.tagName !== 'SPAN' && el.tagName !== 'DIV') return;
var src = el.textContent;
try {
katex.render(src, el, {
displayMode: el.classList.contains('display'),
output: 'htmlAndMathml',
throwOnError: false
});
} catch (_) {
/* leave the original source visible if KaTeX rejects it */
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', renderAll);
} else {
renderAll();
}
})();

View File

@ -15,7 +15,10 @@
var img = document.createElement('img'); var img = document.createElement('img');
img.className = 'lightbox-img'; img.className = 'lightbox-img';
img.alt = ''; /* Default accessible name; overwritten in open() with the
triggering image's alt when present. Avoids a nameless
lightbox image when the source img has no alt text. */
img.alt = 'Lightbox image';
var caption = document.createElement('p'); var caption = document.createElement('p');
caption.className = 'lightbox-caption'; caption.className = 'lightbox-caption';
@ -39,7 +42,10 @@
function open(src, alt, captionText, trigger) { function open(src, alt, captionText, trigger) {
triggerEl = trigger || null; triggerEl = trigger || null;
img.src = src; img.src = src;
img.alt = alt || ''; /* Prefer the source img's alt; fall back to the figure
caption; fall back to a generic label so the lightbox
image always has an accessible name. */
img.alt = alt || captionText || 'Lightbox image';
caption.textContent = captionText || ''; caption.textContent = captionText || '';
caption.hidden = !captionText; caption.hidden = !captionText;
overlay.classList.add('is-open'); overlay.classList.add('is-open');

View File

@ -99,6 +99,18 @@
}); });
} }
/* Public re-init hook used by transclude.js after it injects new
content into the DOM. Idempotent bind() marks each element so
repeated calls don't stack listeners. */
window.reinitPopups = function (container) {
if (!popup) return; /* init() not yet run / touch device */
if (annotations === null) {
loadAnnotations().then(function () { bindTargets(container || document.body); });
} else {
bindTargets(container || document.body);
}
};
/* Returns the appropriate provider function for a given URL, or null. */ /* Returns the appropriate provider function for a given URL, or null. */
function getProvider(href) { function getProvider(href) {
if (!href) return null; if (!href) return null;
@ -118,6 +130,11 @@
} }
function bind(el, provider) { function bind(el, provider) {
/* Idempotent: skip elements that already have a popup binding,
so reinitPopups() called from transclude.js cannot stack
listeners on already-bound nodes. */
if (el.dataset.popupBound === '1') return;
el.dataset.popupBound = '1';
el.addEventListener('mouseenter', function () { scheduleShow(el, provider); }); el.addEventListener('mouseenter', function () { scheduleShow(el, provider); });
el.addEventListener('mouseleave', scheduleHide); el.addEventListener('mouseleave', scheduleHide);
el.addEventListener('focus', function () { scheduleShow(el, provider); }); el.addEventListener('focus', function () { scheduleShow(el, provider); });
@ -133,9 +150,20 @@
clearTimeout(showTimer); clearTimeout(showTimer);
activeTarget = target; activeTarget = target;
showTimer = setTimeout(function () { showTimer = setTimeout(function () {
provider(target).then(function (html) { provider(target).then(function (content) {
if (!html || activeTarget !== target) return; if (!content || activeTarget !== target) return;
popup.innerHTML = html; /* Providers may return either an HTML string or a DOM
Node the latter is used by epistemicContent so the
popup receives cloned nodes instead of a re-parsed
HTML round-trip. Handle both forms. */
popup.innerHTML = '';
if (typeof content === 'string') {
popup.innerHTML = content;
} else if (content instanceof Node) {
popup.appendChild(content);
} else {
return;
}
positionPopup(target); positionPopup(target);
popup.classList.add('is-visible'); popup.classList.add('is-visible');
popup.setAttribute('aria-hidden', 'false'); popup.setAttribute('aria-hidden', 'false');
@ -183,6 +211,34 @@
Content providers Content providers
------------------------------------------------------------------ */ ------------------------------------------------------------------ */
/* Cross-origin JSON fetch helper.
Validates Content-Type before parsing so a CORS-enabled endpoint
cannot return text/html and have it interpreted as JSON. The
caller's `.catch` still applies if the JSON parse itself fails.
Mirror helpers exist for text/* (XML/Atom) and HTML responses. */
function fetchJson(url, init) {
return fetch(url, init).then(function (r) {
if (!r.ok) return null;
var ct = (r.headers.get('content-type') || '').toLowerCase();
if (ct && !/(?:^|[\s;,])(?:application\/[a-z+.-]*json|text\/json)\b/.test(ct)) {
return null;
}
return r.json();
});
}
function fetchXml(url, init) {
return fetch(url, init).then(function (r) {
if (!r.ok) return null;
var ct = (r.headers.get('content-type') || '').toLowerCase();
if (ct && !/(?:xml|atom)/.test(ct)) {
return null;
}
return r.text();
});
}
/* 0. Local annotations — synchronous map lookup after eager load */ /* 0. Local annotations — synchronous map lookup after eager load */
function loadAnnotations() { function loadAnnotations() {
if (annotations !== null) return Promise.resolve(annotations); if (annotations !== null) return Promise.resolve(annotations);
@ -288,8 +344,7 @@
+ '?action=query&prop=extracts&exintro=1&format=json&redirects=1' + '?action=query&prop=extracts&exintro=1&format=json&redirects=1'
+ '&titles=' + encodeURIComponent(decodeURIComponent(m[1])) + '&origin=*'; + '&titles=' + encodeURIComponent(decodeURIComponent(m[1])) + '&origin=*';
return fetch(apiUrl) return fetchJson(apiUrl)
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
var pages = data && data.query && data.query.pages; var pages = data && data.query && data.query.pages;
if (!pages) return null; if (!pages) return null;
@ -323,8 +378,7 @@
if (!m) return Promise.resolve(null); if (!m) return Promise.resolve(null);
var id = m[1].replace(/v\d+$/, ''); var id = m[1].replace(/v\d+$/, '');
return fetch('https://export.arxiv.org/api/query?id_list=' + encodeURIComponent(id)) return fetchXml('https://export.arxiv.org/api/query?id_list=' + encodeURIComponent(id))
.then(function (r) { return r.ok ? r.text() : null; })
.then(function (xml) { .then(function (xml) {
if (!xml) return null; if (!xml) return null;
var doc = new DOMParser().parseFromString(xml, 'application/xml'); var doc = new DOMParser().parseFromString(xml, 'application/xml');
@ -357,8 +411,7 @@
var m = href.match(/(?:dx\.)?doi\.org\/(10\.[^?#\s]+)/); var m = href.match(/(?:dx\.)?doi\.org\/(10\.[^?#\s]+)/);
if (!m) return Promise.resolve(null); if (!m) return Promise.resolve(null);
return fetch('https://api.crossref.org/works/' + encodeURIComponent(m[1])) return fetchJson('https://api.crossref.org/works/' + encodeURIComponent(m[1]))
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
var msg = data && data.message; var msg = data && data.message;
if (!msg) return null; if (!msg) return null;
@ -394,9 +447,8 @@
var m = href.match(/github\.com\/([^/]+)\/([^/?#]+)/); var m = href.match(/github\.com\/([^/]+)\/([^/?#]+)/);
if (!m) return Promise.resolve(null); if (!m) return Promise.resolve(null);
return fetch('https://api.github.com/repos/' + m[1] + '/' + m[2], return fetchJson('https://api.github.com/repos/' + m[1] + '/' + m[2],
{ headers: { 'Accept': 'application/vnd.github.v3+json' } }) { headers: { 'Accept': 'application/vnd.github.v3+json' } })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
if (!data || !data.full_name) return null; if (!data || !data.full_name) return null;
var meta = [data.language, data.stargazers_count != null ? '\u2605\u00a0' + data.stargazers_count : null] var meta = [data.language, data.stargazers_count != null ? '\u2605\u00a0' + data.stargazers_count : null]
@ -421,8 +473,7 @@
if (!m) return Promise.resolve(null); if (!m) return Promise.resolve(null);
var apiUrl = 'https://git.levineuwirth.org/api/v1/repos/' + m[1] + '/' + m[2]; var apiUrl = 'https://git.levineuwirth.org/api/v1/repos/' + m[1] + '/' + m[2];
return fetch(apiUrl) return fetchJson(apiUrl)
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
if (!data || !data.full_name) return null; if (!data || !data.full_name) return null;
var meta = [data.language, data.stars_count != null ? '\u2605\u00a0' + data.stars_count : null] var meta = [data.language, data.stars_count != null ? '\u2605\u00a0' + data.stars_count : null]
@ -446,8 +497,7 @@
var base = href.replace(/[?#].*$/, ''); var base = href.replace(/[?#].*$/, '');
var apiUrl = base + '.json'; var apiUrl = base + '.json';
return fetch(apiUrl) return fetchJson(apiUrl)
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
if (!data || !data.title) return null; if (!data || !data.title) return null;
var desc = data.description; var desc = data.description;
@ -476,8 +526,7 @@
var server = /medrxiv/.test(href) ? 'medrxiv' : 'biorxiv'; var server = /medrxiv/.test(href) ? 'medrxiv' : 'biorxiv';
var label = server === 'medrxiv' ? 'medRxiv' : 'bioRxiv'; var label = server === 'medrxiv' ? 'medRxiv' : 'bioRxiv';
return fetch('https://api.biorxiv.org/details/' + server + '/' + encodeURIComponent(doi) + '/json') return fetchJson('https://api.biorxiv.org/details/' + server + '/' + encodeURIComponent(doi) + '/json')
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
var paper = data && data.collection && data.collection[0]; var paper = data && data.collection && data.collection[0];
if (!paper || !paper.title) return null; if (!paper || !paper.title) return null;
@ -505,8 +554,7 @@
var href = target.getAttribute('href'); var href = target.getAttribute('href');
if (!href || cache[href]) return Promise.resolve(cache[href] || null); if (!href || cache[href]) return Promise.resolve(cache[href] || null);
return fetch('https://www.youtube.com/oembed?url=' + encodeURIComponent(href) + '&format=json') return fetchJson('https://www.youtube.com/oembed?url=' + encodeURIComponent(href) + '&format=json')
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
if (!data || !data.title) return null; if (!data || !data.title) return null;
return store(href, return store(href,
@ -527,8 +575,7 @@
var m = href.match(/archive\.org\/details\/([^/?#]+)/); var m = href.match(/archive\.org\/details\/([^/?#]+)/);
if (!m) return Promise.resolve(null); if (!m) return Promise.resolve(null);
return fetch('https://archive.org/metadata/' + encodeURIComponent(m[1])) return fetchJson('https://archive.org/metadata/' + encodeURIComponent(m[1]))
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
var meta = data && data.metadata; var meta = data && data.metadata;
if (!meta) return null; if (!meta) return null;
@ -564,8 +611,7 @@
var apiUrl = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi' var apiUrl = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi'
+ '?db=pubmed&id=' + pmid + '&retmode=json'; + '?db=pubmed&id=' + pmid + '&retmode=json';
return fetch(apiUrl) return fetchJson(apiUrl)
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { .then(function (data) {
var paper = data && data.result && data.result[pmid]; var paper = data && data.result && data.result[pmid];
if (!paper || !paper.title) return null; if (!paper || !paper.title) return null;
@ -597,25 +643,34 @@
/* Epistemic jump link reads the #epistemic section already in the DOM /* Epistemic jump link reads the #epistemic section already in the DOM
and renders a compact summary: status/confidence/dots + expanded DL. and renders a compact summary: status/confidence/dots + expanded DL.
All ep-* classes are already styled via components.css. */ All ep-* classes are already styled via components.css.
Returns a DocumentFragment instead of an HTML string so the popup
receives cloned nodes (defense in depth if a future change ever
allowed user-authored HTML into the source section, the popup
would still see exactly the same already-rendered DOM rather than
a re-parsed string). */
function epistemicContent() { function epistemicContent() {
var section = document.getElementById('epistemic'); var section = document.getElementById('epistemic');
if (!section) return Promise.resolve(null); if (!section) return Promise.resolve(null);
var html = '<div class="popup-epistemic">'; var wrap = document.createElement('div');
wrap.className = 'popup-epistemic';
var compact = section.querySelector('.ep-compact'); var compact = section.querySelector('.ep-compact');
if (compact) { if (compact) {
html += '<div class="ep-compact">' + compact.innerHTML + '</div>'; var compactClone = compact.cloneNode(true);
wrap.appendChild(compactClone);
} }
var expanded = section.querySelector('.ep-expanded'); var expanded = section.querySelector('.ep-expanded');
if (expanded) { if (expanded) {
html += '<dl class="ep-expanded">' + expanded.innerHTML + '</dl>'; var expandedClone = expanded.cloneNode(true);
wrap.appendChild(expandedClone);
} }
html += '</div>'; if (!compact && !expanded) return Promise.resolve(null);
return Promise.resolve(html || null); return Promise.resolve(wrap);
} }
/* Local PDF shows the build-time first-page thumbnail (.thumb.png). /* Local PDF shows the build-time first-page thumbnail (.thumb.png).
@ -652,12 +707,11 @@
}); });
} }
/* Defer to the shared utility (loaded synchronously from
templates/partials/head.html) so this file cannot drift from
annotations.js, semantic-search.js, or build/Utils.hs. */
function esc(s) { function esc(s) {
return String(s) return window.lnUtils.escapeHtml(s);
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
} }
/* Emit a .popup-source label with a data-popup-source attribute so CSS /* Emit a .popup-source label with a data-popup-source attribute so CSS

View File

@ -62,10 +62,17 @@
Model loading dynamic import from CDN, lazy Model loading dynamic import from CDN, lazy
------------------------------------------------------------------ */ ------------------------------------------------------------------ */
/* In-flight promise so concurrent searches share a single model
load. Without this guard, two rapid keystrokes would each call
`import(CDN)` and `pipeline(...)`, wasting CPU and memory before
the second resolves. */
var loadModelPromise = null;
function loadModel() { function loadModel() {
if (extractor) return Promise.resolve(extractor); if (extractor) return Promise.resolve(extractor);
if (loadModelPromise) return loadModelPromise;
setStatus('Loading model…'); setStatus('Loading model…');
return import(CDN).then(function (mod) { loadModelPromise = import(CDN).then(function (mod) {
/* Point transformers.js at our self-hosted model files. */ /* Point transformers.js at our self-hosted model files. */
mod.env.localModelPath = MODEL_PATH; mod.env.localModelPath = MODEL_PATH;
mod.env.allowRemoteModels = false; mod.env.allowRemoteModels = false;
@ -73,7 +80,13 @@
}).then(function (pipe) { }).then(function (pipe) {
extractor = pipe; extractor = pipe;
return extractor; return extractor;
}).catch(function (err) {
/* Allow a retry on the next call instead of caching the
failed promise forever. */
loadModelPromise = null;
throw err;
}); });
return loadModelPromise;
} }
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------
@ -163,12 +176,11 @@
statusEl.textContent = msg; statusEl.textContent = msg;
} }
/* Defer to the shared utility (loaded synchronously from
templates/partials/head.html) so this file cannot drift from
popups.js, annotations.js, or build/Utils.hs. */
function esc(s) { function esc(s) {
return String(s) return window.lnUtils.escapeHtml(s);
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
} }
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------

View File

@ -1,11 +1,19 @@
/* settings.js Settings panel: theme, text size, print. /* settings.js Settings panel: theme, text size, print.
Must stay in sync with TEXT_SIZES in theme.js. */ Must stay in sync with TEXT_SIZES in theme.js.
All localStorage access routes through window.lnUtils.safeStorage so
Safari private-mode SecurityErrors on writes do not throw uncaught. */
(function () { (function () {
'use strict'; 'use strict';
var TEXT_SIZES = [20, 23, 26]; var TEXT_SIZES = [20, 23, 26];
var TEXT_SIZE_DEFAULT = 1; /* index of 23px */ var TEXT_SIZE_DEFAULT = 1; /* index of 23px */
var TEXT_SIZE_KEY = 'text-size'; var TEXT_SIZE_KEY = 'text-size';
var store = (window.lnUtils && window.lnUtils.safeStorage) || {
get: function () { return null; },
set: function () { return false; },
remove: function () { return false; }
};
/* ------------------------------------------------------------------ /* ------------------------------------------------------------------
Init Init
@ -102,7 +110,7 @@
function setTheme(theme) { function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme); store.set('theme', theme);
syncThemeButtons(); syncThemeButtons();
} }
@ -122,13 +130,13 @@
/* Text size ------------------------------------------------------- */ /* Text size ------------------------------------------------------- */
function getSizeIndex() { function getSizeIndex() {
var n = parseInt(localStorage.getItem(TEXT_SIZE_KEY), 10); var n = parseInt(store.get(TEXT_SIZE_KEY), 10);
return (isNaN(n) || n < 0 || n >= TEXT_SIZES.length) ? TEXT_SIZE_DEFAULT : n; return (isNaN(n) || n < 0 || n >= TEXT_SIZES.length) ? TEXT_SIZE_DEFAULT : n;
} }
function shiftSize(delta) { function shiftSize(delta) {
var idx = Math.max(0, Math.min(TEXT_SIZES.length - 1, getSizeIndex() + delta)); var idx = Math.max(0, Math.min(TEXT_SIZES.length - 1, getSizeIndex() + delta));
localStorage.setItem(TEXT_SIZE_KEY, idx); store.set(TEXT_SIZE_KEY, idx);
document.documentElement.style.setProperty('--text-size', TEXT_SIZES[idx] + 'px'); document.documentElement.style.setProperty('--text-size', TEXT_SIZES[idx] + 'px');
syncTextSizeButtons(); syncTextSizeButtons();
} }
@ -140,10 +148,10 @@
var on = html.hasAttribute(attrName); var on = html.hasAttribute(attrName);
if (on) { if (on) {
html.removeAttribute(attrName); html.removeAttribute(attrName);
localStorage.removeItem(storageKey); store.remove(storageKey);
} else { } else {
html.setAttribute(attrName, ''); html.setAttribute(attrName, '');
localStorage.setItem(storageKey, '1'); store.set(storageKey, '1');
} }
syncToggleButton(storageKey, attrName); syncToggleButton(storageKey, attrName);
} }

View File

@ -52,13 +52,19 @@
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Hover + click wiring */ /* Hover + click + keyboard wiring */
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* At most one sidenote is "focused" (click-sticky) at a time. */ /* At most one sidenote is "focused" (click-sticky) at a time. */
let focusedPair = null; let focusedPair = null;
function wireHover(ref, sn) { function wireHover(ref, sn) {
/* Idempotent: skip pairs already wired so 'reinitSidenotes'
after a transclusion injection cannot stack listeners. */
if (ref.dataset.snBound === '1') return;
ref.dataset.snBound = '1';
sn.dataset.snBound = '1';
let hovering = false; let hovering = false;
let focused = false; let focused = false;
@ -70,6 +76,22 @@
function unfocus() { focused = false; update(); } function unfocus() { focused = false; update(); }
function toggleFocus() {
/* Sticky focus only makes sense on wide viewports where the
sidenote is actually visible. On narrow screens there's
nothing to pin. */
if (getComputedStyle(sn).display === 'none') return;
if (focused) {
focused = false;
focusedPair = null;
} else {
if (focusedPair) focusedPair.unfocus();
focused = true;
focusedPair = { ref: ref, sn: sn, unfocus: unfocus };
}
update();
}
ref.addEventListener('mouseenter', function () { hovering = true; update(); }); ref.addEventListener('mouseenter', function () { hovering = true; update(); });
ref.addEventListener('mouseleave', function () { hovering = false; update(); }); ref.addEventListener('mouseleave', function () { hovering = false; update(); });
sn.addEventListener('mouseenter', function () { hovering = true; update(); }); sn.addEventListener('mouseenter', function () { hovering = true; update(); });
@ -81,16 +103,25 @@
if (link) { if (link) {
link.addEventListener('click', function (e) { link.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
if (getComputedStyle(sn).display === 'none') return; /* narrow: no sidenote to focus */ toggleFocus();
if (focused) { });
/* Keyboard activation: Enter follows the link by default (the
browser synthesizes a click), but Space does not. Both are
expected to toggle focus on a focus-activated element, so
we normalize: Enter/Space toggle, Escape clears if focused.
The <a href="#sn-N"> retains its native focusability so
Tab reaches it; we only intercept the key. */
link.addEventListener('keydown', function (e) {
if (e.key === ' ') {
e.preventDefault();
toggleFocus();
} else if (e.key === 'Escape' && focused) {
e.preventDefault();
focused = false; focused = false;
focusedPair = null; focusedPair = null;
} else { update();
if (focusedPair) focusedPair.unfocus();
focused = true;
focusedPair = { ref: ref, sn: sn, unfocus: unfocus };
} }
update();
}); });
} }
} }
@ -105,19 +136,39 @@
} }
}); });
function init() { /* Global Escape dismisses any sticky-focused sidenote, even if focus
const body = document.getElementById('markdownBody'); has moved away from the ref link. */
if (!body) return; document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && focusedPair) {
focusedPair.unfocus();
focusedPair = null;
}
});
body.querySelectorAll('.sidenote').forEach(function (sn) { function wireAll(root) {
root.querySelectorAll('.sidenote').forEach(function (sn) {
const refId = 'snref-' + sn.id.slice(3); const refId = 'snref-' + sn.id.slice(3);
const ref = document.getElementById(refId); const ref = document.getElementById(refId);
if (ref) wireHover(ref, sn); if (ref) wireHover(ref, sn);
}); });
}
function init() {
const body = document.getElementById('markdownBody');
if (!body) return;
wireAll(body);
positionSidenotes(); positionSidenotes();
} }
/* Public re-init hook used by transclude.js after it injects new
content. wireAll is idempotent so calling this multiple times is
safe. */
window.reinitSidenotes = function (container) {
wireAll(container || document.getElementById('markdownBody') || document);
positionSidenotes();
};
document.addEventListener('DOMContentLoaded', init); document.addEventListener('DOMContentLoaded', init);
window.addEventListener('resize', positionSidenotes); window.addEventListener('resize', positionSidenotes);
}()); }());

View File

@ -1,29 +1,36 @@
/* theme.js Restores theme and text size from localStorage before first paint. /* theme.js Restores theme and text size from localStorage before first paint.
Loaded synchronously (no defer/async) to prevent flash of wrong appearance. Loaded synchronously (no defer/async) to prevent flash of wrong appearance.
DOM interaction (button wiring) is handled by settings.js (deferred). DOM interaction (button wiring) is handled by settings.js (deferred).
All storage access routes through window.lnUtils.safeStorage (from
utils.js, loaded immediately before this script) so Safari private-mode
SecurityErrors degrade to default appearance rather than blowing up
the synchronous bootstrap.
*/ */
(function () { (function () {
var TEXT_SIZES = [20, 23, 26]; var TEXT_SIZES = [20, 23, 26];
var store = window.lnUtils && window.lnUtils.safeStorage;
function safeGet(key) { return store ? store.get(key) : null; }
/* Theme */ /* Theme */
var storedTheme = localStorage.getItem('theme'); var storedTheme = safeGet('theme');
if (storedTheme === 'dark' || storedTheme === 'light') { if (storedTheme === 'dark' || storedTheme === 'light') {
document.documentElement.setAttribute('data-theme', storedTheme); document.documentElement.setAttribute('data-theme', storedTheme);
} }
/* Text size */ /* Text size */
var storedSize = parseInt(localStorage.getItem('text-size'), 10); var storedSize = parseInt(safeGet('text-size'), 10);
if (!isNaN(storedSize) && storedSize >= 0 && storedSize < TEXT_SIZES.length) { if (!isNaN(storedSize) && storedSize >= 0 && storedSize < TEXT_SIZES.length) {
document.documentElement.style.setProperty('--text-size', TEXT_SIZES[storedSize] + 'px'); document.documentElement.style.setProperty('--text-size', TEXT_SIZES[storedSize] + 'px');
} }
/* Focus mode */ /* Focus mode */
if (localStorage.getItem('focus-mode')) { if (safeGet('focus-mode')) {
document.documentElement.setAttribute('data-focus-mode', ''); document.documentElement.setAttribute('data-focus-mode', '');
} }
/* Reduce motion */ /* Reduce motion */
if (localStorage.getItem('reduce-motion')) { if (safeGet('reduce-motion')) {
document.documentElement.setAttribute('data-reduce-motion', ''); document.documentElement.setAttribute('data-reduce-motion', '');
} }
})(); })();

View File

@ -39,11 +39,26 @@
} }
} }
// Collapse / expand // Collapse / expand. The collapsed state is hidden from
// assistive technology (aria-hidden="true") and removed from
// the keyboard tab order (tabindex=-1 on each link), so users
// navigating with a screen reader or only the keyboard cannot
// land on links inside a collapsed TOC.
const tocNav = toc.querySelector('.toc-nav');
function setExpanded(open) { function setExpanded(open) {
if (!toggleBtn) return; if (!toggleBtn) return;
toc.classList.toggle('is-collapsed', !open); toc.classList.toggle('is-collapsed', !open);
toggleBtn.setAttribute('aria-expanded', String(open)); toggleBtn.setAttribute('aria-expanded', String(open));
if (tocNav) {
tocNav.setAttribute('aria-hidden', String(!open));
}
links.forEach(function (a) {
if (open) {
a.removeAttribute('tabindex');
} else {
a.setAttribute('tabindex', '-1');
}
});
} }
setExpanded(true); setExpanded(true);
@ -54,13 +69,6 @@
}); });
} }
// Auto-collapse once the first section becomes active via scrolling.
let autoCollapsed = false;
function collapseOnce() {
if (autoCollapsed) return;
autoCollapsed = true;
setExpanded(false);
}
// Progress indicator — drives the horizontal bar under .toc-header. // Progress indicator — drives the horizontal bar under .toc-header.
function updateProgress() { function updateProgress() {
@ -83,7 +91,7 @@
if (visible.size > 0) { if (visible.size > 0) {
// Activate the topmost visible heading in document order. // Activate the topmost visible heading in document order.
const top = headings.find(h => visible.has(h)); const top = headings.find(h => visible.has(h));
if (top) { activate(top.id); collapseOnce(); } if (top) { activate(top.id); }
} else { } else {
// Nothing in the trigger band: activate the last heading // Nothing in the trigger band: activate the last heading
// whose top edge is above the sticky nav bar. // whose top edge is above the sticky nav bar.
@ -93,7 +101,7 @@
if (h.getBoundingClientRect().top < navHeight + 16) candidate = h; if (h.getBoundingClientRect().top < navHeight + 16) candidate = h;
else break; else break;
} }
if (candidate) { activate(candidate.id); collapseOnce(); } if (candidate) { activate(candidate.id); }
else activateTitle(); else activateTitle();
} }
}, { }, {

View File

@ -81,8 +81,21 @@
/* After injection, retrigger layout-dependent subsystems. */ /* After injection, retrigger layout-dependent subsystems. */
function reinitFragment(container) { function reinitFragment(container) {
/* sidenotes.js repositions on resize — dispatch to trigger it. */ /* sidenotes.js wire newly injected sidenote refs/spans and
window.dispatchEvent(new Event('resize')); reposition the column. Falls back to a manual resize event
for older builds that haven't been redeployed yet. */
if (typeof window.reinitSidenotes === 'function') {
window.reinitSidenotes(container);
} else {
window.dispatchEvent(new Event('resize'));
}
/* popups.js bind hover popups for newly injected links so
transcluded content has the same preview behaviour as the
host page. */
if (typeof window.reinitPopups === 'function') {
window.reinitPopups(container);
}
/* collapse.js exposes reinitCollapse for newly added headings. */ /* collapse.js exposes reinitCollapse for newly added headings. */
if (typeof window.reinitCollapse === 'function') { if (typeof window.reinitCollapse === 'function') {

48
static/js/utils.js Normal file
View File

@ -0,0 +1,48 @@
/* utils.js Tiny shared helpers loaded before any other script.
Loaded synchronously (no `defer`) from templates/partials/head.html so
that defer'd scripts can rely on `window.lnUtils` existing at run time.
Keep this file dependency-free and minimal. It's the lowest-level
layer in the JS stack anything heavier should live in a feature
module instead. */
(function (global) {
'use strict';
var lnUtils = global.lnUtils || {};
/* Escape a string for safe interpolation into HTML text content or
double-quoted attribute values. The order of replacements matters:
`&` MUST come first, otherwise the `&amp;` injected by other rules
gets re-escaped to `&amp;amp;`.
Mirror of `Utils.escapeHtml` in build/Utils.hs. */
lnUtils.escapeHtml = function (s) {
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
};
/* Safe localStorage wrapper. Safari throws SecurityError in private
browsing mode on every access including writes so reads AND
writes need to be guarded. Every consumer that touches storage
should route through this helper so degradation is uniform. */
lnUtils.safeStorage = {
get: function (key) {
try { return localStorage.getItem(key); }
catch (_) { return null; }
},
set: function (key, value) {
try { localStorage.setItem(key, value); return true; }
catch (_) { return false; }
},
remove: function (key) {
try { localStorage.removeItem(key); return true; }
catch (_) { return false; }
}
};
global.lnUtils = lnUtils;
})(window);

View File

@ -28,8 +28,8 @@ $if(home)$<script src="/js/random.js" defer></script>$endif$
$if(reading)$<script src="/js/reading.js" defer></script>$endif$ $if(reading)$<script src="/js/reading.js" defer></script>$endif$
$for(page-scripts)$<script src="/$script-src$" defer></script>$endfor$ $for(page-scripts)$<script src="/$script-src$" defer></script>$endfor$
$if(math)$ $if(math)$
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
onload="(function(){var els=Array.from(document.getElementsByClassName('math'));els.forEach(function(el){if(el.tagName==='SPAN'){var src=el.textContent;katex.render(src,el,{displayMode:el.classList.contains('display'),output:'htmlAndMathml',throwOnError:false})}})})()"></script> <script defer src="/js/katex-bootstrap.js"></script>
$endif$ $endif$
</body> </body>
</html> </html>

View File

@ -28,6 +28,7 @@ $endif$
$if(search)$ $if(search)$
<link rel="stylesheet" href="/pagefind/pagefind-ui.css"> <link rel="stylesheet" href="/pagefind/pagefind-ui.css">
$endif$ $endif$
<script src="/js/utils.js"></script>
<script src="/js/theme.js"></script> <script src="/js/theme.js"></script>
$if(viz)$ $if(viz)$
<link rel="stylesheet" href="/css/viz.css"> <link rel="stylesheet" href="/css/viz.css">

View File

@ -12,36 +12,36 @@
<a href="/search.html">Search</a> <a href="/search.html">Search</a>
</div> </div>
<div class="nav-controls"> <div class="nav-controls">
<button class="nav-portal-toggle" aria-label="Toggle portals" aria-expanded="false"> <button type="button" class="nav-portal-toggle" aria-label="Toggle portals" aria-expanded="false">
<span class="nav-portal-arrow"></span>Portals <span class="nav-portal-arrow"></span>Portals
</button> </button>
<div class="settings-wrap"> <div class="settings-wrap">
<button class="settings-toggle" aria-label="Open settings" aria-expanded="false"></button> <button type="button" class="settings-toggle" aria-label="Open settings" aria-expanded="false"></button>
<div class="settings-panel" aria-hidden="true"> <div class="settings-panel" aria-hidden="true">
<div class="settings-section"> <div class="settings-section">
<div class="settings-label">Theme</div> <div class="settings-label">Theme</div>
<div class="settings-row"> <div class="settings-row">
<button class="settings-btn" data-action="theme-light">Light</button> <button type="button" class="settings-btn" data-action="theme-light">Light</button>
<button class="settings-btn" data-action="theme-dark">Dark</button> <button type="button" class="settings-btn" data-action="theme-dark">Dark</button>
</div> </div>
</div> </div>
<div class="settings-section"> <div class="settings-section">
<div class="settings-label">Text size</div> <div class="settings-label">Text size</div>
<div class="settings-row"> <div class="settings-row">
<button class="settings-btn" data-action="text-smaller" aria-label="Decrease text size">A</button> <button type="button" class="settings-btn" data-action="text-smaller" aria-label="Decrease text size">A</button>
<button class="settings-btn" data-action="text-larger" aria-label="Increase text size">A+</button> <button type="button" class="settings-btn" data-action="text-larger" aria-label="Increase text size">A+</button>
</div> </div>
</div> </div>
<div class="settings-section"> <div class="settings-section">
<div class="settings-label">Display</div> <div class="settings-label">Display</div>
<div class="settings-col"> <div class="settings-col">
<button class="settings-btn settings-btn--full" data-action="focus-mode">Focus Mode</button> <button type="button" class="settings-btn settings-btn--full" data-action="focus-mode">Focus Mode</button>
<button class="settings-btn settings-btn--full" data-action="reduce-motion">Reduce Motion</button> <button type="button" class="settings-btn settings-btn--full" data-action="reduce-motion">Reduce Motion</button>
</div> </div>
</div> </div>
<div class="settings-section"> <div class="settings-section">
<button class="settings-btn settings-btn--full" data-action="print">Print</button> <button type="button" class="settings-btn settings-btn--full" data-action="print">Print</button>
<button class="settings-btn settings-btn--full settings-btn--danger" data-action="clear-annotations">Clear Annotations</button> <button type="button" class="settings-btn settings-btn--full settings-btn--danger" data-action="clear-annotations">Clear Annotations</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -25,7 +25,7 @@ skipped=0
while IFS= read -r -d '' img; do while IFS= read -r -d '' img; do
webp="${img%.*}.webp" webp="${img%.*}.webp"
if [ -f "$webp" ]; then if [ -f "$webp" ] && [ ! "$img" -nt "$webp" ]; then
skipped=$((skipped + 1)) skipped=$((skipped + 1))
else else
echo " webp ${img#"$REPO_ROOT/"}" echo " webp ${img#"$REPO_ROOT/"}"

View File

@ -7,25 +7,76 @@
# #
# Run once before deploying. Files are gitignored (binary artifacts). # Run once before deploying. Files are gitignored (binary artifacts).
# Re-running is safe — existing files are skipped. # Re-running is safe — existing files are skipped.
#
# Supply chain hardening: each downloaded file is verified against
# tools/model-checksums.sha256 if that file is present. The checksums
# file is committed to git so a HuggingFace compromise (or a
# transparently rebuilt model) cannot silently land in the deployed
# site. To pin a new model version, run `tools/download-model.sh`,
# verify the model out-of-band, then commit the generated checksums:
#
# (cd static/models/all-MiniLM-L6-v2 && \
# sha256sum config.json tokenizer.json tokenizer_config.json \
# special_tokens_map.json onnx/model_quantized.onnx) \
# > tools/model-checksums.sha256
set -euo pipefail set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MODEL_DIR="$REPO_ROOT/static/models/all-MiniLM-L6-v2" MODEL_DIR="$REPO_ROOT/static/models/all-MiniLM-L6-v2"
BASE_URL="https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main" BASE_URL="https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main"
CHECKSUMS="$REPO_ROOT/tools/model-checksums.sha256"
mkdir -p "$MODEL_DIR/onnx" mkdir -p "$MODEL_DIR/onnx"
# Look up the expected SHA-256 for a relative path, or empty string if
# no checksums file is present (or the path isn't pinned yet).
expected_sha() {
local rel="$1"
if [ ! -f "$CHECKSUMS" ]; then
echo ""
return
fi
# Match lines of the form "<sha> <relative-path>"
awk -v p="$rel" '$2 == p { print $1; exit }' "$CHECKSUMS"
}
verify_sha() {
local rel="$1" dst="$2"
local want
want=$(expected_sha "$rel")
if [ -z "$want" ]; then
return
fi
local got
got=$(sha256sum "$dst" | awk '{ print $1 }')
if [ "$got" != "$want" ]; then
echo " ERROR sha256 mismatch for $rel" >&2
echo " expected $want" >&2
echo " got $got" >&2
rm -f "$dst"
exit 1
fi
echo " ok sha256 verified for $rel"
}
fetch() { fetch() {
local src="$1" dst="$2" local src="$1" dst="$2"
if [ -f "$dst" ]; then if [ -f "$dst" ]; then
echo " skip $src (already present)" echo " skip $src (already present)"
verify_sha "$src" "$dst"
return return
fi fi
echo " fetch $src" echo " fetch $src"
curl -fsSL --progress-bar "$BASE_URL/$src" -o "$dst" curl -fsSL --progress-bar "$BASE_URL/$src" -o "$dst"
verify_sha "$src" "$dst"
} }
if [ ! -f "$CHECKSUMS" ]; then
echo "Note: $CHECKSUMS not found — downloads will not be SHA-verified." >&2
echo " See the comment in tools/download-model.sh to pin checksums." >&2
fi
echo "Downloading all-MiniLM-L6-v2 to $MODEL_DIR ..." echo "Downloading all-MiniLM-L6-v2 to $MODEL_DIR ..."
fetch "config.json" "$MODEL_DIR/config.json" fetch "config.json" "$MODEL_DIR/config.json"

View File

@ -68,8 +68,10 @@ def needs_update() -> bool:
def _url_from_path(html_path: Path) -> str: def _url_from_path(html_path: Path) -> str:
rel = html_path.relative_to(SITE_DIR) rel = html_path.relative_to(SITE_DIR)
if rel.name == "index.html": if rel.name == "index.html":
url = "/" + str(rel.parent) + "/" parent = str(rel.parent)
return url.replace("//", "/") if parent in (".", ""):
return "/"
return "/" + parent + "/"
return "/" + str(rel) return "/" + str(rel)
def _clean_soup(soup: BeautifulSoup) -> None: def _clean_soup(soup: BeautifulSoup) -> None:

View File

@ -44,9 +44,11 @@ _ROMAN_VALS = [
def roman_to_int(s: str) -> Optional[int]: def roman_to_int(s: str) -> Optional[int]:
s = s.upper().strip() s = s.upper().strip()
if not s:
return None
i, result = 0, 0 i, result = 0, 0
for numeral, value in _ROMAN_VALS: for numeral, value in _ROMAN_VALS:
while s[i : i + len(numeral)] == numeral: while i < len(s) and s[i : i + len(numeral)] == numeral:
result += value result += value
i += len(numeral) i += len(numeral)
return result if i == len(s) and result > 0 else None return result if i == len(s) and result > 0 else None
@ -191,15 +193,28 @@ def first_content_line(lines: list[str]) -> str:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def yaml_str(s: str) -> str: def yaml_str(s: str) -> str:
"""Quote a string for YAML if it needs it.""" """Quote a string for YAML if it needs it.
Always quote (and escape) when the string contains characters that
can break YAML parsing including newlines, carriage returns, and
YAML's reserved indicators. Newlines and carriage returns are escaped
using YAML's double-quoted backslash escapes.
"""
needs_quote = ( needs_quote = (
not s not s
or s[0] in " \t" or s[0] in " \t"
or s[-1] in " \t" or s[-1] in " \t"
or any(c in s for c in ':{}[]|>&*!,#?@`\'"') or any(c in s for c in ':{}[]|>&*!,#?@`\'"\n\r\t')
) )
if needs_quote: if needs_quote:
return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"' escaped = (
s.replace("\\", "\\\\")
.replace('"', '\\"')
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t")
)
return '"' + escaped + '"'
return s return s
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -324,9 +339,39 @@ def main() -> None:
print(f"error: file not found: {source}", file=sys.stderr) print(f"error: file not found: {source}", file=sys.stderr)
sys.exit(1) sys.exit(1)
# Validate publication year — flows into YAML unchanged so it must be
# well-formed and within a sane range.
try:
year = int(args.date)
except ValueError:
print(
f"error: --date must be an integer year, got {args.date!r}",
file=sys.stderr,
)
sys.exit(1)
if not (1 <= year <= 2100):
print(
f"error: --date {year} out of plausible range (1..2100)",
file=sys.stderr,
)
sys.exit(1)
# Defaults # Defaults
title_prefix = args.title_prefix or args.collection.rstrip("s") title_prefix = args.title_prefix or args.collection.rstrip("s")
if not title_prefix.strip():
print(
"error: title prefix is empty (check --title-prefix or --collection)",
file=sys.stderr,
)
sys.exit(1)
collection_slug = args.slug or slugify(f"{args.poet}-{args.collection}") collection_slug = args.slug or slugify(f"{args.poet}-{args.collection}")
if not collection_slug or collection_slug == "-":
print(
f"error: collection slug is empty "
f"(check --slug, --collection={args.collection!r}, --poet={args.poet!r})",
file=sys.stderr,
)
sys.exit(1)
tags = [t.strip() for t in args.tags.split(",")] tags = [t.strip() for t in args.tags.split(",")]
out_dir = POETRY_DIR / collection_slug out_dir = POETRY_DIR / collection_slug
@ -389,7 +434,7 @@ def main() -> None:
print(f" skip {path.name}") print(f" skip {path.name}")
skipped += 1 skipped += 1
else: else:
path.write_text(content, encoding="utf-8") path.write_text(content, encoding="utf-8", errors="strict")
print(f" write {path.name}") print(f" write {path.name}")
written += 1 written += 1

View File

@ -40,18 +40,15 @@ if ! GNUPGHOME="$GNUPGHOME" gpg \
fi fi
echo "sign-site: pre-flight OK — signing $SITE_DIR..." >&2 echo "sign-site: pre-flight OK — signing $SITE_DIR..." >&2
count=0 find "$SITE_DIR" -name "*.html" -print0 | xargs -0 -I {} -P $(nproc) \
while IFS= read -r -d '' html; do gpg --homedir "$GNUPGHOME" \
GNUPGHOME="$GNUPGHOME" gpg \
--homedir "$GNUPGHOME" \
--batch \ --batch \
--yes \ --yes \
--detach-sign \ --detach-sign \
--armor \ --armor \
--local-user "$SIGNING_KEY" \ --local-user "$SIGNING_KEY" \
--output "${html}.sig" \ --output "{}.sig" \
"$html" "{}"
count=$((count + 1))
done < <(find "$SITE_DIR" -name "*.html" -print0)
count=$(find "$SITE_DIR" -name "*.html" -printf '.' | wc -c)
echo "Signed $count HTML files in $SITE_DIR." echo "Signed $count HTML files in $SITE_DIR."

334
uv.lock
View File

@ -8,40 +8,18 @@ resolution-markers = [
[[package]] [[package]]
name = "altair" name = "altair"
version = "6.0.0" version = "5.5.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "jinja2" }, { name = "jinja2" },
{ name = "jsonschema" }, { name = "jsonschema" },
{ name = "narwhals" }, { name = "narwhals" },
{ name = "packaging" }, { name = "packaging" },
{ name = "typing-extensions", marker = "python_full_version < '3.15'" }, { name = "typing-extensions", marker = "python_full_version < '3.14'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" } sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305, upload-time = "2024-11-23T23:39:58.542Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410, upload-time = "2025-11-12T08:59:09.804Z" }, { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200, upload-time = "2024-11-23T23:39:56.4Z" },
]
[[package]]
name = "annotated-doc"
version = "0.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
]
[[package]]
name = "anyio"
version = "4.12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" },
] ]
[[package]] [[package]]
@ -76,15 +54,76 @@ wheels = [
] ]
[[package]] [[package]]
name = "click" name = "charset-normalizer"
version = "8.3.1" version = "3.4.7"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" },
{ url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" },
{ url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" },
{ url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" },
{ url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" },
{ url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" },
{ url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" },
{ url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" },
{ url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" },
{ url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" },
{ url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" },
{ url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" },
{ url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" },
{ url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" },
{ url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" },
{ url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" },
{ url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
{ url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
{ url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
{ url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
{ url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
{ url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
{ url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
{ url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
{ url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
{ url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
{ url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
{ url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
{ url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
{ url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
{ url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
{ url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
{ url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
{ url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
{ url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
{ url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
{ url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
{ url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
{ url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
{ url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
{ url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
{ url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
{ url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
{ url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
{ url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
{ url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
{ url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
{ url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
{ url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
{ url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
{ url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
{ url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
{ url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
{ url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
{ url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
{ url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
{ url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
{ url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
{ url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
{ url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
{ url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
{ url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
{ url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
] ]
[[package]] [[package]]
@ -253,15 +292,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" },
] ]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]] [[package]]
name = "hf-xet" name = "hf-xet"
version = "1.4.2" version = "1.4.2"
@ -294,52 +324,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426, upload-time = "2026-03-13T06:58:55.46Z" }, { url = "https://files.pythonhosted.org/packages/b4/7e/ccf239da366b37ba7f0b36095450efae4a64980bdc7ec2f51354205fdf39/hf_xet-1.4.2-cp37-abi3-win_arm64.whl", hash = "sha256:32c012286b581f783653e718c1862aea5b9eb140631685bb0c5e7012c8719a87", size = 3533426, upload-time = "2026-03-13T06:58:55.46Z" },
] ]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]] [[package]]
name = "huggingface-hub" name = "huggingface-hub"
version = "1.7.2" version = "0.36.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "filelock" }, { name = "filelock" },
{ name = "fsspec" }, { name = "fsspec" },
{ name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
{ name = "httpx" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "requests" },
{ name = "tqdm" }, { name = "tqdm" },
{ name = "typer" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/19/15/eafc1c57bf0f8afffb243dcd4c0cceb785e956acc17bba4d9bf2ae21fc9c/huggingface_hub-1.7.2.tar.gz", hash = "sha256:7f7e294e9bbb822e025bdb2ada025fa4344d978175a7f78e824d86e35f7ab43b", size = 724684, upload-time = "2026-03-20T10:36:08.767Z" } sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/08/de/3ad061a05f74728927ded48c90b73521b9a9328c85d841bdefb30e01fb85/huggingface_hub-1.7.2-py3-none-any.whl", hash = "sha256:288f33a0a17b2a73a1359e2a5fd28d1becb2c121748c6173ab8643fb342c850e", size = 618036, upload-time = "2026-03-20T10:36:06.824Z" }, { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" },
] ]
[[package]] [[package]]
@ -502,25 +503,13 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "altair", specifier = ">=5.4" }, { name = "altair", specifier = ">=5.4,<6" },
{ name = "beautifulsoup4", specifier = ">=4.12" }, { name = "beautifulsoup4", specifier = ">=4.12,<5" },
{ name = "faiss-cpu", specifier = ">=1.9" }, { name = "faiss-cpu", specifier = ">=1.9,<2" },
{ name = "matplotlib", specifier = ">=3.9" }, { name = "matplotlib", specifier = ">=3.9,<4" },
{ name = "numpy", specifier = ">=2.0" }, { name = "numpy", specifier = ">=2.0,<3" },
{ name = "sentence-transformers", specifier = ">=3.4" }, { name = "sentence-transformers", specifier = ">=3.4,<4" },
{ name = "torch", specifier = ">=2.5", index = "https://download.pytorch.org/whl/cpu" }, { name = "torch", specifier = ">=2.5,<3", index = "https://download.pytorch.org/whl/cpu" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
] ]
[[package]] [[package]]
@ -640,15 +629,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" },
] ]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]] [[package]]
name = "mpmath" name = "mpmath"
version = "1.3.0" version = "1.3.0"
@ -815,15 +795,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" }, { url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" },
] ]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]] [[package]]
name = "pyparsing" name = "pyparsing"
version = "3.3.2" version = "3.3.2"
@ -994,16 +965,18 @@ wheels = [
] ]
[[package]] [[package]]
name = "rich" name = "requests"
version = "14.3.3" version = "2.33.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "markdown-it-py" }, { name = "certifi" },
{ name = "pygments" }, { name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" },
] ]
[[package]] [[package]]
@ -1216,22 +1189,21 @@ wheels = [
[[package]] [[package]]
name = "sentence-transformers" name = "sentence-transformers"
version = "5.3.0" version = "3.4.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "huggingface-hub" }, { name = "huggingface-hub" },
{ name = "numpy" }, { name = "pillow" },
{ name = "scikit-learn" }, { name = "scikit-learn" },
{ name = "scipy" }, { name = "scipy" },
{ name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" }, { name = "torch", version = "2.10.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
{ name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" }, { name = "torch", version = "2.10.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
{ name = "tqdm" }, { name = "tqdm" },
{ name = "transformers" }, { name = "transformers" },
{ name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/fe/26/448453925b6ce0c29d8b54327caa71ee4835511aef02070467402273079c/sentence_transformers-5.3.0.tar.gz", hash = "sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2", size = 403330, upload-time = "2026-03-12T14:53:40.778Z" } sdist = { url = "https://files.pythonhosted.org/packages/16/74/aca6f8a2b8d62b4daf8c9a0c49d2aa573381caf47dc35cbb343389229376/sentence_transformers-3.4.1.tar.gz", hash = "sha256:68daa57504ff548340e54ff117bd86c1d2f784b21e0fb2689cf3272b8937b24b", size = 223898, upload-time = "2025-01-29T14:25:55.982Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/9c/2fa7224058cad8df68d84bafee21716f30892cecc7ad1ad73bde61d23754/sentence_transformers-5.3.0-py3-none-any.whl", hash = "sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d", size = 512390, upload-time = "2026-03-12T14:53:39.035Z" }, { url = "https://files.pythonhosted.org/packages/05/89/7eb147a37b7f31d3c815543df539d8b8d0425e93296c875cc87719d65232/sentence_transformers-3.4.1-py3-none-any.whl", hash = "sha256:e026dc6d56801fd83f74ad29a30263f401b4b522165c19386d8bc10dcca805da", size = 275896, upload-time = "2025-01-29T14:25:53.614Z" },
] ]
[[package]] [[package]]
@ -1243,15 +1215,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" },
] ]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]] [[package]]
name = "six" name = "six"
version = "1.17.0" version = "1.17.0"
@ -1334,15 +1297,15 @@ dependencies = [
{ name = "typing-extensions", marker = "sys_platform == 'darwin'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin'" },
] ]
wheels = [ wheels = [
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:7fbbf409143a4fe0812a40c0b46a436030a7e1d14fe8c5234dfbe44df47f617e" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:7fbbf409143a4fe0812a40c0b46a436030a7e1d14fe8c5234dfbe44df47f617e" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:b39cafff7229699f9d6e172cac74d85fd71b568268e439e08d9c540e54732a3e" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:b39cafff7229699f9d6e172cac74d85fd71b568268e439e08d9c540e54732a3e" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:358bd7125cbec6e692d60618a5eec7f55a51b29e3652a849fd42af021d818023" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:358bd7125cbec6e692d60618a5eec7f55a51b29e3652a849fd42af021d818023" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:470de4176007c2700735e003a830828a88d27129032a3add07291da07e2a94e8" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:470de4176007c2700735e003a830828a88d27129032a3add07291da07e2a94e8" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:45a1c5057629444aeb1c452c18298fa7f30f2f7aeadd4dc41f9d340980294407" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:45a1c5057629444aeb1c452c18298fa7f30f2f7aeadd4dc41f9d340980294407" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:339e05502b6c839db40e88720cb700f5a3b50cda332284873e851772d41b2c1e" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:339e05502b6c839db40e88720cb700f5a3b50cda332284873e851772d41b2c1e" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:840351da59cedb7bcbc51981880050813c19ef6b898a7fecf73a3afc71aff3fe" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:840351da59cedb7bcbc51981880050813c19ef6b898a7fecf73a3afc71aff3fe" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:c88b1129fd4e14f0f882963c6728315caae35d2f47374d17edeed1edc7697497" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:c88b1129fd4e14f0f882963c6728315caae35d2f47374d17edeed1edc7697497" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f4bea7dc451267c028593751612ad559299589304e68df54ae7672427893ff2c" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f4bea7dc451267c028593751612ad559299589304e68df54ae7672427893ff2c" },
] ]
[[package]] [[package]]
@ -1362,33 +1325,33 @@ dependencies = [
{ name = "typing-extensions", marker = "sys_platform != 'darwin'" }, { name = "typing-extensions", marker = "sys_platform != 'darwin'" },
] ]
wheels = [ wheels = [
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_aarch64.whl", hash = "sha256:8de5a36371b775e2d4881ed12cc7f2de400b1ad3d728aa74a281f649f87c9b8c" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_aarch64.whl", hash = "sha256:8de5a36371b775e2d4881ed12cc7f2de400b1ad3d728aa74a281f649f87c9b8c" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_s390x.whl", hash = "sha256:9accc30b56cb6756d4a9d04fcb8ebc0bb68c7d55c1ed31a8657397d316d31596" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-linux_s390x.whl", hash = "sha256:9accc30b56cb6756d4a9d04fcb8ebc0bb68c7d55c1ed31a8657397d316d31596" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:179451716487f8cb09b56459667fa1f5c4c0946c1e75fbeae77cfc40a5768d87" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:179451716487f8cb09b56459667fa1f5c4c0946c1e75fbeae77cfc40a5768d87" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ee40b8a4b4b2cf0670c6fd4f35a7ef23871af956fecb238fbf5da15a72650b1d" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ee40b8a4b4b2cf0670c6fd4f35a7ef23871af956fecb238fbf5da15a72650b1d" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:21cb5436978ef47c823b7a813ff0f8c2892e266cfe0f1d944879b5fba81bf4e1" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_amd64.whl", hash = "sha256:21cb5436978ef47c823b7a813ff0f8c2892e266cfe0f1d944879b5fba81bf4e1" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:3eaa727e6a73affa61564d86b9d03191df45c8650d0666bd3d57c8597ef61e78" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp312-cp312-win_arm64.whl", hash = "sha256:3eaa727e6a73affa61564d86b9d03191df45c8650d0666bd3d57c8597ef61e78" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_aarch64.whl", hash = "sha256:fd215f3d0f681905c5b56b0630a3d666900a37fcc3ca5b937f95275c66f9fd9c" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_aarch64.whl", hash = "sha256:fd215f3d0f681905c5b56b0630a3d666900a37fcc3ca5b937f95275c66f9fd9c" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:170a0623108055be5199370335cf9b41ba6875b3cb6f086db4aee583331a4899" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-linux_s390x.whl", hash = "sha256:170a0623108055be5199370335cf9b41ba6875b3cb6f086db4aee583331a4899" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e51994492cdb76edce29da88de3672a3022f9ef0ffd90345436948d4992be2c7" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e51994492cdb76edce29da88de3672a3022f9ef0ffd90345436948d4992be2c7" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8d316e5bf121f1eab1147e49ad0511a9d92e4c45cc357d1ab0bee440da71a095" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8d316e5bf121f1eab1147e49ad0511a9d92e4c45cc357d1ab0bee440da71a095" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:b719da5af01b59126ac13eefd6ba3dd12d002dc0e8e79b8b365e55267a8189d3" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_amd64.whl", hash = "sha256:b719da5af01b59126ac13eefd6ba3dd12d002dc0e8e79b8b365e55267a8189d3" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:b67d91326e4ed9eccbd6b7d84ed7ffa43f93103aa3f0b24145f3001f3b11b714" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313-win_arm64.whl", hash = "sha256:b67d91326e4ed9eccbd6b7d84ed7ffa43f93103aa3f0b24145f3001f3b11b714" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_aarch64.whl", hash = "sha256:5af75e5f49de21b0bdf7672bc27139bd285f9e8dbcabe2d617a2eb656514ac36" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_aarch64.whl", hash = "sha256:5af75e5f49de21b0bdf7672bc27139bd285f9e8dbcabe2d617a2eb656514ac36" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_s390x.whl", hash = "sha256:ba51ef01a510baf8fff576174f702c47e1aa54389a9f1fba323bb1a5003ff0bf" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-linux_s390x.whl", hash = "sha256:ba51ef01a510baf8fff576174f702c47e1aa54389a9f1fba323bb1a5003ff0bf" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0fedcb1a77e8f2aaf7bfd21591bf6d1e0b207473268c9be16b17cb7783253969" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0fedcb1a77e8f2aaf7bfd21591bf6d1e0b207473268c9be16b17cb7783253969" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:106dd1930cb30a4a337366ba3f9b25318ebf940f51fd46f789281dd9e736bdc4" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:106dd1930cb30a4a337366ba3f9b25318ebf940f51fd46f789281dd9e736bdc4" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:eb1bde1ce198f05c8770017de27e001d404499cf552aaaa014569eff56ca25c0" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp313-cp313t-win_amd64.whl", hash = "sha256:eb1bde1ce198f05c8770017de27e001d404499cf552aaaa014569eff56ca25c0" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-linux_aarch64.whl", hash = "sha256:ea2bcc9d1fca66974a71d4bf9a502539283f35d61fcab5a799b4e120846f1e02" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-linux_aarch64.whl", hash = "sha256:ea2bcc9d1fca66974a71d4bf9a502539283f35d61fcab5a799b4e120846f1e02" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-linux_s390x.whl", hash = "sha256:f8294fd2fc6dd8f4435a891a0122307a043b14b21f0dac1bca63c85bfb59e586" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-linux_s390x.whl", hash = "sha256:f8294fd2fc6dd8f4435a891a0122307a043b14b21f0dac1bca63c85bfb59e586" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:a28fdbcfa2fbacffec81300f24dd1bed2b0ccfdbed107a823cff12bc1db070f6" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:a28fdbcfa2fbacffec81300f24dd1bed2b0ccfdbed107a823cff12bc1db070f6" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:aada8afc068add586464b2a55adb7cc9091eec55caf5320447204741cb6a0604" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:aada8afc068add586464b2a55adb7cc9091eec55caf5320447204741cb6a0604" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-win_amd64.whl", hash = "sha256:2adc71fe471e98a608723bfc837f7e1929885ebb912c693597711e139c1cda41" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314-win_amd64.whl", hash = "sha256:2adc71fe471e98a608723bfc837f7e1929885ebb912c693597711e139c1cda41" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-linux_aarch64.whl", hash = "sha256:9412bd37b70f5ebd1205242c4ba4cabae35a605947f2b30806d5c9b467936db9" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-linux_aarch64.whl", hash = "sha256:9412bd37b70f5ebd1205242c4ba4cabae35a605947f2b30806d5c9b467936db9" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-linux_s390x.whl", hash = "sha256:e71c476517c33e7db69825a9ff46c7f47a723ec4dac5b2481cff4246d1c632be" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-linux_s390x.whl", hash = "sha256:e71c476517c33e7db69825a9ff46c7f47a723ec4dac5b2481cff4246d1c632be" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:23882f8d882460aca809882fc42f5e343bf07585274f929ced00177d1be1eb67" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:23882f8d882460aca809882fc42f5e343bf07585274f929ced00177d1be1eb67" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4fcd8b4cc2ae20f2b7749fb275349c55432393868778c2d50a08e81d5ee5591e" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4fcd8b4cc2ae20f2b7749fb275349c55432393868778c2d50a08e81d5ee5591e" },
{ url = "https://download.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-win_amd64.whl", hash = "sha256:ffc8da9a1341092d6a90cb5b1c1a33cd61abf0fb43f0cd88443c27fa372c26ae" }, { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp314-cp314t-win_amd64.whl", hash = "sha256:ffc8da9a1341092d6a90cb5b1c1a33cd61abf0fb43f0cd88443c27fa372c26ae" },
] ]
[[package]] [[package]]
@ -1405,37 +1368,23 @@ wheels = [
[[package]] [[package]]
name = "transformers" name = "transformers"
version = "5.3.0" version = "4.57.6"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "filelock" },
{ name = "huggingface-hub" }, { name = "huggingface-hub" },
{ name = "numpy" }, { name = "numpy" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "regex" }, { name = "regex" },
{ name = "requests" },
{ name = "safetensors" }, { name = "safetensors" },
{ name = "tokenizers" }, { name = "tokenizers" },
{ name = "tqdm" }, { name = "tqdm" },
{ name = "typer" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" } sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/88/ae8320064e32679a5429a2c9ebbc05c2bf32cefb6e076f9b07f6d685a9b4/transformers-5.3.0-py3-none-any.whl", hash = "sha256:50ac8c89c3c7033444fb3f9f53138096b997ebb70d4b5e50a2e810bf12d3d29a", size = 10661827, upload-time = "2026-03-04T17:41:42.722Z" }, { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" },
]
[[package]]
name = "typer"
version = "0.24.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-doc" },
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" },
] ]
[[package]] [[package]]
@ -1446,3 +1395,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac8
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
] ]
[[package]]
name = "urllib3"
version = "2.6.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
]