#let mgrid(..args) = context { // Extract named parameters or use defaults let kwargs = args.named() let align = kwargs.at("align", default: center) let gutter = kwargs.at("gutter", default: 1em) // Get positional arguments (the body/eq content) let eq = if args.pos().len() > 0 { args.pos().first() } else { [] } if eq.func() != [].func() { // Body is just a single element, so leave it as is. return eq } // Split body at linebreaks and alignment points. let lines = eq.children.split(linebreak()).map(line => line.split($&$.body).map(array.join)) // Calculate width of each column. let widths = () for line in lines { for (i, part) in line.enumerate() { let width = measure(math.equation(block: true, numbering: none, part)).width if i >= widths.len() { widths.push(width) } else { widths.at(i) = calc.max(widths.at(i), width) } } } // Resolve alignment for each column. let aligns = range(widths.len()).map(i => { if type(align) == alignment { align } else if type(align) == array { align.at(calc.rem(i, align.len())) } else { panic("expected alignment or array as 'align'") } }) // Try to flatten sequence elements (to allow access to an underlying align // element for overriding the alignment of single parts). let flatten(seq) = { if type(seq) != content or seq.func() != [].func() { return seq } let children = seq.children.filter(c => c != [ ]) if children.len() == 1 { children.first() } else { seq } } // Display parts centered in each column and add gutter. let layout-line(line) = { if line.len() < widths.len() { line += (none,) * (widths.len() - line.len()) } line.zip(widths, aligns).map(((part, width, align)) => { let part = flatten(part) let part-width = measure(math.equation(block: true, numbering: none, part)).width let delta = width - part-width // Check if alignment is overridden. if type(part) == content and part.func() == std.align { align = part.alignment.x } let (start, end) = if align == center {( h(delta/2), h(delta/2) )} else if align == left {( none, h(delta) )} else if align == right {( h(delta), none )} start + part + end }).join(h(gutter)) } lines.map(layout-line).join(linebreak()) } #let vapprox = box(baseline: 50%, rotate(90deg, $ approx $)) #let veq = box(baseline: 50%, rotate(90deg, $ = $)) // Sizes used across the template. #let script-size = 7.97224pt #let footnote-size = 8.50012pt #let small-size = 9.24994pt #let normal-size = 10.00002pt #let large-size = 11.74988pt // This function gets your whole document as its `body` and formats // it as an article in the style of the American Mathematical Society. #let ams-article( // The article's title. title: [Paper title], // An array of authors. For each author you can specify a name, // department, organization, location, and email. Everything but // but the name is optional. authors: (), // Your article's abstract. Can be omitted if you don't have one. abstract: none, // The article's paper size. Also affects the margins. paper-size: "us-letter", // The result of a call to the `bibliography` function or `none`. bibliography: none, // The document's content. body, ) = { // Formats the author's names in a list with commas and a // final "and". let names = authors.map(author => author.name) let author-string = if authors.len() == 2 { names.join(" and ") } else { names.join(", ", last: ", and ") } // Set document metadata. set document(title: title, author: names) // Set the body font. AMS uses the LaTeX font. set text(size: normal-size, font: "New Computer Modern") // Configure the page. set page( paper: paper-size, // The margins depend on the paper size. margin: if paper-size != "a4" { ( top: (116pt / 279mm) * 100%, left: (126pt / 216mm) * 100%, right: (128pt / 216mm) * 100%, bottom: (94pt / 279mm) * 100%, ) } else { ( top: 117pt, left: 118pt, right: 119pt, bottom: 96pt, ) }, // The page header should show the page number and list of // authors, except on the first page. The page number is on // the left for even pages and on the right for odd pages. header-ascent: 14pt, header: context { let i = counter(page).get().first() if i == 1 { return } set text(size: script-size) grid( columns: (6em, 1fr, 6em), align: (start, center, end), if calc.even(i) [#i], upper( if calc.odd(i) { title } else { author-string } ), if calc.odd(i) { [#i] } ) }, // On the first page, the footer should contain the page number. footer-descent: 12pt, footer: context { let i = counter(page).get().first() if i == 1 { align(center, text(size: script-size, [#i])) } } ) // Configure headings. set heading(numbering: "1.") show heading: it => { // Create the heading numbering. let number = if it.numbering != none { counter(heading).display(it.numbering) h(7pt, weak: true) } // Level 1 headings are centered and smallcaps. // The other ones are run-in. set text(size: normal-size, weight: 400) set par(first-line-indent: 0em) if it.level == 1 { set align(center) set text(size: normal-size) smallcaps[ #v(15pt, weak: true) #number #it.body #v(normal-size, weak: true) ] counter(figure.where(kind: "theorem")).update(0) } else { v(11pt, weak: true) number let styled = if it.level == 2 { strong } else { emph } styled(it.body + [. ]) h(7pt, weak: true) } } // Configure lists and links. set list(indent: 24pt, body-indent: 5pt) set enum(indent: 24pt, body-indent: 5pt) show link: set text(font: "New Computer Modern Mono") // Configure equations. show math.equation: set block(below: 8pt, above: 9pt) show math.equation: set text(weight: 400) // Configure citation and bibliography styles. set std.bibliography(style: "springer-mathphys", title: [References]) set figure(gap: 17pt) show figure: set block(above: 12.5pt, below: 15pt) show figure: it => { // Customize the figure's caption. show figure.caption: caption => { smallcaps(caption.supplement) if caption.numbering != none { [ ] numbering(caption.numbering, ..caption.counter.at(it.location())) } [. ] caption.body } // We want a bit of space around tables and images. show selector.or(table, image): pad.with(x: 23pt) // Display the figure's body and caption. it } // Theorems. show figure.where(kind: "theorem"): set align(start) show figure.where(kind: "theorem"): it => block(spacing: 11.5pt, { strong({ it.supplement if it.numbering != none { [ ] it.counter.display(it.numbering) } [.] }) [ ] emph(it.body) }) // Display the title and authors. v(35pt, weak: true) align(center, upper({ text(size: large-size, weight: 700, title) v(25pt, weak: true) text(size: footnote-size, author-string) })) // Configure paragraph properties. set par(spacing: 0.58em, first-line-indent: 1.2em, justify: true, leading: 0.58em) // Display the abstract if abstract != none { v(20pt, weak: true) set text(script-size) show: pad.with(x: 35pt) smallcaps[Abstract. ] abstract } // Display the article's contents. v(29pt, weak: true) body // Display the bibliography, if any is given. if bibliography != none { show std.bibliography: set text(footnote-size) show std.bibliography: set block(above: 11pt) show std.bibliography: pad.with(x: 0.5pt) bibliography } // Display details about the authors at the end. v(12pt, weak: true) show: pad.with(x: 11.5pt) set par(first-line-indent: 0pt) set text(script-size) for author in authors { let keys = ("department", "organization", "location") let dept-str = keys .filter(key => key in author) .map(key => author.at(key)) .join(", ") smallcaps(dept-str) linebreak() if "email" in author [ _Email address:_ #link("mailto:" + author.email) \ ] if "url" in author [ _URL:_ #link(author.url) ] v(12pt, weak: true) } } // The ASM template also provides a theorem function. #let theorem(body, numbered: true) = figure( body, kind: "theorem", supplement: [Theorem], numbering: if numbered { n => counter(heading).display() + [#n] } ) // And a function for a proof. #let proof(body) = block(spacing: 11.5pt, { emph[Proof.] [ ] body h(1fr) // Add a word-joiner so that the proof square and the last word before the // 1fr spacing are kept together. sym.wj // Add a non-breaking space to ensure a minimum amount of space between the // text and the proof square. sym.space.nobreak $square.stroked$ })