HELLO
")) (deftest "o-deflink works as expected, bracket links" [o-deflink] (⇝ (⟰ "[[shout:hello]]") "HELLO
") (⇝ (⟰ "[[shout:hello][world!]]") "WORLD!
")) (deftest "o-deflink works as expected, angle links" [o-deflink] (⇝ (⟰ "HELLO WORLD!
")) #+end_src #+end_details **** COMMENT Example links... :TestingPurposes:Already_in_docs: :PROPERTIES: :CUSTOM_ID: COMMENT-Example-links :END: #+begin_src emacs-lisp (o-deflink define "Define the given word using WordNet, along with synonyms and coordinate terms." [:help-echo (shell-command-to-string (format "wn %s -over -synsn -coorn" o-label))] (--> o-label (shell-command-to-string (format "wn %s -over -coorn" it)) (s-replace-regexp "\\\"" "''" it) ;; The presence of ‘\"’ in tooltips breaks things, so omit them. (s-replace-regexp "\n" "item two
helloitem two
world
@@" "\n\n\n\n")
)
"Output the CONTENTS of the block as both parsed Org and unparsed.
Label the source text by SOURCE and the result text by RESULT
finally, the source-result fragments can be shown in a STYLE
that is either “parallel” (default) or “sequential”.
SEP is the separator; e.g., a rule ‘
%s
_Yields_
#+begin_org-demo :style seq
/italics/ and _underline_
$e^{i \times \pi} + 1 = 0$
#+end_org-demo
#+end_parallel
#+end_box
However, since our implementation scheme relies on a preprocessing step before
export, we cannot use ~org-demo~ to show the results of special blocks: They
disappear in the preprocessing step!
#+begin_parallel :bar t
E.g., this
#+begin_example org
,#+begin_org-demo
,#+begin_box
There is no special block ‘box’ to touch!
,#+end_box
,#+end_org-demo
#+end_example
#+html:
*Result* #+begin_tree + Function Application :: f(a) : B - a : A - f : A → B + Modus Ponens :: q - p - p ⇒ q #+end_tree #+end_parallel #+end_box #+begin_solution #+begin_src emacs-lisp (defun o--list-to-math (lst) "Get a result LST from ORG-LIST-TO-LISP and render it as a proof tree." (cond ((symbolp lst) "") ((symbolp (car lst)) (o--list-to-math (cadr lst))) (t (-let* (((conclusion₀ children) lst) ((name named?) (s-split " :: " conclusion₀)) (conclusion (or named? conclusion₀))) (if (not children) (if named? (format "\\frac{}{%s}[%s]" conclusion name) conclusion) (format "\\frac{\\displaystyle %s}{%s}%s" (s-join " \\qquad " (mapcar #'o--list-to-math children)) conclusion (if named? (format "[\\text{%s}]" name) ""))))))) (o-defblock tree (main-arg) nil "Write a proof tree using Org-lists. To get premises₀ … premisesₙ ────────────────────────────[ reason ] conclusion You type ,#+begin_tree + reason :: conclusion - premises₀ - premises₁ ⋮ - premisesₙ ,#+end_tree Where each premisesᵢ may, recursively, also have named reasons and (indented) child premises of its own. If there are multiple trees, they are shown one after the other. The text in this block should be considered LaTeX; as such, Org markup is not recognised. A proof tree, derivation, is then just a deeply nested itemisation. For instance, assuming P = Q(X), X = Y, Q(Y) = R, the following proves P = R. ,#+begin_tree + Trans :: P = R - P = Q(X) + ✓ - Trans :: Q(X) = R + Trans :: Q(X) = Q(Y) - Refl :: Q(X) = Q(X) + ✓ - Leibniz :: Q(X) = Q(Y) + X = Y - ✓ + Sym :: Q(Y) = R - R = Q(Y) - ✓ ,#+end_tree" (s-join "" (--map (format "\\[%s\\]" (o--list-to-math it)) (cdr (with-temp-buffer (insert raw-contents) (goto-char (point-min)) (org-list-to-lisp)))))) #+end_src #+RESULTS: | :export | (lambda (label description backend) (s-replace-all `((@@ . )) (o--tree backend (or description label) label :o-link? t))) | :help-echo | (lambda (window object position) (save-excursion (goto-char position) (-let* (((&plist :path :format :raw-link :contents-begin :contents-end) (cadr (org-element-context))) (description (when (equal format 'bracket) (copy-region-as-kill contents-begin contents-end) (substring-no-properties (car kill-ring))))) (format %s | #+end_solution For more on these ‘proof trees’, see [[https://cpb-us-w2.wpmucdn.com/u.osu.edu/dist/a/4597/files/2014/08/Natural_Logic-2epb48e.pdf][‘Natural Logic’ by Neil Tennant]]. # Out of print, but downloadable as a scanned PDF as the above link. \[\] (*/Warning!/* For MathJax to activate, you should have some math ~$...$~ somewhere /besides/ the ~tree~ blocks; just ~\[\]~ suffices. ) ** What's the rest of this article about? :PROPERTIES: :CUSTOM_ID: Whats_the_rest_of_this_article_about? :END: The rest of the article showcases the special blocks declared with ~defblock~ to allow the above presentation ---with folded regions, coloured boxes, tooltips, parallel columns of text, etc. Enjoy ;-) ** COMMENT The Older =o--𝒳= Utility :PROPERTIES: :CUSTOM_ID: Core-Utility :END: For posterity, below is the original route taken to solve the same problem. In particular, the route outlined below /may/ be faster. Why is ~defblock~ better? - The approach below requires an awkward way to handle arguments, key-values. - It requires the user to learn a new interface. - Even if it's slower, ~defblock~ uses a very familiar interface and requires less Lisp mastery on the user's part. -------------------------------------------------------------------------------- :Hide: #+BEGIN_SRC emacs-lisp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Core utility ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+END_SRC :End: The simplest route is to ‘advise’ ---i.e., function patch or overload--- the Org export utility for special blocks to consider calling a method =o--𝒳= whenever it encounters a special block named =𝒳=. #+BEGIN_SRC emacs-lisp :noweb-ref enable-mode :tangle no (advice-add #'org-html-special-block :before-until (apply-partially #'o--advice 'html)) (advice-add #'org-latex-special-block :before-until (apply-partially #'o--advice 'latex)) #+END_SRC #+RESULTS: Here is the actual advice: #+BEGIN_SRC emacs-lisp (defun o--advice (backend blk contents _) "Invoke the appropriate custom block handler, if any. A given custom block BLK has a TYPE extracted from it, then we send the block CONTENTS along with the current export BACKEND to the formatting function O--TYPE if it is defined, otherwise, we leave the CONTENTS of the block as is. We also support the seemingly useless blocks that have no contents at all, not even an empty new line." (let* ((type (nth 1 (nth 1 blk))) (handler (intern (format "o--%s" type)))) (ignore-errors (apply handler backend (or contents "") nil)))) #+END_SRC #+RESULTS: : #+latex: \noindent *To support a new block named 𝒳:* 1. Define a function =o--𝒳=. 2. It must take two arguments: - ~backend~ ⇒ A symbol such as ='html= or ='latex=, - ~content~ ⇒ The string contents of the special block. 3. The function must return a string, possibly depending on the backend being exported to. The resulting string is inserted literally in the exported file. 4. Test out your function as in =(o--𝒳 'html "some input")= ---this is a quick way to find errors. 5. Enjoy ^_^ #+begin_center If no such function is defined, we export =𝒳= blocks using the default mechanism, as discussed earlier, as a LaTeX environment or an HTML =div=. #+end_center #+latex: \noindent An example is provided at the end of this section. #+latex: \noindent Of-course, when the user disables our mode, then we remove such advice. #+BEGIN_SRC emacs-lisp :noweb-ref disable-mode :tangle no (advice-remove #'org-html-special-block (apply-partially #'o--advice 'html)) (advice-remove #'org-latex-special-block (apply-partially #'o--advice 'latex)) #+END_SRC #+RESULTS: *** =:argument:= Extraction :PROPERTIES: :CUSTOM_ID: argument-Extraction :END: As far as I can tell, there is no way to provide arguments to special blocks. As such, the following utility looks for lines of the form =:argument: value= within the contents of a block and returns an updated contents string that no longer has such lines followed by an association list of such argument-value pairs. #+BEGIN_SRC emacs-lisp (defun o--extract-arguments (contents &rest args) "Get list of CONTENTS string with ARGS lines stripped out and values of ARGS. Example usage: (-let [(contents′ . (&alist 'k₀ … 'kₙ)) (…extract-arguments contents 'k₀ … 'kₙ)] body) Within ‘body’, each ‘kᵢ’ refers to the ‘value’ of argument ‘:kᵢ:’ in the CONTENTS text and ‘contents′’ is CONTENTS with all ‘:kᵢ:’ lines stripped out. + If ‘:k:’ is not an argument in CONTENTS, then it is assigned value NIL. + If ‘:k:’ is an argument in CONTENTS but is not given a value in CONTENTS, then it has value the empty string." (let ((ctnts contents) (values (cl-loop for a in args for regex = (format ":%s:\\(.*\\)" a) for v = (cadr (s-match regex contents)) collect (cons a v)))) (cl-loop for a in args for regex = (format ":%s:\\(.*\\)" a) do (setq ctnts (s-replace-regexp regex "" ctnts))) (cons ctnts values))) #+END_SRC #+RESULTS: : o--extract-arguments For example, we use this feature to indicate when a column break should happen in a =parallel= block and which person is making editorial remarks in an =remark= block. Why the =:𝒳:= notation? At the start of a line, a string of this form is coloured ---I don't recall why that is--- and that's a good enough reason to make use of such an existing support. #+begin_remark Aside In org-mode, ‘drawers’ are pieces of text that begin with =:my_drawer_name:= on a line by itself and end with =:end:= on a line by itself, and these delimiters allow us to fold away such regions and possibly exclude them from export. That is, drawers act as a light-weight form of blocks. Anyhow, Org colours drawer delimiters, #+end_remark *** An Example Special Block ---=foo= :PROPERTIES: :CUSTOM_ID: COMMENT-An-Example-Special-Block-foo :END: Herein we show an example function =o--𝒳= that makes use of arguments. In a so-called =foo= block, all occurrences of the word =foo= are replaced by =bar= unless the argument =:replacement:= is given a value. [[file:images/foo_block.png]] #+name: startup-code #+begin_src emacs-lisp :tangle no (defun o--foo (backend contents) "The FOO block type replaces all occurances of ‘foo’ with ‘bar’, unless a ‘:replacement:’ is provided." (-let [(contents′ . (&alist 'replacement)) (o--extract-arguments contents 'replacement)] (s-replace "foo" (or replacement "bar") contents′))) #+end_src :Outdated_hide: Here's an example usage: #+begin_parallel #+begin_example org #+begin_foo :replacement: woah I am foo; Indeed FoO is what I fOo! #+end_foo #+end_example :columnbreak: #+begin_foo :replacement: woah I am foo; Indeed FoO is what I fOo! #+end_foo #+end_parallel # See the implementation matter of ~edcomm~ or ~parallel~ for a more involved definition # that behaves differently depending on the export backend. :End: * COMMENT refactor defblock signature :way_in_future:someday: :PROPERTIES: :CUSTOM_ID: COMMENT-refactor-defblock-signature :END: (defblock name args &rest more) + args ⇒ (main-arg default-value keys-and-values) + more ⇒ (optional-link-face-setup optional-newline-decl- optional-docstring actual-body) * Editor Comments :PROPERTIES: :CUSTOM_ID: editor-comments :END: “Editor Comments” are intended to be top-level first-class comments in an article that are inline with the surrounding text and are delimited in such a way that they are visible but drawing attention. I first learned about this idea from Wolfram Kahl ---who introduced me to Emacs many years ago. We # implement editor comments as special blocks named [[doc:o--remark][remark]]. #+begin_box Example #+begin_parallel _This_ #+begin_src org :tangle no :tangle no In LaTeX, a =remark= appears inline with the text surrounding it. ,#+begin_remark Bobert org-mode is dope, yo! ,#+replacewith: Org-mode is essentially a path toward enlightenment. ,#+end_remark Unfortunately, in the HTML rendition, the =remark= is its own paragraph and thus separated by new lines from its surrounding text. #+end_src #+html:
_Yields_ In LaTeX, an =remark= appears inline with the text surrounding it. #+begin_remark Bobert org-mode is dope, yo! #+replacewith: Org-mode is essentially a path toward enlightenment. #+end_remark Unfortunately, in the HTML rendition, the =remark= is its own paragraph and thus separated by new lines from its surrounding text. #+end_parallel #+end_box :Pics_old: #+caption: In order: Chrome, Emacs Web Wowser, Org source, PDF [[file:images/edcomm.png]] :End: # | /Any new ---possibly empty--- inner lines in the =remark= are desirably preserved/ | -------------------------------------------------------------------------------- #+begin_details "Implementing ‘remark’, from §ection 1, with more bells and whistles" :title-color pink #+BEGIN_SRC emacs-lisp (defvar o-hide-editor-comments nil "Should editor comments be shown in the output or not.") (o-defblock remark (editor "Editor Remark" :face '(:foreground "red" :weight bold)) (color "black" signoff "" strong nil) ; :inline-please__see_margin_block_for_a_similar_incantation ; ⇒ crashes! "Format CONTENTS as an first-class editor comment according to BACKEND. The CONTENTS string has an optional switch: If it contains a line with having only ‘#+replacewith:’, then the text preceding this clause should be replaced by the text after it; i.e., this is what the EDITOR (the person editing) intends and so we fromat the replacement instruction (to the authour) as such. In Emacs, as links, editor remarks are shown with a bold red; but the exported COLOR of a remark is black by default and it is not STRONG ---i.e., bold---. There is an optional SIGNOFF message that is appended to the remark. " (-let* (;; Are we in the html backend? (tex? (equal backend 'latex)) ;; fancy display style (boxed (lambda (x) (if tex? (concat "\\fbox{\\bf " x "}") (concat "" "" x "")))) ;; Is this a replacement clause? ((this that) (s-split "\\#\\+replacewith:" contents)) (replacement-clause? that) ;; There is a ‘that’ (replace-keyword (if tex? "\\underline{Replace:}" " Replace:")) (with-keyword (if tex? "\\underline{With:}" "With:" )) (editor (format "[%s:%s" editor (if replacement-clause? replace-keyword ""))) (contents′ (if replacement-clause? (format "%s %s %s" this (org-export (funcall boxed with-keyword)) that) contents)) ;; “[Editor Comment:” (edcomm-begin (funcall boxed editor)) ;; “]” (edcomm-end (funcall boxed "]"))) (setq org-export-allow-bind-keywords t) ;; So users can use “#+bind” immediately (if o-hide-editor-comments "" (format (pcase backend ('latex (format "{\\color{%%s}%s %%s %%s %%s %%s}" (if strong "\\bfseries" ""))) (_ (format "<%s style=\"color: %%s;\">%%s %%s %%s %%s%s>" (if strong "strong" "p") (if strong "strong" "p")))) color edcomm-begin contents′ signoff edcomm-end)))) #+END_SRC #+RESULTS: | :face | (:foreground red :weight bold) | :export | (lambda (label description backend) (s-replace-all `((@@ . )) (o--remark backend (or description label) label :o-link? t))) | :help-echo | (lambda (window object position) (save-excursion (goto-char position) (-let* (((&plist :path :format :raw-link :contents-begin :contents-end) (cadr (org-element-context))) (description (when (equal format 'bracket) (copy-region-as-kill contents-begin contents-end) (substring-no-properties (car kill-ring))))) (format %s | :Older_version: #+BEGIN_SRC emacs-lisp :tangle no (defvar o-hide-editor-comments nil "Should editor comments be shown in the output or not.") (defun o--edcomm (backend contents) "Format CONTENTS as an first-class editor comment according to BACKEND. The CONTENTS string has two optional argument switches: 1. :ed: ⇒ To declare an editor of the comment. 2. :replacewith: ⇒ [Nullary] The text preceding this clause should be replaced by the text after it." (-let* ( ;; Get arguments ((contents₁ . (&alist 'ed)) (o--extract-arguments contents 'ed)) ;; Strip out any
tags (_ (setq contents₁ (s-replace-regexp "
" "" contents₁))) (_ (setq contents₁ (s-replace-regexp "
" "" contents₁))) ;; Are we in the html backend? (html? (equal backend 'html)) ;; fancy display style (boxed (lambda (x) (if html? (concat "" "" x "") (concat "\\fbox{\\bf " x "}")))) ;; Is this a replacement clause? ((this that) (s-split ":replacewith:" contents₁)) (replacement-clause? that) ;; There is a ‘that’ (replace-keyword (if html? " Replace:" "\\underline{Replace:}")) (with-keyword (if html? "With:" "\\underline{With:}")) (editor (format "[%s:%s" (if (s-blank? ed) "Editor Comment" ed) (if replacement-clause? replace-keyword ""))) (contents₂ (if replacement-clause? (format "%s %s %s" this (funcall boxed with-keyword) that) contents₁)) ;; “[Editor Comment:” (edcomm-begin (funcall boxed editor)) ;; “]” (edcomm-end (funcall boxed "]"))) (setq org-export-allow-bind-keywords t) ;; So users can use “#+bind” immediately (if o-hide-editor-comments "" (format (pcase backend ('latex "%s %s %s") (_ "%s %s %s
")) edcomm-begin contents₂ edcomm-end)))) #+END_SRC :End: In the HTML export, the =edcomm= special block is /not/ in-line with the text surrounding it ---ideally, it would be inline so that existing paragraphs are not split into multiple paragraphs but instead have an editor's comment indicating suggested alterations. Let's have some sanity tests... #+begin_src emacs-lisp :tangle tests.el :comments link (deftest "The user's remark is enclosed in the default delimiters" [remark] (⇝ (⟰ "#+begin_remark Here is some meta-commentary... ,#+end_remark") (* anything) "[Editor Remark:" (* anything) "Here is some meta-commentary" (* anything) "]")) ;; The other features of remark blocks should be tested; ;; but this is not a pressing, nor interesting, concern. #+end_src #+end_details -------------------------------------------------------------------------------- #+begin_details Example: No optional arguments #+begin_remark /Please/ *change* _this_ section to be more, ya know, professional. #+end_remark -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark /Please/ *change* _this_ section to be more, ya know, professional. ,#+end_remark #+end_src #+end_details #+begin_details "Example: Only providing a main argument ---i.e., the remark author, the editor" #+begin_remark Bobert /Please/ *change* _this_ section to be more, ya know, professional. #+end_remark #+latex: \vspace{1em}\noindent -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark Bobert /Please/ *change* _this_ section to be more, ya know, professional. ,#+end_remark #+end_src #+end_details #+begin_details Example: Possibly with no contents: #+begin_remark Bobert #+end_remark -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark Bobert ,#+end_remark #+end_src #+end_details #+begin_details "Example: Empty contents, no authour, nothing" #+begin_remark #+end_remark -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark ,#+end_remark #+end_src #+end_details #+latex: \vspace{1em}\noindent #+begin_details Example: Possibly with an empty new line #+begin_remark #+end_remark -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark ,#+end_remark #+end_src #+end_details #+latex: \iffalse #+begin_details "Example: With a “#+replacewith:” clause" #+begin_remark The two-dimensional notation; e.g., $\sum_{i = 0}^n i^2$ #+replacewith: A linear one-dimensional notation; e.g., $(\Sigma i : 0..n \;\bullet\; i^2)$ #+end_remark -------------------------------------------------------------------------------- *Source:* #+begin_src org :tangle no ,#+begin_remark The two-dimensional notation; e.g., $\sum_{i = 0}^n i^2$ ,#+replacewith: A linear one-dimensional notation; e.g., $(\Sigma i : 0..n \;\bullet\; i^2)$ ,#+end_remark #+end_src #+end_details #+latex: \fi #+latex: \vspace{1em}\noindent #+begin_details Example: Possibly “malformed” replacement clauses Forgot the thing to be replaced… #+begin_remark #+replacewith: A linear one-dimensional notation; e.g., $(\Sigma i : 0..n \;\bullet\; i^2)$ #+end_remark -------------------------------------------------------------------------------- Forgot the new replacement thing… #+begin_remark The two-dimensional notation; e.g., $\sum_{i = 0}^n i^2$ #+replacewith: #+end_remark -------------------------------------------------------------------------------- Completely lost one's train of thought… #+begin_parallel #+begin_remark #+replacewith: #+end_remark #+columnbreak: *Source:* #+begin_src org :tangle no ,#+begin_remark ,#+replacewith: ,#+end_remark #+end_src #+end_parallel #+end_details -------------------------------------------------------------------------------- A block to make an editorial comment could be overkill in some cases; luckily [[doc:o-defblock][defblock]] automatically provides an associated link type for the declared special blocks. - Syntax: =[[remark:person_name][editorial remark]]=. - This link type exports the same as the =remark= block type; however, in Emacs it is shown with an ‘angry’ ---bold--- red face. :Old_unnecessary_implementaiton: #+begin_src emacs-lisp -n -r (org-link-set-parameters "edcomm" :follow (lambda (_)) :export (lambda (label description backend) (o--edcomm backend (format ":ed:%s\n%s" label description))) :help-echo (lambda (_ __ position) (save-excursion (goto-char position) (-let [(&plist :path) (cadr (org-element-context))] (format "%s made this remark" (s-upcase path))))) :face '(:foreground "red" :weight bold)) #+end_src :End: #+begin_box Example: Terse remarks via links #+begin_parallel :bar t ~[[edcomm:Jasim][Hello, where are you?]]~ # +html:
’,
which sometimes accomplishes the desired goal.
"
(let ((rule (pcase backend
(`latex (if bar 2 0))
(_ (if bar "solid" "none"))))
(contents′ (s-replace "#+columnbreak:"
(if (equal 'latex backend) "\\columnbreak" "@@html:
@@")
contents)))
(format (pcase backend
(`latex "\\par \\setlength{\\columnseprule}{%s pt}
\\begin{minipage}[t]{\\linewidth}
\\begin{multicols}{%s}
%s
\\end{multicols}\\end{minipage}")
(_ "
" ;; “#+columnbreak” above
(* anything)
"Y"
(* anything)
"Z"
(* anything)))
#+end_src
#+end_details
#+html:
#+begin_box Example
_This_
#+begin_src org :tangle no
,#+begin_parallel 2 :bar yes-or-any-other-text
X
,#+columnbreak:
Y
Z
,#+end_parallel
#+end_src
_Yields_
#+begin_parallel 2 :bar t
X
#+columnbreak:
Y
Z
#+end_parallel
#+end_box
#+begin_center
( The [[https://www.gnu.org/software/emacs/manual/html_mono/eww.html][Emacs Web Wowser]], ~M-x eww~, does not display =parallel= environments as
desired. )
#+end_center
** COMMENT Older setup :Possibly_delete:
:PROPERTIES:
:CUSTOM_ID: COMMENT-Older-setup
:END:
:Header:
#+BEGIN_SRC emacs-lisp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Parallel blocks: 𝓃parallel[NB] for n:2..5, optionally with ‘N’o ‘b’ar
;; in-between the columns.
;;
;; Common case is to have three columns, and we want to avoid invoking the
;; attribute via org, so making this.
#+END_SRC
:End:
We want to be able to reduce the amount of whitespace noise in our articles, and
so use the =parallel= block to place ideas side-by-side ---with up to the chosen
limit of 5 columns.
#+caption: Displaying thoughts side-by-side ^_^ Top is browser, then Emacs, then PDF
[[file:images/parallel.png]]
#+LATEX_HEADER: \usepackage{multicol}
| =#+LATEX_HEADER: \usepackage{multicol}= |
I initially used the names =parallel𝓃= but names ending with a number =𝓃= did not
inherit highlighting, so I shifted the number to being a prefix instead.
+ For LaTeX, new lines are used to suggest opportunities for column breaks
and are needed even if explicit columnbreaks are declared.
+ Use the nullary switch =:columnbreak:= to request a columnbreak; this has no
effect on HTML export since HTML describes how text should be formatted on a
browser, which can dynamically shrink and grow and thus it makes no sense to have
hard columnbreaks.
+ We also provide ~𝓃parallelNB~ for users who want ‘N’o ‘B’ar separator
between columns.
#+BEGIN_SRC emacs-lisp
(cl-loop for cols in '("1" "2" "3" "4" "5")
do (cl-loop for rule in '("solid" "none")
do (eval (read (concat
"(defun o--" cols "parallel"
(if (equal rule "solid") "" "NB")
"(backend contents)"
"(format (pcase backend"
"(`html \"
" "" "
" "" "\\{" "{" "\\}" "}")) do (setq contents (s-replace this that contents))) (format (pcase backend ('html " ") (_ "%s")) contents)) #+end_src - Org escapes ~{,}~ in LaTeX export, so we need to ‘unescape’ them. This is clearly a hack. :End: #+end_details #+latex: \iffalse Here ---which you cannot see, as /desired/---is an example usage, where we declare ~\LL~ to produce a violet left parenthesis. We then use these to produce an example of quantification notation. #+begin_latex-definitions \def\LL{\color{violet}(} \def\RR{\color{violet})} #+end_latex-definitions # +begin_org-demo :source-color white :result-color white $$ {\color{teal}\bigoplus} _{ {\color{violet} x} = {\color{red} a}} ^{\color{cyan} b} {\color{brown}{\,f\, x}} \quad=\quad {\color{brown}{f\,\LL {\color{red} a} \RR}} \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL a + 1 \RR }} \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL a + 2 \RR }} \;{\color{teal}\oplus}\; \cdots \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL {\color{cyan} b} \RR}} $$ | [[teal:⊕]] | /Loop sequentially with loop-bodies fused using [[teal:⊕][⊕]]/ | | /[[violet:x]]/ | /Use [[violet:x][x]] as the name of the current element/ | | /[[red:a]]/ | /Start with [[violet:x][x]] being [[red:a][a]]/ | | /[[cyan:b]]/ | /End with [[violet:x][x]] being [[cyan:b][b]]/ | | /[[color:brown][f x]]/ | /At each [[violet:x][x]] value, compute [[color:brown][f x]]/ | # Note that /[[color:brown][f x]]/ is obtained by =/[[color:brown][f x]]/.= # +end_org-demo ( Markdown does not support MathJax; go look at the HTML or PDF! ) #+latex: \fi -------------------------------------------------------------------------------- Unfortunately, MathJax does not easily support arbitrary HTML elements to occur within the =$=-delimiters ---see [[https://stackoverflow.com/questions/58883048/mathjax-or-similar-render-arbitrary-html-element-inside-expression][this]] and [[https://github.com/mathjax/MathJax/issues/1707][this]] for ‘workarounds’. As such, the MathJax producing the above example is rather ugly whereas its subsequent explanatory table is prettier on the writer's side. :Verbatim_pasted_from_the_above_THIS_link: MathJax will not process math that contains HTML tags (other than a select few), so you will not be able to do the kind of replacements inside an expression like you are attempting to do here. #+begin_export html\nC-u 80_-∀
")) (deftest "[[It]] becomes tags" [kbd square-org-links] (⇝ (⟰ "[[kbd:C-u_80_-]]") "\nC-u 80 -
")) (deftest "\nC-u 80 -
")) (deftest "It has a tooltip documenting the underlying Lisp function, when possible" [kbd tooltip] (⇝ (⟰ "We now use this as Existential Angst.
")) #+end_src #+end_details ** Implementation Details: =doc= link, ~documentation~ block, and [[https://iamceege.github.io/tooltipster/#triggers][tooltipster]] :PROPERTIES: :CUSTOM_ID: The-doc-link-type :END: We begin by making use of a list of documentation-glossary entries ---a lightweight database of information, if you will. #+begin_box Overview of relevant Lisp + doc:o--docs is the global variable that holds the glossary/database for the current session. Entries may be inserted with doc:o-docs-set and retrieved with doc:o-docs-get. + doc:o-docs-fallback is a /fallback method/ for retriving documentation-glossary entries from elsewhere besides the current buffer; e.g., using Emacs' doc:documentation function or the ~wn~ WordNet command for definitions of English words. We use doc:o-docs-load-libraries to load documentation-glossary entries from external files. + doc:o-docs-insert for interactively inserting links for documented-glossary words. #+end_box #+begin_src emacs-lisp (defvar o--docs nil "An alist of (LABEL NAME DESCRIPTION) entries; our glossary. Example setter: 0. (o-docs-set \"os\" \"Emacs\" \"A place wherein I do all of my computing.\") Example getters: 0. (o-docs-get LABEL) 1. (-let [(name description) (cdr (assoc LABEL o--docs))] ⋯) See also `o--docs-from-libraries' and `o-docs-load-libraries'.") (cl-defun o-docs-set (label name description) "Add a new documentation-glossary entry, if it is not already present. We associate LABEL to have title NAME and glossary value DESCRIPTION. Example usage: (o-docs-set \"cat\" \"Category Theory\" \"A theory of typed composition; e.g., typed monoids.\")" (add-to-list 'o--docs (list label name description))) (cl-defun o-docs-get (label) "Return the name and documentation-glossary values associated with LABEL. It returns a list of length 2. Example uses: ;; Get the Lisp documentation of `thread-last' (o-docs-get \"thread-last\") ;; Get the English definition of ‘computing’ (o-docs-get \"computing\") We look for LABEL from within the current buffer first, using `o--docs', and otherwise look among the loaded libraries, using `o--docs-from-libraries', and, finally, look for the documentation entry using `o-docs-fallback'." (cdr (or (assoc label o--docs) (assoc label o--docs-from-libraries) (funcall o-docs-fallback label) (error "Error: No documentation-glossary entry for “%s”!" label)))) (cl-defun o-docs-insert () "Insert a “doc:𝒳” link from user's documentation-glossary database. It can be tricky to remember what you have, or what documentation entries mention, and so this command gives a searchable way to insert doc links." (interactive) (thread-last (cl-remove-duplicates (-concat o--docs o--docs-from-libraries) :test (lambda (x y) (cl-equalp (car x) (car y)))) (--map (format "%s ∷ %s" (car it) (cl-third it))) (completing-read "Insert doc link ∷ ") (s-split "∷") car (concat "doc:") (insert))) #+end_src :Hide_startup_code: #+name: startup-code #+begin_src emacs-lisp :tangle no (o-docs-set "cat" "Category Theory" "A theory of typed composition; e.g., typed monoids.") #+end_src :End: We may wish to use Emacs' doc:documentation (or doc:lf-documentation) command to retrieve entries ---this is useful for an online article that refers to unfamiliar Emacs terms ;-) To avoid copy-pasting documentation entries from one location to another, users may declare a fallback method. Besides Emacs' doc:documentation, the fallback can be refer to a user's personal ‘global glossary’ variable ---which may live in their Emacs' init file---, for more see: doc:o-docs-load-libraries #+begin_src emacs-lisp (defvar o-docs-fallback (lambda (label) (list ;; label label ;; name label ;; documentation (or (ignore-errors (documentation (intern label))) (ignore-errors (documentation-property (intern label) 'variable-documentation)) (-let [it (shell-command-to-string (format "wn %s -over -synsn" label))] (if (s-blank-p it) (error "Error: No documentation-glossary entry for “%s”!" label) it))))) "The fallback method to retriving documentation or glossary entries. We try to retrive the Emacs Lisp function documentation of the given LABEL, if possible, otherwise we try to retrive the Emacs Lisp variable documentation, and if that fails then we look up the word in the English dictionary. The English definition is obtained from the command line tool ‘wn’, WordNet.") #+end_src #+begin_details Implementation matter for user libraries #+begin_src emacs-lisp (defvar o-docs-libraries nil "List of Org files that have ‘#+begin_documentation’ blocks that should be loaded for use with the ‘doc:𝒳’ link type.") #+end_src In your init, you could do the following. ( See the installation instructions below, for more! ) #+name: startup-code #+begin_src emacs-lisp :tangle no (setq o-docs-libraries '("~/org-special-block-extras/documentation.org")) #+end_src My personal documentation library can be seen here: badge:Htmlized|Org|green|https://alhassy.github.io/org-special-block-extras/documentation.org.html or badge:Pretty|HTML|green|https://alhassy.github.io/org-special-block-extras/documentation.html. #+begin_src emacs-lisp (cl-defun o-docs-load-libraries (&optional (libs o-docs-libraries)) "Load documentation-glossary libraries LIBS. If no LIBS are provided, simply use those declared o-docs-libraries. See `o-docs-from-libraries'." (interactive) (cl-loop for lib in libs do (with-temp-buffer (insert-file-contents lib) ;; doc only activates after an export (-let [org-export-with-broken-links t] (org-html-export-as-html)) (kill-buffer) (delete-window) (setq o--docs-from-libraries (-concat o--docs o--docs-from-libraries)) (setq o--docs nil)))) (defvar o--docs-from-libraries nil "The alist of (label name description) entries loaded from the libraries. The ‘doc:𝒳’ link will load the libraries, possibly setting this variable to ‘nil’, then make use of this variable when looking for documentation strings. Interactively call `o-docs-load-libraries' to force your documentation libraries to be reloaded. See also `o-docs-libraries'.") #+end_src When the mode is actually enabled, then we load the user's libraries. #+begin_src emacs-lisp :noweb-ref enable-mode :tangle no ;; Ensure user's documentation libraries have loaded (unless o--docs-from-libraries (o-docs-load-libraries)) #+end_src #+RESULTS: #+end_details Let's keep track of where documentation comes from ---either the current article or from the fallback--- so that we may process it later on. #+begin_src emacs-lisp (defvar o--docs-actually-used nil "Which words are actually cited in the current article. We use this listing to actually print a glossary using ‘show:GLOSSARY’.") #+end_src #+RESULTS: : o--docs-actually-used Now HTML exporting such links as tooltips and displaying them in Emacs as tooltips happens in two stages: First we check the documentation, if there is no entry, we try the fallback ---if that falls, an error is reported at export time. E.g., upon export =doc:wombo= will produce a no-entry error. # doc:thread-first #+begin_details ‘doc’ link implementation #+name: startup-code #+begin_src emacs-lisp (o-deflink doc "Export O-LABEL as itself, or as the provided O-DESCRIPTION, along with a tooltip that shows the user's documentation-glossary for o-LABEL and using that entry's name when no O-DESCRIPTION is provided." [:let (entry (o-docs-get o-label) name (cl-first entry) docs (cl-second entry) display-name (or o-description name)) :help-echo (format "[%s] %s :: %s" o-label name docs) :face '(custom-button)] (add-to-list 'o--docs-actually-used (list o-label name docs)) (pcase o-backend (`html (format "%s" (o-html-export-preserving-whitespace docs) display-name)) ;; Make the current word refer to its glosary entry; ;; also declare the location that the glossary should refer back to. (`latex (format (concat "\\hyperref" "[o-glossary-%s]{%s}" "\\label{o-glossary" "-declaration-site-%s}") label display-name label)))) ;; WHERE ... (defun o-html-export-preserving-whitespace (s) "Make Org-markup'd string S ready for HTML presentation, preserving whitespace. This is orthogonal to the `org-export-string-as' command; i.e., (org-export-string-as s 'html :body-only-please) does something else! In particular, what this yields is an HTML rendition that does not account for whitespace, such as indentation and newlines. " ;; Make it look pretty! (thread-last s (s-replace-regexp "\\#\\+begin_src [^\n]*\n" "") (s-replace-regexp "\\( \\)*\\#\\+end_src\n" "") (s-replace-regexp "\\#\\+begin_export [^\n]*\n" "") (s-replace-regexp "\\( \\)*\\#\\+end_export" "") (s-replace " " " ") ; Preserve newlines (s-replace "\n" "
\1") ; (s-replace-regexp "\\#\\+begin_src [^<]*
") ; (s-replace-regexp "") ;; Translate Org markup ;; Only replace /.*/ by .* when it does not have an alphanum,:,/,< before it. (s-replace-regexp "\\([^a-z0-9A-Z:/<]\\)/\\(.+?\\)/" "\\1\\2") (s-replace-regexp "\\*\\(.+?\\)\\*" "\\1") (s-replace-regexp "\\~\\([^ ].*?\\)\\~" "
\\( \\)*\\#\\+end_src
" "
\1
\\1
")
;; No, sometimes we want equalities.
;; (s-replace-regexp "=\\([^ \n].*?\\)=" "\\1
")
(s-replace-regexp "\\$\\(.+?\\)\\$" "\\1")
(s-replace-regexp "\\[\\[\\(.*\\)\\]\\[\\(.*\\)\\]\\]" "\\2 (\\1)")
;; 5+ dashes result in a horizontal line
(s-replace-regexp "-----+" "tags ;; Musa: Make these three lines part of the core utility? (setq contents (substring-no-properties contents)) (setq contents (s-replace-regexp "
" "" contents)) (setq contents (s-replace-regexp "
" "" contents)) (setq contents (s-trim contents)) (cl-loop for entry in (cdr (s-split ":name:" contents)) do (-let [(contents′ . (&alist 'label 'name)) (o--extract-arguments (s-concat ":name:" entry) 'label 'name)] (unless (and label name) (error (message-box (concat "#+begin_documentation: " "Ensure the entry has a :name followed by a :label " "\n\n " contents)))) (add-to-list 'o--docs (mapcar #'s-trim (list label name contents′))))) ;; The special block is not shown upon export. "") #+END_SRC :End: #+end_details # # [[https://iamceege.github.io/tooltipster/#triggers][Tooltipster]] ---Fast, Sleek, & Beautiful Tooltips Thus far, Org entities are converted into HTML tags such as == for italicised text. However, HTML's default tooltip utility ---using ~title="⋯"~ in a ~div~--- does not render arbitrary HTML elements. Moreover, the default tooltip utility is rather slow. As such, we move to using /tooltipster/. The incantation below sets up the required magic to make this happen. #+begin_details "Fast, Sleek, and Beautiful Tooltips: Touching ‘org-html-head-extra’" #+begin_src emacs-lisp :noweb-ref enable-mode :tangle no (defvar o--tooltip-html-setup nil "Has the necessary HTML beeen added?") (unless o--tooltip-html-setup (setq o--tooltip-html-setup t) (setq org-html-head-extra (concat org-html-head-extra " "))) #+end_src Note that we have a conditional in the middle to avoid loading jQuery multiple times ---e.g., when one uses the =read-the-org= them in combination with this library. Multiple imports lead to broken features. #+end_details ** Wait, what about the LaTeX? :PROPERTIES: :CUSTOM_ID: hola :END: A PDF is essentially a fancy piece of paper, so tooltips will take on the form of glossary entries: Using =doc:𝒳= will result in the word =𝒳= being printed as a hyperlink to a glossary entry, which you the user will eventually declare using =show:GLOSSARY=; moreover, the glossary entry will also have a link back to where the =doc:𝒳= was declared. E.g., doc:defmacro and doc:lambda. We make a ~show:𝒳~ link type to print the value of the variable =𝒳= as follows, with =GLOSSARY= being a reserved name. #+begin_details Implementation of ‘show’ #+begin_src emacs-lisp (o-deflink show "Yield the value of the expression O-LABEL, with =GLOSSARY= being a reserved name. Example uses: show:user-full-name
*Result:*
#+begin_calc :hint-format "\\left\\{ %s\\right."
+ x
+ y -- Explanation of why $x \;=\; y$
Actually, let me explain:
* x
* x′ -- hint 1
* y -- hint 2
No words can appear (in the export) *after* a nested calculation, for now.
+ [≤] z
--
Explanation of why $y \;\leq\; z$
-- explain it more, this is ignored from export ;-)
#+end_calc
#+end_parallel
Benefits of Org-lists for proofs:
+ Nested proofs can be /folded-away/ with kbd:TAB.
- This provides a nice /proof outline!/
+ All Org support for lists active; e.g., [[kbd:M-↑,↓]] for moving /proof steps/ (list
items) and [[kbd:M-RET]] for new /proof steps/.
+ Intentional explanations can make the proof argument more /convincing/, or
accurate.
Examples below: Rational root equivales perfect power, Handshaking lemma,
calculating train length, and last, but not least, “1 + 1 = 2”.
--------------------------------------------------------------------------------
Align the proof hints with [[kbd:M-x align-regexp RET -- RET]] ...let's calculate that $a ⊆ e$...
#+begin_parallel
*Source:*
#+begin_src org :tangle no
,#+begin_calc
+ a
+ b -- reason for the equality
+ [⊆] c -- reason for the inclusion
+ d -- reason for the equality
+ e -- final justification
,#+end_calc
#+end_src
#+html:
*Result:*
#+begin_calc
+ a
+ b -- reason for the equality
+ [⊆] c -- reason for the inclusion
+ d -- reason for the equality
+ e -- final justification
#+end_calc
#+end_parallel
#+end_box
#+begin_details Tests
#+begin_src emacs-lisp :tangle tests.el :comments link
(setq calc (⟰ "#+begin_calc :hint-format \"\\\\left\\{ %s\\\\right.\"
+ x
+ y -- Explanation of why $x \\;=\\; y$
Actually, let me explain:
,* x
,* x′ -- hint 1
,* y -- hint 2
No words can appear (in the export) *after* a nested calculation, for now.
+ [≤] z
--
Explanation of why $y \\;\\leq\\; z$
-- explain it more, this is ignored from export ;-)
,#+end_calc"))
(deftest "It's an align environment, in displayed math mode"
[calc]
(⇝ calc "$$\\begin{align*}" (* anything) "\\end{align*}$$"))
(deftest "The calculation has 4 proof steps"
[calc]
;; The number of steps in a calculation is the number of items in each nesting, minus 1 (at each nesting).
;; Above we have depth 2 with 3 items in each depth, for a total of (3-1) + (3-1) = 2 + 2 = 4.
(should (= 4 (s-count-matches (rx (seq "\\" (* whitespace) (any "=" "≤"))) calc))))
(deftest "Of our 4 steps, 3 of them are equalities and one is an inclusion."
[calc]
(should (= 3 (s-count-matches (rx "= \\;\\;") calc)))
(should (= 1 (s-count-matches (rx "≤ \\;\\;") calc))))
(deftest "All of the hints actually appear in the calculational proof"
[calc]
(mapc (lambda (hint) (should (s-contains? hint calc)))
'("Explanation of why $x \\;=\\; y$"
"Actually, let me explain:"
"hint 1"
"hint 2"
"Explanation of why $y \\;\\leq\\; z$")))
#+end_src
#+end_details
--------------------------------------------------------------------------------
Examples...
#+begin_details ★ "Proof of “1 + 1 = 2” with source" :title-color orange
link-here:1+1=2
#+begin_calc
+ 1 + 1
+ \suc \zero + \suc \zero
--
$\def\suc{\mathsf{suc}\,} \def\zero{\mathsf{zero}}$
“1” abbreviates “$\suc \zero$”
+ \zero + \suc (\suc \zero) --
Addition is defined by moving the $\suc$-s from the left argument to the
right.
$\def\eqn#1#2{\begin{equation} #2 \tag{#1} \label{#1} \end{equation}}$
That is, we define $x + y$ by considering the possible $shape$ of $x$,
which could only be a $\zero$ or a $\suc$-cessor.
When $x$ is zero, there are no $\suc$-s to move to the right, so we yield the right.
$\eqn{Base Case}{\zero + n \; = \; n}$
When $x$ is a $\suc$, we move the $\suc$ to the right, and continue to try adding.
$\eqn{Inductive Step}{\suc m \,+\, n \; = \; m \,+\, \suc n}$
Since we're moving a $\suc$ at each step, eventually we'll hit the
base case and be done adding.
For now, we apply the \eqref{Inductive Step}.
+ \suc (\suc \zero)
-- Using the \eqref{Base Case} definition of addition.
+ 2 -- Switching to ‘conventional’ notation
#+end_calc
--------------------------------------------------------------------------------
*Source:*
#+begin_src org :tangle no
,#+begin_calc
+ 1 + 1
+ \suc \zero + \suc \zero
--
$\def\suc{\mathsf{suc}\,} \def\zero{\mathsf{zero}}$
“1” abbreviates “$\suc \zero$”
+ \zero + \suc (\suc \zero) --
Addition is defined by moving the $\suc$-s from the left argument to the
right.
$\def\eqn#1#2{\begin{equation} #2 \tag{#1} \label{#1} \end{equation}}$
That is, we define $x + y$ by considering the possible $shape$ of $x$,
which could only be a $\zero$ or a $\suc$-cessor.
When $x$ is zero, there are no $\suc$-s to move to the right, so we yield the right.
$\eqn{Base Case}{\zero + n \; = \; n}$
When $x$ is a $\suc$, we move the $\suc$ to the right, and continue to try adding.
$\eqn{Inductive Step}{\suc m \,+\, n \; = \; m \,+\, \suc n}$
Since we're moving a $\suc$ at each step, eventually we'll hit the
base case and be done adding.
For now, we apply the \eqref{Inductive Step}.
+ \suc (\suc \zero)
-- Using the \eqref{Base Case} definition of addition.
+ 2 -- Switching to ‘conventional’ notation
,#+end_calc
#+end_src
#+end_details
#+begin_details “Calculating” the length of a train
link-here:calculating-the-length-of-a-train
Let us *green:calculate* the length of a train, that is travelling at a speed of
60 km/hr and, on its way, it crosses a pole in 9 seconds.
# Implicitly assuming that it is going straight, no turns or curves.
#+begin_calc
+ \text{ the length of the train }
+ \text{ the distance from the end of the train to its front }
-- Express length as a difference of distance
+ & \left(\begin{split} &\text{ difference in time from when the train's front}
\\ &\text{ crosses the pole until its back crosses the pole } \end{split}\right)
\;\times\; \text{speed of the train}
-- distance = time × speed
+ & \text{ how long it takes the train to cross the pole }
\;\times\; \text{speed of the train}
-- Rephrase
+ 9 \text{ seconds } \times 60 \text{ km / hr}
-- $\def\meters{\,\mathsf{meters}\,} \def\seconds{\,\mathsf{seconds}\,}$
The train crosses the pole in 9 seconds;
and the train is travelling at the speed of 60km/hr.
+ 9 \seconds \times 60 × (1000 / 60 × 60) \meters / \seconds
--
Express things in common units, such as seconds; i.e.,:
* 1 \text{ km / hr}
* 1 \text{ km } /\; 1 \text{ hr } -- Be more explicit
* 1000 \meters /\, 1 \text{ hr } -- A kilometre is 1000 meters
* 1000 \meters /\, (60 × 60) \seconds -- Express hours in terms of seconds:
+ 1 \text{ hr }
+ 60 \,\mathsf{minutes} -- Express in minutes
+ 60 \,\times\, 1 \,\mathsf{minutes} -- Be more explicit
+ 60 \,\times\, 60 \,\seconds -- A minute has 60 seconds
* (1000 / 60 × 60) \meters / \seconds -- Collecting like-terms
Original justification, not shown in export:
A kilometre is 1000 meters and an hour is 60 minutes, each having 60 seconds:
$1 \text{ km / hr} \;=\; 1 \text{ km } /\; 1 \text{ hr } =\; 1000 \meters /\, (60 × 60) \seconds \;=\; (1000 / 60 × 60) \meters / \seconds$
+ 9 \seconds \times (1000 / 60) \meters / \seconds
-- Division cancels multiplication: $b × (a / b) = a$
+ (1000 / 60) × 9 \seconds × (\meters / \seconds)
-- Collect like-terms; i.e., commutativity of ×
+ (1000 / 60) × 9 \meters
-- Division cancels multiplication: $b × (a / b) = a$
+ 150 \meters
-- Arithmetic
#+end_calc
#+end_details
#+begin_details "Euler's Handshaking Lemma"
link-here:handshaking-lemma
/At a party of 7 people, is it possible that everybody knows exactly 3 other
people?/
We could consider all possible cases, which is not particularly enlightening; or
we could /calculate/...
#+begin_calc :hint-format "\\left[ %s \\right]"
+ \text{“Obviously”, the sum of everyone's friendship count is twice the total
number of friendships}
+ ∑_p f(p) = 2×F -- Formalise...
Let $f(p)$ denote the number of friends that person $p$ has,
which is 0 if $p$ has no friends.
Let $F$ be the total number of friendships (between any 2 people).
Since friendships are always between two people, we have
that $\sum_p f(p) = 2 × F$: Counting-up the number of friends
that everyone has will always be twice the total number of relationships
---which is always an even number.
Formally,
* \text{sum of everyone's friendship count}
* \sum_p f(p) -- Formalise, where $p$ ranges over people
* \sum_p \sum_r 𝑭𝒓_{p, r} -- Definition of $f$:
Let $𝑭𝒓_{p, r}$ be 1 exactly when $p$ participates in
friendship/relationship $r$,
and 0 otherwise, then $f(p) = \sum_r 𝑭𝒓_{p, r}$.
* \sum_r \sum_p 𝑭𝒓_{p, r} -- Quantifier interchange; i.e., Fubini's Principle
* \sum_r 2 -- Every relationship $r$ is between 2 people;
i.e., $\sum_p 𝑭𝒓_{p, r} = 2$
* 2 × \sum_r 1 -- Factoring
* 2 × F -- The total number of friendships is $F$
+ [⇒] \even{∑_p f(p)} ≡ \even{2 × F} -- Equals for equals;
i.e., $if$ those numbers are equal $then$ their parities are equal
$\def\even#1{\mathsf{even}\,\left(#1\right)}$
$\def\odd#1{\mathsf{odd}\,\left(#1\right)}$
+ \even{∑_p f(p)} -- The right side is clearly true;
(‘true’ is the identity of ‘≡’)
+ {\LARGE ≡}_p \;\even{f(p)} -- $\even{-}$ distributes over sums turning them into equivalences ‘≡’
+ \even{ ♯\{p ∣ ¬ \even{f(p)}\} } -- By definition, a chain of equivalences is true exactly when
it has an even number of false values
+ \even{ ♯\{p ∣ \odd{f(p)} \} } -- A non-even number is precisely an odd number
+ \text{an even number of people have an odd number of friends} -- Un-formalise
#+end_calc
Back to the problem: At a party of 7 people, is it possible that everybody knows
exactly 3 other people?
If such a party were possible, the number of people with an odd friend count
must be even, but 7 is not even, and so no such party is possible. (
Alternatively, if such a party were possible, then by the Handshaking Lemma, $F
= {∑_p f(p) \over 2} = {7 × 3 \over 2} = 10.5$: This is absurd since there
cannot be a ‘.5’ relationship! Hence, no such party is possible. )
+ Likewise, if exactly 5 people in a room each claim to have shaken 3 hands,
then someone is lying.
( Rephrased: The sum of the degrees of the vertices of a (total) doc:graph is
twice the number of edges of the graph; moreover, an even number of vertices
have an odd degree.
The party problem is then: /Is there a graph of 7 nodes with each node being
connected to exactly 3 other nodes?/ Apparently not, and we didn't need to draw
any graphs at all to prove it.
For more on Graph Theory, [[https://ptwiddle.github.io/MAS341-Graph-Theory-2017/][here]] are some lecture notes. )
+ [[http://homepage.tudelft.nl/64a8q/papers/handshake.pdf][The power of shaking hands]] has a few non-trivial applications of this lemma.
#+end_details
#+begin_details ★ Rational root equivales perfect power :title-color orange
link-here:rational-root-equivales-perfect-power
#+begin_calc :rel ⇔
+ \sqrt[n]{k} \text{ is a rational number }
+ ∃\, a, b •\; \sqrt[n]{k} = {a \over b} --
A rational number is the fraction of two integers.
Let variables $a,\, b$ range over integer numbers.
+ ∃\, a, b •\; k · a ^n = b ^n --
Use arithmetic to eliminate the $n$-th root operator.
$\def\exp#1#2{\mathsf{exp}_#1\,#2}$
$\def\eq{\;=\;}$
+ ∃\, a, b •\; ∀\, p •\; \exp{p}(k · a ^n) \eq \exp{p}(b ^n ) --
Let $\exp{m} x$ be the number of times that $m$ divides $x$.
For example, $\exp{2} 48 \eq 4$ and $\exp{2} 49 \eq 0$.
The numbers $p$ with $∀ m : ℤ⁺ \;•\; \exp{m}p \,≠\, 0 \,≡\, m \,=\, p$
are called $prime$ numbers. Let variable $p$ range over prime numbers.
Fundamental theorem of arithmetic: Numbers are determined by their prime powers.
That is, $\big(∀ \,p\, •\;\; \exp{p} x \eq f(p)\big) \;\;≡\;\; x \eq \big(Π\, p\, •\; p^{f(p)}\big)$ for any $f$.
As such, every number is the product of its prime powers:
$\qquad x \eq \big(Π \,p\, •\; p^{\exp{p} x}\big)$.
And so, any two numbers are the same precisely when they have the same primes:
$\qquad x \eq y \;\;≡\;\; \big(∀ p \,•\, \exp{p} x \eq \exp{p} y\big)$.
+ ∃\, a, b •\; ∀\, p •\; \exp{p} k + n · \exp{p} a \eq n · \exp{p} b --
When $p$ is prime, $\exp{p}(x · y) \eq \exp{p} x \,+\, \exp{p} y$.
Aside: In general, $\exp{p}(Π \,i\, \,•\, x_i) \eq (Σ \,i\, \,•\, \exp{p} x_i)$.
+ ∃\, a, b •\; ∀\, p •\; \exp{p} k \eq n · \Big(\exp{p} b - \exp{p} a\Big) --
Use arithmetic to collect similar terms.
+ ∀\, p •\; \exp{p} k \text{ is a multiple of } n --
(⇒) is the definition of multiplicity;
(⇐) take $a \,≔\, 1$ and define $b$ by its prime powers:
$\qquad ∀\, p \,•\, \exp{p} b \,≔\, {\exp{p} k \,/\, n}$
+ k \text{ is a perfect $n$-th power; i.e., of the shape } x^n --
Fundamental theorem of arithmetic and definition of ‘perfect’
#+end_calc
#+end_details
# Not ideal because current design decision favours hard line breaks...
# $\begin{align} \label{hi}\zero \,+\, n &\;=\; n \\ (\suc m) \,+\, n &\;=\; \suc (m + n) \end{align}$
# For some reason, I need /some/ math to “activate” MathJax
# ${}$
--------------------------------------------------------------------------------
Possibly Useful links:
+ [[https://www.onemathematicalcat.org/MathJaxDocumentation/TeXSyntax.htm][TeX Commands available in MathJax]]
+ MathJax crashes if ~\label~ names are not unique ---e.g., for labelled
equations, see “1 + 1 = 2” example above.
#+begin_details "★ What if I want to do this in LaTeX (e.g., for math.stackexchange.com)?" :title-color orange
link-here:equational-proofs-in-LaTeX
Copy-paste the following...
#+begin_src latex :tangle no :exports code
$
% set up \step command
\def\BEGINstep{ \left\langle }
\def\ENDstep{ \right\rangle }
\newcommand{\step}[2][=]{ \\ #1 \;\; & \qquad \color{maroon}{\BEGINstep\text{ #2 } \ENDstep} \\ & }
% multi-line step with many lines of text
\newcommand{\line}[1]{ \text{#1}\hfill\\ }
\newcommand{\stepmany}[2][=]{ \\ #1 \;\; & \qquad \color{maroon}{\BEGINstep \large\substack{ #2 } \ENDstep} \\ & }
% multi-line step with 4 lines of text
\newcommand{\stepfour}[5][=]{ \stepmany[#1]{\line{#2} \line{#3} \line{#4}
\line{#5}} }
\newenvironment{calc}{\begin{align*} & }{\end{align*}}
$
#+end_src
Then, use it as follows...
#+begin_src latex :tangle no :exports code
\begin{calc}
x
\step{reason for equality}
y
\step[\leq]{reason for inclusion}
z
\end{calc}
#+end_src
#+end_details
* Summary
:PROPERTIES:
:CUSTOM_ID: Summary
:END:
#+begin_quote
The full article may be read as a [[https://alhassy.github.io/org-special-block-extras/index.pdf][PDF]] or as [[https://alhassy.github.io/org-special-block-extras][HTML]] ---or visit the [[https://github.com/alhassy/org-special-block-extras][repo]].
#+end_quote
link-here:summary
Let =𝒞= be any of the following: =black=, =blue=, =brown=, =cyan=, =darkgray=, =gray=, =green=,
=lightgray=, =lime=, =magenta=, =olive orange=, =pink=, =purple=, =red=, =teal=, =violet=, =white=,
=yellow=.
| Idea | Documentation | Link only? |
|------------------------+-----------------------------+---------------------|
| Colours | =𝒞=, [[doc:o--latex-definitions][latex-definitions]], [[doc:o--color][color]] | |
| Parallel | [[doc:o--parallel][parallel]] | |
| Editorial Comments | [[doc:o--remark][remark]] | |
| Folded Details | [[doc:o--details][details]] , [[doc:o--box][box]] | |
| Keystrokes | | ~kbd~ |
| OctoIcons & Link Here | | ~octoicon~, ~link-here~ |
| Documentation-Glossary | [[doc:o--documentation][documentation]] | ~doc~ |
| Marginal remarks | [[doc:o--margin][margin]] | |
| Badges | [[doc:o-make-badge][badge]] | |
| Equational proofs | [[doc:o--calc][calc]] | |
Other fun stuff: [[doc:o--solution][solution]], [[doc:o--org-demo][org-demo]], [[doc:o--stutter][stutter]], [[doc:o--rename][rename]], [[doc:o--spoiler][spoiler]], [[doc:o--tree][tree]] :grin:
There are also the social badge links:
=reddit-subscribe-to=, =github-followers=, =github-forks=, =github-stars, github-watchers=, =twitter-follow=, and =tweet=.
# [[color:orange][Going forward,]] it'd be nice to a centralised ‘user manual’ which may be
# consulted rather than reading the literate implementation above.
** Installation Instructions
:PROPERTIES:
:CUSTOM_ID: Installation-Instructions
:END:
Manually or using [[https://github.com/alhassy/emacs.d#installing-emacs-packages-directly-from-source][quelpa]]:
#+BEGIN_SRC emacs-lisp :tangle no
;; ⟨0⟩ Download the org-special-block-extras.el file manually or using quelpa
(quelpa '(org-special-block-extras :fetcher github :repo
"alhassy/org-special-block-extras"))
;; ⟨1⟩ Have this always active in Org buffers
(add-hook #'org-mode-hook #'org-special-block-extras-mode)
;; ⟨1′⟩ Or use: “M-x org-special-block-extras-mode” to turn it on/off
#+END_SRC
*Or* with [[https://github.com/alhassy/emacs.d#use-package-the-start-of-initel][use-package]]:
#+BEGIN_SRC emacs-lisp :tangle no
(use-package org-special-block-extras
:ensure t
:hook (org-mode . org-special-block-extras-mode)
:custom
;; The places where I keep my ‘#+documentation’
(o-docs-libraries
'("~/org-special-block-extras/documentation.org"))
;; Details heading “flash pink” whenever the user hovers over them?
(org-html-head-extra (concat org-html-head-extra ""))
;; The message prefixing a ‘tweet:url’ badge
(o-link-twitter-excitement
"This looks super neat (•̀ᴗ•́)و:"))
#+END_SRC
Then, provide support for a new type of special block, say re-using the ~src~
blocks that, say, folds up all such blocks in HTML export, by declaring the
following.
#+BEGIN_SRC emacs-lisp :tangle no
(o-defblock src (lang nil) (title nil exports nil file nil)
"Fold-away all ‘src’ blocks as ‘ %s
%s
,#+begin_box
octoicon:report Note that kbd:C-x_C-e evaluates a Lisp form!
,#+end_box
/Allah[[margin:][The God of Abraham; known as Elohim in the Bible]] does
not burden a soul beyond what it can bear./ --- Quran 2:286
#+LATEX_HEADER: \usepackage{multicol}
#+LATEX_HEADER: \usepackage{tcolorbox}
#+latex: In the LaTeX output, we have a glossary.
show:GLOSSARY
badge:Thanks|for_reading
tweet:https://github.com/alhassy/org-special-block-extras
badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee
#+end_example
Here is what it looks like as HTML (left) and LaTeX (right):
#+attr_html: :width 800px
#+attr_latex: :width 100px
[[file:images/minimal-working-example.png]]
#+begin_details Tests
#+BEGIN_SRC emacs-lisp :tangle tests.el :comments link
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Run all MWE tests
;; (ert "mwe")
(setq mwe (⟰
"
,#+begin_parallel
[[color:orange][Are you excited to learn some Lisp?]] [[blue:Yes!]]
Pop-quiz: How does doc:apply work?
,#+end_parallel
,#+begin_details Answer
link-here:solution
Syntactically, ~(apply f '(x0 ... xN)) = (f x0 ... xN)~.
[[remark:Musa][Ain't that cool?]]
,#+begin_spoiler aqua
That is, [[color:magenta][we can ((apply)) a function to a list of arguments!]]
,#+end_spoiler
,#+end_details
,#+html:
,#+begin_box
octoicon:report Note that kbd:C-x_C-e evaluates a Lisp form!
,#+end_box
/Allah [[margin:][The God of Abraham; known as Elohim in the Bible]] does not burden a soul
beyond what it can bear./ --- Quran 2:286
,#+LATEX_HEADER: \\usepackage{multicol}
,#+LATEX_HEADER: \\usepackage{tcolorbox}
,#+latex: In the LaTeX output, we have a glossary.
show:GLOSSARY
badge:Thanks|for_reading
tweet:https://github.com/alhassy/org-special-block-extras
badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee
"))
(deftest "It exports to HTML without any problems"
[mwe html-export]
(find-file "mwe.org")
(should (org-html-export-to-html)))
(deftest "It starts with a 2-column div for ‘parallel’"
[mwe parallel]
(⇝ mwe "
Thus, (apply '+ 1 2 '(3 4)) returns 10.
(fn FUNCTION &rest ARGUMENTS)\""
">apply work?"))
(deftest "Its ‘details’ block is titled “Answer”, in green"
[mwe details]
(⇝ mwe
"