From f6c3b1a506ad6dbdab54425408b9b3ce7ff4a7f0 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Mon, 21 Sep 2026 13:07:29 -0500 Subject: [PATCH] pampa: record byte-for-byte provenance of Math.text (Math.text_source) (bd-ieldbghj) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Math.source_info` spans the whole `$…$` / `$$…$$` node, but `text` is not a plain substring of it once a math node spans lines: the reader folds each soft break of inline math to a literal `\n` and strips the block-continuation gutter (`> `, list indentation) from the interior lines of display math. Consumers that parse `text` further (the coming quarto-math crate, bd-entbg6x3) need to map any byte of it back to the `.qmd`. This adds that mapping. quarto-pandoc-types - `Math.text_source: Option`: length == `text.len()`, `map_offset(i)` lands on the byte `text[i]` came from. `None` means no mapping finer than `source_info` is known. pampa reader - `location::provenance_builder_with_context`: a ProvenanceBuilder rooted like `node_source_info_with_context` (in_parent under an embedded re-parse, in_file otherwise). - inline math: text segments verbatim; a soft break whose bytes are exactly `\n` verbatim; any other soft break (gutter, CRLF) a `replacement(range, 1)` so the folded newline maps to the source line ending. - display math: first line and every `\n` verbatim; each stripped continuation gutter a zero-length replacement (deletion). Behavior of the strip itself is unchanged (bd-q6ed / bd-qpa2 still own it). JSON wire - new `textS` sidecar (bare pool ref) after `t`, emitted by both the Value and the streaming writer, read back by the reader; absent or null reads as `None`. Documented next to `a`/`targetS` in claude-notes/designs/raw-json-format.md. Filters and transforms - Lua: assigning `.text` clears the mapping; `pandoc.Math()` builds with `None`. - crossref_render: appending `\tag{N}` keeps the mapping by concatenating a zero-width synthesized piece at the node end. - every other constructor (tests, generators, equation label, math_js, JSON reader) passes `None`. Tests - crates/pampa/tests/integration/math_text_source.rs (15): single- and multi-line inline math, blockquote and list gutters, display math plain / quoted / nested-quoted / listed / labeled / inside strong, several nodes per paragraph, JSON round trip of `textS`, JSON without the sidecar reads as `None`, and a sweep over every in-tree `.qmd` with math (21 files, 36 nodes) asserting each text byte maps inside the node span to the identical source byte unless it is a folded newline. - quarto-core crossref_render: `equation_tag_extends_text_source_ instead_of_dropping_it`. - test_location_health now validates `text_source` alongside `source_info`. - `textS` added to every source-key strip list: Rust `remove_location_fields`; TS `stripSourceInfoFields` (+ unit test), `pampaOracle`, tiptap spike `canonical`/`pampa`; `textS?: number` on the TS `Annotated_Inline_Math` type. Snapshots (1 file updated, reviewed) - crates/pampa/snapshots/json/math-with-attr.snap: each of the three Math nodes gains a `textS` ref and the pool gains the matching body ranges (e.g. 29..37 for `E = mc^2` inside `$E = mc^2$` at 28..38). No other change. - ts-packages/annotated-qmd/examples/academic-paper.json regenerated with the live writer (guard test); only `textS` refs + pool rows added. Verified: cargo nextest run --workspace (14,032 passed) and full cargo xtask verify (all steps passed). Co-Authored-By: Claude Fable 5.1 --- claude-notes/designs/raw-json-format.md | 2 +- crates/comrak-to-pandoc/src/compare.rs | 2 + .../pampa/snapshots/json/math-with-attr.snap | 2 +- crates/pampa/src/citeproc_filter.rs | 1 + crates/pampa/src/filters.rs | 1 + crates/pampa/src/lua/constructors.rs | 1 + crates/pampa/src/lua/filter.rs | 3 +- crates/pampa/src/lua/filter_tests.rs | 3 + crates/pampa/src/lua/types.rs | 6 + crates/pampa/src/pandoc/location.rs | 24 + crates/pampa/src/pandoc/treesitter.rs | 73 ++- crates/pampa/src/readers/json.rs | 7 + crates/pampa/src/writers/json.rs | 70 ++- crates/pampa/src/writers/plaintext.rs | 1 + crates/pampa/tests/integration/main.rs | 1 + .../tests/integration/math_text_source.rs | 477 ++++++++++++++++++ crates/pampa/tests/integration/test.rs | 1 + .../tests/integration/test_ansi_writer.rs | 1 + .../tests/integration/test_location_health.rs | 3 + crates/quarto-ast-reconcile/src/generators.rs | 1 + crates/quarto-ast-reconcile/src/hash.rs | 5 + .../quarto-core/src/stage/stages/math_js.rs | 2 + crates/quarto-core/src/template.rs | 1 + .../src/transforms/crossref_index.rs | 1 + .../src/transforms/crossref_render.rs | 70 ++- .../src/transforms/equation_label.rs | 4 + .../src/transforms/metadata_normalize.rs | 1 + crates/quarto-pandoc-types/src/inline.rs | 19 + .../examples/academic-paper.json | 2 +- ts-packages/pandoc-types/src/types.ts | 7 + .../q2-preview/stripSourceInfoFields.test.ts | 21 + .../src/q2-preview/stripSourceInfoFields.ts | 3 +- .../tiptap-roundtrip-spike/canonical.ts | 2 +- .../tiptap-roundtrip-spike/pampa.ts | 1 + .../src/test-utils/pampaOracle.ts | 2 +- 35 files changed, 780 insertions(+), 41 deletions(-) create mode 100644 crates/pampa/tests/integration/math_text_source.rs diff --git a/claude-notes/designs/raw-json-format.md b/claude-notes/designs/raw-json-format.md index 7b53a6249..75d7dc8d0 100644 --- a/claude-notes/designs/raw-json-format.md +++ b/claude-notes/designs/raw-json-format.md @@ -71,7 +71,7 @@ What is explicitly promised and not promised: ## Node vocabulary (delta over the Pandoc-superset format) Everything the `-t json` format emits is unchanged (same `t`/`c` tags, -same `s`/`a`/`targetS` sidecars, same pool codes — see +same `s`/`a`/`targetS`/`textS` sidecars, same pool codes — see `wire-format-source-info-codes.md`, which raw-json shares). Raw mode adds: | Tag | Shape | AST type | diff --git a/crates/comrak-to-pandoc/src/compare.rs b/crates/comrak-to-pandoc/src/compare.rs index a00168c0a..c4b5dee75 100644 --- a/crates/comrak-to-pandoc/src/compare.rs +++ b/crates/comrak-to-pandoc/src/compare.rs @@ -445,11 +445,13 @@ mod tests { math_type: quarto_pandoc_types::MathType::InlineMath, text: "x^2".to_string(), source_info: dummy_source(), + text_source: None, }); let b = Inline::Math(quarto_pandoc_types::Math { math_type: quarto_pandoc_types::MathType::InlineMath, text: "x^2".to_string(), source_info: other_source(), + text_source: None, }); assert!(inline_eq(&a, &b)); } diff --git a/crates/pampa/snapshots/json/math-with-attr.snap b/crates/pampa/snapshots/json/math-with-attr.snap index de8caa797..160be5514 100644 --- a/crates/pampa/snapshots/json/math-with-attr.snap +++ b/crates/pampa/snapshots/json/math-with-attr.snap @@ -2,4 +2,4 @@ source: crates/pampa/tests/integration/test.rs expression: output --- -{"blocks":[{"c":[{"c":"Inline","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"math","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"with","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"attribute:","s":9,"t":"Str"},{"s":10,"t":"Space"},{"a":{"classes":[],"id":12,"kvs":[]},"c":[["eq-einstein",["quarto-math-with-attribute"],[]],[{"c":[{"t":"InlineMath"},"E = mc^2"],"s":13,"t":"Math"}]],"s":11,"t":"Span"}],"s":0,"t":"Para"},{"c":[{"c":"Display","s":15,"t":"Str"},{"s":16,"t":"Space"},{"c":"math","s":17,"t":"Str"},{"s":18,"t":"Space"},{"c":"with","s":19,"t":"Str"},{"s":20,"t":"Space"},{"c":"attribute:","s":23,"t":"Str"}],"s":14,"t":"Para"},{"c":[{"a":{"classes":[],"id":26,"kvs":[]},"c":[["eq-gaussian",["quarto-math-with-attribute"],[]],[{"c":[{"t":"DisplayMath"},"\n\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}\n"],"s":27,"t":"Math"}]],"s":25,"t":"Span"}],"s":24,"t":"Para"},{"c":[{"c":"Another","s":29,"t":"Str"},{"s":30,"t":"Space"},{"c":"inline","s":31,"t":"Str"},{"s":32,"t":"Space"},{"c":"example:","s":35,"t":"Str"},{"s":36,"t":"Space"},{"a":{"classes":[],"id":38,"kvs":[]},"c":[["eq-pythagorean",["quarto-math-with-attribute"],[]],[{"c":[{"t":"InlineMath"},"a^2 + b^2 = c^2"],"s":39,"t":"Math"}]],"s":37,"t":"Span"}],"s":28,"t":"Para"}],"meta":{},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[53,54,83,84,87,136,154,155,215],"name":"tests/snapshots/json/math-with-attr.qmd","total_length":216}],"p":[{"d":0,"r":[0,54],"t":0},{"d":0,"r":[0,6],"t":0},{"d":0,"r":[6,7],"t":0},{"d":0,"r":[7,11],"t":0},{"d":0,"r":[11,12],"t":0},{"d":0,"r":[12,16],"t":0},{"d":0,"r":[16,17],"t":0},{"d":0,"r":[17,26],"t":0},{"d":0,"r":[26,27],"t":0},{"d":[[7,0,9],[8,9,1]],"r":[0,10],"t":2},{"d":0,"r":[27,28],"t":0},{"d":0,"r":[28,53],"t":0},{"d":0,"r":[40,52],"t":0},{"d":0,"r":[28,38],"t":0},{"d":0,"r":[55,84],"t":0},{"d":0,"r":[55,62],"t":0},{"d":0,"r":[62,63],"t":0},{"d":0,"r":[63,67],"t":0},{"d":0,"r":[67,68],"t":0},{"d":0,"r":[68,72],"t":0},{"d":0,"r":[72,73],"t":0},{"d":0,"r":[73,82],"t":0},{"d":0,"r":[82,83],"t":0},{"d":[[21,0,9],[22,9,1]],"r":[0,10],"t":2},{"d":0,"r":[85,155],"t":0},{"d":0,"r":[85,154],"t":0},{"d":0,"r":[141,153],"t":0},{"d":0,"r":[85,139],"t":0},{"d":0,"r":[156,216],"t":0},{"d":0,"r":[156,163],"t":0},{"d":0,"r":[163,164],"t":0},{"d":0,"r":[164,170],"t":0},{"d":0,"r":[170,171],"t":0},{"d":0,"r":[171,178],"t":0},{"d":0,"r":[178,179],"t":0},{"d":[[33,0,7],[34,7,1]],"r":[0,8],"t":2},{"d":0,"r":[179,180],"t":0},{"d":0,"r":[180,215],"t":0},{"d":0,"r":[199,214],"t":0},{"d":0,"r":[180,197],"t":0}]}} +{"blocks":[{"c":[{"c":"Inline","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"math","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"with","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"attribute:","s":9,"t":"Str"},{"s":10,"t":"Space"},{"a":{"classes":[],"id":12,"kvs":[]},"c":[["eq-einstein",["quarto-math-with-attribute"],[]],[{"c":[{"t":"InlineMath"},"E = mc^2"],"s":13,"t":"Math","textS":14}]],"s":11,"t":"Span"}],"s":0,"t":"Para"},{"c":[{"c":"Display","s":16,"t":"Str"},{"s":17,"t":"Space"},{"c":"math","s":18,"t":"Str"},{"s":19,"t":"Space"},{"c":"with","s":20,"t":"Str"},{"s":21,"t":"Space"},{"c":"attribute:","s":24,"t":"Str"}],"s":15,"t":"Para"},{"c":[{"a":{"classes":[],"id":27,"kvs":[]},"c":[["eq-gaussian",["quarto-math-with-attribute"],[]],[{"c":[{"t":"DisplayMath"},"\n\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}\n"],"s":28,"t":"Math","textS":29}]],"s":26,"t":"Span"}],"s":25,"t":"Para"},{"c":[{"c":"Another","s":31,"t":"Str"},{"s":32,"t":"Space"},{"c":"inline","s":33,"t":"Str"},{"s":34,"t":"Space"},{"c":"example:","s":37,"t":"Str"},{"s":38,"t":"Space"},{"a":{"classes":[],"id":40,"kvs":[]},"c":[["eq-pythagorean",["quarto-math-with-attribute"],[]],[{"c":[{"t":"InlineMath"},"a^2 + b^2 = c^2"],"s":41,"t":"Math","textS":42}]],"s":39,"t":"Span"}],"s":30,"t":"Para"}],"meta":{},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[53,54,83,84,87,136,154,155,215],"name":"tests/snapshots/json/math-with-attr.qmd","total_length":216}],"p":[{"d":0,"r":[0,54],"t":0},{"d":0,"r":[0,6],"t":0},{"d":0,"r":[6,7],"t":0},{"d":0,"r":[7,11],"t":0},{"d":0,"r":[11,12],"t":0},{"d":0,"r":[12,16],"t":0},{"d":0,"r":[16,17],"t":0},{"d":0,"r":[17,26],"t":0},{"d":0,"r":[26,27],"t":0},{"d":[[7,0,9],[8,9,1]],"r":[0,10],"t":2},{"d":0,"r":[27,28],"t":0},{"d":0,"r":[28,53],"t":0},{"d":0,"r":[40,52],"t":0},{"d":0,"r":[28,38],"t":0},{"d":0,"r":[29,37],"t":0},{"d":0,"r":[55,84],"t":0},{"d":0,"r":[55,62],"t":0},{"d":0,"r":[62,63],"t":0},{"d":0,"r":[63,67],"t":0},{"d":0,"r":[67,68],"t":0},{"d":0,"r":[68,72],"t":0},{"d":0,"r":[72,73],"t":0},{"d":0,"r":[73,82],"t":0},{"d":0,"r":[82,83],"t":0},{"d":[[22,0,9],[23,9,1]],"r":[0,10],"t":2},{"d":0,"r":[85,155],"t":0},{"d":0,"r":[85,154],"t":0},{"d":0,"r":[141,153],"t":0},{"d":0,"r":[85,139],"t":0},{"d":0,"r":[87,137],"t":0},{"d":0,"r":[156,216],"t":0},{"d":0,"r":[156,163],"t":0},{"d":0,"r":[163,164],"t":0},{"d":0,"r":[164,170],"t":0},{"d":0,"r":[170,171],"t":0},{"d":0,"r":[171,178],"t":0},{"d":0,"r":[178,179],"t":0},{"d":[[35,0,7],[36,7,1]],"r":[0,8],"t":2},{"d":0,"r":[179,180],"t":0},{"d":0,"r":[180,215],"t":0},{"d":0,"r":[199,214],"t":0},{"d":0,"r":[180,197],"t":0},{"d":0,"r":[181,196],"t":0}]}} diff --git a/crates/pampa/src/citeproc_filter.rs b/crates/pampa/src/citeproc_filter.rs index 0a8371d0c..2d261c4dc 100644 --- a/crates/pampa/src/citeproc_filter.rs +++ b/crates/pampa/src/citeproc_filter.rs @@ -1136,6 +1136,7 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".to_string(), source_info: si(), + text_source: None, })]; assert_eq!(inlines_to_text(&inlines), "x^2"); } diff --git a/crates/pampa/src/filters.rs b/crates/pampa/src/filters.rs index 175cf208d..10bf74e99 100644 --- a/crates/pampa/src/filters.rs +++ b/crates/pampa/src/filters.rs @@ -1392,6 +1392,7 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".to_string(), source_info: si(), + text_source: None, }); let result = topdown_traverse_inline(inline, &mut filter, &mut ctx); assert_eq!(result.len(), 1); diff --git a/crates/pampa/src/lua/constructors.rs b/crates/pampa/src/lua/constructors.rs index 69cd3fabb..e22358463 100644 --- a/crates/pampa/src/lua/constructors.rs +++ b/crates/pampa/src/lua/constructors.rs @@ -1222,6 +1222,7 @@ fn register_inline_constructors(lua: &Lua, pandoc: &LuaTable) -> Result<()> { math_type: mt, text, source_info: filter_source_info(lua), + text_source: None, }))) })?, )?; diff --git a/crates/pampa/src/lua/filter.rs b/crates/pampa/src/lua/filter.rs index 9ad592c62..a519cae18 100644 --- a/crates/pampa/src/lua/filter.rs +++ b/crates/pampa/src/lua/filter.rs @@ -1476,7 +1476,8 @@ mod unit_tests { inline_tag(&Inline::Math(Math { math_type: MathType::InlineMath, text: String::new(), - source_info: source_info.clone() + source_info: source_info.clone(), + text_source: None, })), "Math" ); diff --git a/crates/pampa/src/lua/filter_tests.rs b/crates/pampa/src/lua/filter_tests.rs index 6eeebafa6..cf30dd590 100644 --- a/crates/pampa/src/lua/filter_tests.rs +++ b/crates/pampa/src/lua/filter_tests.rs @@ -2406,6 +2406,7 @@ end math_type: crate::pandoc::MathType::InlineMath, text: "x^2".to_string(), source_info: quarto_source_map::SourceInfo::for_test(), + text_source: None, })], source_info: quarto_source_map::SourceInfo::for_test(), })], @@ -2438,6 +2439,7 @@ end math_type: crate::pandoc::MathType::DisplayMath, text: "E=mc^2".to_string(), source_info: quarto_source_map::SourceInfo::for_test(), + text_source: None, })], source_info: quarto_source_map::SourceInfo::for_test(), })], @@ -3496,6 +3498,7 @@ end text: "x = a + b".to_string(), math_type: crate::pandoc::MathType::InlineMath, source_info: quarto_source_map::SourceInfo::for_test(), + text_source: None, })], source_info: quarto_source_map::SourceInfo::for_test(), })], diff --git a/crates/pampa/src/lua/types.rs b/crates/pampa/src/lua/types.rs index 5cf674765..0fe9d980b 100644 --- a/crates/pampa/src/lua/types.rs +++ b/crates/pampa/src/lua/types.rs @@ -694,6 +694,10 @@ impl LuaInline { // Math (Inline::Math(m), "text") => { m.text = String::from_lua(val, lua)?; + // The text no longer comes from the source bytes the + // reader mapped; drop the mapping rather than keep a stale + // one (bd-ieldbghj). + m.text_source = None; Ok(()) } (Inline::Math(m), "mathtype") => { @@ -3753,6 +3757,7 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".into(), source_info: si(), + text_source: None, }); assert_eq!(LuaInline::new(inline).tag_name(), "Math"); } @@ -3988,6 +3993,7 @@ mod tests { math_type: MathType::DisplayMath, text: "E=mc^2".into(), source_info: si(), + text_source: None, }); assert_eq!( LuaInline::new(inline).field_names(), diff --git a/crates/pampa/src/pandoc/location.rs b/crates/pampa/src/pandoc/location.rs index 2c0753caa..855586ef4 100644 --- a/crates/pampa/src/pandoc/location.rs +++ b/crates/pampa/src/pandoc/location.rs @@ -321,6 +321,30 @@ pub fn node_source_info_with_options( } } +/// A [`ProvenanceBuilder`] rooted the way [`node_source_info_with_context`] +/// roots node spans: as `Substring` pieces over the parent `SourceInfo` when +/// this parse is a re-parse of an embedded string, and as `Original` pieces +/// in the current file otherwise. In both cases the byte ranges fed to the +/// builder are tree-sitter node offsets, exactly the coordinates +/// `node_source_info_with_context` uses. +/// +/// Use it whenever a reader decodes a node's bytes into text that is not a +/// plain substring of the node (folded soft breaks, stripped continuation +/// gutters, collapsed escapes) and wants a `SourceInfo` for the decoded +/// text (e.g. `Math.text_source`, bd-ieldbghj). +/// +/// `anchor` is the node-offset the empty-piece case falls back to; see +/// [`ProvenanceBuilder::in_file`]. +pub fn provenance_builder_with_context( + context: &ASTContext, + anchor: usize, +) -> quarto_source_map::ProvenanceBuilder { + match &context.parent_source_info { + Some(parent) => quarto_source_map::ProvenanceBuilder::in_parent(parent.clone(), anchor), + None => quarto_source_map::ProvenanceBuilder::in_file(context.current_file_id(), anchor), + } +} + /// Convert a Range to SourceInfo using the context's primary file ID. /// /// # Arguments diff --git a/crates/pampa/src/pandoc/treesitter.rs b/crates/pampa/src/pandoc/treesitter.rs index 61f3b95d7..0e779fad2 100644 --- a/crates/pampa/src/pandoc/treesitter.rs +++ b/crates/pampa/src/pandoc/treesitter.rs @@ -43,6 +43,7 @@ use crate::pandoc::treesitter_utils::text_helpers::*; use crate::pandoc::treesitter_utils::thematic_break::process_thematic_break; use crate::pandoc::treesitter_utils::uri_autolink::process_uri_autolink; use quarto_error_reporting::DiagnosticMessageBuilder; +use quarto_source_map::ProvenanceBuilder; use crate::pandoc::ast_context::ASTContext; use crate::pandoc::attr::AttrSourceInfo; @@ -52,7 +53,9 @@ use crate::pandoc::inline::{ RawInline, SoftBreak, Space, Str, Strikeout, Strong, Subscript, Superscript, }; use crate::pandoc::list::{ListAttributes, ListNumberDelim, ListNumberStyle}; -use crate::pandoc::location::{node_location, node_source_info_with_context}; +use crate::pandoc::location::{ + node_location, node_source_info_with_context, provenance_builder_with_context, +}; use crate::pandoc::pandoc::Pandoc; use core::panic; use once_cell::sync::Lazy; @@ -471,29 +474,59 @@ fn block_continuation_column(node: &tree_sitter::Node) -> usize { /// The first piece (between the opening `$$` and the first `\n`) is /// content right after the delimiter, never a continuation line, and is /// always left untouched. -fn strip_continuation_prefix(content: &str, start_col: usize) -> String { +/// +/// Provenance: every kept byte is recorded on `builder` as `verbatim` and +/// every stripped gutter as a zero-length `replacement` (a deletion), so +/// `builder.finish()` yields a `SourceInfo` for the returned string whose +/// offsets map byte-for-byte into the source (`Math.text_source`, +/// bd-ieldbghj). `content_start` is the byte offset of `content[0]` in the +/// builder's coordinate space (the node offset of the byte after `$$`). +fn strip_continuation_prefix( + content: &str, + content_start: usize, + start_col: usize, + builder: &mut ProvenanceBuilder, +) -> String { if start_col == 0 { + builder.verbatim(content_start..content_start + content.len()); return content.to_string(); } let mut result = String::with_capacity(content.len()); let mut first = true; + // Byte offset of `line`'s first byte, in the same coordinates as + // `content_start`. + let mut pos = content_start; for line in content.split('\n') { if first { result.push_str(line); + builder.verbatim(pos..pos + line.len()); first = false; + pos += line.len() + 1; continue; } + // The `\n` separator that `split` consumed sits at `pos - 1` and is + // kept as-is. result.push('\n'); + builder.verbatim(pos - 1..pos); let line_bytes = line.as_bytes(); let strip_n = std::cmp::min(start_col, line_bytes.len()); let prefix_is_continuation = line_bytes[..strip_n] .iter() .all(|b| matches!(*b, b'>' | b' ' | b'\t')); if prefix_is_continuation { + // The gutter is a deletion: source bytes with no content bytes. + if strip_n > 0 { + builder.replacement(pos..pos + strip_n, 0); + } result.push_str(&line[strip_n..]); + if strip_n < line.len() { + builder.verbatim(pos + strip_n..pos + line.len()); + } } else { result.push_str(line); + builder.verbatim(pos..pos + line.len()); } + pos += line.len() + 1; } result } @@ -507,7 +540,17 @@ fn strip_continuation_prefix(content: &str, start_col: usize) -> String { /// blockquotes, the indent for list items). Pandoc preserves the literal `\n` /// in `Math InlineMath` text and strips the gutter, so we replace each /// soft_break range with `\n` and append the text segments verbatim. -fn extract_inline_math_text(node: &tree_sitter::Node, input_bytes: &[u8]) -> String { +/// +/// Returns the text together with its provenance (`Math.text_source`, +/// bd-ieldbghj): text segments are `verbatim` pieces; a soft break whose +/// source bytes are exactly `\n` is verbatim too, any other soft break (one +/// that swallowed a gutter, or a CRLF) is a `replacement` of its whole range +/// by the one `\n` it folds to, so that byte maps to the source line ending. +fn extract_inline_math_text( + node: &tree_sitter::Node, + input_bytes: &[u8], + context: &ASTContext, +) -> (String, quarto_source_map::SourceInfo) { let start = node.start_byte(); let end = node.end_byte(); // Strip the opening / closing `$` (each is exactly 1 byte). @@ -515,12 +558,15 @@ fn extract_inline_math_text(node: &tree_sitter::Node, input_bytes: &[u8]) -> Str let body_end = end - 1; debug_assert!(body_start <= body_end); + let mut builder = provenance_builder_with_context(context, body_start); let mut text = String::new(); let mut cursor = node.walk(); if !cursor.goto_first_child() { // No structural children — single-line math. let bytes = &input_bytes[body_start..body_end]; - return std::str::from_utf8(bytes).unwrap().to_string(); + text.push_str(std::str::from_utf8(bytes).unwrap()); + builder.verbatim(body_start..body_end); + return (text, builder.finish()); } let mut byte_cursor = body_start; loop { @@ -530,8 +576,15 @@ fn extract_inline_math_text(node: &tree_sitter::Node, input_bytes: &[u8]) -> Str if child.start_byte() > byte_cursor { let bytes = &input_bytes[byte_cursor..child.start_byte()]; text.push_str(std::str::from_utf8(bytes).unwrap()); + builder.verbatim(byte_cursor..child.start_byte()); } text.push('\n'); + let break_range = child.start_byte()..child.end_byte(); + if &input_bytes[break_range.clone()] == b"\n" { + builder.verbatim(break_range); + } else { + builder.replacement(break_range, 1); + } byte_cursor = child.end_byte(); } // Other kinds — including anonymous `$` delimiter tokens — are @@ -545,8 +598,9 @@ fn extract_inline_math_text(node: &tree_sitter::Node, input_bytes: &[u8]) -> Str if byte_cursor < body_end { let bytes = &input_bytes[byte_cursor..body_end]; text.push_str(std::str::from_utf8(bytes).unwrap()); + builder.verbatim(byte_cursor..body_end); } - text + (text, builder.finish()) } /// Detect whether a list item's tree-sitter node contains a blank line between @@ -746,12 +800,13 @@ fn native_visitor( // blockquotes, the indent for list items). Pandoc preserves the // literal `\n` in the math text and strips the gutter, so we // collapse each pandoc_soft_break range to a single `\n`. - let text = extract_inline_math_text(node, input_bytes); + let (text, text_source) = extract_inline_math_text(node, input_bytes, context); PandocNativeIntermediate::IntermediateInline(Inline::Math(Math { math_type: MathType::InlineMath, text, source_info: node_source_info_with_context(node, context), + text_source: Some(text_source), })) } "pandoc_display_math" => { @@ -771,12 +826,16 @@ fn native_visitor( // on the opening line (e.g. `_`, `[`, `**`) don't shift the // column away from the true continuation prefix width (bd-qpa2). let start_col = block_continuation_column(node); - let text = strip_continuation_prefix(content, start_col); + // `content` starts right after the opening `$$`. + let content_start = node.start_byte() + 2; + let mut builder = provenance_builder_with_context(context, content_start); + let text = strip_continuation_prefix(content, content_start, start_col, &mut builder); PandocNativeIntermediate::IntermediateInline(Inline::Math(Math { math_type: MathType::DisplayMath, text, source_info: node_source_info_with_context(node, context), + text_source: Some(builder.finish()), })) } "pandoc_str" => { diff --git a/crates/pampa/src/readers/json.rs b/crates/pampa/src/readers/json.rs index 07db518c1..2abc8a563 100644 --- a/crates/pampa/src/readers/json.rs +++ b/crates/pampa/src/readers/json.rs @@ -921,10 +921,17 @@ fn read_inline(value: &Value, deserializer: &SourceInfoDeserializer) -> Result Some(deserializer.from_json_ref(v)?), + _ => None, + }; Ok(Inline::Math(Math { math_type, text, source_info, + text_source, })) } "Underline" => { diff --git a/crates/pampa/src/writers/json.rs b/crates/pampa/src/writers/json.rs index bf6eab405..85497818c 100644 --- a/crates/pampa/src/writers/json.rs +++ b/crates/pampa/src/writers/json.rs @@ -792,12 +792,16 @@ fn write_inline(inline: &Inline, ctx: &mut JsonWriterContext) -> Value { crate::pandoc::MathType::InlineMath => json!({"t": "InlineMath"}), crate::pandoc::MathType::DisplayMath => json!({"t": "DisplayMath"}), }; - node_with_source( - "Math", - Some(json!([math_type, m.text])), - &m.source_info, - ctx, - ) + let mut obj = serde_json::Map::new(); + obj.insert("t".to_string(), json!("Math")); + obj.insert("c".to_string(), json!([math_type, m.text])); + ctx.serializer.add_source_info(&mut obj, &m.source_info); + // `textS`: provenance of the math text itself (bd-ieldbghj), a + // sidecar like `a` / `targetS`. Omitted when unknown. + if let Some(text_source) = &m.text_source { + obj.insert("textS".to_string(), ctx.serializer.to_json_ref(text_source)); + } + Value::Object(obj) } Inline::Underline(u) => node_with_source( "Underline", @@ -2815,26 +2819,40 @@ fn stream_write_inline( Ok(()) }) } - Inline::Math(m) => stream_write_simple_node( - w, - "Math", - &m.source_info, - ctx, - |w: &mut JsonStreamWriter, _ctx: &mut JsonWriterContext| { - use crate::pandoc::MathType; - w.begin_array()?; - w.begin_object()?; - w.key("t")?; - w.str_value(match m.math_type { - MathType::InlineMath => "InlineMath", - MathType::DisplayMath => "DisplayMath", - })?; - w.end_object()?; - w.str_value(&m.text)?; - w.end_array()?; - Ok(()) - }, - ), + Inline::Math(m) => { + // Same shape as `stream_write_simple_node`, plus the optional + // `textS` sidecar (provenance of the math text, bd-ieldbghj) + // after `t`, where the sorted-key non-streaming writer puts it. + use crate::pandoc::MathType; + let s_id = ctx.serializer.intern(&m.source_info); + ctx.maybe_record_attribution_for(&m.source_info, s_id); + w.begin_object()?; + w.key("c")?; + w.begin_array()?; + w.begin_object()?; + w.key("t")?; + w.str_value(match m.math_type { + MathType::InlineMath => "InlineMath", + MathType::DisplayMath => "DisplayMath", + })?; + w.end_object()?; + w.str_value(&m.text)?; + w.end_array()?; + if ctx.serializer.config.include_inline_locations { + let ast_context = ctx.serializer.context; + stream_write_location_key_if_mapped(w, "l", &m.source_info, ast_context)?; + } + w.key("s")?; + w.u64_value(s_id as u64)?; + w.key("t")?; + w.str_value("Math")?; + if let Some(text_source) = &m.text_source { + w.key("textS")?; + stream_source_ref(w, ctx, text_source)?; + } + w.end_object()?; + Ok(()) + } Inline::Underline(u) => stream_write_simple_node( w, "Underline", diff --git a/crates/pampa/src/writers/plaintext.rs b/crates/pampa/src/writers/plaintext.rs index b43c4c179..c5e75b214 100644 --- a/crates/pampa/src/writers/plaintext.rs +++ b/crates/pampa/src/writers/plaintext.rs @@ -728,6 +728,7 @@ mod tests { math_type: MathType::InlineMath, text: "E = mc^2".to_string(), source_info: dummy_source_info(), + text_source: None, })]; let (result, diags) = inlines_to_string(&inlines); assert_eq!(result, "E = mc^2"); diff --git a/crates/pampa/tests/integration/main.rs b/crates/pampa/tests/integration/main.rs index e176b6eb5..2e63043b1 100644 --- a/crates/pampa/tests/integration/main.rs +++ b/crates/pampa/tests/integration/main.rs @@ -59,6 +59,7 @@ pub mod json_location_test; pub mod json_reader_smoke_tests; pub mod lua_conformance; pub mod lua_differential; +pub mod math_text_source; pub mod nesting_cursor_roundtrip_tests; pub mod node_edit_tests; pub mod qmd_writer_source_info; diff --git a/crates/pampa/tests/integration/math_text_source.rs b/crates/pampa/tests/integration/math_text_source.rs new file mode 100644 index 000000000..1146c2918 --- /dev/null +++ b/crates/pampa/tests/integration/math_text_source.rs @@ -0,0 +1,477 @@ +//! Provenance of `Math.text` (`Math.text_source`, bd-ieldbghj). +//! +//! `Math.source_info` spans the whole `$…$` / `$$…$$` node, but `text` is +//! not a plain substring of it once the node spans lines: the reader folds +//! each soft break of inline math to a literal `\n` and strips the +//! block-continuation gutter (`> `, list indentation) from the interior +//! lines of display math. `text_source` records that decode so a consumer +//! that parses `text` further (quarto-math) can map any byte of it back to +//! the `.qmd`. +//! +//! The contract these tests pin: +//! +//! 1. the qmd reader always sets `text_source`; +//! 2. `text_source.length() == text.len()`; +//! 3. every byte of `text` maps (via `map_offset`) to a byte inside the +//! node span, and to the *identical* source byte unless it is a `\n` +//! synthesized from a soft break; +//! 4. the mapping survives a JSON round trip (`textS` sidecar); +//! 5. text that did not come from the reader carries no mapping. + +use pampa::filter_context::FilterContext; +use pampa::filters::{Filter, FilterReturn, topdown_traverse}; +use pampa::pandoc::ast_context::ASTContext; +use pampa::pandoc::{Math, MathType, Pandoc}; +use pampa::{readers, writers}; +use quarto_source_map::{FileId, SourceInfo}; + +struct Parsed { + source: String, + doc: Pandoc, + ctx: ASTContext, + file: FileId, +} + +fn parse(source: &str) -> Parsed { + let (doc, ctx, diagnostics) = readers::qmd::read( + source.as_bytes(), + false, + "math-text-source.qmd", + &mut std::io::sink(), + true, + None, + ) + .expect("fixture parses"); + assert!( + diagnostics.is_empty(), + "fixture parsed with diagnostics: {diagnostics:?}" + ); + let file = ctx.current_file_id(); + Parsed { + source: source.to_string(), + doc, + ctx, + file, + } +} + +/// Every `Math` inline in document order, wherever it is nested. +fn collect_math(doc: &Pandoc) -> Vec { + let mut out = Vec::new(); + { + let mut filter = Filter::new().with_math(|m, _ctx| { + out.push(m.clone()); + FilterReturn::Unchanged(m) + }); + let mut fctx = FilterContext::new(); + topdown_traverse(doc.clone(), &mut filter, &mut fctx); + } + out +} + +fn only_math(p: &Parsed) -> Math { + let all = collect_math(&p.doc); + assert_eq!(all.len(), 1, "expected exactly one Math node, got {all:?}"); + all.into_iter().next().unwrap() +} + +/// The invariants every reader-produced `Math` must satisfy. Returns the +/// file byte offset each text byte maps to, for tests that want to assert +/// specific positions on top. +fn check_text_provenance(p: &Parsed, m: &Math) -> Vec { + let text_source = m + .text_source + .as_ref() + .unwrap_or_else(|| panic!("reader did not record text_source for {:?}", m.text)); + assert_eq!( + text_source.length(), + m.text.len(), + "text_source length must equal text.len() for {:?}", + m.text + ); + + let node = m + .source_info + .preimage_in(p.file) + .expect("node span resolves in the parsed file"); + let src = p.source.as_bytes(); + assert!( + p.source[node.clone()].starts_with('$') && p.source[node.clone()].ends_with('$'), + "node span should cover the delimiters, got {:?}", + &p.source[node.clone()] + ); + + let mut mapped = Vec::with_capacity(m.text.len()); + for (i, b) in m.text.bytes().enumerate() { + let loc = text_source + .map_offset(i, &p.ctx.source_context) + .unwrap_or_else(|| panic!("text offset {i} of {:?} does not map", m.text)); + assert_eq!(loc.file_id, p.file); + let off = loc.location.offset; + assert!( + node.contains(&off), + "text[{i}] of {:?} maps to file offset {off}, outside node span {node:?}", + m.text + ); + if src[off] != b { + assert_eq!( + b, b'\n', + "text[{i}] = {:?} of {:?} maps to source byte {:?} at {off}; only a folded \ + soft break may differ from its source byte", + b as char, m.text, src[off] as char + ); + } + mapped.push(off); + } + // Offsets never move backwards: the decode is monotone in the source. + assert!( + mapped.windows(2).all(|w| w[0] <= w[1]), + "text bytes map out of order: {mapped:?}" + ); + // The exclusive end maps too (consumers slice `text[i..text.len()]`). + assert!( + text_source + .map_offset(m.text.len(), &p.ctx.source_context) + .is_some(), + "exclusive end offset must map" + ); + mapped +} + +fn offset_of(haystack: &str, needle: &str) -> usize { + haystack + .find(needle) + .unwrap_or_else(|| panic!("{needle:?} not in {haystack:?}")) +} + +// --------------------------------------------------------------------------- +// Inline math +// --------------------------------------------------------------------------- + +#[test] +fn inline_single_line_is_a_verbatim_substring() { + let p = parse("Inline $x^2$ here.\n"); + let m = only_math(&p); + assert_eq!(m.math_type, MathType::InlineMath); + assert_eq!(m.text, "x^2"); + let mapped = check_text_provenance(&p, &m); + let start = offset_of(&p.source, "x^2"); + assert_eq!(mapped, vec![start, start + 1, start + 2]); + // No decode happened, so the mapping is a single contiguous range, not + // a Concat. + let ts = m.text_source.as_ref().unwrap(); + assert!( + matches!(ts, SourceInfo::Original { .. }), + "single-line inline math should map as one Original range, got {ts:?}" + ); + assert_eq!(ts.preimage_in(p.file), Some(start..start + 3)); +} + +#[test] +fn inline_multi_line_soft_break_maps_to_the_newline() { + let p = parse("$a +\nb$\n"); + let m = only_math(&p); + assert_eq!(m.text, "a +\nb"); + let mapped = check_text_provenance(&p, &m); + // "a +\n" is byte-identical to the source, then `b`. + assert_eq!(mapped, vec![1, 2, 3, 4, 5]); +} + +#[test] +fn inline_multi_line_in_blockquote_drops_the_gutter() { + let source = "> $a\n> b$\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "a\nb"); + let mapped = check_text_provenance(&p, &m); + let a = offset_of(source, "a"); + let b = offset_of(source, "b$"); + assert_eq!(mapped[0], a); + assert_eq!(mapped[2], b, "the byte after the fold maps to the real `b`"); + // The folded `\n` maps to the start of the soft break (the source `\n`), + // not to the `> ` gutter that was dropped. + assert_eq!(source.as_bytes()[mapped[1]], b'\n'); + let ts = m.text_source.as_ref().unwrap(); + assert!( + matches!(ts, SourceInfo::Concat { .. }), + "a fold must produce a Concat, got {ts:?}" + ); +} + +#[test] +fn inline_multi_line_in_list_item_drops_the_indent() { + let source = "- item $a\n b$ tail\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "a\nb"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped[0], offset_of(source, "a\n")); + assert_eq!(mapped[2], offset_of(source, "b$")); +} + +// --------------------------------------------------------------------------- +// Display math +// --------------------------------------------------------------------------- + +#[test] +fn display_single_line_is_a_verbatim_substring() { + let p = parse("$$x^2$$\n"); + let m = only_math(&p); + assert_eq!(m.math_type, MathType::DisplayMath); + assert_eq!(m.text, "x^2"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped, vec![2, 3, 4]); + let ts = m.text_source.as_ref().unwrap(); + assert!(matches!(ts, SourceInfo::Original { .. }), "got {ts:?}"); +} + +#[test] +fn display_multi_line_without_gutter_is_verbatim() { + let source = "$$\n\\frac{a}{b}\n$$\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "\n\\frac{a}{b}\n"); + let mapped = check_text_provenance(&p, &m); + let expected: Vec = (2..2 + m.text.len()).collect(); + assert_eq!(mapped, expected); + let ts = m.text_source.as_ref().unwrap(); + assert!( + matches!(ts, SourceInfo::Original { .. }), + "nothing was stripped, so the mapping should stay one range, got {ts:?}" + ); +} + +#[test] +fn display_in_blockquote_strips_gutter_with_provenance() { + let source = "> $$\n> x + y\n> $$\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "\nx + y\n"); + let mapped = check_text_provenance(&p, &m); + // Every kept byte is the real source byte: `x + y` sits after the + // stripped `> `. + let x = offset_of(source, "x + y"); + assert_eq!(&mapped[1..6], &[x, x + 1, x + 2, x + 3, x + 4]); + let ts = m.text_source.as_ref().unwrap(); + assert!(matches!(ts, SourceInfo::Concat { .. }), "got {ts:?}"); + // The hull covers exactly the body between the delimiters, stripped + // gutters included: deletions still tile the source. + let body_start = offset_of(source, "\n> x"); + let body_end = source.rfind("$$").unwrap(); + assert_eq!(ts.preimage_in(p.file), Some(body_start..body_end)); +} + +#[test] +fn display_in_nested_blockquote_strips_every_level() { + let source = "> > $$\n> > x\n> > $$\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "\nx\n"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped[1], offset_of(source, "x\n")); +} + +#[test] +fn display_in_list_item_strips_indent_with_provenance() { + let source = "- $$\n x\n $$\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "\nx\n"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped[1], offset_of(source, "x\n")); +} + +#[test] +fn display_with_label_attribute_is_mapped() { + let source = "$$\nx + y\n$$ {#eq-1}\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "\nx + y\n"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped[1], offset_of(source, "x + y")); +} + +#[test] +fn display_inside_strong_multi_line_is_mapped() { + // bd-qpa2 territory: an inline construct precedes `$$` on the opening + // line. Whatever the strip decides, kept bytes must map to themselves. + let source = "**$$y\nz$$**\n"; + let p = parse(source); + let m = only_math(&p); + assert_eq!(m.text, "y\nz"); + let mapped = check_text_provenance(&p, &m); + assert_eq!(mapped, vec![4, 5, 6]); +} + +#[test] +fn several_math_nodes_in_one_paragraph_map_independently() { + let source = "Let $a$ and $$b\nc$$ and $d$.\n"; + let p = parse(source); + let all = collect_math(&p.doc); + assert_eq!(all.len(), 3); + let firsts: Vec = all + .iter() + .map(|m| check_text_provenance(&p, m)[0]) + .collect(); + assert_eq!( + firsts, + vec![ + offset_of(source, "a$"), + offset_of(source, "b\n"), + offset_of(source, "d$"), + ] + ); +} + +// --------------------------------------------------------------------------- +// Corpus-wide property +// --------------------------------------------------------------------------- + +fn qmd_files_under(dir: &std::path::Path, out: &mut Vec) { + let Ok(entries) = std::fs::read_dir(dir) else { + return; + }; + for entry in entries { + let path = entry.unwrap().path(); + if path.is_dir() { + qmd_files_under(&path, out); + } else if path.extension().and_then(|e| e.to_str()) == Some("qmd") { + out.push(path); + } + } +} + +/// Every `Math` node in every in-tree fixture that contains math satisfies +/// the provenance contract. Files that do not parse cleanly are skipped +/// (they are other tests' business), but the sweep must find real math. +#[test] +fn every_in_tree_fixture_math_maps_byte_for_byte() { + let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + let repo = manifest.parent().unwrap().parent().unwrap(); + let mut files = Vec::new(); + for rel in [ + "crates/pampa/tests", + "crates/quarto/tests/smoke-all", + "crates/quarto-core/tests", + "docs", + ] { + qmd_files_under(&repo.join(rel), &mut files); + } + files.sort(); + + let mut checked_files = 0usize; + let mut checked_math = 0usize; + for path in files { + let Ok(source) = std::fs::read_to_string(&path) else { + continue; + }; + if !source.contains('$') { + continue; + } + let Ok((doc, ctx, diagnostics)) = readers::qmd::read( + source.as_bytes(), + false, + &path.to_string_lossy(), + &mut std::io::sink(), + true, + None, + ) else { + continue; + }; + if !diagnostics.is_empty() { + continue; + } + let file = ctx.current_file_id(); + let p = Parsed { + source, + doc, + ctx, + file, + }; + let all = collect_math(&p.doc); + if all.is_empty() { + continue; + } + checked_files += 1; + for m in &all { + // Reader-produced math only; the sweep parses raw files, so every + // node here came from the reader. + check_text_provenance(&p, m); + checked_math += 1; + } + } + // 21 files / 36 nodes when written (2026-09-21); the floor only guards + // against the sweep silently finding nothing. + assert!( + checked_files >= 15 && checked_math >= 30, + "sweep found too little math: {checked_math} nodes in {checked_files} files" + ); +} + +// --------------------------------------------------------------------------- +// Round trips and invalidation +// --------------------------------------------------------------------------- + +#[test] +fn text_source_survives_a_json_round_trip() { + let source = "> $a\n> b$\n"; + let p = parse(source); + let before = only_math(&p); + let before_ts = before.text_source.clone().expect("reader sets text_source"); + + let mut buf = Vec::new(); + writers::json::write(&p.doc, &p.ctx, &mut buf).expect("json write"); + let json = String::from_utf8(buf.clone()).unwrap(); + assert!( + json.contains("\"textS\""), + "writer emits the textS sidecar: {json}" + ); + + let (doc2, _ctx2) = readers::json::read(&mut buf.as_slice()).expect("json read"); + let after = collect_math(&doc2); + assert_eq!(after.len(), 1); + assert_eq!(after[0].text, "a\nb"); + assert_eq!( + after[0].text_source.as_ref(), + Some(&before_ts), + "text_source must round-trip structurally" + ); +} + +#[test] +fn json_without_text_sidecar_reads_as_none() { + let source = "$x$\n"; + let p = parse(source); + let mut buf = Vec::new(); + writers::json::write(&p.doc, &p.ctx, &mut buf).expect("json write"); + let json = String::from_utf8(buf).unwrap(); + // Strip the sidecar the way a third-party tool that only knows Pandoc's + // JSON would: drop the key entirely. + let v: serde_json::Value = serde_json::from_str(&json).unwrap(); + let stripped = strip_key(v, "textS"); + let bytes = serde_json::to_vec(&stripped).unwrap(); + let (doc2, _ctx2) = readers::json::read(&mut bytes.as_slice()).expect("json read"); + let after = collect_math(&doc2); + assert_eq!(after.len(), 1); + assert_eq!(after[0].text, "x"); + assert!( + after[0].text_source.is_none(), + "no sidecar means no mapping, got {:?}", + after[0].text_source + ); +} + +fn strip_key(v: serde_json::Value, key: &str) -> serde_json::Value { + match v { + serde_json::Value::Object(map) => serde_json::Value::Object( + map.into_iter() + .filter(|(k, _)| k != key) + .map(|(k, v)| (k, strip_key(v, key))) + .collect(), + ), + serde_json::Value::Array(items) => { + serde_json::Value::Array(items.into_iter().map(|v| strip_key(v, key)).collect()) + } + other => other, + } +} diff --git a/crates/pampa/tests/integration/test.rs b/crates/pampa/tests/integration/test.rs index ef8dd2dcc..9847b49a2 100644 --- a/crates/pampa/tests/integration/test.rs +++ b/crates/pampa/tests/integration/test.rs @@ -608,6 +608,7 @@ fn remove_location_fields(json: &mut serde_json::Value) { obj.remove("headS"); obj.remove("rowsS"); obj.remove("targetS"); + obj.remove("textS"); // Math text provenance (bd-ieldbghj) for value in obj.values_mut() { remove_location_fields(value); } diff --git a/crates/pampa/tests/integration/test_ansi_writer.rs b/crates/pampa/tests/integration/test_ansi_writer.rs index b9603d204..00c082cf9 100644 --- a/crates/pampa/tests/integration/test_ansi_writer.rs +++ b/crates/pampa/tests/integration/test_ansi_writer.rs @@ -731,6 +731,7 @@ fn test_inline_math() { math_type: pampa::pandoc::MathType::InlineMath, text: "E=mc^2".to_string(), source_info: empty_source(), + text_source: None, })], source_info: empty_source(), })], diff --git a/crates/pampa/tests/integration/test_location_health.rs b/crates/pampa/tests/integration/test_location_health.rs index 6226105ab..5f962a412 100644 --- a/crates/pampa/tests/integration/test_location_health.rs +++ b/crates/pampa/tests/integration/test_location_health.rs @@ -325,6 +325,9 @@ fn collect_source_info_from_inline(inline: &Inline, source_infos: &mut Vec { source_infos.push(math.source_info.clone()); + if let Some(text_source) = &math.text_source { + source_infos.push(text_source.clone()); + } } Inline::RawInline(raw) => { source_infos.push(raw.source_info.clone()); diff --git a/crates/quarto-ast-reconcile/src/generators.rs b/crates/quarto-ast-reconcile/src/generators.rs index 5e2b930a3..02e414a31 100644 --- a/crates/quarto-ast-reconcile/src/generators.rs +++ b/crates/quarto-ast-reconcile/src/generators.rs @@ -559,6 +559,7 @@ fn gen_math() -> impl Strategy { math_type, text, source_info: dummy_source(), + text_source: None, }) }) } diff --git a/crates/quarto-ast-reconcile/src/hash.rs b/crates/quarto-ast-reconcile/src/hash.rs index 66b9749d1..9629afee5 100644 --- a/crates/quarto-ast-reconcile/src/hash.rs +++ b/crates/quarto-ast-reconcile/src/hash.rs @@ -1590,12 +1590,14 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".to_string(), source_info: dummy_source(), + text_source: None, }); let math2 = Inline::Math(Math { math_type: MathType::DisplayMath, text: "x^2".to_string(), source_info: dummy_source(), + text_source: None, }); // Different math type -> different hash @@ -2078,18 +2080,21 @@ mod tests { math_type: MathType::InlineMath, text: "x".to_string(), source_info: dummy_source(), + text_source: None, }); let m2 = Inline::Math(Math { math_type: MathType::InlineMath, text: "x".to_string(), source_info: other_source(), + text_source: None, }); let m3 = Inline::Math(Math { math_type: MathType::DisplayMath, text: "x".to_string(), source_info: dummy_source(), + text_source: None, }); assert!(structural_eq_inline(&m1, &m2)); diff --git a/crates/quarto-core/src/stage/stages/math_js.rs b/crates/quarto-core/src/stage/stages/math_js.rs index 02693c320..31ca737df 100644 --- a/crates/quarto-core/src/stage/stages/math_js.rs +++ b/crates/quarto-core/src/stage/stages/math_js.rs @@ -571,6 +571,7 @@ mod tests { math_type: MathType::InlineMath, text: text.to_string(), source_info: SourceInfo::for_test(), + text_source: None, }) } @@ -579,6 +580,7 @@ mod tests { math_type: MathType::DisplayMath, text: text.to_string(), source_info: SourceInfo::for_test(), + text_source: None, }) } diff --git a/crates/quarto-core/src/template.rs b/crates/quarto-core/src/template.rs index 99d621962..18af143b0 100644 --- a/crates/quarto-core/src/template.rs +++ b/crates/quarto-core/src/template.rs @@ -2096,6 +2096,7 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".to_string(), source_info: dummy_source_info(), + text_source: None, })]; let text = inlines_to_text(&inlines); assert_eq!(text, "x^2"); diff --git a/crates/quarto-core/src/transforms/crossref_index.rs b/crates/quarto-core/src/transforms/crossref_index.rs index 495d0d836..1fef669d8 100644 --- a/crates/quarto-core/src/transforms/crossref_index.rs +++ b/crates/quarto-core/src/transforms/crossref_index.rs @@ -727,6 +727,7 @@ mod tests { math_type: MathType::DisplayMath, text: math_text.to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), diff --git a/crates/quarto-core/src/transforms/crossref_render.rs b/crates/quarto-core/src/transforms/crossref_render.rs index 0559c54df..1052c3526 100644 --- a/crates/quarto-core/src/transforms/crossref_render.rs +++ b/crates/quarto-core/src/transforms/crossref_render.rs @@ -1087,11 +1087,24 @@ fn render_equation(node: CustomNode) -> Inline { let content_inline = if let Some(n) = number { match math_inline { Inline::Math(math) => { - let tagged_text = format!("{}\\tag{{{}}}", math.text, n); + let tag = format!("\\tag{{{}}}", n); + let tagged_text = format!("{}{}", math.text, tag); + // Extend the text mapping rather than drop it: the original + // text keeps its byte-for-byte provenance and the appended + // tag becomes a synthesized piece (zero source bytes) anchored + // at the end of the node, the same shape ProvenanceBuilder + // uses for content with no source byte (bd-ieldbghj). + let text_source = math.text_source.map(|ts| { + let node_len = math.source_info.length(); + let synthesized = + SourceInfo::substring(math.source_info.clone(), node_len, node_len); + SourceInfo::concat(vec![(ts, math.text.len()), (synthesized, tag.len())]) + }); Inline::Math(Math { math_type: math.math_type, text: tagged_text, source_info: math.source_info, + text_source, }) } other => other, @@ -2451,6 +2464,7 @@ mod tests { math_type: MathType::DisplayMath, text: math_text.to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), @@ -2483,6 +2497,60 @@ mod tests { ); } + /// The `\tag{N}` append must not throw away the reader's byte-for-byte + /// mapping of the math text (bd-ieldbghj): the original text keeps its + /// provenance and the tag becomes a synthesized, zero-source piece. + #[tokio::test] + async fn equation_tag_extends_text_source_instead_of_dropping_it() { + // Source: `$$e = mc^2$$` at file offsets 0..12; text at 2..10. + let node = SourceInfo::original(FileId(0), 0, 12); + let text = SourceInfo::original(FileId(0), 2, 10); + let block = Block::Paragraph(Paragraph { + content: vec![Inline::Span(Span { + attr: ( + "eq-einstein".to_string(), + vec!["quarto-math-with-attribute".to_string()], + LinkedHashMap::new(), + ), + content: vec![Inline::Math(Math { + math_type: MathType::DisplayMath, + text: "e = mc^2".to_string(), + source_info: node.clone(), + text_source: Some(text), + })], + source_info: node.clone(), + attr_source: AttrSourceInfo::empty(), + })], + source_info: node, + }); + let ast = run_full(vec![block]).await; + let Block::Paragraph(p) = &ast.blocks[0] else { + panic!("expected Paragraph, got {:?}", ast.blocks[0]); + }; + let Inline::Span(span) = &p.content[0] else { + panic!("expected Span, got {:?}", p.content[0]); + }; + let Inline::Math(math) = &span.content[0] else { + panic!("expected Math, got {:?}", span.content[0]); + }; + assert_eq!(math.text, "e = mc^2\\tag{1}"); + let ts = math + .text_source + .as_ref() + .expect("tagging keeps the text mapping"); + assert_eq!(ts.length(), math.text.len()); + let SourceInfo::Concat { pieces } = ts else { + panic!("expected a Concat of [original text, synthesized tag], got {ts:?}"); + }; + assert_eq!(pieces.len(), 2); + assert_eq!(pieces[0].length, "e = mc^2".len()); + assert_eq!(pieces[0].source_info.preimage_in(FileId(0)), Some(2..10)); + assert_eq!(pieces[1].offset_in_concat, "e = mc^2".len()); + assert_eq!(pieces[1].length, "\\tag{1}".len()); + // The tag has no source bytes: a zero-width piece at the node's end. + assert_eq!(pieces[1].source_info.preimage_in(FileId(0)), Some(12..12)); + } + #[tokio::test] async fn equation_ref_resolves_to_link() { let blocks = vec![ diff --git a/crates/quarto-core/src/transforms/equation_label.rs b/crates/quarto-core/src/transforms/equation_label.rs index 4c338f78c..f44d3d81f 100644 --- a/crates/quarto-core/src/transforms/equation_label.rs +++ b/crates/quarto-core/src/transforms/equation_label.rs @@ -252,6 +252,7 @@ mod tests { math_type: MathType::DisplayMath, text: text.to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), @@ -296,6 +297,7 @@ mod tests { math_type: MathType::DisplayMath, text: "x^2".to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), @@ -317,6 +319,7 @@ mod tests { math_type: MathType::InlineMath, text: "x^2".to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), @@ -337,6 +340,7 @@ mod tests { math_type: MathType::DisplayMath, text: "x".to_string(), source_info: si(), + text_source: None, })], source_info: si(), attr_source: AttrSourceInfo::empty(), diff --git a/crates/quarto-core/src/transforms/metadata_normalize.rs b/crates/quarto-core/src/transforms/metadata_normalize.rs index 91ae2f678..efaf2e0c0 100644 --- a/crates/quarto-core/src/transforms/metadata_normalize.rs +++ b/crates/quarto-core/src/transforms/metadata_normalize.rs @@ -718,6 +718,7 @@ mod tests { math_type: quarto_pandoc_types::inline::MathType::InlineMath, text: "E=mc^2".to_string(), source_info: dummy_source_info(), + text_source: None, })]; let text = inlines_to_plain_text(&inlines); assert_eq!(text, "E=mc^2"); diff --git a/crates/quarto-pandoc-types/src/inline.rs b/crates/quarto-pandoc-types/src/inline.rs index ecaef0c00..65deccc1b 100644 --- a/crates/quarto-pandoc-types/src/inline.rs +++ b/crates/quarto-pandoc-types/src/inline.rs @@ -214,7 +214,25 @@ pub struct Code { pub struct Math { pub math_type: MathType, pub text: String, + /// Span of the whole math node in the source, delimiters included + /// (`$…$` / `$$…$$`). pub source_info: quarto_source_map::SourceInfo, + /// Provenance of `text` itself: a `SourceInfo` whose length equals + /// `text.len()` and whose offsets map byte-for-byte into the source + /// (`SourceInfo::map_offset(i)` lands on the byte `text[i]` came from). + /// + /// `text` is not a plain substring of `source_info` once a math node + /// spans lines: the qmd reader folds each soft break to a literal `\n` + /// in inline math and strips the block-continuation gutter (`> `, + /// list indentation) from the interior lines of display math. This + /// field records that decode so consumers that parse `text` further + /// (`quarto-math`) can attach exact source locations to sub-expressions. + /// + /// `None` means no mapping finer than `source_info` is known: the node + /// came from a filter (`pandoc.Math(...)` in Lua, JSON without `textS`), + /// or its `text` was rewritten (a Lua filter assigned `.text`). The + /// reader always sets it. See bd-ieldbghj. + pub text_source: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -1257,6 +1275,7 @@ mod tests { math_type: MathType::DisplayMath, text: "E = mc^2".to_string(), source_info: dummy_source_info(), + text_source: None, }; assert_eq!(math.text, "E = mc^2"); assert_eq!(math.math_type, MathType::DisplayMath); diff --git a/ts-packages/annotated-qmd/examples/academic-paper.json b/ts-packages/annotated-qmd/examples/academic-paper.json index c82839f3f..60078f2ed 100644 --- a/ts-packages/annotated-qmd/examples/academic-paper.json +++ b/ts-packages/annotated-qmd/examples/academic-paper.json @@ -1 +1 @@ -{"blocks":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["introduction",[],[]],[{"c":"Introduction","s":1,"t":"Str"}]],"s":0,"t":"Header"},{"c":[{"c":"Climate","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"prediction","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"is","s":7,"t":"Str"},{"s":8,"t":"Space"},{"c":"a","s":9,"t":"Str"},{"s":10,"t":"Space"},{"c":"critical","s":11,"t":"Str"},{"s":12,"t":"Space"},{"c":"challenge","s":13,"t":"Str"},{"s":14,"t":"Space"},{"c":"in","s":15,"t":"Str"},{"s":16,"t":"Space"},{"c":"modern","s":17,"t":"Str"},{"s":18,"t":"Space"},{"c":"science","s":19,"t":"Str"},{"c":[{"c":[{"c":"See","s":22,"t":"Str"},{"s":23,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"ipcc2021","citationIdS":25,"citationMode":{"t":"AuthorInText"},"citationNoteNum":1,"citationPrefix":[],"citationSuffix":[]}],[{"c":"@ipcc2021","s":26,"t":"Str"}]],"s":24,"t":"Cite"},{"s":27,"t":"Space"},{"c":"for","s":28,"t":"Str"},{"s":29,"t":"Space"},{"c":"comprehensive","s":30,"t":"Str"},{"s":31,"t":"Space"},{"c":"discussion","s":32,"t":"Str"}],"s":21,"t":"Para"}],"s":20,"t":"Note"},{"c":".","s":33,"t":"Str"},{"s":34,"t":"Space"},{"c":"Traditional","s":35,"t":"Str"},{"s":36,"t":"Space"},{"c":"statistical","s":37,"t":"Str"},{"s":38,"t":"Space"},{"c":"methods","s":39,"t":"Str"},{"s":40,"t":"Space"},{"c":"have","s":41,"t":"Str"},{"s":42,"t":"Space"},{"c":"limitations","s":43,"t":"Str"},{"s":44,"t":"Space"},{"c":"when","s":45,"t":"Str"},{"s":46,"t":"Space"},{"c":"dealing","s":47,"t":"Str"},{"s":48,"t":"Space"},{"c":"with","s":49,"t":"Str"},{"s":50,"t":"Space"},{"c":"complex,","s":51,"t":"Str"},{"s":52,"t":"Space"},{"c":"non-linear","s":53,"t":"Str"},{"s":54,"t":"Space"},{"c":"relationships","s":55,"t":"Str"},{"s":56,"t":"Space"},{"c":"in","s":57,"t":"Str"},{"s":58,"t":"Space"},{"c":"climate","s":59,"t":"Str"},{"s":60,"t":"Space"},{"c":"data.","s":61,"t":"Str"}],"s":2,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["background",[],[]],[{"c":"Background","s":63,"t":"Str"}]],"s":62,"t":"Header"},{"c":[{"c":"The","s":65,"t":"Str"},{"s":66,"t":"Space"},{"c":"field","s":67,"t":"Str"},{"s":68,"t":"Space"},{"c":"of","s":69,"t":"Str"},{"s":70,"t":"Space"},{"c":"climate","s":71,"t":"Str"},{"s":72,"t":"Space"},{"c":"science","s":73,"t":"Str"},{"s":74,"t":"Space"},{"c":"has","s":75,"t":"Str"},{"s":76,"t":"Space"},{"c":"evolved","s":77,"t":"Str"},{"s":78,"t":"Space"},{"c":"significantly","s":79,"t":"Str"},{"s":80,"t":"Space"},{"c":"over","s":81,"t":"Str"},{"s":82,"t":"Space"},{"c":"the","s":83,"t":"Str"},{"s":84,"t":"Space"},{"c":"past","s":85,"t":"Str"},{"s":86,"t":"Space"},{"c":"decades","s":87,"t":"Str"},{"s":88,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"doe2020","citationIdS":90,"citationMode":{"t":"NormalCitation"},"citationNoteNum":2,"citationPrefix":[],"citationSuffix":[]},{"citationHash":0,"citationId":"smith2019","citationIdS":91,"citationMode":{"t":"NormalCitation"},"citationNoteNum":2,"citationPrefix":[{"s":92,"t":"Space"}],"citationSuffix":[]}],[]],"s":89,"t":"Cite"},{"c":".","s":93,"t":"Str"},{"s":94,"t":"Space"},{"c":"Recent","s":95,"t":"Str"},{"s":96,"t":"Space"},{"c":"advances","s":97,"t":"Str"},{"s":98,"t":"Space"},{"c":"in","s":99,"t":"Str"},{"s":100,"t":"Space"},{"c":"computational","s":101,"t":"Str"},{"s":102,"t":"Space"},{"c":"power","s":103,"t":"Str"},{"s":104,"t":"Space"},{"c":"enable","s":105,"t":"Str"},{"s":106,"t":"Space"},{"c":"new","s":107,"t":"Str"},{"s":108,"t":"Space"},{"c":"approaches.","s":109,"t":"Str"}],"s":64,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["methods",[],[]],[{"c":"Methods","s":111,"t":"Str"}]],"s":110,"t":"Header"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["data-collection",[],[]],[{"c":"Data","s":113,"t":"Str"},{"s":114,"t":"Space"},{"c":"Collection","s":115,"t":"Str"}]],"s":112,"t":"Header"},{"c":[{"c":"We","s":117,"t":"Str"},{"s":118,"t":"Space"},{"c":"collected","s":119,"t":"Str"},{"s":120,"t":"Space"},{"c":"data","s":121,"t":"Str"},{"s":122,"t":"Space"},{"c":"from","s":123,"t":"Str"},{"s":124,"t":"Space"},{"c":"three","s":125,"t":"Str"},{"s":126,"t":"Space"},{"c":"sources:","s":129,"t":"Str"}],"s":116,"t":"Para"},{"a":{"classes":[],"id":131,"kvs":[]},"bodiesS":[{"a":{"classes":[],"id":null,"kvs":[]},"bodyS":[{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":132},{"a":{"classes":[],"id":null,"kvs":[]},"s":133},{"a":{"classes":[],"id":null,"kvs":[]},"s":134}],"s":135},{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":136},{"a":{"classes":[],"id":null,"kvs":[]},"s":137},{"a":{"classes":[],"id":null,"kvs":[]},"s":138}],"s":139},{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":140},{"a":{"classes":[],"id":null,"kvs":[]},"s":141},{"a":{"classes":[],"id":null,"kvs":[]},"s":142}],"s":143}],"headS":[],"s":144}],"c":[["tbl-data",[],[]],[null,[{"c":[{"c":"Climate","s":146,"t":"Str"},{"s":147,"t":"Space"},{"c":"data","s":148,"t":"Str"},{"s":149,"t":"Space"},{"c":"sources","s":150,"t":"Str"}],"s":145,"t":"Plain"}]],[[{"t":"AlignDefault"},{"t":"ColWidthDefault"}],[{"t":"AlignDefault"},{"t":"ColWidthDefault"}],[{"t":"AlignDefault"},{"t":"ColWidthDefault"}]],[["",[],[]],[[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Source","s":152,"t":"Str"}],"s":151,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Years","s":154,"t":"Str"}],"s":153,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Variables","s":156,"t":"Str"}],"s":155,"t":"Plain"}]]]]]],[[["",[],[]],0,[],[[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Station","s":158,"t":"Str"},{"s":159,"t":"Space"},{"c":"A","s":160,"t":"Str"}],"s":157,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"1990-2020","s":162,"t":"Str"}],"s":161,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Temp,","s":164,"t":"Str"},{"s":165,"t":"Space"},{"c":"Precip","s":166,"t":"Str"}],"s":163,"t":"Plain"}]]]],[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Station","s":168,"t":"Str"},{"s":169,"t":"Space"},{"c":"B","s":170,"t":"Str"}],"s":167,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"1995-2020","s":172,"t":"Str"}],"s":171,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Temp,","s":174,"t":"Str"},{"s":175,"t":"Space"},{"c":"Wind","s":176,"t":"Str"}],"s":173,"t":"Plain"}]]]],[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Satellite","s":178,"t":"Str"}],"s":177,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"2000-2020","s":180,"t":"Str"}],"s":179,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"All","s":182,"t":"Str"}],"s":181,"t":"Plain"}]]]]]]],[["",[],[]],[]]],"captionS":183,"footS":{"a":{"classes":[],"id":null,"kvs":[]},"rowsS":[],"s":184},"headS":{"a":{"classes":[],"id":null,"kvs":[]},"rowsS":[{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":185},{"a":{"classes":[],"id":null,"kvs":[]},"s":186},{"a":{"classes":[],"id":null,"kvs":[]},"s":187}],"s":188}],"s":189},"s":130,"t":"Table"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["model-architecture",[],[]],[{"c":"Model","s":191,"t":"Str"},{"s":192,"t":"Space"},{"c":"Architecture","s":193,"t":"Str"}]],"s":190,"t":"Header"},{"c":[{"c":"Our","s":195,"t":"Str"},{"s":196,"t":"Space"},{"c":"model","s":197,"t":"Str"},{"s":198,"t":"Space"},{"c":"uses","s":199,"t":"Str"},{"s":200,"t":"Space"},{"c":"a","s":201,"t":"Str"},{"s":202,"t":"Space"},{"c":"transformer","s":203,"t":"Str"},{"s":204,"t":"Space"},{"c":"architecture","s":205,"t":"Str"},{"s":206,"t":"Space"},{"c":"with","s":207,"t":"Str"},{"s":208,"t":"Space"},{"c":"attention","s":209,"t":"Str"},{"s":210,"t":"Space"},{"c":"mechanisms.","s":211,"t":"Str"},{"s":212,"t":"Space"},{"c":"The","s":213,"t":"Str"},{"s":214,"t":"Space"},{"c":"loss","s":215,"t":"Str"},{"s":216,"t":"Space"},{"c":"function","s":217,"t":"Str"},{"s":218,"t":"Space"},{"c":"is","s":219,"t":"Str"},{"s":220,"t":"Space"},{"c":"defined","s":221,"t":"Str"},{"s":222,"t":"Space"},{"c":"as:","s":225,"t":"Str"}],"s":194,"t":"Para"},{"c":[{"c":[{"t":"DisplayMath"},"\nL(\\theta) = \\frac{1}{N} \\sum_{i=1}^{N} (y_i - \\hat{y}_i)^2\n"],"s":227,"t":"Math"}],"s":226,"t":"Para"},{"c":[{"c":"where","s":229,"t":"Str"},{"s":230,"t":"Space"},{"c":[{"t":"InlineMath"},"\\theta"],"s":231,"t":"Math"},{"s":232,"t":"Space"},{"c":"represents","s":233,"t":"Str"},{"s":234,"t":"Space"},{"c":"model","s":235,"t":"Str"},{"s":236,"t":"Space"},{"c":"parameters","s":237,"t":"Str"},{"s":238,"t":"Space"},{"c":"and","s":239,"t":"Str"},{"s":240,"t":"Space"},{"c":[{"t":"InlineMath"},"N"],"s":241,"t":"Math"},{"s":242,"t":"Space"},{"c":"is","s":243,"t":"Str"},{"s":244,"t":"Space"},{"c":"the","s":245,"t":"Str"},{"s":246,"t":"Space"},{"c":"sample","s":247,"t":"Str"},{"s":248,"t":"Space"},{"c":"size.","s":249,"t":"Str"}],"s":228,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["results",[],[]],[{"c":"Results","s":251,"t":"Str"}]],"s":250,"t":"Header"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["performance-metrics",[],[]],[{"c":"Performance","s":253,"t":"Str"},{"s":254,"t":"Space"},{"c":"Metrics","s":255,"t":"Str"}]],"s":252,"t":"Header"},{"c":[{"c":"The","s":257,"t":"Str"},{"s":258,"t":"Space"},{"c":"model","s":259,"t":"Str"},{"s":260,"t":"Space"},{"c":"achieved","s":261,"t":"Str"},{"s":262,"t":"Space"},{"c":"significant","s":263,"t":"Str"},{"s":264,"t":"Space"},{"c":"improvements:","s":267,"t":"Str"}],"s":256,"t":"Para"},{"c":[[{"c":[{"c":"RMSE:","s":272,"t":"Str"},{"s":273,"t":"Space"},{"c":"0.45","s":274,"t":"Str"},{"s":275,"t":"Space"},{"c":"(baseline:","s":280,"t":"Str"},{"s":281,"t":"Space"},{"c":"0.78)","s":284,"t":"Str"}],"s":269,"t":"Plain"}],[{"c":[{"c":[{"t":"InlineMath"},"R^2"],"s":286,"t":"Math"},{"c":":","s":287,"t":"Str"},{"s":288,"t":"Space"},{"c":"0.92","s":289,"t":"Str"},{"s":290,"t":"Space"},{"c":"(baseline:","s":295,"t":"Str"},{"s":296,"t":"Space"},{"c":"0.65)","s":299,"t":"Str"}],"s":285,"t":"Plain"}],[{"c":[{"c":"MAE:","s":303,"t":"Str"},{"s":304,"t":"Space"},{"c":"0.31","s":305,"t":"Str"},{"s":306,"t":"Space"},{"c":"(baseline:","s":311,"t":"Str"},{"s":312,"t":"Space"},{"c":"0.58)","s":315,"t":"Str"}],"s":300,"t":"Plain"}]],"s":268,"t":"BulletList"},{"c":[{"c":"See","s":317,"t":"Str"},{"s":318,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"tbl-data","citationIdS":320,"citationMode":{"t":"AuthorInText"},"citationNoteNum":3,"citationPrefix":[],"citationSuffix":[]}],[{"c":"@tbl-data","s":321,"t":"Str"}]],"s":319,"t":"Cite"},{"s":322,"t":"Space"},{"c":"for","s":323,"t":"Str"},{"s":324,"t":"Space"},{"c":"data","s":325,"t":"Str"},{"s":326,"t":"Space"},{"c":"sources","s":327,"t":"Str"},{"s":328,"t":"Space"},{"c":"used","s":329,"t":"Str"},{"s":330,"t":"Space"},{"c":"in","s":331,"t":"Str"},{"s":332,"t":"Space"},{"c":"training.","s":333,"t":"Str"}],"s":316,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["discussion",[],[]],[{"c":"Discussion","s":335,"t":"Str"}]],"s":334,"t":"Header"},{"c":[{"c":"Our","s":337,"t":"Str"},{"s":338,"t":"Space"},{"c":"results","s":339,"t":"Str"},{"s":340,"t":"Space"},{"c":"demonstrate","s":341,"t":"Str"},{"s":342,"t":"Space"},{"c":"that","s":343,"t":"Str"},{"s":344,"t":"Space"},{"c":"machine","s":345,"t":"Str"},{"s":346,"t":"Space"},{"c":"learning","s":347,"t":"Str"},{"s":348,"t":"Space"},{"c":"methods","s":349,"t":"Str"},{"s":350,"t":"Space"},{"c":"can","s":351,"t":"Str"},{"s":352,"t":"Space"},{"c":"effectively","s":353,"t":"Str"},{"s":354,"t":"Space"},{"c":"capture","s":355,"t":"Str"},{"s":356,"t":"Space"},{"c":"complex","s":357,"t":"Str"},{"s":358,"t":"Space"},{"c":"patterns","s":359,"t":"Str"},{"s":360,"t":"Space"},{"c":"in","s":361,"t":"Str"},{"s":362,"t":"Space"},{"c":"climate","s":363,"t":"Str"},{"s":364,"t":"Space"},{"c":"data.","s":365,"t":"Str"},{"s":366,"t":"Space"},{"c":"However,","s":367,"t":"Str"},{"s":368,"t":"Space"},{"c":"several","s":369,"t":"Str"},{"s":370,"t":"Space"},{"c":"limitations","s":371,"t":"Str"},{"s":372,"t":"Space"},{"c":"remain","s":373,"t":"Str"},{"c":[{"c":[{"c":"Further","s":376,"t":"Str"},{"s":377,"t":"Space"},{"c":"work","s":378,"t":"Str"},{"s":379,"t":"Space"},{"c":"is","s":380,"t":"Str"},{"s":381,"t":"Space"},{"c":"needed","s":382,"t":"Str"},{"s":383,"t":"Space"},{"c":"to","s":384,"t":"Str"},{"s":385,"t":"Space"},{"c":"address","s":386,"t":"Str"},{"s":387,"t":"Space"},{"c":"regional","s":388,"t":"Str"},{"s":389,"t":"Space"},{"c":"variations","s":390,"t":"Str"}],"s":375,"t":"Para"}],"s":374,"t":"Note"},{"c":".","s":391,"t":"Str"}],"s":336,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["limitations",[],[]],[{"c":"Limitations","s":393,"t":"Str"}]],"s":392,"t":"Header"},{"c":[{"c":"The","s":395,"t":"Str"},{"s":396,"t":"Space"},{"c":"current","s":397,"t":"Str"},{"s":398,"t":"Space"},{"c":"study","s":399,"t":"Str"},{"s":400,"t":"Space"},{"c":"focuses","s":401,"t":"Str"},{"s":402,"t":"Space"},{"c":"on","s":403,"t":"Str"},{"s":404,"t":"Space"},{"c":"temperature","s":405,"t":"Str"},{"s":406,"t":"Space"},{"c":"prediction.","s":407,"t":"Str"},{"s":408,"t":"Space"},{"c":"Future","s":409,"t":"Str"},{"s":410,"t":"Space"},{"c":"work","s":411,"t":"Str"},{"s":412,"t":"Space"},{"c":"should","s":413,"t":"Str"},{"s":414,"t":"Space"},{"c":"extend","s":415,"t":"Str"},{"s":416,"t":"Space"},{"c":"to:","s":419,"t":"Str"}],"s":394,"t":"Para"},{"c":[[1,{"t":"Decimal"},{"t":"Period"}],[[{"c":[{"c":"Precipitation","s":422,"t":"Str"},{"s":423,"t":"Space"},{"c":"patterns","s":424,"t":"Str"}],"s":421,"t":"Plain"}],[{"c":[{"c":"Extreme","s":426,"t":"Str"},{"s":427,"t":"Space"},{"c":"weather","s":428,"t":"Str"},{"s":429,"t":"Space"},{"c":"events","s":430,"t":"Str"}],"s":425,"t":"Plain"}],[{"c":[{"c":"Long-term","s":432,"t":"Str"},{"s":433,"t":"Space"},{"c":"trends","s":434,"t":"Str"}],"s":431,"t":"Plain"}]]],"s":420,"t":"OrderedList"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["conclusion",[],[]],[{"c":"Conclusion","s":436,"t":"Str"}]],"s":435,"t":"Header"},{"c":[{"c":"Machine","s":438,"t":"Str"},{"s":439,"t":"Space"},{"c":"learning","s":440,"t":"Str"},{"s":441,"t":"Space"},{"c":"offers","s":442,"t":"Str"},{"s":443,"t":"Space"},{"c":"promising","s":444,"t":"Str"},{"s":445,"t":"Space"},{"c":"approaches","s":446,"t":"Str"},{"s":447,"t":"Space"},{"c":"to","s":448,"t":"Str"},{"s":449,"t":"Space"},{"c":"climate","s":450,"t":"Str"},{"s":451,"t":"Space"},{"c":"prediction.","s":452,"t":"Str"},{"s":453,"t":"Space"},{"c":"The","s":454,"t":"Str"},{"s":455,"t":"Space"},{"c":"techniques","s":456,"t":"Str"},{"s":457,"t":"Space"},{"c":"presented","s":458,"t":"Str"},{"s":459,"t":"Space"},{"c":"here","s":460,"t":"Str"},{"s":461,"t":"Space"},{"c":"provide","s":462,"t":"Str"},{"s":463,"t":"Space"},{"c":"a","s":464,"t":"Str"},{"s":465,"t":"Space"},{"c":"foundation","s":466,"t":"Str"},{"s":467,"t":"Space"},{"c":"for","s":468,"t":"Str"},{"s":469,"t":"Space"},{"c":"future","s":470,"t":"Str"},{"s":471,"t":"Space"},{"c":"research","s":472,"t":"Str"},{"s":473,"t":"Space"},{"c":"in","s":474,"t":"Str"},{"s":475,"t":"Space"},{"c":"this","s":476,"t":"Str"},{"s":477,"t":"Space"},{"c":"critical","s":478,"t":"Str"},{"s":479,"t":"Space"},{"c":"domain","s":480,"t":"Str"},{"s":481,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"johnson2021","citationIdS":483,"citationMode":{"t":"NormalCitation"},"citationNoteNum":4,"citationPrefix":[],"citationSuffix":[]}],[]],"s":482,"t":"Cite"},{"c":".","s":484,"t":"Str"}],"s":437,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["references",[],[]],[{"c":"References","s":486,"t":"Str"}]],"s":485,"t":"Header"},{"a":{"classes":[],"id":488,"kvs":[]},"c":[["refs",[],[]],[]],"s":487,"t":"Div"}],"meta":{"abstract":{"c":[{"c":"This","s":493,"t":"Str"},{"s":495,"t":"Space"},{"c":"paper","s":497,"t":"Str"},{"s":499,"t":"Space"},{"c":"presents","s":501,"t":"Str"},{"s":503,"t":"Space"},{"c":"a","s":505,"t":"Str"},{"s":507,"t":"Space"},{"c":"novel","s":509,"t":"Str"},{"s":511,"t":"Space"},{"c":"approach","s":513,"t":"Str"},{"s":515,"t":"Space"},{"c":"to","s":517,"t":"Str"},{"s":519,"t":"Space"},{"c":"climate","s":521,"t":"Str"},{"s":523,"t":"Space"},{"c":"prediction","s":525,"t":"Str"},{"s":527,"t":"Space"},{"c":"using","s":529,"t":"Str"},{"s":531,"t":"Space"},{"c":"machine","s":533,"t":"Str"},{"s":535,"t":"Space"},{"c":"learning","s":537,"t":"Str"},{"s":539,"t":"Space"},{"c":"techniques.","s":541,"t":"Str"},{"s":543,"t":"Space"},{"c":"We","s":545,"t":"Str"},{"s":547,"t":"Space"},{"c":"demonstrate","s":549,"t":"Str"},{"s":551,"t":"Space"},{"c":"that","s":553,"t":"Str"},{"s":555,"t":"Space"},{"c":"deep","s":557,"t":"Str"},{"s":559,"t":"Space"},{"c":"learning","s":561,"t":"Str"},{"s":563,"t":"Space"},{"c":"models","s":565,"t":"Str"},{"s":567,"t":"Space"},{"c":"can","s":569,"t":"Str"},{"s":571,"t":"Space"},{"c":"achieve","s":573,"t":"Str"},{"s":575,"t":"Space"},{"c":"significant","s":577,"t":"Str"},{"s":579,"t":"Space"},{"c":"improvements","s":581,"t":"Str"},{"s":583,"t":"Space"},{"c":"over","s":585,"t":"Str"},{"s":587,"t":"Space"},{"c":"traditional","s":589,"t":"Str"},{"s":591,"t":"Space"},{"c":"methods.","s":593,"t":"Str"}],"s":490,"t":"MetaInlines"},"affiliation":{"c":[{"c":"Department","s":598,"t":"Str"},{"s":600,"t":"Space"},{"c":"of","s":602,"t":"Str"},{"s":604,"t":"Space"},{"c":"Computer","s":606,"t":"Str"},{"s":608,"t":"Space"},{"c":"Science,","s":610,"t":"Str"},{"s":612,"t":"Space"},{"c":"University","s":614,"t":"Str"},{"s":616,"t":"Space"},{"c":"of","s":618,"t":"Str"},{"s":620,"t":"Space"},{"c":"Example","s":622,"t":"Str"}],"s":595,"t":"MetaInlines"},"author":{"c":[{"c":"Dr. Alice","s":625,"t":"Str"},{"s":628,"t":"Space"},{"c":"Smith","s":630,"t":"Str"}],"s":624,"t":"MetaInlines"},"bibliography":{"c":[{"c":"references.bib","s":635,"t":"Str"}],"s":632,"t":"MetaInlines"},"keywords":{"c":[{"c":[{"c":"machine","s":642,"t":"Str"},{"s":644,"t":"Space"},{"c":"learning","s":646,"t":"Str"}],"s":639,"t":"MetaInlines"},{"c":[{"c":"climate","s":651,"t":"Str"}],"s":648,"t":"MetaInlines"},{"c":[{"c":"prediction","s":656,"t":"Str"}],"s":653,"t":"MetaInlines"},{"c":[{"c":"deep","s":661,"t":"Str"},{"s":663,"t":"Space"},{"c":"learning","s":665,"t":"Str"}],"s":658,"t":"MetaInlines"}],"s":637,"t":"MetaList"},"title":{"c":[{"c":"Machine","s":670,"t":"Str"},{"s":672,"t":"Space"},{"c":"Learning","s":674,"t":"Str"},{"s":676,"t":"Space"},{"c":"Approaches","s":678,"t":"Str"},{"s":680,"t":"Space"},{"c":"to","s":682,"t":"Str"},{"s":684,"t":"Space"},{"c":"Climate","s":686,"t":"Str"},{"s":688,"t":"Space"},{"c":"Prediction","s":690,"t":"Str"}],"s":667,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,62,88,157,367,432,461,465,466,481,482,707,708,722,723,886,887,897,898,917,918,956,957,988,1019,1060,1099,1131,1132,1167,1168,1190,1191,1293,1294,1297,1356,1359,1360,1431,1432,1442,1443,1466,1467,1512,1513,1543,1574,1603,1604,1653,1654,1667,1668,1873,1874,1889,1890,1973,1974,2000,2026,2046,2047,2060,2061,2240,2241,2254,2255,2267,2271],"name":"ts-packages/annotated-qmd/examples/academic-paper.qmd","total_length":2272}],"metaTopLevelKeySources":{"abstract":692,"affiliation":694,"author":696,"bibliography":698,"keywords":700,"title":702},"p":[{"d":0,"r":[467,482],"t":0},{"d":0,"r":[469,481],"t":0},{"d":0,"r":[483,708],"t":0},{"d":0,"r":[483,490],"t":0},{"d":0,"r":[490,491],"t":0},{"d":0,"r":[491,501],"t":0},{"d":0,"r":[501,502],"t":0},{"d":0,"r":[502,504],"t":0},{"d":0,"r":[504,505],"t":0},{"d":0,"r":[505,506],"t":0},{"d":0,"r":[506,507],"t":0},{"d":0,"r":[507,515],"t":0},{"d":0,"r":[515,516],"t":0},{"d":0,"r":[516,525],"t":0},{"d":0,"r":[525,526],"t":0},{"d":0,"r":[526,528],"t":0},{"d":0,"r":[528,529],"t":0},{"d":0,"r":[529,535],"t":0},{"d":0,"r":[535,536],"t":0},{"d":0,"r":[536,543],"t":0},{"d":0,"r":[543,588],"t":0},{"d":0,"r":[543,588],"t":0},{"d":0,"r":[545,548],"t":0},{"d":0,"r":[548,549],"t":0},{"d":0,"r":[549,558],"t":0},{"d":0,"r":[550,558],"t":0},{"d":0,"r":[549,558],"t":0},{"d":0,"r":[558,559],"t":0},{"d":0,"r":[559,562],"t":0},{"d":0,"r":[562,563],"t":0},{"d":0,"r":[563,576],"t":0},{"d":0,"r":[576,577],"t":0},{"d":0,"r":[577,587],"t":0},{"d":0,"r":[588,589],"t":0},{"d":0,"r":[589,590],"t":0},{"d":0,"r":[590,601],"t":0},{"d":0,"r":[601,602],"t":0},{"d":0,"r":[602,613],"t":0},{"d":0,"r":[613,614],"t":0},{"d":0,"r":[614,621],"t":0},{"d":0,"r":[621,622],"t":0},{"d":0,"r":[622,626],"t":0},{"d":0,"r":[626,627],"t":0},{"d":0,"r":[627,638],"t":0},{"d":0,"r":[638,639],"t":0},{"d":0,"r":[639,643],"t":0},{"d":0,"r":[643,644],"t":0},{"d":0,"r":[644,651],"t":0},{"d":0,"r":[651,652],"t":0},{"d":0,"r":[652,656],"t":0},{"d":0,"r":[656,657],"t":0},{"d":0,"r":[657,665],"t":0},{"d":0,"r":[665,666],"t":0},{"d":0,"r":[666,676],"t":0},{"d":0,"r":[676,677],"t":0},{"d":0,"r":[677,690],"t":0},{"d":0,"r":[690,691],"t":0},{"d":0,"r":[691,693],"t":0},{"d":0,"r":[693,694],"t":0},{"d":0,"r":[694,701],"t":0},{"d":0,"r":[701,702],"t":0},{"d":0,"r":[702,707],"t":0},{"d":0,"r":[709,723],"t":0},{"d":0,"r":[712,722],"t":0},{"d":0,"r":[724,887],"t":0},{"d":0,"r":[724,727],"t":0},{"d":0,"r":[727,728],"t":0},{"d":0,"r":[728,733],"t":0},{"d":0,"r":[733,734],"t":0},{"d":0,"r":[734,736],"t":0},{"d":0,"r":[736,737],"t":0},{"d":0,"r":[737,744],"t":0},{"d":0,"r":[744,745],"t":0},{"d":0,"r":[745,752],"t":0},{"d":0,"r":[752,753],"t":0},{"d":0,"r":[753,756],"t":0},{"d":0,"r":[756,757],"t":0},{"d":0,"r":[757,764],"t":0},{"d":0,"r":[764,765],"t":0},{"d":0,"r":[765,778],"t":0},{"d":0,"r":[778,779],"t":0},{"d":0,"r":[779,783],"t":0},{"d":0,"r":[783,784],"t":0},{"d":0,"r":[784,787],"t":0},{"d":0,"r":[787,788],"t":0},{"d":0,"r":[788,792],"t":0},{"d":0,"r":[792,793],"t":0},{"d":0,"r":[793,800],"t":0},{"d":0,"r":[800,801],"t":0},{"d":0,"r":[801,823],"t":0},{"d":0,"r":[803,810],"t":0},{"d":0,"r":[813,822],"t":0},{"d":0,"r":[811,812],"t":0},{"d":0,"r":[823,824],"t":0},{"d":0,"r":[824,825],"t":0},{"d":0,"r":[825,831],"t":0},{"d":0,"r":[831,832],"t":0},{"d":0,"r":[832,840],"t":0},{"d":0,"r":[840,841],"t":0},{"d":0,"r":[841,843],"t":0},{"d":0,"r":[843,844],"t":0},{"d":0,"r":[844,857],"t":0},{"d":0,"r":[857,858],"t":0},{"d":0,"r":[858,863],"t":0},{"d":0,"r":[863,864],"t":0},{"d":0,"r":[864,870],"t":0},{"d":0,"r":[870,871],"t":0},{"d":0,"r":[871,874],"t":0},{"d":0,"r":[874,875],"t":0},{"d":0,"r":[875,886],"t":0},{"d":0,"r":[888,898],"t":0},{"d":0,"r":[890,897],"t":0},{"d":0,"r":[899,918],"t":0},{"d":0,"r":[902,906],"t":0},{"d":0,"r":[906,907],"t":0},{"d":0,"r":[907,917],"t":0},{"d":0,"r":[919,957],"t":0},{"d":0,"r":[919,921],"t":0},{"d":0,"r":[921,922],"t":0},{"d":0,"r":[922,931],"t":0},{"d":0,"r":[931,932],"t":0},{"d":0,"r":[932,936],"t":0},{"d":0,"r":[936,937],"t":0},{"d":0,"r":[937,941],"t":0},{"d":0,"r":[941,942],"t":0},{"d":0,"r":[942,947],"t":0},{"d":0,"r":[947,948],"t":0},{"d":0,"r":[948,955],"t":0},{"d":0,"r":[955,956],"t":0},{"d":[[127,0,7],[128,7,1]],"r":[0,8],"t":2},{"d":0,"r":[958,1168],"t":0},{"d":0,"r":[1157,1166],"t":0},{"d":0,"r":[1022,1031],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1046,1058],"t":0},{"d":0,"r":[1020,1060],"t":0},{"d":0,"r":[1063,1072],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1087,1097],"t":0},{"d":0,"r":[1061,1099],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1100,1131],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[1133,1168],"t":0},{"d":0,"r":[1135,1142],"t":0},{"d":0,"r":[1142,1143],"t":0},{"d":0,"r":[1143,1147],"t":0},{"d":0,"r":[1147,1148],"t":0},{"d":0,"r":[1148,1155],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[1022,1031],"t":0},{"d":0,"r":[1022,1029],"t":0},{"d":0,"r":[1029,1030],"t":0},{"d":0,"r":[1030,1031],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1046,1058],"t":0},{"d":0,"r":[1046,1051],"t":0},{"d":0,"r":[1051,1052],"t":0},{"d":0,"r":[1052,1058],"t":0},{"d":0,"r":[1063,1072],"t":0},{"d":0,"r":[1063,1070],"t":0},{"d":0,"r":[1070,1071],"t":0},{"d":0,"r":[1071,1072],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1087,1097],"t":0},{"d":0,"r":[1087,1092],"t":0},{"d":0,"r":[1092,1093],"t":0},{"d":0,"r":[1093,1097],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1133,1168],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[958,988],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[1169,1191],"t":0},{"d":0,"r":[1172,1177],"t":0},{"d":0,"r":[1177,1178],"t":0},{"d":0,"r":[1178,1190],"t":0},{"d":0,"r":[1192,1294],"t":0},{"d":0,"r":[1192,1195],"t":0},{"d":0,"r":[1195,1196],"t":0},{"d":0,"r":[1196,1201],"t":0},{"d":0,"r":[1201,1202],"t":0},{"d":0,"r":[1202,1206],"t":0},{"d":0,"r":[1206,1207],"t":0},{"d":0,"r":[1207,1208],"t":0},{"d":0,"r":[1208,1209],"t":0},{"d":0,"r":[1209,1220],"t":0},{"d":0,"r":[1220,1221],"t":0},{"d":0,"r":[1221,1233],"t":0},{"d":0,"r":[1233,1234],"t":0},{"d":0,"r":[1234,1238],"t":0},{"d":0,"r":[1238,1239],"t":0},{"d":0,"r":[1239,1248],"t":0},{"d":0,"r":[1248,1249],"t":0},{"d":0,"r":[1249,1260],"t":0},{"d":0,"r":[1260,1261],"t":0},{"d":0,"r":[1261,1264],"t":0},{"d":0,"r":[1264,1265],"t":0},{"d":0,"r":[1265,1269],"t":0},{"d":0,"r":[1269,1270],"t":0},{"d":0,"r":[1270,1278],"t":0},{"d":0,"r":[1278,1279],"t":0},{"d":0,"r":[1279,1281],"t":0},{"d":0,"r":[1281,1282],"t":0},{"d":0,"r":[1282,1289],"t":0},{"d":0,"r":[1289,1290],"t":0},{"d":0,"r":[1290,1292],"t":0},{"d":0,"r":[1292,1293],"t":0},{"d":[[223,0,2],[224,2,1]],"r":[0,3],"t":2},{"d":0,"r":[1295,1360],"t":0},{"d":0,"r":[1295,1359],"t":0},{"d":0,"r":[1361,1432],"t":0},{"d":0,"r":[1361,1366],"t":0},{"d":0,"r":[1366,1367],"t":0},{"d":0,"r":[1367,1375],"t":0},{"d":0,"r":[1375,1376],"t":0},{"d":0,"r":[1376,1386],"t":0},{"d":0,"r":[1386,1387],"t":0},{"d":0,"r":[1387,1392],"t":0},{"d":0,"r":[1392,1393],"t":0},{"d":0,"r":[1393,1403],"t":0},{"d":0,"r":[1403,1404],"t":0},{"d":0,"r":[1404,1407],"t":0},{"d":0,"r":[1407,1408],"t":0},{"d":0,"r":[1408,1411],"t":0},{"d":0,"r":[1411,1412],"t":0},{"d":0,"r":[1412,1414],"t":0},{"d":0,"r":[1414,1415],"t":0},{"d":0,"r":[1415,1418],"t":0},{"d":0,"r":[1418,1419],"t":0},{"d":0,"r":[1419,1425],"t":0},{"d":0,"r":[1425,1426],"t":0},{"d":0,"r":[1426,1431],"t":0},{"d":0,"r":[1433,1443],"t":0},{"d":0,"r":[1435,1442],"t":0},{"d":0,"r":[1444,1467],"t":0},{"d":0,"r":[1447,1458],"t":0},{"d":0,"r":[1458,1459],"t":0},{"d":0,"r":[1459,1466],"t":0},{"d":0,"r":[1468,1513],"t":0},{"d":0,"r":[1468,1471],"t":0},{"d":0,"r":[1471,1472],"t":0},{"d":0,"r":[1472,1477],"t":0},{"d":0,"r":[1477,1478],"t":0},{"d":0,"r":[1478,1486],"t":0},{"d":0,"r":[1486,1487],"t":0},{"d":0,"r":[1487,1498],"t":0},{"d":0,"r":[1498,1499],"t":0},{"d":0,"r":[1499,1511],"t":0},{"d":0,"r":[1511,1512],"t":0},{"d":[[265,0,12],[266,12,1]],"r":[0,13],"t":2},{"d":0,"r":[1514,1605],"t":0},{"d":0,"r":[1516,1544],"t":0},{"d":0,"r":[1516,1520],"t":0},{"d":0,"r":[1520,1521],"t":0},{"d":[[270,0,4],[271,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1521,1522],"t":0},{"d":0,"r":[1522,1526],"t":0},{"d":0,"r":[1526,1527],"t":0},{"d":0,"r":[1527,1528],"t":0},{"d":0,"r":[1528,1536],"t":0},{"d":[[276,0,1],[277,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1536,1537],"t":0},{"d":[[278,0,9],[279,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1537,1538],"t":0},{"d":0,"r":[1538,1542],"t":0},{"d":0,"r":[1542,1543],"t":0},{"d":[[282,0,4],[283,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1546,1575],"t":0},{"d":0,"r":[1546,1551],"t":0},{"d":0,"r":[1551,1552],"t":0},{"d":0,"r":[1552,1553],"t":0},{"d":0,"r":[1553,1557],"t":0},{"d":0,"r":[1557,1558],"t":0},{"d":0,"r":[1558,1559],"t":0},{"d":0,"r":[1559,1567],"t":0},{"d":[[291,0,1],[292,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1567,1568],"t":0},{"d":[[293,0,9],[294,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1568,1569],"t":0},{"d":0,"r":[1569,1573],"t":0},{"d":0,"r":[1573,1574],"t":0},{"d":[[297,0,4],[298,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1577,1604],"t":0},{"d":0,"r":[1577,1580],"t":0},{"d":0,"r":[1580,1581],"t":0},{"d":[[301,0,3],[302,3,1]],"r":[0,4],"t":2},{"d":0,"r":[1581,1582],"t":0},{"d":0,"r":[1582,1586],"t":0},{"d":0,"r":[1586,1587],"t":0},{"d":0,"r":[1587,1588],"t":0},{"d":0,"r":[1588,1596],"t":0},{"d":[[307,0,1],[308,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1596,1597],"t":0},{"d":[[309,0,9],[310,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1597,1598],"t":0},{"d":0,"r":[1598,1602],"t":0},{"d":0,"r":[1602,1603],"t":0},{"d":[[313,0,4],[314,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1605,1654],"t":0},{"d":0,"r":[1605,1608],"t":0},{"d":0,"r":[1608,1609],"t":0},{"d":0,"r":[1609,1618],"t":0},{"d":0,"r":[1610,1618],"t":0},{"d":0,"r":[1609,1618],"t":0},{"d":0,"r":[1618,1619],"t":0},{"d":0,"r":[1619,1622],"t":0},{"d":0,"r":[1622,1623],"t":0},{"d":0,"r":[1623,1627],"t":0},{"d":0,"r":[1627,1628],"t":0},{"d":0,"r":[1628,1635],"t":0},{"d":0,"r":[1635,1636],"t":0},{"d":0,"r":[1636,1640],"t":0},{"d":0,"r":[1640,1641],"t":0},{"d":0,"r":[1641,1643],"t":0},{"d":0,"r":[1643,1644],"t":0},{"d":0,"r":[1644,1653],"t":0},{"d":0,"r":[1655,1668],"t":0},{"d":0,"r":[1657,1667],"t":0},{"d":0,"r":[1669,1874],"t":0},{"d":0,"r":[1669,1672],"t":0},{"d":0,"r":[1672,1673],"t":0},{"d":0,"r":[1673,1680],"t":0},{"d":0,"r":[1680,1681],"t":0},{"d":0,"r":[1681,1692],"t":0},{"d":0,"r":[1692,1693],"t":0},{"d":0,"r":[1693,1697],"t":0},{"d":0,"r":[1697,1698],"t":0},{"d":0,"r":[1698,1705],"t":0},{"d":0,"r":[1705,1706],"t":0},{"d":0,"r":[1706,1714],"t":0},{"d":0,"r":[1714,1715],"t":0},{"d":0,"r":[1715,1722],"t":0},{"d":0,"r":[1722,1723],"t":0},{"d":0,"r":[1723,1726],"t":0},{"d":0,"r":[1726,1727],"t":0},{"d":0,"r":[1727,1738],"t":0},{"d":0,"r":[1738,1739],"t":0},{"d":0,"r":[1739,1746],"t":0},{"d":0,"r":[1746,1747],"t":0},{"d":0,"r":[1747,1754],"t":0},{"d":0,"r":[1754,1755],"t":0},{"d":0,"r":[1755,1763],"t":0},{"d":0,"r":[1763,1764],"t":0},{"d":0,"r":[1764,1766],"t":0},{"d":0,"r":[1766,1767],"t":0},{"d":0,"r":[1767,1774],"t":0},{"d":0,"r":[1774,1775],"t":0},{"d":0,"r":[1775,1780],"t":0},{"d":0,"r":[1780,1781],"t":0},{"d":0,"r":[1781,1789],"t":0},{"d":0,"r":[1789,1790],"t":0},{"d":0,"r":[1790,1797],"t":0},{"d":0,"r":[1797,1798],"t":0},{"d":0,"r":[1798,1809],"t":0},{"d":0,"r":[1809,1810],"t":0},{"d":0,"r":[1810,1816],"t":0},{"d":0,"r":[1816,1872],"t":0},{"d":0,"r":[1816,1872],"t":0},{"d":0,"r":[1818,1825],"t":0},{"d":0,"r":[1825,1826],"t":0},{"d":0,"r":[1826,1830],"t":0},{"d":0,"r":[1830,1831],"t":0},{"d":0,"r":[1831,1833],"t":0},{"d":0,"r":[1833,1834],"t":0},{"d":0,"r":[1834,1840],"t":0},{"d":0,"r":[1840,1841],"t":0},{"d":0,"r":[1841,1843],"t":0},{"d":0,"r":[1843,1844],"t":0},{"d":0,"r":[1844,1851],"t":0},{"d":0,"r":[1851,1852],"t":0},{"d":0,"r":[1852,1860],"t":0},{"d":0,"r":[1860,1861],"t":0},{"d":0,"r":[1861,1871],"t":0},{"d":0,"r":[1872,1873],"t":0},{"d":0,"r":[1875,1890],"t":0},{"d":0,"r":[1878,1889],"t":0},{"d":0,"r":[1891,1974],"t":0},{"d":0,"r":[1891,1894],"t":0},{"d":0,"r":[1894,1895],"t":0},{"d":0,"r":[1895,1902],"t":0},{"d":0,"r":[1902,1903],"t":0},{"d":0,"r":[1903,1908],"t":0},{"d":0,"r":[1908,1909],"t":0},{"d":0,"r":[1909,1916],"t":0},{"d":0,"r":[1916,1917],"t":0},{"d":0,"r":[1917,1919],"t":0},{"d":0,"r":[1919,1920],"t":0},{"d":0,"r":[1920,1931],"t":0},{"d":0,"r":[1931,1932],"t":0},{"d":0,"r":[1932,1943],"t":0},{"d":0,"r":[1943,1944],"t":0},{"d":0,"r":[1944,1950],"t":0},{"d":0,"r":[1950,1951],"t":0},{"d":0,"r":[1951,1955],"t":0},{"d":0,"r":[1955,1956],"t":0},{"d":0,"r":[1956,1962],"t":0},{"d":0,"r":[1962,1963],"t":0},{"d":0,"r":[1963,1969],"t":0},{"d":0,"r":[1969,1970],"t":0},{"d":0,"r":[1970,1972],"t":0},{"d":0,"r":[1972,1973],"t":0},{"d":[[417,0,2],[418,2,1]],"r":[0,3],"t":2},{"d":0,"r":[1975,2048],"t":0},{"d":0,"r":[1978,2001],"t":0},{"d":0,"r":[1978,1991],"t":0},{"d":0,"r":[1991,1992],"t":0},{"d":0,"r":[1992,2000],"t":0},{"d":0,"r":[2004,2027],"t":0},{"d":0,"r":[2004,2011],"t":0},{"d":0,"r":[2011,2012],"t":0},{"d":0,"r":[2012,2019],"t":0},{"d":0,"r":[2019,2020],"t":0},{"d":0,"r":[2020,2026],"t":0},{"d":0,"r":[2030,2047],"t":0},{"d":0,"r":[2030,2039],"t":0},{"d":0,"r":[2039,2040],"t":0},{"d":0,"r":[2040,2046],"t":0},{"d":0,"r":[2048,2061],"t":0},{"d":0,"r":[2050,2060],"t":0},{"d":0,"r":[2062,2241],"t":0},{"d":0,"r":[2062,2069],"t":0},{"d":0,"r":[2069,2070],"t":0},{"d":0,"r":[2070,2078],"t":0},{"d":0,"r":[2078,2079],"t":0},{"d":0,"r":[2079,2085],"t":0},{"d":0,"r":[2085,2086],"t":0},{"d":0,"r":[2086,2095],"t":0},{"d":0,"r":[2095,2096],"t":0},{"d":0,"r":[2096,2106],"t":0},{"d":0,"r":[2106,2107],"t":0},{"d":0,"r":[2107,2109],"t":0},{"d":0,"r":[2109,2110],"t":0},{"d":0,"r":[2110,2117],"t":0},{"d":0,"r":[2117,2118],"t":0},{"d":0,"r":[2118,2129],"t":0},{"d":0,"r":[2129,2130],"t":0},{"d":0,"r":[2130,2133],"t":0},{"d":0,"r":[2133,2134],"t":0},{"d":0,"r":[2134,2144],"t":0},{"d":0,"r":[2144,2145],"t":0},{"d":0,"r":[2145,2154],"t":0},{"d":0,"r":[2154,2155],"t":0},{"d":0,"r":[2155,2159],"t":0},{"d":0,"r":[2159,2160],"t":0},{"d":0,"r":[2160,2167],"t":0},{"d":0,"r":[2167,2168],"t":0},{"d":0,"r":[2168,2169],"t":0},{"d":0,"r":[2169,2170],"t":0},{"d":0,"r":[2170,2180],"t":0},{"d":0,"r":[2180,2181],"t":0},{"d":0,"r":[2181,2184],"t":0},{"d":0,"r":[2184,2185],"t":0},{"d":0,"r":[2185,2191],"t":0},{"d":0,"r":[2191,2192],"t":0},{"d":0,"r":[2192,2200],"t":0},{"d":0,"r":[2200,2201],"t":0},{"d":0,"r":[2201,2203],"t":0},{"d":0,"r":[2203,2204],"t":0},{"d":0,"r":[2204,2208],"t":0},{"d":0,"r":[2208,2209],"t":0},{"d":0,"r":[2209,2217],"t":0},{"d":0,"r":[2217,2218],"t":0},{"d":0,"r":[2218,2224],"t":0},{"d":0,"r":[2224,2225],"t":0},{"d":0,"r":[2225,2239],"t":0},{"d":0,"r":[2227,2238],"t":0},{"d":0,"r":[2239,2240],"t":0},{"d":0,"r":[2242,2255],"t":0},{"d":0,"r":[2244,2254],"t":0},{"d":0,"r":[2256,2272],"t":0},{"d":0,"r":[2261,2266],"t":0},{"d":0,"r":[4,462],"t":0},{"d":489,"r":[164,363],"t":1},{"d":0,"r":[4,462],"t":0},{"d":491,"r":[165,362],"t":1},{"d":492,"r":[0,4],"t":1},{"d":491,"r":[165,362],"t":1},{"d":494,"r":[4,5],"t":1},{"d":491,"r":[165,362],"t":1},{"d":496,"r":[5,10],"t":1},{"d":491,"r":[165,362],"t":1},{"d":498,"r":[10,11],"t":1},{"d":491,"r":[165,362],"t":1},{"d":500,"r":[11,19],"t":1},{"d":491,"r":[165,362],"t":1},{"d":502,"r":[19,20],"t":1},{"d":491,"r":[165,362],"t":1},{"d":504,"r":[20,21],"t":1},{"d":491,"r":[165,362],"t":1},{"d":506,"r":[21,22],"t":1},{"d":491,"r":[165,362],"t":1},{"d":508,"r":[22,27],"t":1},{"d":491,"r":[165,362],"t":1},{"d":510,"r":[27,28],"t":1},{"d":491,"r":[165,362],"t":1},{"d":512,"r":[28,36],"t":1},{"d":491,"r":[165,362],"t":1},{"d":514,"r":[36,37],"t":1},{"d":491,"r":[165,362],"t":1},{"d":516,"r":[37,39],"t":1},{"d":491,"r":[165,362],"t":1},{"d":518,"r":[39,40],"t":1},{"d":491,"r":[165,362],"t":1},{"d":520,"r":[40,47],"t":1},{"d":491,"r":[165,362],"t":1},{"d":522,"r":[47,48],"t":1},{"d":491,"r":[165,362],"t":1},{"d":524,"r":[48,58],"t":1},{"d":491,"r":[165,362],"t":1},{"d":526,"r":[58,59],"t":1},{"d":491,"r":[165,362],"t":1},{"d":528,"r":[59,64],"t":1},{"d":491,"r":[165,362],"t":1},{"d":530,"r":[64,65],"t":1},{"d":491,"r":[165,362],"t":1},{"d":532,"r":[65,72],"t":1},{"d":491,"r":[165,362],"t":1},{"d":534,"r":[72,73],"t":1},{"d":491,"r":[165,362],"t":1},{"d":536,"r":[73,81],"t":1},{"d":491,"r":[165,362],"t":1},{"d":538,"r":[81,82],"t":1},{"d":491,"r":[165,362],"t":1},{"d":540,"r":[82,93],"t":1},{"d":491,"r":[165,362],"t":1},{"d":542,"r":[93,94],"t":1},{"d":491,"r":[165,362],"t":1},{"d":544,"r":[94,96],"t":1},{"d":491,"r":[165,362],"t":1},{"d":546,"r":[96,97],"t":1},{"d":491,"r":[165,362],"t":1},{"d":548,"r":[97,108],"t":1},{"d":491,"r":[165,362],"t":1},{"d":550,"r":[108,109],"t":1},{"d":491,"r":[165,362],"t":1},{"d":552,"r":[109,113],"t":1},{"d":491,"r":[165,362],"t":1},{"d":554,"r":[113,114],"t":1},{"d":491,"r":[165,362],"t":1},{"d":556,"r":[114,118],"t":1},{"d":491,"r":[165,362],"t":1},{"d":558,"r":[118,119],"t":1},{"d":491,"r":[165,362],"t":1},{"d":560,"r":[119,127],"t":1},{"d":491,"r":[165,362],"t":1},{"d":562,"r":[127,128],"t":1},{"d":491,"r":[165,362],"t":1},{"d":564,"r":[128,134],"t":1},{"d":491,"r":[165,362],"t":1},{"d":566,"r":[134,135],"t":1},{"d":491,"r":[165,362],"t":1},{"d":568,"r":[135,138],"t":1},{"d":491,"r":[165,362],"t":1},{"d":570,"r":[138,139],"t":1},{"d":491,"r":[165,362],"t":1},{"d":572,"r":[139,146],"t":1},{"d":491,"r":[165,362],"t":1},{"d":574,"r":[146,147],"t":1},{"d":491,"r":[165,362],"t":1},{"d":576,"r":[147,158],"t":1},{"d":491,"r":[165,362],"t":1},{"d":578,"r":[158,159],"t":1},{"d":491,"r":[165,362],"t":1},{"d":580,"r":[159,171],"t":1},{"d":491,"r":[165,362],"t":1},{"d":582,"r":[171,172],"t":1},{"d":491,"r":[165,362],"t":1},{"d":584,"r":[172,176],"t":1},{"d":491,"r":[165,362],"t":1},{"d":586,"r":[176,177],"t":1},{"d":491,"r":[165,362],"t":1},{"d":588,"r":[177,188],"t":1},{"d":491,"r":[165,362],"t":1},{"d":590,"r":[188,189],"t":1},{"d":491,"r":[165,362],"t":1},{"d":592,"r":[189,197],"t":1},{"d":0,"r":[4,462],"t":0},{"d":594,"r":[98,153],"t":1},{"d":0,"r":[4,462],"t":0},{"d":596,"r":[99,152],"t":1},{"d":597,"r":[0,10],"t":1},{"d":596,"r":[99,152],"t":1},{"d":599,"r":[10,11],"t":1},{"d":596,"r":[99,152],"t":1},{"d":601,"r":[11,13],"t":1},{"d":596,"r":[99,152],"t":1},{"d":603,"r":[13,14],"t":1},{"d":596,"r":[99,152],"t":1},{"d":605,"r":[14,22],"t":1},{"d":596,"r":[99,152],"t":1},{"d":607,"r":[22,23],"t":1},{"d":596,"r":[99,152],"t":1},{"d":609,"r":[23,31],"t":1},{"d":596,"r":[99,152],"t":1},{"d":611,"r":[31,32],"t":1},{"d":596,"r":[99,152],"t":1},{"d":613,"r":[32,42],"t":1},{"d":596,"r":[99,152],"t":1},{"d":615,"r":[42,43],"t":1},{"d":596,"r":[99,152],"t":1},{"d":617,"r":[43,45],"t":1},{"d":596,"r":[99,152],"t":1},{"d":619,"r":[45,46],"t":1},{"d":596,"r":[99,152],"t":1},{"d":621,"r":[46,53],"t":1},{"d":0,"r":[4,462],"t":0},{"d":623,"r":[67,84],"t":1},{"d":0,"r":[72,81],"t":0},{"d":0,"r":[4,462],"t":0},{"d":626,"r":[68,83],"t":1},{"d":627,"r":[9,10],"t":1},{"d":626,"r":[68,83],"t":1},{"d":629,"r":[10,15],"t":1},{"d":0,"r":[4,462],"t":0},{"d":631,"r":[443,457],"t":1},{"d":0,"r":[4,462],"t":0},{"d":633,"r":[443,457],"t":1},{"d":634,"r":[0,14],"t":1},{"d":0,"r":[4,462],"t":0},{"d":636,"r":[374,428],"t":1},{"d":0,"r":[4,462],"t":0},{"d":638,"r":[375,391],"t":1},{"d":0,"r":[4,462],"t":0},{"d":640,"r":[375,391],"t":1},{"d":641,"r":[0,7],"t":1},{"d":640,"r":[375,391],"t":1},{"d":643,"r":[7,8],"t":1},{"d":640,"r":[375,391],"t":1},{"d":645,"r":[8,16],"t":1},{"d":0,"r":[4,462],"t":0},{"d":647,"r":[393,400],"t":1},{"d":0,"r":[4,462],"t":0},{"d":649,"r":[393,400],"t":1},{"d":650,"r":[0,7],"t":1},{"d":0,"r":[4,462],"t":0},{"d":652,"r":[402,412],"t":1},{"d":0,"r":[4,462],"t":0},{"d":654,"r":[402,412],"t":1},{"d":655,"r":[0,10],"t":1},{"d":0,"r":[4,462],"t":0},{"d":657,"r":[414,427],"t":1},{"d":0,"r":[4,462],"t":0},{"d":659,"r":[414,427],"t":1},{"d":660,"r":[0,4],"t":1},{"d":659,"r":[414,427],"t":1},{"d":662,"r":[4,5],"t":1},{"d":659,"r":[414,427],"t":1},{"d":664,"r":[5,13],"t":1},{"d":0,"r":[4,462],"t":0},{"d":666,"r":[7,58],"t":1},{"d":0,"r":[4,462],"t":0},{"d":668,"r":[8,57],"t":1},{"d":669,"r":[0,7],"t":1},{"d":668,"r":[8,57],"t":1},{"d":671,"r":[7,8],"t":1},{"d":668,"r":[8,57],"t":1},{"d":673,"r":[8,16],"t":1},{"d":668,"r":[8,57],"t":1},{"d":675,"r":[16,17],"t":1},{"d":668,"r":[8,57],"t":1},{"d":677,"r":[17,27],"t":1},{"d":668,"r":[8,57],"t":1},{"d":679,"r":[27,28],"t":1},{"d":668,"r":[8,57],"t":1},{"d":681,"r":[28,30],"t":1},{"d":668,"r":[8,57],"t":1},{"d":683,"r":[30,31],"t":1},{"d":668,"r":[8,57],"t":1},{"d":685,"r":[31,38],"t":1},{"d":668,"r":[8,57],"t":1},{"d":687,"r":[38,39],"t":1},{"d":668,"r":[8,57],"t":1},{"d":689,"r":[39,49],"t":1},{"d":0,"r":[4,462],"t":0},{"d":691,"r":[154,162],"t":1},{"d":0,"r":[4,462],"t":0},{"d":693,"r":[85,96],"t":1},{"d":0,"r":[4,462],"t":0},{"d":695,"r":[59,65],"t":1},{"d":0,"r":[4,462],"t":0},{"d":697,"r":[429,441],"t":1},{"d":0,"r":[4,462],"t":0},{"d":699,"r":[364,372],"t":1},{"d":0,"r":[4,462],"t":0},{"d":701,"r":[0,5],"t":1}]}} \ No newline at end of file +{"blocks":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["introduction",[],[]],[{"c":"Introduction","s":1,"t":"Str"}]],"s":0,"t":"Header"},{"c":[{"c":"Climate","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"prediction","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"is","s":7,"t":"Str"},{"s":8,"t":"Space"},{"c":"a","s":9,"t":"Str"},{"s":10,"t":"Space"},{"c":"critical","s":11,"t":"Str"},{"s":12,"t":"Space"},{"c":"challenge","s":13,"t":"Str"},{"s":14,"t":"Space"},{"c":"in","s":15,"t":"Str"},{"s":16,"t":"Space"},{"c":"modern","s":17,"t":"Str"},{"s":18,"t":"Space"},{"c":"science","s":19,"t":"Str"},{"c":[{"c":[{"c":"See","s":22,"t":"Str"},{"s":23,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"ipcc2021","citationIdS":25,"citationMode":{"t":"AuthorInText"},"citationNoteNum":1,"citationPrefix":[],"citationSuffix":[]}],[{"c":"@ipcc2021","s":26,"t":"Str"}]],"s":24,"t":"Cite"},{"s":27,"t":"Space"},{"c":"for","s":28,"t":"Str"},{"s":29,"t":"Space"},{"c":"comprehensive","s":30,"t":"Str"},{"s":31,"t":"Space"},{"c":"discussion","s":32,"t":"Str"}],"s":21,"t":"Para"}],"s":20,"t":"Note"},{"c":".","s":33,"t":"Str"},{"s":34,"t":"Space"},{"c":"Traditional","s":35,"t":"Str"},{"s":36,"t":"Space"},{"c":"statistical","s":37,"t":"Str"},{"s":38,"t":"Space"},{"c":"methods","s":39,"t":"Str"},{"s":40,"t":"Space"},{"c":"have","s":41,"t":"Str"},{"s":42,"t":"Space"},{"c":"limitations","s":43,"t":"Str"},{"s":44,"t":"Space"},{"c":"when","s":45,"t":"Str"},{"s":46,"t":"Space"},{"c":"dealing","s":47,"t":"Str"},{"s":48,"t":"Space"},{"c":"with","s":49,"t":"Str"},{"s":50,"t":"Space"},{"c":"complex,","s":51,"t":"Str"},{"s":52,"t":"Space"},{"c":"non-linear","s":53,"t":"Str"},{"s":54,"t":"Space"},{"c":"relationships","s":55,"t":"Str"},{"s":56,"t":"Space"},{"c":"in","s":57,"t":"Str"},{"s":58,"t":"Space"},{"c":"climate","s":59,"t":"Str"},{"s":60,"t":"Space"},{"c":"data.","s":61,"t":"Str"}],"s":2,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["background",[],[]],[{"c":"Background","s":63,"t":"Str"}]],"s":62,"t":"Header"},{"c":[{"c":"The","s":65,"t":"Str"},{"s":66,"t":"Space"},{"c":"field","s":67,"t":"Str"},{"s":68,"t":"Space"},{"c":"of","s":69,"t":"Str"},{"s":70,"t":"Space"},{"c":"climate","s":71,"t":"Str"},{"s":72,"t":"Space"},{"c":"science","s":73,"t":"Str"},{"s":74,"t":"Space"},{"c":"has","s":75,"t":"Str"},{"s":76,"t":"Space"},{"c":"evolved","s":77,"t":"Str"},{"s":78,"t":"Space"},{"c":"significantly","s":79,"t":"Str"},{"s":80,"t":"Space"},{"c":"over","s":81,"t":"Str"},{"s":82,"t":"Space"},{"c":"the","s":83,"t":"Str"},{"s":84,"t":"Space"},{"c":"past","s":85,"t":"Str"},{"s":86,"t":"Space"},{"c":"decades","s":87,"t":"Str"},{"s":88,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"doe2020","citationIdS":90,"citationMode":{"t":"NormalCitation"},"citationNoteNum":2,"citationPrefix":[],"citationSuffix":[]},{"citationHash":0,"citationId":"smith2019","citationIdS":91,"citationMode":{"t":"NormalCitation"},"citationNoteNum":2,"citationPrefix":[{"s":92,"t":"Space"}],"citationSuffix":[]}],[]],"s":89,"t":"Cite"},{"c":".","s":93,"t":"Str"},{"s":94,"t":"Space"},{"c":"Recent","s":95,"t":"Str"},{"s":96,"t":"Space"},{"c":"advances","s":97,"t":"Str"},{"s":98,"t":"Space"},{"c":"in","s":99,"t":"Str"},{"s":100,"t":"Space"},{"c":"computational","s":101,"t":"Str"},{"s":102,"t":"Space"},{"c":"power","s":103,"t":"Str"},{"s":104,"t":"Space"},{"c":"enable","s":105,"t":"Str"},{"s":106,"t":"Space"},{"c":"new","s":107,"t":"Str"},{"s":108,"t":"Space"},{"c":"approaches.","s":109,"t":"Str"}],"s":64,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["methods",[],[]],[{"c":"Methods","s":111,"t":"Str"}]],"s":110,"t":"Header"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["data-collection",[],[]],[{"c":"Data","s":113,"t":"Str"},{"s":114,"t":"Space"},{"c":"Collection","s":115,"t":"Str"}]],"s":112,"t":"Header"},{"c":[{"c":"We","s":117,"t":"Str"},{"s":118,"t":"Space"},{"c":"collected","s":119,"t":"Str"},{"s":120,"t":"Space"},{"c":"data","s":121,"t":"Str"},{"s":122,"t":"Space"},{"c":"from","s":123,"t":"Str"},{"s":124,"t":"Space"},{"c":"three","s":125,"t":"Str"},{"s":126,"t":"Space"},{"c":"sources:","s":129,"t":"Str"}],"s":116,"t":"Para"},{"a":{"classes":[],"id":131,"kvs":[]},"bodiesS":[{"a":{"classes":[],"id":null,"kvs":[]},"bodyS":[{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":132},{"a":{"classes":[],"id":null,"kvs":[]},"s":133},{"a":{"classes":[],"id":null,"kvs":[]},"s":134}],"s":135},{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":136},{"a":{"classes":[],"id":null,"kvs":[]},"s":137},{"a":{"classes":[],"id":null,"kvs":[]},"s":138}],"s":139},{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":140},{"a":{"classes":[],"id":null,"kvs":[]},"s":141},{"a":{"classes":[],"id":null,"kvs":[]},"s":142}],"s":143}],"headS":[],"s":144}],"c":[["tbl-data",[],[]],[null,[{"c":[{"c":"Climate","s":146,"t":"Str"},{"s":147,"t":"Space"},{"c":"data","s":148,"t":"Str"},{"s":149,"t":"Space"},{"c":"sources","s":150,"t":"Str"}],"s":145,"t":"Plain"}]],[[{"t":"AlignDefault"},{"t":"ColWidthDefault"}],[{"t":"AlignDefault"},{"t":"ColWidthDefault"}],[{"t":"AlignDefault"},{"t":"ColWidthDefault"}]],[["",[],[]],[[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Source","s":152,"t":"Str"}],"s":151,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Years","s":154,"t":"Str"}],"s":153,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Variables","s":156,"t":"Str"}],"s":155,"t":"Plain"}]]]]]],[[["",[],[]],0,[],[[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Station","s":158,"t":"Str"},{"s":159,"t":"Space"},{"c":"A","s":160,"t":"Str"}],"s":157,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"1990-2020","s":162,"t":"Str"}],"s":161,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Temp,","s":164,"t":"Str"},{"s":165,"t":"Space"},{"c":"Precip","s":166,"t":"Str"}],"s":163,"t":"Plain"}]]]],[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Station","s":168,"t":"Str"},{"s":169,"t":"Space"},{"c":"B","s":170,"t":"Str"}],"s":167,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"1995-2020","s":172,"t":"Str"}],"s":171,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Temp,","s":174,"t":"Str"},{"s":175,"t":"Space"},{"c":"Wind","s":176,"t":"Str"}],"s":173,"t":"Plain"}]]]],[["",[],[]],[[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"Satellite","s":178,"t":"Str"}],"s":177,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"2000-2020","s":180,"t":"Str"}],"s":179,"t":"Plain"}]],[["",[],[]],{"t":"AlignDefault"},1,1,[{"c":[{"c":"All","s":182,"t":"Str"}],"s":181,"t":"Plain"}]]]]]]],[["",[],[]],[]]],"captionS":183,"footS":{"a":{"classes":[],"id":null,"kvs":[]},"rowsS":[],"s":184},"headS":{"a":{"classes":[],"id":null,"kvs":[]},"rowsS":[{"a":{"classes":[],"id":null,"kvs":[]},"cellsS":[{"a":{"classes":[],"id":null,"kvs":[]},"s":185},{"a":{"classes":[],"id":null,"kvs":[]},"s":186},{"a":{"classes":[],"id":null,"kvs":[]},"s":187}],"s":188}],"s":189},"s":130,"t":"Table"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["model-architecture",[],[]],[{"c":"Model","s":191,"t":"Str"},{"s":192,"t":"Space"},{"c":"Architecture","s":193,"t":"Str"}]],"s":190,"t":"Header"},{"c":[{"c":"Our","s":195,"t":"Str"},{"s":196,"t":"Space"},{"c":"model","s":197,"t":"Str"},{"s":198,"t":"Space"},{"c":"uses","s":199,"t":"Str"},{"s":200,"t":"Space"},{"c":"a","s":201,"t":"Str"},{"s":202,"t":"Space"},{"c":"transformer","s":203,"t":"Str"},{"s":204,"t":"Space"},{"c":"architecture","s":205,"t":"Str"},{"s":206,"t":"Space"},{"c":"with","s":207,"t":"Str"},{"s":208,"t":"Space"},{"c":"attention","s":209,"t":"Str"},{"s":210,"t":"Space"},{"c":"mechanisms.","s":211,"t":"Str"},{"s":212,"t":"Space"},{"c":"The","s":213,"t":"Str"},{"s":214,"t":"Space"},{"c":"loss","s":215,"t":"Str"},{"s":216,"t":"Space"},{"c":"function","s":217,"t":"Str"},{"s":218,"t":"Space"},{"c":"is","s":219,"t":"Str"},{"s":220,"t":"Space"},{"c":"defined","s":221,"t":"Str"},{"s":222,"t":"Space"},{"c":"as:","s":225,"t":"Str"}],"s":194,"t":"Para"},{"c":[{"c":[{"t":"DisplayMath"},"\nL(\\theta) = \\frac{1}{N} \\sum_{i=1}^{N} (y_i - \\hat{y}_i)^2\n"],"s":227,"t":"Math","textS":228}],"s":226,"t":"Para"},{"c":[{"c":"where","s":230,"t":"Str"},{"s":231,"t":"Space"},{"c":[{"t":"InlineMath"},"\\theta"],"s":232,"t":"Math","textS":233},{"s":234,"t":"Space"},{"c":"represents","s":235,"t":"Str"},{"s":236,"t":"Space"},{"c":"model","s":237,"t":"Str"},{"s":238,"t":"Space"},{"c":"parameters","s":239,"t":"Str"},{"s":240,"t":"Space"},{"c":"and","s":241,"t":"Str"},{"s":242,"t":"Space"},{"c":[{"t":"InlineMath"},"N"],"s":243,"t":"Math","textS":244},{"s":245,"t":"Space"},{"c":"is","s":246,"t":"Str"},{"s":247,"t":"Space"},{"c":"the","s":248,"t":"Str"},{"s":249,"t":"Space"},{"c":"sample","s":250,"t":"Str"},{"s":251,"t":"Space"},{"c":"size.","s":252,"t":"Str"}],"s":229,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["results",[],[]],[{"c":"Results","s":254,"t":"Str"}]],"s":253,"t":"Header"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["performance-metrics",[],[]],[{"c":"Performance","s":256,"t":"Str"},{"s":257,"t":"Space"},{"c":"Metrics","s":258,"t":"Str"}]],"s":255,"t":"Header"},{"c":[{"c":"The","s":260,"t":"Str"},{"s":261,"t":"Space"},{"c":"model","s":262,"t":"Str"},{"s":263,"t":"Space"},{"c":"achieved","s":264,"t":"Str"},{"s":265,"t":"Space"},{"c":"significant","s":266,"t":"Str"},{"s":267,"t":"Space"},{"c":"improvements:","s":270,"t":"Str"}],"s":259,"t":"Para"},{"c":[[{"c":[{"c":"RMSE:","s":275,"t":"Str"},{"s":276,"t":"Space"},{"c":"0.45","s":277,"t":"Str"},{"s":278,"t":"Space"},{"c":"(baseline:","s":283,"t":"Str"},{"s":284,"t":"Space"},{"c":"0.78)","s":287,"t":"Str"}],"s":272,"t":"Plain"}],[{"c":[{"c":[{"t":"InlineMath"},"R^2"],"s":289,"t":"Math","textS":290},{"c":":","s":291,"t":"Str"},{"s":292,"t":"Space"},{"c":"0.92","s":293,"t":"Str"},{"s":294,"t":"Space"},{"c":"(baseline:","s":299,"t":"Str"},{"s":300,"t":"Space"},{"c":"0.65)","s":303,"t":"Str"}],"s":288,"t":"Plain"}],[{"c":[{"c":"MAE:","s":307,"t":"Str"},{"s":308,"t":"Space"},{"c":"0.31","s":309,"t":"Str"},{"s":310,"t":"Space"},{"c":"(baseline:","s":315,"t":"Str"},{"s":316,"t":"Space"},{"c":"0.58)","s":319,"t":"Str"}],"s":304,"t":"Plain"}]],"s":271,"t":"BulletList"},{"c":[{"c":"See","s":321,"t":"Str"},{"s":322,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"tbl-data","citationIdS":324,"citationMode":{"t":"AuthorInText"},"citationNoteNum":3,"citationPrefix":[],"citationSuffix":[]}],[{"c":"@tbl-data","s":325,"t":"Str"}]],"s":323,"t":"Cite"},{"s":326,"t":"Space"},{"c":"for","s":327,"t":"Str"},{"s":328,"t":"Space"},{"c":"data","s":329,"t":"Str"},{"s":330,"t":"Space"},{"c":"sources","s":331,"t":"Str"},{"s":332,"t":"Space"},{"c":"used","s":333,"t":"Str"},{"s":334,"t":"Space"},{"c":"in","s":335,"t":"Str"},{"s":336,"t":"Space"},{"c":"training.","s":337,"t":"Str"}],"s":320,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["discussion",[],[]],[{"c":"Discussion","s":339,"t":"Str"}]],"s":338,"t":"Header"},{"c":[{"c":"Our","s":341,"t":"Str"},{"s":342,"t":"Space"},{"c":"results","s":343,"t":"Str"},{"s":344,"t":"Space"},{"c":"demonstrate","s":345,"t":"Str"},{"s":346,"t":"Space"},{"c":"that","s":347,"t":"Str"},{"s":348,"t":"Space"},{"c":"machine","s":349,"t":"Str"},{"s":350,"t":"Space"},{"c":"learning","s":351,"t":"Str"},{"s":352,"t":"Space"},{"c":"methods","s":353,"t":"Str"},{"s":354,"t":"Space"},{"c":"can","s":355,"t":"Str"},{"s":356,"t":"Space"},{"c":"effectively","s":357,"t":"Str"},{"s":358,"t":"Space"},{"c":"capture","s":359,"t":"Str"},{"s":360,"t":"Space"},{"c":"complex","s":361,"t":"Str"},{"s":362,"t":"Space"},{"c":"patterns","s":363,"t":"Str"},{"s":364,"t":"Space"},{"c":"in","s":365,"t":"Str"},{"s":366,"t":"Space"},{"c":"climate","s":367,"t":"Str"},{"s":368,"t":"Space"},{"c":"data.","s":369,"t":"Str"},{"s":370,"t":"Space"},{"c":"However,","s":371,"t":"Str"},{"s":372,"t":"Space"},{"c":"several","s":373,"t":"Str"},{"s":374,"t":"Space"},{"c":"limitations","s":375,"t":"Str"},{"s":376,"t":"Space"},{"c":"remain","s":377,"t":"Str"},{"c":[{"c":[{"c":"Further","s":380,"t":"Str"},{"s":381,"t":"Space"},{"c":"work","s":382,"t":"Str"},{"s":383,"t":"Space"},{"c":"is","s":384,"t":"Str"},{"s":385,"t":"Space"},{"c":"needed","s":386,"t":"Str"},{"s":387,"t":"Space"},{"c":"to","s":388,"t":"Str"},{"s":389,"t":"Space"},{"c":"address","s":390,"t":"Str"},{"s":391,"t":"Space"},{"c":"regional","s":392,"t":"Str"},{"s":393,"t":"Space"},{"c":"variations","s":394,"t":"Str"}],"s":379,"t":"Para"}],"s":378,"t":"Note"},{"c":".","s":395,"t":"Str"}],"s":340,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[2,["limitations",[],[]],[{"c":"Limitations","s":397,"t":"Str"}]],"s":396,"t":"Header"},{"c":[{"c":"The","s":399,"t":"Str"},{"s":400,"t":"Space"},{"c":"current","s":401,"t":"Str"},{"s":402,"t":"Space"},{"c":"study","s":403,"t":"Str"},{"s":404,"t":"Space"},{"c":"focuses","s":405,"t":"Str"},{"s":406,"t":"Space"},{"c":"on","s":407,"t":"Str"},{"s":408,"t":"Space"},{"c":"temperature","s":409,"t":"Str"},{"s":410,"t":"Space"},{"c":"prediction.","s":411,"t":"Str"},{"s":412,"t":"Space"},{"c":"Future","s":413,"t":"Str"},{"s":414,"t":"Space"},{"c":"work","s":415,"t":"Str"},{"s":416,"t":"Space"},{"c":"should","s":417,"t":"Str"},{"s":418,"t":"Space"},{"c":"extend","s":419,"t":"Str"},{"s":420,"t":"Space"},{"c":"to:","s":423,"t":"Str"}],"s":398,"t":"Para"},{"c":[[1,{"t":"Decimal"},{"t":"Period"}],[[{"c":[{"c":"Precipitation","s":426,"t":"Str"},{"s":427,"t":"Space"},{"c":"patterns","s":428,"t":"Str"}],"s":425,"t":"Plain"}],[{"c":[{"c":"Extreme","s":430,"t":"Str"},{"s":431,"t":"Space"},{"c":"weather","s":432,"t":"Str"},{"s":433,"t":"Space"},{"c":"events","s":434,"t":"Str"}],"s":429,"t":"Plain"}],[{"c":[{"c":"Long-term","s":436,"t":"Str"},{"s":437,"t":"Space"},{"c":"trends","s":438,"t":"Str"}],"s":435,"t":"Plain"}]]],"s":424,"t":"OrderedList"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["conclusion",[],[]],[{"c":"Conclusion","s":440,"t":"Str"}]],"s":439,"t":"Header"},{"c":[{"c":"Machine","s":442,"t":"Str"},{"s":443,"t":"Space"},{"c":"learning","s":444,"t":"Str"},{"s":445,"t":"Space"},{"c":"offers","s":446,"t":"Str"},{"s":447,"t":"Space"},{"c":"promising","s":448,"t":"Str"},{"s":449,"t":"Space"},{"c":"approaches","s":450,"t":"Str"},{"s":451,"t":"Space"},{"c":"to","s":452,"t":"Str"},{"s":453,"t":"Space"},{"c":"climate","s":454,"t":"Str"},{"s":455,"t":"Space"},{"c":"prediction.","s":456,"t":"Str"},{"s":457,"t":"Space"},{"c":"The","s":458,"t":"Str"},{"s":459,"t":"Space"},{"c":"techniques","s":460,"t":"Str"},{"s":461,"t":"Space"},{"c":"presented","s":462,"t":"Str"},{"s":463,"t":"Space"},{"c":"here","s":464,"t":"Str"},{"s":465,"t":"Space"},{"c":"provide","s":466,"t":"Str"},{"s":467,"t":"Space"},{"c":"a","s":468,"t":"Str"},{"s":469,"t":"Space"},{"c":"foundation","s":470,"t":"Str"},{"s":471,"t":"Space"},{"c":"for","s":472,"t":"Str"},{"s":473,"t":"Space"},{"c":"future","s":474,"t":"Str"},{"s":475,"t":"Space"},{"c":"research","s":476,"t":"Str"},{"s":477,"t":"Space"},{"c":"in","s":478,"t":"Str"},{"s":479,"t":"Space"},{"c":"this","s":480,"t":"Str"},{"s":481,"t":"Space"},{"c":"critical","s":482,"t":"Str"},{"s":483,"t":"Space"},{"c":"domain","s":484,"t":"Str"},{"s":485,"t":"Space"},{"c":[[{"citationHash":0,"citationId":"johnson2021","citationIdS":487,"citationMode":{"t":"NormalCitation"},"citationNoteNum":4,"citationPrefix":[],"citationSuffix":[]}],[]],"s":486,"t":"Cite"},{"c":".","s":488,"t":"Str"}],"s":441,"t":"Para"},{"a":{"classes":[],"id":null,"kvs":[]},"c":[1,["references",[],[]],[{"c":"References","s":490,"t":"Str"}]],"s":489,"t":"Header"},{"a":{"classes":[],"id":492,"kvs":[]},"c":[["refs",[],[]],[]],"s":491,"t":"Div"}],"meta":{"abstract":{"c":[{"c":"This","s":497,"t":"Str"},{"s":499,"t":"Space"},{"c":"paper","s":501,"t":"Str"},{"s":503,"t":"Space"},{"c":"presents","s":505,"t":"Str"},{"s":507,"t":"Space"},{"c":"a","s":509,"t":"Str"},{"s":511,"t":"Space"},{"c":"novel","s":513,"t":"Str"},{"s":515,"t":"Space"},{"c":"approach","s":517,"t":"Str"},{"s":519,"t":"Space"},{"c":"to","s":521,"t":"Str"},{"s":523,"t":"Space"},{"c":"climate","s":525,"t":"Str"},{"s":527,"t":"Space"},{"c":"prediction","s":529,"t":"Str"},{"s":531,"t":"Space"},{"c":"using","s":533,"t":"Str"},{"s":535,"t":"Space"},{"c":"machine","s":537,"t":"Str"},{"s":539,"t":"Space"},{"c":"learning","s":541,"t":"Str"},{"s":543,"t":"Space"},{"c":"techniques.","s":545,"t":"Str"},{"s":547,"t":"Space"},{"c":"We","s":549,"t":"Str"},{"s":551,"t":"Space"},{"c":"demonstrate","s":553,"t":"Str"},{"s":555,"t":"Space"},{"c":"that","s":557,"t":"Str"},{"s":559,"t":"Space"},{"c":"deep","s":561,"t":"Str"},{"s":563,"t":"Space"},{"c":"learning","s":565,"t":"Str"},{"s":567,"t":"Space"},{"c":"models","s":569,"t":"Str"},{"s":571,"t":"Space"},{"c":"can","s":573,"t":"Str"},{"s":575,"t":"Space"},{"c":"achieve","s":577,"t":"Str"},{"s":579,"t":"Space"},{"c":"significant","s":581,"t":"Str"},{"s":583,"t":"Space"},{"c":"improvements","s":585,"t":"Str"},{"s":587,"t":"Space"},{"c":"over","s":589,"t":"Str"},{"s":591,"t":"Space"},{"c":"traditional","s":593,"t":"Str"},{"s":595,"t":"Space"},{"c":"methods.","s":597,"t":"Str"}],"s":494,"t":"MetaInlines"},"affiliation":{"c":[{"c":"Department","s":602,"t":"Str"},{"s":604,"t":"Space"},{"c":"of","s":606,"t":"Str"},{"s":608,"t":"Space"},{"c":"Computer","s":610,"t":"Str"},{"s":612,"t":"Space"},{"c":"Science,","s":614,"t":"Str"},{"s":616,"t":"Space"},{"c":"University","s":618,"t":"Str"},{"s":620,"t":"Space"},{"c":"of","s":622,"t":"Str"},{"s":624,"t":"Space"},{"c":"Example","s":626,"t":"Str"}],"s":599,"t":"MetaInlines"},"author":{"c":[{"c":"Dr. Alice","s":629,"t":"Str"},{"s":632,"t":"Space"},{"c":"Smith","s":634,"t":"Str"}],"s":628,"t":"MetaInlines"},"bibliography":{"c":[{"c":"references.bib","s":639,"t":"Str"}],"s":636,"t":"MetaInlines"},"keywords":{"c":[{"c":[{"c":"machine","s":646,"t":"Str"},{"s":648,"t":"Space"},{"c":"learning","s":650,"t":"Str"}],"s":643,"t":"MetaInlines"},{"c":[{"c":"climate","s":655,"t":"Str"}],"s":652,"t":"MetaInlines"},{"c":[{"c":"prediction","s":660,"t":"Str"}],"s":657,"t":"MetaInlines"},{"c":[{"c":"deep","s":665,"t":"Str"},{"s":667,"t":"Space"},{"c":"learning","s":669,"t":"Str"}],"s":662,"t":"MetaInlines"}],"s":641,"t":"MetaList"},"title":{"c":[{"c":"Machine","s":674,"t":"Str"},{"s":676,"t":"Space"},{"c":"Learning","s":678,"t":"Str"},{"s":680,"t":"Space"},{"c":"Approaches","s":682,"t":"Str"},{"s":684,"t":"Space"},{"c":"to","s":686,"t":"Str"},{"s":688,"t":"Space"},{"c":"Climate","s":690,"t":"Str"},{"s":692,"t":"Space"},{"c":"Prediction","s":694,"t":"Str"}],"s":671,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,62,88,157,367,432,461,465,466,481,482,707,708,722,723,886,887,897,898,917,918,956,957,988,1019,1060,1099,1131,1132,1167,1168,1190,1191,1293,1294,1297,1356,1359,1360,1431,1432,1442,1443,1466,1467,1512,1513,1543,1574,1603,1604,1653,1654,1667,1668,1873,1874,1889,1890,1973,1974,2000,2026,2046,2047,2060,2061,2240,2241,2254,2255,2267,2271],"name":"ts-packages/annotated-qmd/examples/academic-paper.qmd","total_length":2272}],"metaTopLevelKeySources":{"abstract":696,"affiliation":698,"author":700,"bibliography":702,"keywords":704,"title":706},"p":[{"d":0,"r":[467,482],"t":0},{"d":0,"r":[469,481],"t":0},{"d":0,"r":[483,708],"t":0},{"d":0,"r":[483,490],"t":0},{"d":0,"r":[490,491],"t":0},{"d":0,"r":[491,501],"t":0},{"d":0,"r":[501,502],"t":0},{"d":0,"r":[502,504],"t":0},{"d":0,"r":[504,505],"t":0},{"d":0,"r":[505,506],"t":0},{"d":0,"r":[506,507],"t":0},{"d":0,"r":[507,515],"t":0},{"d":0,"r":[515,516],"t":0},{"d":0,"r":[516,525],"t":0},{"d":0,"r":[525,526],"t":0},{"d":0,"r":[526,528],"t":0},{"d":0,"r":[528,529],"t":0},{"d":0,"r":[529,535],"t":0},{"d":0,"r":[535,536],"t":0},{"d":0,"r":[536,543],"t":0},{"d":0,"r":[543,588],"t":0},{"d":0,"r":[543,588],"t":0},{"d":0,"r":[545,548],"t":0},{"d":0,"r":[548,549],"t":0},{"d":0,"r":[549,558],"t":0},{"d":0,"r":[550,558],"t":0},{"d":0,"r":[549,558],"t":0},{"d":0,"r":[558,559],"t":0},{"d":0,"r":[559,562],"t":0},{"d":0,"r":[562,563],"t":0},{"d":0,"r":[563,576],"t":0},{"d":0,"r":[576,577],"t":0},{"d":0,"r":[577,587],"t":0},{"d":0,"r":[588,589],"t":0},{"d":0,"r":[589,590],"t":0},{"d":0,"r":[590,601],"t":0},{"d":0,"r":[601,602],"t":0},{"d":0,"r":[602,613],"t":0},{"d":0,"r":[613,614],"t":0},{"d":0,"r":[614,621],"t":0},{"d":0,"r":[621,622],"t":0},{"d":0,"r":[622,626],"t":0},{"d":0,"r":[626,627],"t":0},{"d":0,"r":[627,638],"t":0},{"d":0,"r":[638,639],"t":0},{"d":0,"r":[639,643],"t":0},{"d":0,"r":[643,644],"t":0},{"d":0,"r":[644,651],"t":0},{"d":0,"r":[651,652],"t":0},{"d":0,"r":[652,656],"t":0},{"d":0,"r":[656,657],"t":0},{"d":0,"r":[657,665],"t":0},{"d":0,"r":[665,666],"t":0},{"d":0,"r":[666,676],"t":0},{"d":0,"r":[676,677],"t":0},{"d":0,"r":[677,690],"t":0},{"d":0,"r":[690,691],"t":0},{"d":0,"r":[691,693],"t":0},{"d":0,"r":[693,694],"t":0},{"d":0,"r":[694,701],"t":0},{"d":0,"r":[701,702],"t":0},{"d":0,"r":[702,707],"t":0},{"d":0,"r":[709,723],"t":0},{"d":0,"r":[712,722],"t":0},{"d":0,"r":[724,887],"t":0},{"d":0,"r":[724,727],"t":0},{"d":0,"r":[727,728],"t":0},{"d":0,"r":[728,733],"t":0},{"d":0,"r":[733,734],"t":0},{"d":0,"r":[734,736],"t":0},{"d":0,"r":[736,737],"t":0},{"d":0,"r":[737,744],"t":0},{"d":0,"r":[744,745],"t":0},{"d":0,"r":[745,752],"t":0},{"d":0,"r":[752,753],"t":0},{"d":0,"r":[753,756],"t":0},{"d":0,"r":[756,757],"t":0},{"d":0,"r":[757,764],"t":0},{"d":0,"r":[764,765],"t":0},{"d":0,"r":[765,778],"t":0},{"d":0,"r":[778,779],"t":0},{"d":0,"r":[779,783],"t":0},{"d":0,"r":[783,784],"t":0},{"d":0,"r":[784,787],"t":0},{"d":0,"r":[787,788],"t":0},{"d":0,"r":[788,792],"t":0},{"d":0,"r":[792,793],"t":0},{"d":0,"r":[793,800],"t":0},{"d":0,"r":[800,801],"t":0},{"d":0,"r":[801,823],"t":0},{"d":0,"r":[803,810],"t":0},{"d":0,"r":[813,822],"t":0},{"d":0,"r":[811,812],"t":0},{"d":0,"r":[823,824],"t":0},{"d":0,"r":[824,825],"t":0},{"d":0,"r":[825,831],"t":0},{"d":0,"r":[831,832],"t":0},{"d":0,"r":[832,840],"t":0},{"d":0,"r":[840,841],"t":0},{"d":0,"r":[841,843],"t":0},{"d":0,"r":[843,844],"t":0},{"d":0,"r":[844,857],"t":0},{"d":0,"r":[857,858],"t":0},{"d":0,"r":[858,863],"t":0},{"d":0,"r":[863,864],"t":0},{"d":0,"r":[864,870],"t":0},{"d":0,"r":[870,871],"t":0},{"d":0,"r":[871,874],"t":0},{"d":0,"r":[874,875],"t":0},{"d":0,"r":[875,886],"t":0},{"d":0,"r":[888,898],"t":0},{"d":0,"r":[890,897],"t":0},{"d":0,"r":[899,918],"t":0},{"d":0,"r":[902,906],"t":0},{"d":0,"r":[906,907],"t":0},{"d":0,"r":[907,917],"t":0},{"d":0,"r":[919,957],"t":0},{"d":0,"r":[919,921],"t":0},{"d":0,"r":[921,922],"t":0},{"d":0,"r":[922,931],"t":0},{"d":0,"r":[931,932],"t":0},{"d":0,"r":[932,936],"t":0},{"d":0,"r":[936,937],"t":0},{"d":0,"r":[937,941],"t":0},{"d":0,"r":[941,942],"t":0},{"d":0,"r":[942,947],"t":0},{"d":0,"r":[947,948],"t":0},{"d":0,"r":[948,955],"t":0},{"d":0,"r":[955,956],"t":0},{"d":[[127,0,7],[128,7,1]],"r":[0,8],"t":2},{"d":0,"r":[958,1168],"t":0},{"d":0,"r":[1157,1166],"t":0},{"d":0,"r":[1022,1031],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1046,1058],"t":0},{"d":0,"r":[1020,1060],"t":0},{"d":0,"r":[1063,1072],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1087,1097],"t":0},{"d":0,"r":[1061,1099],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1100,1131],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[1133,1168],"t":0},{"d":0,"r":[1135,1142],"t":0},{"d":0,"r":[1142,1143],"t":0},{"d":0,"r":[1143,1147],"t":0},{"d":0,"r":[1147,1148],"t":0},{"d":0,"r":[1148,1155],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[1022,1031],"t":0},{"d":0,"r":[1022,1029],"t":0},{"d":0,"r":[1029,1030],"t":0},{"d":0,"r":[1030,1031],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1034,1043],"t":0},{"d":0,"r":[1046,1058],"t":0},{"d":0,"r":[1046,1051],"t":0},{"d":0,"r":[1051,1052],"t":0},{"d":0,"r":[1052,1058],"t":0},{"d":0,"r":[1063,1072],"t":0},{"d":0,"r":[1063,1070],"t":0},{"d":0,"r":[1070,1071],"t":0},{"d":0,"r":[1071,1072],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1075,1084],"t":0},{"d":0,"r":[1087,1097],"t":0},{"d":0,"r":[1087,1092],"t":0},{"d":0,"r":[1092,1093],"t":0},{"d":0,"r":[1093,1097],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1102,1111],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1114,1123],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1126,1129],"t":0},{"d":0,"r":[1133,1168],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[960,966],"t":0},{"d":0,"r":[969,974],"t":0},{"d":0,"r":[977,986],"t":0},{"d":0,"r":[958,988],"t":0},{"d":0,"r":[958,1132],"t":0},{"d":0,"r":[1169,1191],"t":0},{"d":0,"r":[1172,1177],"t":0},{"d":0,"r":[1177,1178],"t":0},{"d":0,"r":[1178,1190],"t":0},{"d":0,"r":[1192,1294],"t":0},{"d":0,"r":[1192,1195],"t":0},{"d":0,"r":[1195,1196],"t":0},{"d":0,"r":[1196,1201],"t":0},{"d":0,"r":[1201,1202],"t":0},{"d":0,"r":[1202,1206],"t":0},{"d":0,"r":[1206,1207],"t":0},{"d":0,"r":[1207,1208],"t":0},{"d":0,"r":[1208,1209],"t":0},{"d":0,"r":[1209,1220],"t":0},{"d":0,"r":[1220,1221],"t":0},{"d":0,"r":[1221,1233],"t":0},{"d":0,"r":[1233,1234],"t":0},{"d":0,"r":[1234,1238],"t":0},{"d":0,"r":[1238,1239],"t":0},{"d":0,"r":[1239,1248],"t":0},{"d":0,"r":[1248,1249],"t":0},{"d":0,"r":[1249,1260],"t":0},{"d":0,"r":[1260,1261],"t":0},{"d":0,"r":[1261,1264],"t":0},{"d":0,"r":[1264,1265],"t":0},{"d":0,"r":[1265,1269],"t":0},{"d":0,"r":[1269,1270],"t":0},{"d":0,"r":[1270,1278],"t":0},{"d":0,"r":[1278,1279],"t":0},{"d":0,"r":[1279,1281],"t":0},{"d":0,"r":[1281,1282],"t":0},{"d":0,"r":[1282,1289],"t":0},{"d":0,"r":[1289,1290],"t":0},{"d":0,"r":[1290,1292],"t":0},{"d":0,"r":[1292,1293],"t":0},{"d":[[223,0,2],[224,2,1]],"r":[0,3],"t":2},{"d":0,"r":[1295,1360],"t":0},{"d":0,"r":[1295,1359],"t":0},{"d":0,"r":[1297,1357],"t":0},{"d":0,"r":[1361,1432],"t":0},{"d":0,"r":[1361,1366],"t":0},{"d":0,"r":[1366,1367],"t":0},{"d":0,"r":[1367,1375],"t":0},{"d":0,"r":[1368,1374],"t":0},{"d":0,"r":[1375,1376],"t":0},{"d":0,"r":[1376,1386],"t":0},{"d":0,"r":[1386,1387],"t":0},{"d":0,"r":[1387,1392],"t":0},{"d":0,"r":[1392,1393],"t":0},{"d":0,"r":[1393,1403],"t":0},{"d":0,"r":[1403,1404],"t":0},{"d":0,"r":[1404,1407],"t":0},{"d":0,"r":[1407,1408],"t":0},{"d":0,"r":[1408,1411],"t":0},{"d":0,"r":[1409,1410],"t":0},{"d":0,"r":[1411,1412],"t":0},{"d":0,"r":[1412,1414],"t":0},{"d":0,"r":[1414,1415],"t":0},{"d":0,"r":[1415,1418],"t":0},{"d":0,"r":[1418,1419],"t":0},{"d":0,"r":[1419,1425],"t":0},{"d":0,"r":[1425,1426],"t":0},{"d":0,"r":[1426,1431],"t":0},{"d":0,"r":[1433,1443],"t":0},{"d":0,"r":[1435,1442],"t":0},{"d":0,"r":[1444,1467],"t":0},{"d":0,"r":[1447,1458],"t":0},{"d":0,"r":[1458,1459],"t":0},{"d":0,"r":[1459,1466],"t":0},{"d":0,"r":[1468,1513],"t":0},{"d":0,"r":[1468,1471],"t":0},{"d":0,"r":[1471,1472],"t":0},{"d":0,"r":[1472,1477],"t":0},{"d":0,"r":[1477,1478],"t":0},{"d":0,"r":[1478,1486],"t":0},{"d":0,"r":[1486,1487],"t":0},{"d":0,"r":[1487,1498],"t":0},{"d":0,"r":[1498,1499],"t":0},{"d":0,"r":[1499,1511],"t":0},{"d":0,"r":[1511,1512],"t":0},{"d":[[268,0,12],[269,12,1]],"r":[0,13],"t":2},{"d":0,"r":[1514,1605],"t":0},{"d":0,"r":[1516,1544],"t":0},{"d":0,"r":[1516,1520],"t":0},{"d":0,"r":[1520,1521],"t":0},{"d":[[273,0,4],[274,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1521,1522],"t":0},{"d":0,"r":[1522,1526],"t":0},{"d":0,"r":[1526,1527],"t":0},{"d":0,"r":[1527,1528],"t":0},{"d":0,"r":[1528,1536],"t":0},{"d":[[279,0,1],[280,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1536,1537],"t":0},{"d":[[281,0,9],[282,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1537,1538],"t":0},{"d":0,"r":[1538,1542],"t":0},{"d":0,"r":[1542,1543],"t":0},{"d":[[285,0,4],[286,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1546,1575],"t":0},{"d":0,"r":[1546,1551],"t":0},{"d":0,"r":[1547,1550],"t":0},{"d":0,"r":[1551,1552],"t":0},{"d":0,"r":[1552,1553],"t":0},{"d":0,"r":[1553,1557],"t":0},{"d":0,"r":[1557,1558],"t":0},{"d":0,"r":[1558,1559],"t":0},{"d":0,"r":[1559,1567],"t":0},{"d":[[295,0,1],[296,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1567,1568],"t":0},{"d":[[297,0,9],[298,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1568,1569],"t":0},{"d":0,"r":[1569,1573],"t":0},{"d":0,"r":[1573,1574],"t":0},{"d":[[301,0,4],[302,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1577,1604],"t":0},{"d":0,"r":[1577,1580],"t":0},{"d":0,"r":[1580,1581],"t":0},{"d":[[305,0,3],[306,3,1]],"r":[0,4],"t":2},{"d":0,"r":[1581,1582],"t":0},{"d":0,"r":[1582,1586],"t":0},{"d":0,"r":[1586,1587],"t":0},{"d":0,"r":[1587,1588],"t":0},{"d":0,"r":[1588,1596],"t":0},{"d":[[311,0,1],[312,1,8]],"r":[0,9],"t":2},{"d":0,"r":[1596,1597],"t":0},{"d":[[313,0,9],[314,9,1]],"r":[0,10],"t":2},{"d":0,"r":[1597,1598],"t":0},{"d":0,"r":[1598,1602],"t":0},{"d":0,"r":[1602,1603],"t":0},{"d":[[317,0,4],[318,4,1]],"r":[0,5],"t":2},{"d":0,"r":[1605,1654],"t":0},{"d":0,"r":[1605,1608],"t":0},{"d":0,"r":[1608,1609],"t":0},{"d":0,"r":[1609,1618],"t":0},{"d":0,"r":[1610,1618],"t":0},{"d":0,"r":[1609,1618],"t":0},{"d":0,"r":[1618,1619],"t":0},{"d":0,"r":[1619,1622],"t":0},{"d":0,"r":[1622,1623],"t":0},{"d":0,"r":[1623,1627],"t":0},{"d":0,"r":[1627,1628],"t":0},{"d":0,"r":[1628,1635],"t":0},{"d":0,"r":[1635,1636],"t":0},{"d":0,"r":[1636,1640],"t":0},{"d":0,"r":[1640,1641],"t":0},{"d":0,"r":[1641,1643],"t":0},{"d":0,"r":[1643,1644],"t":0},{"d":0,"r":[1644,1653],"t":0},{"d":0,"r":[1655,1668],"t":0},{"d":0,"r":[1657,1667],"t":0},{"d":0,"r":[1669,1874],"t":0},{"d":0,"r":[1669,1672],"t":0},{"d":0,"r":[1672,1673],"t":0},{"d":0,"r":[1673,1680],"t":0},{"d":0,"r":[1680,1681],"t":0},{"d":0,"r":[1681,1692],"t":0},{"d":0,"r":[1692,1693],"t":0},{"d":0,"r":[1693,1697],"t":0},{"d":0,"r":[1697,1698],"t":0},{"d":0,"r":[1698,1705],"t":0},{"d":0,"r":[1705,1706],"t":0},{"d":0,"r":[1706,1714],"t":0},{"d":0,"r":[1714,1715],"t":0},{"d":0,"r":[1715,1722],"t":0},{"d":0,"r":[1722,1723],"t":0},{"d":0,"r":[1723,1726],"t":0},{"d":0,"r":[1726,1727],"t":0},{"d":0,"r":[1727,1738],"t":0},{"d":0,"r":[1738,1739],"t":0},{"d":0,"r":[1739,1746],"t":0},{"d":0,"r":[1746,1747],"t":0},{"d":0,"r":[1747,1754],"t":0},{"d":0,"r":[1754,1755],"t":0},{"d":0,"r":[1755,1763],"t":0},{"d":0,"r":[1763,1764],"t":0},{"d":0,"r":[1764,1766],"t":0},{"d":0,"r":[1766,1767],"t":0},{"d":0,"r":[1767,1774],"t":0},{"d":0,"r":[1774,1775],"t":0},{"d":0,"r":[1775,1780],"t":0},{"d":0,"r":[1780,1781],"t":0},{"d":0,"r":[1781,1789],"t":0},{"d":0,"r":[1789,1790],"t":0},{"d":0,"r":[1790,1797],"t":0},{"d":0,"r":[1797,1798],"t":0},{"d":0,"r":[1798,1809],"t":0},{"d":0,"r":[1809,1810],"t":0},{"d":0,"r":[1810,1816],"t":0},{"d":0,"r":[1816,1872],"t":0},{"d":0,"r":[1816,1872],"t":0},{"d":0,"r":[1818,1825],"t":0},{"d":0,"r":[1825,1826],"t":0},{"d":0,"r":[1826,1830],"t":0},{"d":0,"r":[1830,1831],"t":0},{"d":0,"r":[1831,1833],"t":0},{"d":0,"r":[1833,1834],"t":0},{"d":0,"r":[1834,1840],"t":0},{"d":0,"r":[1840,1841],"t":0},{"d":0,"r":[1841,1843],"t":0},{"d":0,"r":[1843,1844],"t":0},{"d":0,"r":[1844,1851],"t":0},{"d":0,"r":[1851,1852],"t":0},{"d":0,"r":[1852,1860],"t":0},{"d":0,"r":[1860,1861],"t":0},{"d":0,"r":[1861,1871],"t":0},{"d":0,"r":[1872,1873],"t":0},{"d":0,"r":[1875,1890],"t":0},{"d":0,"r":[1878,1889],"t":0},{"d":0,"r":[1891,1974],"t":0},{"d":0,"r":[1891,1894],"t":0},{"d":0,"r":[1894,1895],"t":0},{"d":0,"r":[1895,1902],"t":0},{"d":0,"r":[1902,1903],"t":0},{"d":0,"r":[1903,1908],"t":0},{"d":0,"r":[1908,1909],"t":0},{"d":0,"r":[1909,1916],"t":0},{"d":0,"r":[1916,1917],"t":0},{"d":0,"r":[1917,1919],"t":0},{"d":0,"r":[1919,1920],"t":0},{"d":0,"r":[1920,1931],"t":0},{"d":0,"r":[1931,1932],"t":0},{"d":0,"r":[1932,1943],"t":0},{"d":0,"r":[1943,1944],"t":0},{"d":0,"r":[1944,1950],"t":0},{"d":0,"r":[1950,1951],"t":0},{"d":0,"r":[1951,1955],"t":0},{"d":0,"r":[1955,1956],"t":0},{"d":0,"r":[1956,1962],"t":0},{"d":0,"r":[1962,1963],"t":0},{"d":0,"r":[1963,1969],"t":0},{"d":0,"r":[1969,1970],"t":0},{"d":0,"r":[1970,1972],"t":0},{"d":0,"r":[1972,1973],"t":0},{"d":[[421,0,2],[422,2,1]],"r":[0,3],"t":2},{"d":0,"r":[1975,2048],"t":0},{"d":0,"r":[1978,2001],"t":0},{"d":0,"r":[1978,1991],"t":0},{"d":0,"r":[1991,1992],"t":0},{"d":0,"r":[1992,2000],"t":0},{"d":0,"r":[2004,2027],"t":0},{"d":0,"r":[2004,2011],"t":0},{"d":0,"r":[2011,2012],"t":0},{"d":0,"r":[2012,2019],"t":0},{"d":0,"r":[2019,2020],"t":0},{"d":0,"r":[2020,2026],"t":0},{"d":0,"r":[2030,2047],"t":0},{"d":0,"r":[2030,2039],"t":0},{"d":0,"r":[2039,2040],"t":0},{"d":0,"r":[2040,2046],"t":0},{"d":0,"r":[2048,2061],"t":0},{"d":0,"r":[2050,2060],"t":0},{"d":0,"r":[2062,2241],"t":0},{"d":0,"r":[2062,2069],"t":0},{"d":0,"r":[2069,2070],"t":0},{"d":0,"r":[2070,2078],"t":0},{"d":0,"r":[2078,2079],"t":0},{"d":0,"r":[2079,2085],"t":0},{"d":0,"r":[2085,2086],"t":0},{"d":0,"r":[2086,2095],"t":0},{"d":0,"r":[2095,2096],"t":0},{"d":0,"r":[2096,2106],"t":0},{"d":0,"r":[2106,2107],"t":0},{"d":0,"r":[2107,2109],"t":0},{"d":0,"r":[2109,2110],"t":0},{"d":0,"r":[2110,2117],"t":0},{"d":0,"r":[2117,2118],"t":0},{"d":0,"r":[2118,2129],"t":0},{"d":0,"r":[2129,2130],"t":0},{"d":0,"r":[2130,2133],"t":0},{"d":0,"r":[2133,2134],"t":0},{"d":0,"r":[2134,2144],"t":0},{"d":0,"r":[2144,2145],"t":0},{"d":0,"r":[2145,2154],"t":0},{"d":0,"r":[2154,2155],"t":0},{"d":0,"r":[2155,2159],"t":0},{"d":0,"r":[2159,2160],"t":0},{"d":0,"r":[2160,2167],"t":0},{"d":0,"r":[2167,2168],"t":0},{"d":0,"r":[2168,2169],"t":0},{"d":0,"r":[2169,2170],"t":0},{"d":0,"r":[2170,2180],"t":0},{"d":0,"r":[2180,2181],"t":0},{"d":0,"r":[2181,2184],"t":0},{"d":0,"r":[2184,2185],"t":0},{"d":0,"r":[2185,2191],"t":0},{"d":0,"r":[2191,2192],"t":0},{"d":0,"r":[2192,2200],"t":0},{"d":0,"r":[2200,2201],"t":0},{"d":0,"r":[2201,2203],"t":0},{"d":0,"r":[2203,2204],"t":0},{"d":0,"r":[2204,2208],"t":0},{"d":0,"r":[2208,2209],"t":0},{"d":0,"r":[2209,2217],"t":0},{"d":0,"r":[2217,2218],"t":0},{"d":0,"r":[2218,2224],"t":0},{"d":0,"r":[2224,2225],"t":0},{"d":0,"r":[2225,2239],"t":0},{"d":0,"r":[2227,2238],"t":0},{"d":0,"r":[2239,2240],"t":0},{"d":0,"r":[2242,2255],"t":0},{"d":0,"r":[2244,2254],"t":0},{"d":0,"r":[2256,2272],"t":0},{"d":0,"r":[2261,2266],"t":0},{"d":0,"r":[4,462],"t":0},{"d":493,"r":[164,363],"t":1},{"d":0,"r":[4,462],"t":0},{"d":495,"r":[165,362],"t":1},{"d":496,"r":[0,4],"t":1},{"d":495,"r":[165,362],"t":1},{"d":498,"r":[4,5],"t":1},{"d":495,"r":[165,362],"t":1},{"d":500,"r":[5,10],"t":1},{"d":495,"r":[165,362],"t":1},{"d":502,"r":[10,11],"t":1},{"d":495,"r":[165,362],"t":1},{"d":504,"r":[11,19],"t":1},{"d":495,"r":[165,362],"t":1},{"d":506,"r":[19,20],"t":1},{"d":495,"r":[165,362],"t":1},{"d":508,"r":[20,21],"t":1},{"d":495,"r":[165,362],"t":1},{"d":510,"r":[21,22],"t":1},{"d":495,"r":[165,362],"t":1},{"d":512,"r":[22,27],"t":1},{"d":495,"r":[165,362],"t":1},{"d":514,"r":[27,28],"t":1},{"d":495,"r":[165,362],"t":1},{"d":516,"r":[28,36],"t":1},{"d":495,"r":[165,362],"t":1},{"d":518,"r":[36,37],"t":1},{"d":495,"r":[165,362],"t":1},{"d":520,"r":[37,39],"t":1},{"d":495,"r":[165,362],"t":1},{"d":522,"r":[39,40],"t":1},{"d":495,"r":[165,362],"t":1},{"d":524,"r":[40,47],"t":1},{"d":495,"r":[165,362],"t":1},{"d":526,"r":[47,48],"t":1},{"d":495,"r":[165,362],"t":1},{"d":528,"r":[48,58],"t":1},{"d":495,"r":[165,362],"t":1},{"d":530,"r":[58,59],"t":1},{"d":495,"r":[165,362],"t":1},{"d":532,"r":[59,64],"t":1},{"d":495,"r":[165,362],"t":1},{"d":534,"r":[64,65],"t":1},{"d":495,"r":[165,362],"t":1},{"d":536,"r":[65,72],"t":1},{"d":495,"r":[165,362],"t":1},{"d":538,"r":[72,73],"t":1},{"d":495,"r":[165,362],"t":1},{"d":540,"r":[73,81],"t":1},{"d":495,"r":[165,362],"t":1},{"d":542,"r":[81,82],"t":1},{"d":495,"r":[165,362],"t":1},{"d":544,"r":[82,93],"t":1},{"d":495,"r":[165,362],"t":1},{"d":546,"r":[93,94],"t":1},{"d":495,"r":[165,362],"t":1},{"d":548,"r":[94,96],"t":1},{"d":495,"r":[165,362],"t":1},{"d":550,"r":[96,97],"t":1},{"d":495,"r":[165,362],"t":1},{"d":552,"r":[97,108],"t":1},{"d":495,"r":[165,362],"t":1},{"d":554,"r":[108,109],"t":1},{"d":495,"r":[165,362],"t":1},{"d":556,"r":[109,113],"t":1},{"d":495,"r":[165,362],"t":1},{"d":558,"r":[113,114],"t":1},{"d":495,"r":[165,362],"t":1},{"d":560,"r":[114,118],"t":1},{"d":495,"r":[165,362],"t":1},{"d":562,"r":[118,119],"t":1},{"d":495,"r":[165,362],"t":1},{"d":564,"r":[119,127],"t":1},{"d":495,"r":[165,362],"t":1},{"d":566,"r":[127,128],"t":1},{"d":495,"r":[165,362],"t":1},{"d":568,"r":[128,134],"t":1},{"d":495,"r":[165,362],"t":1},{"d":570,"r":[134,135],"t":1},{"d":495,"r":[165,362],"t":1},{"d":572,"r":[135,138],"t":1},{"d":495,"r":[165,362],"t":1},{"d":574,"r":[138,139],"t":1},{"d":495,"r":[165,362],"t":1},{"d":576,"r":[139,146],"t":1},{"d":495,"r":[165,362],"t":1},{"d":578,"r":[146,147],"t":1},{"d":495,"r":[165,362],"t":1},{"d":580,"r":[147,158],"t":1},{"d":495,"r":[165,362],"t":1},{"d":582,"r":[158,159],"t":1},{"d":495,"r":[165,362],"t":1},{"d":584,"r":[159,171],"t":1},{"d":495,"r":[165,362],"t":1},{"d":586,"r":[171,172],"t":1},{"d":495,"r":[165,362],"t":1},{"d":588,"r":[172,176],"t":1},{"d":495,"r":[165,362],"t":1},{"d":590,"r":[176,177],"t":1},{"d":495,"r":[165,362],"t":1},{"d":592,"r":[177,188],"t":1},{"d":495,"r":[165,362],"t":1},{"d":594,"r":[188,189],"t":1},{"d":495,"r":[165,362],"t":1},{"d":596,"r":[189,197],"t":1},{"d":0,"r":[4,462],"t":0},{"d":598,"r":[98,153],"t":1},{"d":0,"r":[4,462],"t":0},{"d":600,"r":[99,152],"t":1},{"d":601,"r":[0,10],"t":1},{"d":600,"r":[99,152],"t":1},{"d":603,"r":[10,11],"t":1},{"d":600,"r":[99,152],"t":1},{"d":605,"r":[11,13],"t":1},{"d":600,"r":[99,152],"t":1},{"d":607,"r":[13,14],"t":1},{"d":600,"r":[99,152],"t":1},{"d":609,"r":[14,22],"t":1},{"d":600,"r":[99,152],"t":1},{"d":611,"r":[22,23],"t":1},{"d":600,"r":[99,152],"t":1},{"d":613,"r":[23,31],"t":1},{"d":600,"r":[99,152],"t":1},{"d":615,"r":[31,32],"t":1},{"d":600,"r":[99,152],"t":1},{"d":617,"r":[32,42],"t":1},{"d":600,"r":[99,152],"t":1},{"d":619,"r":[42,43],"t":1},{"d":600,"r":[99,152],"t":1},{"d":621,"r":[43,45],"t":1},{"d":600,"r":[99,152],"t":1},{"d":623,"r":[45,46],"t":1},{"d":600,"r":[99,152],"t":1},{"d":625,"r":[46,53],"t":1},{"d":0,"r":[4,462],"t":0},{"d":627,"r":[67,84],"t":1},{"d":0,"r":[72,81],"t":0},{"d":0,"r":[4,462],"t":0},{"d":630,"r":[68,83],"t":1},{"d":631,"r":[9,10],"t":1},{"d":630,"r":[68,83],"t":1},{"d":633,"r":[10,15],"t":1},{"d":0,"r":[4,462],"t":0},{"d":635,"r":[443,457],"t":1},{"d":0,"r":[4,462],"t":0},{"d":637,"r":[443,457],"t":1},{"d":638,"r":[0,14],"t":1},{"d":0,"r":[4,462],"t":0},{"d":640,"r":[374,428],"t":1},{"d":0,"r":[4,462],"t":0},{"d":642,"r":[375,391],"t":1},{"d":0,"r":[4,462],"t":0},{"d":644,"r":[375,391],"t":1},{"d":645,"r":[0,7],"t":1},{"d":644,"r":[375,391],"t":1},{"d":647,"r":[7,8],"t":1},{"d":644,"r":[375,391],"t":1},{"d":649,"r":[8,16],"t":1},{"d":0,"r":[4,462],"t":0},{"d":651,"r":[393,400],"t":1},{"d":0,"r":[4,462],"t":0},{"d":653,"r":[393,400],"t":1},{"d":654,"r":[0,7],"t":1},{"d":0,"r":[4,462],"t":0},{"d":656,"r":[402,412],"t":1},{"d":0,"r":[4,462],"t":0},{"d":658,"r":[402,412],"t":1},{"d":659,"r":[0,10],"t":1},{"d":0,"r":[4,462],"t":0},{"d":661,"r":[414,427],"t":1},{"d":0,"r":[4,462],"t":0},{"d":663,"r":[414,427],"t":1},{"d":664,"r":[0,4],"t":1},{"d":663,"r":[414,427],"t":1},{"d":666,"r":[4,5],"t":1},{"d":663,"r":[414,427],"t":1},{"d":668,"r":[5,13],"t":1},{"d":0,"r":[4,462],"t":0},{"d":670,"r":[7,58],"t":1},{"d":0,"r":[4,462],"t":0},{"d":672,"r":[8,57],"t":1},{"d":673,"r":[0,7],"t":1},{"d":672,"r":[8,57],"t":1},{"d":675,"r":[7,8],"t":1},{"d":672,"r":[8,57],"t":1},{"d":677,"r":[8,16],"t":1},{"d":672,"r":[8,57],"t":1},{"d":679,"r":[16,17],"t":1},{"d":672,"r":[8,57],"t":1},{"d":681,"r":[17,27],"t":1},{"d":672,"r":[8,57],"t":1},{"d":683,"r":[27,28],"t":1},{"d":672,"r":[8,57],"t":1},{"d":685,"r":[28,30],"t":1},{"d":672,"r":[8,57],"t":1},{"d":687,"r":[30,31],"t":1},{"d":672,"r":[8,57],"t":1},{"d":689,"r":[31,38],"t":1},{"d":672,"r":[8,57],"t":1},{"d":691,"r":[38,39],"t":1},{"d":672,"r":[8,57],"t":1},{"d":693,"r":[39,49],"t":1},{"d":0,"r":[4,462],"t":0},{"d":695,"r":[154,162],"t":1},{"d":0,"r":[4,462],"t":0},{"d":697,"r":[85,96],"t":1},{"d":0,"r":[4,462],"t":0},{"d":699,"r":[59,65],"t":1},{"d":0,"r":[4,462],"t":0},{"d":701,"r":[429,441],"t":1},{"d":0,"r":[4,462],"t":0},{"d":703,"r":[364,372],"t":1},{"d":0,"r":[4,462],"t":0},{"d":705,"r":[0,5],"t":1}]}} \ No newline at end of file diff --git a/ts-packages/pandoc-types/src/types.ts b/ts-packages/pandoc-types/src/types.ts index 86169b3b0..23b31edd8 100644 --- a/ts-packages/pandoc-types/src/types.ts +++ b/ts-packages/pandoc-types/src/types.ts @@ -15,6 +15,7 @@ * - Elements with attributes add `a: AttrSourceInfo` for attribute source tracking * (renamed from `attrS` in Plan 7f Phase 5) * - Elements with targets add `targetS: TargetSourceInfo` for target source tracking + * - Math adds an optional `textS: number` for the provenance of its text * * This design ensures that quarto-markdown-pandoc JSON output is valid Pandoc JSON * and can be processed by the standard Pandoc toolchain. @@ -462,6 +463,12 @@ export interface Annotated_Inline_Math { t: "Math"; c: [MathType, string]; s: number; + /** + * Source ID for the math *text* itself (provenance of `c[1]`, whose + * length equals the text length), distinct from the node span in `s`. + * Absent when the text did not come from the reader (bd-ieldbghj). + */ + textS?: number; } export interface Annotated_Inline_RawInline { diff --git a/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.test.ts b/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.test.ts index 65172d426..7510002dd 100644 --- a/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.test.ts +++ b/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.test.ts @@ -143,6 +143,27 @@ describe('sidecar keys beyond s/a (bare pool indices on specific nodes)', () => expect((result as any).c[0].c[2]).toEqual(['https://example.com', '']); }); + test('strips textS from Math (bd-ieldbghj)', () => { + // textS is a bare pool index: the provenance of the math text + // itself, distinct from the node span in `s`. + const block = { + t: 'Para', + s: 1, + c: [{ + t: 'Math', + s: 2, + textS: 3, + c: [{ t: 'InlineMath' }, 'x^2'], + }], + }; + const result = stripSourceInfoFields(block); + const json = JSON.stringify(result); + expect(json).not.toMatch(/"textS":/); + expect(json).not.toMatch(/"s":/); + // The math type and text must survive. + expect((result as any).c[0].c).toEqual([{ t: 'InlineMath' }, 'x^2']); + }); + test('strips targetS from Image', () => { const block = { t: 'Para', diff --git a/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.ts b/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.ts index 7d4a29fa8..1c1348a08 100644 --- a/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.ts +++ b/ts-packages/preview-renderer/src/q2-preview/stripSourceInfoFields.ts @@ -15,6 +15,7 @@ * - `a` — AttrSourceInfo (attr-bearing nodes; `id`/`classes`/`kvs` * values are pool indices) * - `targetS` — Link/Image `[urlRef, titleRef]` + * - `textS` — Math text provenance ref (a bare pool index; bd-ieldbghj) * - `captionS` — Table/Figure caption ref (a bare pool index) * - `citationIdS` — citation id ref on Cite citation objects (bare index) * @@ -25,7 +26,7 @@ * deferred until table editing is actually supported. */ -const STRIPPED_KEYS = new Set(['s', 'a', 'targetS', 'captionS', 'citationIdS']); +const STRIPPED_KEYS = new Set(['s', 'a', 'targetS', 'textS', 'captionS', 'citationIdS']); export function stripSourceInfoFields(block: T): T { return JSON.parse(JSON.stringify(block, (key, value) => diff --git a/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/canonical.ts b/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/canonical.ts index 36caa9a15..57a229ad4 100644 --- a/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/canonical.ts +++ b/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/canonical.ts @@ -6,7 +6,7 @@ import type { AstNode } from './pampa'; // Source-tracking / volatile fields that must not affect equivalence. -const STRIP_KEYS = new Set(['s', 'a', 'l', 'targetS']); +const STRIP_KEYS = new Set(['s', 'a', 'l', 'targetS', 'textS']); const VOLATILE_CITE_KEYS = new Set(['citationHash', 'citationNoteNum', 'citationIdS']); function stripObj(value: unknown): unknown { diff --git a/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/pampa.ts b/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/pampa.ts index 705e44e20..80692bd5a 100644 --- a/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/pampa.ts +++ b/ts-packages/preview-renderer/src/q2-preview/tiptap-roundtrip-spike/pampa.ts @@ -35,6 +35,7 @@ export interface AstNode { a?: unknown; l?: unknown; targetS?: unknown; + textS?: unknown; [k: string]: unknown; } diff --git a/ts-packages/preview-renderer/src/test-utils/pampaOracle.ts b/ts-packages/preview-renderer/src/test-utils/pampaOracle.ts index 080152459..affeebe20 100644 --- a/ts-packages/preview-renderer/src/test-utils/pampaOracle.ts +++ b/ts-packages/preview-renderer/src/test-utils/pampaOracle.ts @@ -75,7 +75,7 @@ export function parseUntransformed(qmd: string): RustQmdJson { // ---- semantic comparison --------------------------------------------------- -const STRIP_KEYS = new Set(['s', 'a', 'l', 'targetS']); +const STRIP_KEYS = new Set(['s', 'a', 'l', 'targetS', 'textS']); const VOLATILE_CITE_KEYS = new Set(['citationHash', 'citationNoteNum', 'citationIdS']); function stripObj(value: unknown): unknown {