diff --git a/answers-script.cabal b/answers-script.cabal
index 83bc552..4561f4c 100644
--- a/answers-script.cabal
+++ b/answers-script.cabal
@@ -73,7 +73,7 @@ library
filepath,
lens,
text,
- cmark,
+ cmark-gfm,
containers,
directory,
cryptohash-sha1,
@@ -86,8 +86,9 @@ library
transformers,
monad-loops,
aeson,
+ aeson-pretty,
attoparsec,
- cmark-lens
+ cmark-gfm-lens
-- Directories containing source files.
hs-source-dirs: src
@@ -155,6 +156,6 @@ test-suite answers-script-test
bytestring,
lens,
attoparsec,
- cmark,
- cmark-lens,
+ cmark-gfm,
+ cmark-gfm-lens,
directory
diff --git a/cabal.project b/cabal.project
index 35c286b..6d4ef88 100644
--- a/cabal.project
+++ b/cabal.project
@@ -25,4 +25,10 @@ source-repository-package
type: git
location: https://github.com/ingun37/cmark-lens.git
tag: 4456f10deccb61419ce28811db448c266931190f
- --sha256: 0s1zzpd1bgap403awkjar365qxsysw4lwq6i23whjydqj3whqdhb
\ No newline at end of file
+ --sha256: 0s1zzpd1bgap403awkjar365qxsysw4lwq6i23whjydqj3whqdhb
+
+source-repository-package
+ type: git
+ location: https://github.com/ingun37/cmark-gfm-lens.git
+ tag: 7c57b38c7ddb8888df8b3b92800a42a1ac0ac359
+ --sha256: 0ssznk105b7ai59pr2d6i1if7h4s4f00vs810afpyby6mvfvbvfy
\ No newline at end of file
diff --git a/src/MatlabMark.hs b/src/MatlabMark.hs
index 933ee2b..606da5e 100644
--- a/src/MatlabMark.hs
+++ b/src/MatlabMark.hs
@@ -3,8 +3,8 @@
module MatlabMark (generateMatlabAnswersDB, readMatlabMD) where
-import CMark
-import CMark.Lens
+import CMarkGFM
+import CMarkGFM.Lens
import Control.Lens
import Data.Attoparsec.Text qualified as A
import Data.Text qualified as T
@@ -38,7 +38,7 @@ parseVersion = do
generateMatlabAnswersDB :: FilePath -> Node -> IO ()
generateMatlabAnswersDB outputDirPath node =
let (intro, groups) = groupByProblems node
- toDocText = CMark.nodeToCommonmark [] Nothing . Node Nothing DOCUMENT
+ toDocText = nodeToCommonmark [] Nothing . Node Nothing DOCUMENT
writeMD name nodes = do
D.createDirectory $ outputDirPath F.> name
TIO.writeFile (outputDirPath F.> name F.> "a.md") (toDocText nodes)
@@ -47,4 +47,4 @@ generateMatlabAnswersDB outputDirPath node =
mapM_ (uncurry writeMD) groups
readMatlabMD :: FilePath -> IO Node
-readMatlabMD mdFilePath = CMark.commonmarkToNode [] . changeMatlabMarkdownDelimeters <$> TIO.readFile mdFilePath
\ No newline at end of file
+readMatlabMD mdFilePath = commonmarkToNode [] [] . changeMatlabMarkdownDelimeters <$> TIO.readFile mdFilePath
\ No newline at end of file
diff --git a/src/MyLib.hs b/src/MyLib.hs
index 38b5895..d39a4b0 100644
--- a/src/MyLib.hs
+++ b/src/MyLib.hs
@@ -3,13 +3,15 @@
module MyLib (someFunc) where
-import CMark qualified
+import CMarkGFM qualified as CMark
import Control.Lens
import Control.Monad qualified as Monad
import Crypto.Hash.SHA1 qualified as SHA (hash)
import Data.Aeson as Json
+import Data.Aeson.Encode.Pretty qualified as PrettyJson
import Data.ByteString.Base16 qualified as B16 (encode)
import Data.ByteString.Char8 qualified as C8
+import Data.ByteString.Lazy qualified as B
import Data.Foldable qualified as Foldable
import Data.List qualified as List
import Data.List.NonEmpty qualified as NE
@@ -153,7 +155,7 @@ theWriter source destination prefix (parentPathComponents, item) = do
putStrLn $ " Compiling " ++ src ++ " -> " ++ dst
let safePrefix = List.dropWhileEnd (== '/') $ dropWhile (== '/') prefix
let finalPrefix = List.intercalate "/" (filter (not . null) [safePrefix, "resources", _hash])
- TIO.writeFile dst (CMark.nodeToHtml [] $ MyMark.prefixImageUrl finalPrefix $ CMark.commonmarkToNode [] _content)
+ TIO.writeFile dst (CMark.nodeToHtml [CMark.optUnsafe] [] $ MyMark.prefixImageUrl finalPrefix $ CMark.commonmarkToNode [] [CMark.extTable] _content)
let writeFileType key =
\case
@@ -193,7 +195,7 @@ someFunc prefixPath source destination = do
let pageDatas = Tree.foldTree folder tree
let pagesDir = destination File.> "pages"
Dir.createDirectoryIfMissing True pagesDir
- let writePageData pg = Json.encodeFile (pagesDir File.> (pg ^. pageContent . hash) ++ ".json") pg
+ let writePageData pg = B.writeFile (pagesDir File.> (pg ^. pageContent . hash) ++ ".json") (PrettyJson.encodePretty pg)
Monad.forM_ pageDatas writePageData
return pageDatas
diff --git a/src/MyMark.hs b/src/MyMark.hs
index fe21117..357ad6d 100644
--- a/src/MyMark.hs
+++ b/src/MyMark.hs
@@ -1,17 +1,29 @@
+{-# LANGUAGE OverloadedStrings #-}
+
module MyMark (prefixImageUrl) where
-import CMark
+import CMarkGFM
import Data.Text qualified as T
+tableCellToHTML :: Node -> T.Text
+tableCellToHTML (Node _ _ nodes) = "
\n" <> T.intercalate "" (map (CMarkGFM.nodeToHtml [] []) (workOnInlineMath nodes)) <> "\n | "
+
+tableRowToHTML :: Node -> T.Text
+tableRowToHTML (Node _ _ nodes) = "\n" <> T.intercalate "\n" (map tableCellToHTML nodes) <> "\n
"
+
+tableToInlineHTML :: [TableCellAlignment] -> [Node] -> Node
+tableToInlineHTML _ nodes = Node Nothing (HTML_BLOCK $ "\n" <> T.intercalate "\n" (map tableRowToHTML nodes) <> "\n
") []
+
prefixImageUrl :: String -> Node -> Node
prefixImageUrl prefix node =
let safePrefix = T.pack ("/" ++ prefix ++ "/")
replaceUrl url = if T.isPrefixOf (T.pack "http") url then url else safePrefix <> T.dropWhile (== '/') url
- recurse (Node posInfo nodeType nodes) =
+ recurse (Node _ nodeType nodes) =
case nodeType of
IMAGE url title -> Node Nothing (IMAGE (replaceUrl url) title) nodes
PARAGRAPH -> Node Nothing PARAGRAPH $ workOnInlineMath (map (prefixImageUrl prefix) nodes)
CODE_BLOCK info text -> if info == T.pack "math" then mathBlock text else Node Nothing nodeType nodes
+ TABLE aligns -> tableToInlineHTML aligns nodes
_ -> Node Nothing nodeType (recurse <$> nodes)
in recurse node
diff --git a/test/Main.hs b/test/Main.hs
index aad14dd..24462b0 100644
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,22 +1,20 @@
{-# LANGUAGE OverloadedStrings #-}
+
module Main (main) where
+import Control.Lens
import Control.Monad
import Data.ByteString qualified as B
import Data.Either qualified as E
import Data.Text qualified as T
-import Data.Text.IO qualified as TIO
import Data.Text.Encoding qualified as Encoding
-import MyLib qualified
+import Data.Text.IO qualified as TIO
import MatlabMark qualified
-import System.Directory.Tree qualified as DT
+import MyLib qualified
import System.Directory qualified as D
+import System.Directory.Tree qualified as DT
import System.FilePath qualified as F
import Test.Hspec
-import Control.Lens
-import CMark qualified
-import CMark.Lens
-import Data.Attoparsec.Text qualified as A
main :: IO ()
main = hspec $ do
@@ -27,6 +25,13 @@ main = hspec $ do
it "asset build test" $ do
testCase
+isEmptyDir :: DT.DirTree a -> Bool
+isEmptyDir (DT.Dir _ xs) = null xs
+isEmptyDir _ = False
+
+makeComparable :: DT.AnchoredDirTree a -> [DT.DirTree a]
+makeComparable = filter (not . isEmptyDir) . DT.flattenDir . set DT._name "" . view DT._dirTree
+
testCase :: IO ()
testCase =
do
@@ -37,25 +42,18 @@ testCase =
let reader x = do
b <- B.readFile x
return $ E.fromRight (T.pack $ F.takeBaseName x ++ ": " ++ show (B.length b)) $ Encoding.decodeUtf8' b
- a <- DT.readDirectoryWith reader dst
- let a' = a ^.DT._dirTree
- let a'' = DT.flattenDir (set DT._name "" a')
- b <- DT.readDirectoryWith reader expect
- let b' = b ^.DT._dirTree
- let b'' = DT.flattenDir (set DT._name "" b')
- zipWithM_ shouldBe a'' b''
+ a <- makeComparable <$> DT.readDirectoryWith reader dst
+ b <- makeComparable <$> DT.readDirectoryWith reader expect
+ zipWithM_ shouldBe a b
matlab :: IO ()
matlab =
do
node <- MatlabMark.readMatlabMD $ "test" F.> "matlab-short.md"
let dst = "test" F.> "matlab-dst"
+ D.removeDirectoryRecursive dst
D.createDirectoryIfMissing True dst
MatlabMark.generateMatlabAnswersDB dst node
- a <- DT.readDirectoryWith TIO.readFile dst
- let a' = a ^.DT._dirTree
- let a'' = DT.flattenDir (set DT._name "" a')
- b <- DT.readDirectoryWith TIO.readFile ("test" F.> "matlab-expect")
- let b' = b ^.DT._dirTree
- let b'' = DT.flattenDir (set DT._name "" b')
- zipWithM_ shouldBe a'' b''
+ a <- makeComparable <$> DT.readDirectoryWith TIO.readFile dst
+ b <- makeComparable <$> DT.readDirectoryWith TIO.readFile ("test" F.> "matlab-expect")
+ zipWithM_ shouldBe a b
diff --git a/test/answers-db b/test/answers-db
index ba858fd..fbdca23 160000
--- a/test/answers-db
+++ b/test/answers-db
@@ -1 +1 @@
-Subproject commit ba858fd9a8f220d6e4001a7c37a3895cefb4b66a
+Subproject commit fbdca2353c2a61c77352856d61be32d9d1b0c41b
diff --git a/test/expect/pages/0af5b76897bd59b64b3d577b459c75a1b5b6b3ca.json b/test/expect/pages/0af5b76897bd59b64b3d577b459c75a1b5b6b3ca.json
new file mode 100644
index 0000000..ee6567f
--- /dev/null
+++ b/test/expect/pages/0af5b76897bd59b64b3d577b459c75a1b5b6b3ca.json
@@ -0,0 +1,17 @@
+{
+ "_childPageContents": [
+ {
+ "_answers": 0,
+ "_attributes": {},
+ "_hash": "ba45627f4d5702a2d14eaced7927fd3798c60167",
+ "_pageTitle": "11. Equilibrium and Elasticity"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 0,
+ "_attributes": {},
+ "_hash": "0af5b76897bd59b64b3d577b459c75a1b5b6b3ca",
+ "_pageTitle": "University Physics with Modern Physics"
+ },
+ "_parentHash": "2aed5404c83f7a46aa249e0a6328af756b19d513"
+}
\ No newline at end of file
diff --git a/test/expect/pages/2125c437aaac928d5852224ff00a83f9d9776dda.json b/test/expect/pages/2125c437aaac928d5852224ff00a83f9d9776dda.json
index 179a691..9f9c1b5 100644
--- a/test/expect/pages/2125c437aaac928d5852224ff00a83f9d9776dda.json
+++ b/test/expect/pages/2125c437aaac928d5852224ff00a83f9d9776dda.json
@@ -1 +1,30 @@
-{"_pageContent":{"_pageTitle":"5.1 Continuous Mappings","_hash":"2125c437aaac928d5852224ff00a83f9d9776dda","_attributes":{},"_answers":1},"_parentHash":"6c11e3a31b4b8bd07bdef3f87887ab202a568679","_childPageContents":[{"_pageTitle":"8","_hash":"3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1","_attributes":{"a.md":{"_time":"2020-10-18T14:48:51+09:00","_attributeFile":{"_content":"I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"}},"q.md":{"_time":"2020-04-12T01:08:01+09:00","_attributeFile":{"_content":"Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."}}},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"
+ },
+ "_time": "2020-10-18T14:48:51+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."
+ },
+ "_time": "2020-04-12T01:08:01+09:00"
+ }
+ },
+ "_hash": "3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1",
+ "_pageTitle": "8"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "2125c437aaac928d5852224ff00a83f9d9776dda",
+ "_pageTitle": "5.1 Continuous Mappings"
+ },
+ "_parentHash": "6c11e3a31b4b8bd07bdef3f87887ab202a568679"
+}
\ No newline at end of file
diff --git a/test/expect/pages/2aed5404c83f7a46aa249e0a6328af756b19d513.json b/test/expect/pages/2aed5404c83f7a46aa249e0a6328af756b19d513.json
index 66b83c4..f713909 100644
--- a/test/expect/pages/2aed5404c83f7a46aa249e0a6328af756b19d513.json
+++ b/test/expect/pages/2aed5404c83f7a46aa249e0a6328af756b19d513.json
@@ -1 +1,50 @@
-{"_pageContent":{"_pageTitle":"books","_hash":"2aed5404c83f7a46aa249e0a6328af756b19d513","_attributes":{"q.md":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."}}},"_answers":3},"_parentHash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","_childPageContents":[{"_pageTitle":"Topology Without Tears","_hash":"4c1513c92422dc16b3c5f13bd03d34ba0feeb6df","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Sidney A. Morris"}}},"_answers":1},{"_pageTitle":"Category Theory For Programmers","_hash":"b614f31d04b3bc2b3d23ee4337475251429e5a9f","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Bartosz Milewski"}}},"_answers":2}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 0,
+ "_attributes": {},
+ "_hash": "0af5b76897bd59b64b3d577b459c75a1b5b6b3ca",
+ "_pageTitle": "University Physics with Modern Physics"
+ },
+ {
+ "_answers": 1,
+ "_attributes": {
+ "author.txt": {
+ "_attributeFile": {
+ "_content": "Sidney A. Morris"
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "4c1513c92422dc16b3c5f13bd03d34ba0feeb6df",
+ "_pageTitle": "Topology Without Tears"
+ },
+ {
+ "_answers": 2,
+ "_attributes": {
+ "author.txt": {
+ "_attributeFile": {
+ "_content": "Bartosz Milewski"
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "b614f31d04b3bc2b3d23ee4337475251429e5a9f",
+ "_pageTitle": "Category Theory For Programmers"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 3,
+ "_attributes": {
+ "q.md": {
+ "_attributeFile": {
+ "_content": "# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "2aed5404c83f7a46aa249e0a6328af756b19d513",
+ "_pageTitle": "books"
+ },
+ "_parentHash": "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+}
\ No newline at end of file
diff --git a/test/expect/pages/3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1.json b/test/expect/pages/3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1.json
index 0c330e9..6ccb5dd 100644
--- a/test/expect/pages/3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1.json
+++ b/test/expect/pages/3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1.json
@@ -1 +1,23 @@
-{"_pageContent":{"_pageTitle":"8","_hash":"3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1","_attributes":{"a.md":{"_time":"2020-10-18T14:48:51+09:00","_attributeFile":{"_content":"I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"}},"q.md":{"_time":"2020-04-12T01:08:01+09:00","_attributeFile":{"_content":"Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."}}},"_answers":1},"_parentHash":"2125c437aaac928d5852224ff00a83f9d9776dda","_childPageContents":[]}
\ No newline at end of file
+{
+ "_childPageContents": [],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "I'll refer $`\\mathcal{T}`$ as $`\\mathcal{T}_X`$, $`\\mathcal{T}_1`$ as $`\\mathcal{T}_Y`$, $`\\mathcal{T}_2`$ as $`\\mathcal{T}_A`$, $`\\mathcal{T}_3`$ as $`\\mathcal{T}_B`$.\n\nLet's define $`x \\in X`$ and $`U \\in \\mathcal{T}_Y`$ such that $`f(x) \\in U`$. By definition of continuous mapping, there must exists a $`V`$ such that $`x \\in V \\in \\mathcal{T}_X`$ and $`f(x) \\in fV \\subseteq U`$.\n\nIf for any $`x \\in A`$ and any B-induced open set $`U_B \\in \\mathcal{T}_B`$, which must imply existance of $`U \\in \\mathcal{T}_Y`$, such that $`g(x) \\in U_B`$,\n\n\n\n...there exists an A-induced open set $`V_A`$, which must imply existance of $`V \\in \\mathcal{T}_A`$, such that the image $`gV_A`$ satisfies $`g(x) \\in gV_A \\subseteq U_B`$, then $`g`$ must be continuous. What we have to know is that if the image $`gV_A`$is subset of $`U_B`$, in other words, every $`x \\in V_A`$ will satisfiy $`g(x) \\in U_B`$. Let's proove it.\n\n\n\n```math\n\\begin{aligned}\n & x \\in V_A \\\\\n & \\rightarrow x \\in V \\\\\n & \\rightarrow g(x) \\in gV \\\\\n & \\rightarrow g(x) \\in \\text{ some } U & \\text{ since } f \\text{ is continuous} \\\\\n & \\rightarrow g(x) \\in U \\cap B & \\text{ since codomain of } g \\text{ is } B \\\\\n & \\rightarrow g(x) \\in U_B\n\\end{aligned}\n```"
+ },
+ "_time": "2020-10-18T14:48:51+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Let $`(X,\\mathcal{T})`$ and $`(Y,\\mathcal{T}_1)`$ be topological spaces and $`f:(X,\\mathcal{T}) \\rightarrow (Y,\\mathcal{T}_1)`$ a continuous mapping. Let $`A`$ be a subset of $`X`$, $`\\mathcal{T}_2`$ the induced topology on $`A`$, $`B = f(A)`$, $`\\mathcal{T}_3`$ the induced topology on $`B`$ and $`g:(A,\\mathcal{T}_2) \\rightarrow (B,\\mathcal{T}_3)`$ the restriction of $`f`$ to $`A`$. Prove that $`g`$ is continuous."
+ },
+ "_time": "2020-04-12T01:08:01+09:00"
+ }
+ },
+ "_hash": "3e8d61043ed9bdb6b3cd4f197f0203bbdcb13db1",
+ "_pageTitle": "8"
+ },
+ "_parentHash": "2125c437aaac928d5852224ff00a83f9d9776dda"
+}
\ No newline at end of file
diff --git a/test/expect/pages/4c1513c92422dc16b3c5f13bd03d34ba0feeb6df.json b/test/expect/pages/4c1513c92422dc16b3c5f13bd03d34ba0feeb6df.json
index e7520fd..6498c13 100644
--- a/test/expect/pages/4c1513c92422dc16b3c5f13bd03d34ba0feeb6df.json
+++ b/test/expect/pages/4c1513c92422dc16b3c5f13bd03d34ba0feeb6df.json
@@ -1 +1,24 @@
-{"_pageContent":{"_pageTitle":"Topology Without Tears","_hash":"4c1513c92422dc16b3c5f13bd03d34ba0feeb6df","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Sidney A. Morris"}}},"_answers":1},"_parentHash":"2aed5404c83f7a46aa249e0a6328af756b19d513","_childPageContents":[{"_pageTitle":"5. Continuous Mappings","_hash":"6c11e3a31b4b8bd07bdef3f87887ab202a568679","_attributes":{},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "6c11e3a31b4b8bd07bdef3f87887ab202a568679",
+ "_pageTitle": "5. Continuous Mappings"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {
+ "author.txt": {
+ "_attributeFile": {
+ "_content": "Sidney A. Morris"
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "4c1513c92422dc16b3c5f13bd03d34ba0feeb6df",
+ "_pageTitle": "Topology Without Tears"
+ },
+ "_parentHash": "2aed5404c83f7a46aa249e0a6328af756b19d513"
+}
\ No newline at end of file
diff --git a/test/expect/pages/54c6ff9a213b80284619a5afb7859d9d30a444bc.json b/test/expect/pages/54c6ff9a213b80284619a5afb7859d9d30a444bc.json
index 2f8bf64..39abf0c 100644
--- a/test/expect/pages/54c6ff9a213b80284619a5afb7859d9d30a444bc.json
+++ b/test/expect/pages/54c6ff9a213b80284619a5afb7859d9d30a444bc.json
@@ -1 +1,30 @@
-{"_pageContent":{"_pageTitle":"10. Natural Transformations","_hash":"54c6ff9a213b80284619a5afb7859d9d30a444bc","_attributes":{},"_answers":1},"_parentHash":"b614f31d04b3bc2b3d23ee4337475251429e5a9f","_childPageContents":[{"_pageTitle":"5","_hash":"ebe52014d6aaf02a4ddeaa7de59a014eac6634b8","_attributes":{"a.md":{"_time":"2020-10-18T14:48:51+09:00","_attributeFile":{"_content":"Let's proove interchange law.\n\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right) =\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)`$\n\n\nLet's say each natural transformation is defined as\n\n\n$`\\alpha :F\\rightarrow F_{2}`$\n$`\\alpha ':F_{2}\\rightarrow F_{3}`$\n$`\\beta :G\\rightarrow G_{2}`$\n$`\\beta ':G_{2}\\rightarrow G_{3}`$\n\n\n\n\nLet's see what we get with first one.\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right)GF`$ \n\nUsing horizontal composition\n$`=\\left( \\beta'\\cdot \\alpha '\\right) G_{2}F_{2}`$\nAgain, using horizontal composition\n$`=G_{3}F_{3}`$\n\nNow the second one.\n\n$`\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)GF`$\n\nUnlike previous one, we cannot directly apply the $`(\\alpha' \\circ \\alpha)`$ to $`GF`$. No problem. We can composite horizontally with $`id:G\\rightarrow G`$\n\n$`=\\left( \\beta '\\circ \\beta\\right) \\cdot \\left( \\left( id\\cdot \\alpha '\\right) \\circ \\left( id\\cdot \\alpha \\right) \\right)GF`$\n$`=\\left( \\beta'\\circ \\beta\\right)GF_3`$\n\nSame way but this time with $`id:F\\rightarrow F`$\n\n$`=\\left( \\left( \\beta'\\cdot id\\right) \\circ \\left( \\beta \\cdot id\\right) \\right) GF_{3}`$\n$`=G_3 F_3`$\n\n\nProooven BAMMM"}},"q.md":{"_time":"2019-12-19T21:51:08+09:00","_attributeFile":{"_content":"Write a short essay about how you may enjoy writing down the evident diagrams needed to prove the interchange law.\n"}}},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "Let's proove interchange law.\n\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right) =\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)`$\n\n\nLet's say each natural transformation is defined as\n\n\n$`\\alpha :F\\rightarrow F_{2}`$\n$`\\alpha ':F_{2}\\rightarrow F_{3}`$\n$`\\beta :G\\rightarrow G_{2}`$\n$`\\beta ':G_{2}\\rightarrow G_{3}`$\n\n\n\n\nLet's see what we get with first one.\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right)GF`$ \n\nUsing horizontal composition\n$`=\\left( \\beta'\\cdot \\alpha '\\right) G_{2}F_{2}`$\nAgain, using horizontal composition\n$`=G_{3}F_{3}`$\n\nNow the second one.\n\n$`\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)GF`$\n\nUnlike previous one, we cannot directly apply the $`(\\alpha' \\circ \\alpha)`$ to $`GF`$. No problem. We can composite horizontally with $`id:G\\rightarrow G`$\n\n$`=\\left( \\beta '\\circ \\beta\\right) \\cdot \\left( \\left( id\\cdot \\alpha '\\right) \\circ \\left( id\\cdot \\alpha \\right) \\right)GF`$\n$`=\\left( \\beta'\\circ \\beta\\right)GF_3`$\n\nSame way but this time with $`id:F\\rightarrow F`$\n\n$`=\\left( \\left( \\beta'\\cdot id\\right) \\circ \\left( \\beta \\cdot id\\right) \\right) GF_{3}`$\n$`=G_3 F_3`$\n\n\nProooven BAMMM"
+ },
+ "_time": "2020-10-18T14:48:51+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Write a short essay about how you may enjoy writing down the evident diagrams needed to prove the interchange law.\n"
+ },
+ "_time": "2019-12-19T21:51:08+09:00"
+ }
+ },
+ "_hash": "ebe52014d6aaf02a4ddeaa7de59a014eac6634b8",
+ "_pageTitle": "5"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "54c6ff9a213b80284619a5afb7859d9d30a444bc",
+ "_pageTitle": "10. Natural Transformations"
+ },
+ "_parentHash": "b614f31d04b3bc2b3d23ee4337475251429e5a9f"
+}
\ No newline at end of file
diff --git a/test/expect/pages/6c11e3a31b4b8bd07bdef3f87887ab202a568679.json b/test/expect/pages/6c11e3a31b4b8bd07bdef3f87887ab202a568679.json
index dc076fd..7694f25 100644
--- a/test/expect/pages/6c11e3a31b4b8bd07bdef3f87887ab202a568679.json
+++ b/test/expect/pages/6c11e3a31b4b8bd07bdef3f87887ab202a568679.json
@@ -1 +1,17 @@
-{"_pageContent":{"_pageTitle":"5. Continuous Mappings","_hash":"6c11e3a31b4b8bd07bdef3f87887ab202a568679","_attributes":{},"_answers":1},"_parentHash":"4c1513c92422dc16b3c5f13bd03d34ba0feeb6df","_childPageContents":[{"_pageTitle":"5.1 Continuous Mappings","_hash":"2125c437aaac928d5852224ff00a83f9d9776dda","_attributes":{},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "2125c437aaac928d5852224ff00a83f9d9776dda",
+ "_pageTitle": "5.1 Continuous Mappings"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "6c11e3a31b4b8bd07bdef3f87887ab202a568679",
+ "_pageTitle": "5. Continuous Mappings"
+ },
+ "_parentHash": "4c1513c92422dc16b3c5f13bd03d34ba0feeb6df"
+}
\ No newline at end of file
diff --git a/test/expect/pages/96d719cd412e4a8d03ac3d84cb0e8858f68ec008.json b/test/expect/pages/96d719cd412e4a8d03ac3d84cb0e8858f68ec008.json
index 578dc2e..6750bcb 100644
--- a/test/expect/pages/96d719cd412e4a8d03ac3d84cb0e8858f68ec008.json
+++ b/test/expect/pages/96d719cd412e4a8d03ac3d84cb0e8858f68ec008.json
@@ -1 +1,24 @@
-{"_pageContent":{"_pageTitle":"answers-db","_hash":"96d719cd412e4a8d03ac3d84cb0e8858f68ec008","_attributes":{},"_answers":3},"_parentHash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","_childPageContents":[{"_pageTitle":"books","_hash":"2aed5404c83f7a46aa249e0a6328af756b19d513","_attributes":{"q.md":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."}}},"_answers":3}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 3,
+ "_attributes": {
+ "q.md": {
+ "_attributeFile": {
+ "_content": "# DON'T PANIC\n\nAlthough it has many omissions and contains much that is apocryphal, or at least wildly inaccurate, but it scores over the other answers over the internet in few important respects. First, the way it is written is very subjective, and second, it has the words DON'T PANIC inscribed in large friendly letters on its home."
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "2aed5404c83f7a46aa249e0a6328af756b19d513",
+ "_pageTitle": "books"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 3,
+ "_attributes": {},
+ "_hash": "96d719cd412e4a8d03ac3d84cb0e8858f68ec008",
+ "_pageTitle": "answers-db"
+ },
+ "_parentHash": "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+}
\ No newline at end of file
diff --git a/test/expect/pages/b018428b6f02cdaa399c4860f9e7808d9ffc9d16.json b/test/expect/pages/b018428b6f02cdaa399c4860f9e7808d9ffc9d16.json
new file mode 100644
index 0000000..5e8ac0f
--- /dev/null
+++ b/test/expect/pages/b018428b6f02cdaa399c4860f9e7808d9ffc9d16.json
@@ -0,0 +1,17 @@
+{
+ "_childPageContents": [],
+ "_pageContent": {
+ "_answers": 0,
+ "_attributes": {
+ "q.md": {
+ "_attributeFile": {
+ "_content": "You need to measure the mass $`M`$ of a $`4.00 \\text{ m}`$-long bar. The bar has a square cross section but has some holes drilled along its length, so you suspect that its center of gravity isn't in the middle of the bar. The bar is too long for you to weigh on your scale. So, first you balance the bar on a knife-edge pivot and determine that the bar's center of gravity is $`1.88 \\text{ m}`$ from its left-hand end. You then place the bar on the pivot so that the point of support is $`1.50 \\text{ m}`$ from the left-hand end of the bar. Next you suspend a $`2.00 \\text{ kg}`$ mass ($`m_1`$) from the bar at a point $`0.200 \\text{ m}`$ from the left-hand end. Finally, you suspend a mass $`m_2 = 1.00 \\text{ kg}`$ from the bar at a distance $`x`$ from the left-hand end and adjust $`x`$ so that the bar is balanced. You repeat this step for other values of $`m_2`$ and record each corresponding value of $`x`$. The table gives your results.\n\n| $`m_2`$ (kg) | 1.00 | 1.50 | 2.00 | 2.50 | 3.00 | 4.00 |\n| :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n| $`x`$ (m) | 3.50 | 2.83 | 2.50 | 2.32 | 2.16 | 2.00 |\n\n(a) Draw a free-body diagram for the bar when $`m_1`$ and $`m_2`$ are suspended from it.\n(b) Apply the static equilibrium equation $`\\Sigma\\tau_z = 0`$ with the axis at the location of the knife-edge pivot. Solve the equation for $`x`$ as a function of $`m_2`$.\n(c) Plot $`x`$ versus $`1/m_2`$. Use the slope of the best-fit straight line and the equation you derived in part (b) to calculate that bar’s mass $`M`$. Use $`g = 9.80 \\text{ m/s}^2`$.\n(d) What is the y-intercept of the straight line that fits the data? Explain why it has this value."
+ },
+ "_time": "2025-09-22T11:03:36+09:00"
+ }
+ },
+ "_hash": "b018428b6f02cdaa399c4860f9e7808d9ffc9d16",
+ "_pageTitle": "11.87"
+ },
+ "_parentHash": "ba45627f4d5702a2d14eaced7927fd3798c60167"
+}
\ No newline at end of file
diff --git a/test/expect/pages/b2a3b650e20b0d34511030969274674aaf259e55.json b/test/expect/pages/b2a3b650e20b0d34511030969274674aaf259e55.json
index 149a224..bc73701 100644
--- a/test/expect/pages/b2a3b650e20b0d34511030969274674aaf259e55.json
+++ b/test/expect/pages/b2a3b650e20b0d34511030969274674aaf259e55.json
@@ -1 +1,30 @@
-{"_pageContent":{"_pageTitle":"24. F-Algebras","_hash":"b2a3b650e20b0d34511030969274674aaf259e55","_attributes":{},"_answers":1},"_parentHash":"b614f31d04b3bc2b3d23ee4337475251429e5a9f","_childPageContents":[{"_pageTitle":"5","_hash":"c4ad78b11338182da6d1e6b792987c7319ef55d8","_attributes":{"a.md":{"_time":"2020-01-30T14:24:00+09:00","_attributeFile":{"_content":"```haskell\nmodule Un\n ( primes\n ) where\n\nimport Data.List\n\nprimes :: [Int]\nprimes = unfoldr (\\(prime:remains) -> Just (prime, filter (notdiv prime) remains)) [2..]\n where notdiv p n = n `mod` p /= 0\n```"}},"q.md":{"_time":"2020-01-30T14:24:00+09:00","_attributeFile":{"_content":"Use `unfoldr` to generate a list of the first n primes."}}},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "```haskell\nmodule Un\n ( primes\n ) where\n\nimport Data.List\n\nprimes :: [Int]\nprimes = unfoldr (\\(prime:remains) -> Just (prime, filter (notdiv prime) remains)) [2..]\n where notdiv p n = n `mod` p /= 0\n```"
+ },
+ "_time": "2020-01-30T14:24:00+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Use `unfoldr` to generate a list of the first n primes."
+ },
+ "_time": "2020-01-30T14:24:00+09:00"
+ }
+ },
+ "_hash": "c4ad78b11338182da6d1e6b792987c7319ef55d8",
+ "_pageTitle": "5"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "b2a3b650e20b0d34511030969274674aaf259e55",
+ "_pageTitle": "24. F-Algebras"
+ },
+ "_parentHash": "b614f31d04b3bc2b3d23ee4337475251429e5a9f"
+}
\ No newline at end of file
diff --git a/test/expect/pages/b614f31d04b3bc2b3d23ee4337475251429e5a9f.json b/test/expect/pages/b614f31d04b3bc2b3d23ee4337475251429e5a9f.json
index 543acb4..1aeedc2 100644
--- a/test/expect/pages/b614f31d04b3bc2b3d23ee4337475251429e5a9f.json
+++ b/test/expect/pages/b614f31d04b3bc2b3d23ee4337475251429e5a9f.json
@@ -1 +1,30 @@
-{"_pageContent":{"_pageTitle":"Category Theory For Programmers","_hash":"b614f31d04b3bc2b3d23ee4337475251429e5a9f","_attributes":{"author.txt":{"_time":"2022-09-25T22:34:28+09:00","_attributeFile":{"_content":"Bartosz Milewski"}}},"_answers":2},"_parentHash":"2aed5404c83f7a46aa249e0a6328af756b19d513","_childPageContents":[{"_pageTitle":"10. Natural Transformations","_hash":"54c6ff9a213b80284619a5afb7859d9d30a444bc","_attributes":{},"_answers":1},{"_pageTitle":"24. F-Algebras","_hash":"b2a3b650e20b0d34511030969274674aaf259e55","_attributes":{},"_answers":1}]}
\ No newline at end of file
+{
+ "_childPageContents": [
+ {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "54c6ff9a213b80284619a5afb7859d9d30a444bc",
+ "_pageTitle": "10. Natural Transformations"
+ },
+ {
+ "_answers": 1,
+ "_attributes": {},
+ "_hash": "b2a3b650e20b0d34511030969274674aaf259e55",
+ "_pageTitle": "24. F-Algebras"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 2,
+ "_attributes": {
+ "author.txt": {
+ "_attributeFile": {
+ "_content": "Bartosz Milewski"
+ },
+ "_time": "2022-09-25T22:34:28+09:00"
+ }
+ },
+ "_hash": "b614f31d04b3bc2b3d23ee4337475251429e5a9f",
+ "_pageTitle": "Category Theory For Programmers"
+ },
+ "_parentHash": "2aed5404c83f7a46aa249e0a6328af756b19d513"
+}
\ No newline at end of file
diff --git a/test/expect/pages/ba45627f4d5702a2d14eaced7927fd3798c60167.json b/test/expect/pages/ba45627f4d5702a2d14eaced7927fd3798c60167.json
new file mode 100644
index 0000000..6b0c670
--- /dev/null
+++ b/test/expect/pages/ba45627f4d5702a2d14eaced7927fd3798c60167.json
@@ -0,0 +1,24 @@
+{
+ "_childPageContents": [
+ {
+ "_answers": 0,
+ "_attributes": {
+ "q.md": {
+ "_attributeFile": {
+ "_content": "You need to measure the mass $`M`$ of a $`4.00 \\text{ m}`$-long bar. The bar has a square cross section but has some holes drilled along its length, so you suspect that its center of gravity isn't in the middle of the bar. The bar is too long for you to weigh on your scale. So, first you balance the bar on a knife-edge pivot and determine that the bar's center of gravity is $`1.88 \\text{ m}`$ from its left-hand end. You then place the bar on the pivot so that the point of support is $`1.50 \\text{ m}`$ from the left-hand end of the bar. Next you suspend a $`2.00 \\text{ kg}`$ mass ($`m_1`$) from the bar at a point $`0.200 \\text{ m}`$ from the left-hand end. Finally, you suspend a mass $`m_2 = 1.00 \\text{ kg}`$ from the bar at a distance $`x`$ from the left-hand end and adjust $`x`$ so that the bar is balanced. You repeat this step for other values of $`m_2`$ and record each corresponding value of $`x`$. The table gives your results.\n\n| $`m_2`$ (kg) | 1.00 | 1.50 | 2.00 | 2.50 | 3.00 | 4.00 |\n| :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n| $`x`$ (m) | 3.50 | 2.83 | 2.50 | 2.32 | 2.16 | 2.00 |\n\n(a) Draw a free-body diagram for the bar when $`m_1`$ and $`m_2`$ are suspended from it.\n(b) Apply the static equilibrium equation $`\\Sigma\\tau_z = 0`$ with the axis at the location of the knife-edge pivot. Solve the equation for $`x`$ as a function of $`m_2`$.\n(c) Plot $`x`$ versus $`1/m_2`$. Use the slope of the best-fit straight line and the equation you derived in part (b) to calculate that bar’s mass $`M`$. Use $`g = 9.80 \\text{ m/s}^2`$.\n(d) What is the y-intercept of the straight line that fits the data? Explain why it has this value."
+ },
+ "_time": "2025-09-22T11:03:36+09:00"
+ }
+ },
+ "_hash": "b018428b6f02cdaa399c4860f9e7808d9ffc9d16",
+ "_pageTitle": "11.87"
+ }
+ ],
+ "_pageContent": {
+ "_answers": 0,
+ "_attributes": {},
+ "_hash": "ba45627f4d5702a2d14eaced7927fd3798c60167",
+ "_pageTitle": "11. Equilibrium and Elasticity"
+ },
+ "_parentHash": "0af5b76897bd59b64b3d577b459c75a1b5b6b3ca"
+}
\ No newline at end of file
diff --git a/test/expect/pages/c4ad78b11338182da6d1e6b792987c7319ef55d8.json b/test/expect/pages/c4ad78b11338182da6d1e6b792987c7319ef55d8.json
index 8960738..317be6a 100644
--- a/test/expect/pages/c4ad78b11338182da6d1e6b792987c7319ef55d8.json
+++ b/test/expect/pages/c4ad78b11338182da6d1e6b792987c7319ef55d8.json
@@ -1 +1,23 @@
-{"_pageContent":{"_pageTitle":"5","_hash":"c4ad78b11338182da6d1e6b792987c7319ef55d8","_attributes":{"a.md":{"_time":"2020-01-30T14:24:00+09:00","_attributeFile":{"_content":"```haskell\nmodule Un\n ( primes\n ) where\n\nimport Data.List\n\nprimes :: [Int]\nprimes = unfoldr (\\(prime:remains) -> Just (prime, filter (notdiv prime) remains)) [2..]\n where notdiv p n = n `mod` p /= 0\n```"}},"q.md":{"_time":"2020-01-30T14:24:00+09:00","_attributeFile":{"_content":"Use `unfoldr` to generate a list of the first n primes."}}},"_answers":1},"_parentHash":"b2a3b650e20b0d34511030969274674aaf259e55","_childPageContents":[]}
\ No newline at end of file
+{
+ "_childPageContents": [],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "```haskell\nmodule Un\n ( primes\n ) where\n\nimport Data.List\n\nprimes :: [Int]\nprimes = unfoldr (\\(prime:remains) -> Just (prime, filter (notdiv prime) remains)) [2..]\n where notdiv p n = n `mod` p /= 0\n```"
+ },
+ "_time": "2020-01-30T14:24:00+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Use `unfoldr` to generate a list of the first n primes."
+ },
+ "_time": "2020-01-30T14:24:00+09:00"
+ }
+ },
+ "_hash": "c4ad78b11338182da6d1e6b792987c7319ef55d8",
+ "_pageTitle": "5"
+ },
+ "_parentHash": "b2a3b650e20b0d34511030969274674aaf259e55"
+}
\ No newline at end of file
diff --git a/test/expect/pages/ebe52014d6aaf02a4ddeaa7de59a014eac6634b8.json b/test/expect/pages/ebe52014d6aaf02a4ddeaa7de59a014eac6634b8.json
index e3d1729..ccb9930 100644
--- a/test/expect/pages/ebe52014d6aaf02a4ddeaa7de59a014eac6634b8.json
+++ b/test/expect/pages/ebe52014d6aaf02a4ddeaa7de59a014eac6634b8.json
@@ -1 +1,23 @@
-{"_pageContent":{"_pageTitle":"5","_hash":"ebe52014d6aaf02a4ddeaa7de59a014eac6634b8","_attributes":{"a.md":{"_time":"2020-10-18T14:48:51+09:00","_attributeFile":{"_content":"Let's proove interchange law.\n\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right) =\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)`$\n\n\nLet's say each natural transformation is defined as\n\n\n$`\\alpha :F\\rightarrow F_{2}`$\n$`\\alpha ':F_{2}\\rightarrow F_{3}`$\n$`\\beta :G\\rightarrow G_{2}`$\n$`\\beta ':G_{2}\\rightarrow G_{3}`$\n\n\n\n\nLet's see what we get with first one.\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right)GF`$ \n\nUsing horizontal composition\n$`=\\left( \\beta'\\cdot \\alpha '\\right) G_{2}F_{2}`$\nAgain, using horizontal composition\n$`=G_{3}F_{3}`$\n\nNow the second one.\n\n$`\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)GF`$\n\nUnlike previous one, we cannot directly apply the $`(\\alpha' \\circ \\alpha)`$ to $`GF`$. No problem. We can composite horizontally with $`id:G\\rightarrow G`$\n\n$`=\\left( \\beta '\\circ \\beta\\right) \\cdot \\left( \\left( id\\cdot \\alpha '\\right) \\circ \\left( id\\cdot \\alpha \\right) \\right)GF`$\n$`=\\left( \\beta'\\circ \\beta\\right)GF_3`$\n\nSame way but this time with $`id:F\\rightarrow F`$\n\n$`=\\left( \\left( \\beta'\\cdot id\\right) \\circ \\left( \\beta \\cdot id\\right) \\right) GF_{3}`$\n$`=G_3 F_3`$\n\n\nProooven BAMMM"}},"q.md":{"_time":"2019-12-19T21:51:08+09:00","_attributeFile":{"_content":"Write a short essay about how you may enjoy writing down the evident diagrams needed to prove the interchange law.\n"}}},"_answers":1},"_parentHash":"54c6ff9a213b80284619a5afb7859d9d30a444bc","_childPageContents":[]}
\ No newline at end of file
+{
+ "_childPageContents": [],
+ "_pageContent": {
+ "_answers": 1,
+ "_attributes": {
+ "a.md": {
+ "_attributeFile": {
+ "_content": "Let's proove interchange law.\n\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right) =\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)`$\n\n\nLet's say each natural transformation is defined as\n\n\n$`\\alpha :F\\rightarrow F_{2}`$\n$`\\alpha ':F_{2}\\rightarrow F_{3}`$\n$`\\beta :G\\rightarrow G_{2}`$\n$`\\beta ':G_{2}\\rightarrow G_{3}`$\n\n\n\n\nLet's see what we get with first one.\n\n$`\\left( \\beta '\\cdot \\alpha '\\right) \\circ \\left( \\beta \\cdot \\alpha \\right)GF`$ \n\nUsing horizontal composition\n$`=\\left( \\beta'\\cdot \\alpha '\\right) G_{2}F_{2}`$\nAgain, using horizontal composition\n$`=G_{3}F_{3}`$\n\nNow the second one.\n\n$`\\left( \\beta '\\circ \\beta \\right) \\cdot \\left( \\alpha '\\circ \\alpha \\right)GF`$\n\nUnlike previous one, we cannot directly apply the $`(\\alpha' \\circ \\alpha)`$ to $`GF`$. No problem. We can composite horizontally with $`id:G\\rightarrow G`$\n\n$`=\\left( \\beta '\\circ \\beta\\right) \\cdot \\left( \\left( id\\cdot \\alpha '\\right) \\circ \\left( id\\cdot \\alpha \\right) \\right)GF`$\n$`=\\left( \\beta'\\circ \\beta\\right)GF_3`$\n\nSame way but this time with $`id:F\\rightarrow F`$\n\n$`=\\left( \\left( \\beta'\\cdot id\\right) \\circ \\left( \\beta \\cdot id\\right) \\right) GF_{3}`$\n$`=G_3 F_3`$\n\n\nProooven BAMMM"
+ },
+ "_time": "2020-10-18T14:48:51+09:00"
+ },
+ "q.md": {
+ "_attributeFile": {
+ "_content": "Write a short essay about how you may enjoy writing down the evident diagrams needed to prove the interchange law.\n"
+ },
+ "_time": "2019-12-19T21:51:08+09:00"
+ }
+ },
+ "_hash": "ebe52014d6aaf02a4ddeaa7de59a014eac6634b8",
+ "_pageTitle": "5"
+ },
+ "_parentHash": "54c6ff9a213b80284619a5afb7859d9d30a444bc"
+}
\ No newline at end of file
diff --git a/test/expect/resources/b018428b6f02cdaa399c4860f9e7808d9ffc9d16/q.md.html b/test/expect/resources/b018428b6f02cdaa399c4860f9e7808d9ffc9d16/q.md.html
new file mode 100644
index 0000000..f737803
--- /dev/null
+++ b/test/expect/resources/b018428b6f02cdaa399c4860f9e7808d9ffc9d16/q.md.html
@@ -0,0 +1,6 @@
+You need to measure the mass $`M`$ of a $`4.00 \text{ m}`$-long bar. The bar has a square cross section but has some holes drilled along its length, so you suspect that its center of gravity isn't in the middle of the bar. The bar is too long for you to weigh on your scale. So, first you balance the bar on a knife-edge pivot and determine that the bar's center of gravity is $`1.88 \text{ m}`$ from its left-hand end. You then place the bar on the pivot so that the point of support is $`1.50 \text{ m}`$ from the left-hand end of the bar. Next you suspend a $`2.00 \text{ kg}`$ mass ($`m_1`$) from the bar at a point $`0.200 \text{ m}`$ from the left-hand end. Finally, you suspend a mass $`m_2 = 1.00 \text{ kg}`$ from the bar at a distance $`x`$ from the left-hand end and adjust $`x`$ so that the bar is balanced. You repeat this step for other values of $`m_2`$ and record each corresponding value of $`x`$. The table gives your results.
+some table
+(a) Draw a free-body diagram for the bar when $`m_1`$ and $`m_2`$ are suspended from it.
+(b) Apply the static equilibrium equation $`\Sigma\tau_z = 0`$ with the axis at the location of the knife-edge pivot. Solve the equation for $`x`$ as a function of $`m_2`$.
+(c) Plot $`x`$ versus $`1/m_2`$. Use the slope of the best-fit straight line and the equation you derived in part (b) to calculate that bar’s mass $`M`$. Use $`g = 9.80 \text{ m/s}^2`$.
+(d) What is the y-intercept of the straight line that fits the data? Explain why it has this value.
diff --git a/test/matlab-expect/11.31/a.md b/test/matlab-expect/11.31/a.md
index 0a4efaa..f3aabde 100644
--- a/test/matlab-expect/11.31/a.md
+++ b/test/matlab-expect/11.31/a.md
@@ -4,3 +4,10 @@ code
in\_pascal =
$`\displaystyle 205000000`$
+
+| Start with *H* | Start with *T* |
+| -------------- | -------------- |
+| HH | THH |
+| HT HH | TH THH |
+| HT HT HH | TH TH THH |
+| ... | ... |
diff --git a/test/matlab-short.md b/test/matlab-short.md
index e34f951..fb35b70 100644
--- a/test/matlab-short.md
+++ b/test/matlab-short.md
@@ -51,3 +51,9 @@ code
in_pascal =
$\displaystyle 205000000$
+| Start with *H* | Start with *T* |
+| -------------- | -------------- |
+| HH | THH |
+| HT HH | TH THH |
+| HT HT HH | TH TH THH |
+| ... | ... |
\ No newline at end of file