Posts tagged meta
I recently added equation numbering to my blog. To see it in action, here's one of my homework exercises. I like this problem because I actually found it useful to diagonalize to exponentiate the matrix.
Let $\{X_n\}$ be the random walk on the space $\{0,1,2,3,4\},$ with $0$ and $4$ absorbing and with $p(i,i) = 1/2,$ $p(i,i+1) = p(i,i-1)= 1/4$ for $1 \leq i \leq 3.$ Let $\tau$ denote the absorption time. The graph can be seen in the title picture.
Part A
What is the limit as $n \rightarrow \infty$ of the law $X_n$ conditioned on non-absorption, that is, $$\lim_{n \rightarrow \infty}\mathcal{L}(X_n \mid \tau > n)?$$
Solution
We have that $\displaystyle \boxed{ \lim_{n \rightarrow \infty}\mathcal{L}\left(X_n \mid \tau > n\right) = \begin{cases} 1 - \frac{1}{\sqrt{2}}, &X_n \in \{1,3\} \\ \sqrt{2} - 1, &X_n = 2. \end{cases}}$
To see this, without loss of generality, let $X_0 = 2.$ We can do this since if $X_0 = 1$ or $X_0 = 3,$ then $\mathbb{P}(X_m = X_0,~\forall m \leq n \mid \tau > n) \rightarrow 0$ as $n \rightarrow \infty.$ Then, we can apply the Markov property.
Now, define $p_n^*(x,y) = \mathbb{P}(X_{n} = y \mid \tau > n,~X_0 = 2).$
We have that
\begin{equation}
p^*_1(2,y) = \begin{cases}
1/4, &y \in \{1,3\} \\
1/2, &y = 2.
\end{cases}
~\text{and, in general,}~
p_n^*(2,y) = \begin{cases}
a_n, &y \in \{1,3\} \\
b_n = 1 - 2a_n, &y = 2
\end{cases}
\label{eqn:3a_a_def}
\end{equation}
by symmetry, where $a_1 = 1/4$ and $b_1 = 1/2 = 1 - 2a_1.$
Now, we have that
\begin{align}
a_{n+1} = \mathbb{P}(X_{n+1} = 1 \mid \tau > n + 1,~X_0 = 2)
&= p_{n+1}^*(2,1) \nonumber\\
&= \frac{p_n^*(2,1)p(1,1) + p_n^*(2,2)p(2,1)}{\mathbb{P}(\tau > n +
1 \mid X_0 = 2, \tau > n)} \nonumber\\
&= \frac{p_n^*(2,1)p(1,1) + p_n^*(2,2)p(2,1)}{1 -
p_n^*(2,1)p(1,0) - p_n^*(2,3)p(3,4)} \nonumber\\
&= \frac{a_n(1/2) + b_n(1/4)}{1 - (2a_n/4)} \nonumber\\
&= \frac{a_n(1/2) + (1-2a_n)(1/4)}{(4 - 2a_n)/4} \nonumber\\
&= \frac{1}{4 - 2a_n}.
\label{eqn:3a_a_recurs}
\end{align}
$a_n$ converges by induction because
$$
|a_{n+1} - a_n| = \left|\frac{1}{4-2a_n} -
\frac{1}{4-a_{n-1}}\right|
= \left|\frac{4 - 2a_{n-1} - 4 +
2a_n}{(4-2a_{n})(4-2a_{n-1})}\right|
\leq \frac{1}{2}\left|a_n-a_{n-1}\right|
$$
since $0 \leq a_n \leq 1$ for all $n.$ Solving for $a$ in
\begin{equation}
a = \frac{1}{4-2a} \Rightarrow 2a^2 - 4a + 1 = 0 \Rightarrow
a = 1 \pm \frac{1}{\sqrt{2}}.
\label{eqn:3a_a}
\end{equation}
Thus, we must have that $a_n \rightarrow 1 - \frac{1}{\sqrt{2}}$
since $a_n \leq 1$ for all $n.$ Then, we have that $b_n
\rightarrow b = 1 - 2a = \sqrt{2} - 1.$
Part B
What is the limiting distribution conditioned on never getting absorbed, that is, $$ \lim_{n \rightarrow \infty}\lim_{M \rightarrow \infty} \mathcal{L}(X_n \mid \tau > M)? $$
Solution
We have that $\displaystyle\boxed{\lim_{n \rightarrow \infty}\lim_{M \rightarrow \infty}\mathcal{L}(X_n \mid \tau > M)= \begin{cases}1/4, &X_n \in \{1,3\}\\ 1/2, &X_n = 2.\end{cases}}$
Again, we can assume without loss of generality that $X_0 = 2.$ Fix $n \in \mathbb{N}.$ From Equation \ref{eqn:3a_a_def} in the previous part, we know $\mathbb{P}(X_n = 2\mid \tau > n) = 1-2a_{n},$ where we define $a_0 = 0.$ Now, suppose, we know $\mathbb{P}(X_n = 2 \mid \tau > M)$ for $M \geq n.$ Then, by Bayes' theorem, \begin{align} \mathbb{P}(X_n = 2 \mid \tau > M + 1) &= \mathbb{P}(X_n = 2 \mid \tau > M + 1, \tau > M) \nonumber\\ &= \frac{\mathbb{P}(\tau > M + 1 \mid X_n = 2, \tau > M)\mathbb{P}(X_n = 2 \mid \tau > M)}{\mathbb{P}(\tau > M + 1 \mid \tau > M)}. \label{eqn:3b_bayes} \end{align} Calculating the two unknown factors, we have that in the denominator, \begin{align} \mathbb{P}(\tau > M + 1 \mid \tau > M) &= \sum_{x=1}^3 \mathbb{P}(\tau > M + 1, X_M = x \mid \tau > M) \nonumber\\ &= \frac{3}{4}\left(\mathbb{P}(X_M = 1 \mid \tau > M) + \mathbb{P}(X_M = 3 \mid \tau > M)\right) + \mathbb{P}(X_M = 2 \mid \tau > M)\nonumber\\ &= \frac{3}{4}\left(a_{M} + a_{M}\right) + (1-2a_{M}) \nonumber\\ &= 1 - \frac{1}{2}a_{M}, \label{eqn:3b_tau} \end{align} and in the numerator, \begin{align} \mathbb{P}(\tau > M + 1 \mid X_n = 2, \tau > M) &= \sum_{x=1}^3\mathbb{P}(\tau > M + 1, X_M = x \mid X_n = 2, \tau > M)\nonumber\\ &= \sum_{x=1}^3\mathbb{P}(\tau > M + 1, X_M = x \mid X_n = 2, \tau > M)\nonumber\\ &= \frac{3}{2}\mathbb{P}(X_M = 1 \mid X_n = 2, \tau > M) + \mathbb{P}(X_M = 2 \mid X_n = 2, \tau > M) \nonumber\\ &= \frac{3}{2}a_{M - n} + 1 - 2a_{M - n} \nonumber\\ &= 1 - \frac{1}{2}a_{M - n}, \label{eqn:3b_bayes_num} \end{align} where we use that $\mathbb{P}(\tau > M + 1, X_M = 1 \mid X_n = 2, \tau > M) = \mathbb{P}(\tau > M + 1, X_M = 3 \mid X_n = 2, \tau > M),$ and the fact that $\mathbb{P}(X_M = x \mid X_n = 2, \tau > M)$ is the probability of a chain starting at $2$ that doesn't hit an absorbing state in $M - n$ transistions.
Putting together Equations \ref{eqn:3b_bayes_num}, \ref{eqn:3b_tau}, and \ref{eqn:3b_bayes}, we have that \begin{equation*} \mathbb{P}(X_n = 2 \mid \tau > M + 1) = \frac{1 - \frac{1}{2}a_{M-n}}{1 - \frac{1}{2}a_{M}}\mathbb{P}(X_n = 2 \mid \tau > M), \end{equation*} and by induction, we'll have that \begin{equation*} \mathbb{P}(X_n = 2 \mid \tau > M) = (1 - 2a_{n}) \prod_{k=n}^{M - 1} \frac{1 - \frac{1}{2}a_{k-n}}{1 - \frac{1}{2}a_{k}}, \end{equation*} where the product is just $1 - 2a_{n}$ if $M = n.$ For large enough $M,$ when $k \geq 2n$ the factors in the numerator will cancel, so we will have that for $M \geq 2n$ that \begin{align} \mathbb{P}(X_n = 2 \mid \tau > M) &= (1 - 2a_{n}) \prod_{k=0}^{n - 1}\left(1-\frac{1}{2}a_{k}\right) \prod_{k=M - n}^{M-1}\frac{1}{1-\frac{1}{2}a_{k}} \nonumber\\ &= (1 - 2a_{n}) \prod_{k=0}^{n - 1}\left(4-2a_{k}\right) \prod_{k=M - n}^{M-1}\frac{1}{4-2a_{k}} \nonumber\\ &= (1 - 2a_{n}) \prod_{k=1}^{n}\frac{1}{a_{k}} \prod_{k=M - n + 1}^{M}a_k \label{eqn:3b_p2} \end{align} by the recursive definition of $a_n$ in Equation \ref{eqn:3a_a_recurs}.
Taking the limit as $M \rightarrow \infty,$ we have that \begin{equation} \lim_{M \rightarrow \infty} \mathbb{P}(X_n = 2 \mid \tau > M) = (1 - 2a_{n})a^{n}\prod_{k=1}^{n}\frac{1}{a_{k}}. \label{eqn:3b_p_lim} \end{equation} by Equation \ref{eqn:3a_a}, where $\displaystyle a = 1 - \frac{1}{\sqrt{2}} = \frac{1}{2 + \sqrt{2}}.$
Define $c_{-1} = 0$ and $c_0 = 1/4,$ so $a_0 = 0 = \frac{c_{-1}}{c_0}.$ In general, we can write $a_n = \frac{c_{n-1}}{c_n},$ where $c_n = -2c_{n-2} + 4c_{n-1}$ for $k \geq 1.$ To see this, by definition of $a_n$ in Equation \ref{eqn:3a_a_recurs}, \begin{equation} a_{n + 1} = \frac{1}{4 - 2a_n} = \frac{1}{4 - 2\frac{c_{n-1}}{c_{n}}} = \frac{c_n}{-2c_{n-1} + 4c_n} = \frac{c_n}{c_{n+1}}. \label{eqn:3b_a_c} \end{equation}
Now, with Equation \ref{eqn:3b_a_c}, Equation \ref{eqn:3b_p_lim} becomes \begin{equation} \lim_{M \rightarrow \infty} \mathbb{P}(X_n = 2 \mid \tau > M) = (1 - 2a_{n})a^{n}\prod_{k=1}^{n}\frac{c_k}{c_{k-1}} = (1 - 2a_{n})a^{n}\frac{c_{n}}{c_0} \label{eqn:3b_p_lim_2} \end{equation}
Note, that we can rewrite $c_n$ by exponentiating a matrix: \begin{align} \begin{pmatrix} c_{n} \\ c_{n + 1} \end{pmatrix} &= \begin{pmatrix} 0 & 1 \\ -2 & 4 \end{pmatrix}^n \begin{pmatrix} c_0 \\ c_1 \end{pmatrix} \nonumber\\ &= \begin{pmatrix} 1 & 1 \\ 2 + \sqrt{2} & 2 - \sqrt{2} \end{pmatrix} \begin{pmatrix} 2 + \sqrt{2} & 0 \\ 0 & 2 - \sqrt{2} \end{pmatrix}^n \begin{pmatrix} \frac{1}{2}-\frac{1}{\sqrt{2}} & \frac{1}{2\sqrt{2}} \\ \frac{1}{2}+\frac{1}{\sqrt{2}} & -\frac{1}{2\sqrt{2}} \end{pmatrix} \begin{pmatrix} 1/4 \\ 1 \end{pmatrix} \nonumber\\ &= \begin{pmatrix} \frac{1}{8}\left((2+\sqrt{2})^n(1+\sqrt{2}) + (2-\sqrt{2})^n(1-\sqrt{2})\right) \\ \frac{1}{8}\left((2+\sqrt{2})^{n+1}(1+\sqrt{2}) + (2-\sqrt{2})^{n+1}(1-\sqrt{2})\right) \end{pmatrix} \label{eqn:3b_c_matrix} \end{align} by diagonalization. Using Equation \ref{eqn:3b_c_matrix}, Equation \ref{eqn:3b_p_lim_2} becomes \begin{equation} \lim_{M \rightarrow \infty} \mathbb{P}(X_n = 2 \mid \tau > M) = \frac{1}{2}(1 - 2a_{n})\left((1+\sqrt{2}) + \left(\frac{2-\sqrt{2}}{2+\sqrt{2}}\right)^n(1-\sqrt{2})\right), \label{eqn:3b_p_lim_3} \end{equation} where we recall that $a = \frac{1}{2 + \sqrt{2}}.$ $|2-\sqrt{2}| < 1$ and so, taking the limit as $n \rightarrow \infty$ of Equation \ref{eqn:3b_p_lim_3}, the second term goes to $0$: \begin{equation} \lim_{n\rightarrow 0}\lim_{M \rightarrow \infty} \mathbb{P}(X_n = 2 \mid \tau > M) = \frac{1-2a}{2}(1+\sqrt{2}) = \frac{\sqrt{2} - 1}{2}(1+\sqrt{2}) = \frac{1}{2}. \label{eqn:3b_p_lim_lim} \end{equation} And finally, by symmetry, \begin{align} \lim_{n\rightarrow 0}\lim_{M \rightarrow \infty} \mathbb{P}(X_n = 1 \mid \tau > M) &= \lim_{n\rightarrow 0}\lim_{M \rightarrow \infty} \mathbb{P}(X_n = 3 \mid \tau > M) \nonumber\\ &= \frac{1 - \lim_{n\rightarrow 0}\lim_{M \rightarrow \infty} \mathbb{P}(X_n = 2 \mid \tau > M)}{2} \nonumber\\ &= \frac{1}{4}. \label{eqn:3b_p_1_3} \end{align} Equations \ref{eqn:3b_p_lim_lim} and \ref{eqn:3b_p_1_3} combine to give us the limiting distribution.
Graphs in $\LaTeX$
Here's the code for the graph. I used a package called tikz
.
\documentclass[10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usetikzlibrary{arrows}
\usepackage[active, tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\renewcommand\PreviewBbAdjust {-0bp -25bp 0bp 25bp}
\begin{document}
\begin{tikzpicture}[->,>=stealth',shorten >=1pt,auto,node distance=1.75cm,
thick,main node/.style={circle,draw}]
\node[main node] (0) {$0$};
\node[main node] (1) [right of=0] {$1$};
\node[main node] (2) [right of=1] {$2$};
\node[main node] (3) [right of=2] {$3$};
\node[main node] (4) [right of=3] {$4$};
\path[every node/.style={font=\sffamily}]
(0) edge [loop above] node {$1$} (0)
(0)
(1) edge [bend right] node [below] {$1/4$} (2)
edge [loop above] node {$1/2$} (1)
edge node [below] {$1/4$} (0)
(1)
(2) edge [bend right] node [above] {$1/4$} (1)
edge [loop below] node {$1/2$} (2)
edge [bend left] node [above] {$1/4$} (3)
(2)
(3) edge [bend left] node [below] {$1/4$} (2)
edge [loop above] node {$1/2$} (3)
edge node [below] {$1/4$} (4)
(3)
(4) edge [loop above] node {$1$} (4);
\end{tikzpicture}
\end{document}
Then, to convert the graph to a png
file with a transparent background, I used ImageMagick.
convert -density 300 graph.pdf -format png graph.png
Now that I have a substantial number of posts and some good quality content, I often find the need to look back at prior posts. Unfortunately, this has consisted of paging through my blog or clicking on tags. To resolve this, I'm introducing the search feature. To visit the search page, hover over Blog
in the header, and click Search Posts.
Probably, the easiest and most effective way to implement search is to use Google Site Search, but I thought that it would be more fun to use PostgreSQL's full text search capabilities. Plus, I'd rather keep my site ad-free, and I don't want to pay. From the title picture, one can see that PostgreSQL full text search has lots of features like the ability to understand grammar, rank matches, and extract the relevant text.
Under the hood, posts are stored as tsvector
s.
phillypham::DATABASE=> SELECT setweight(to_tsvector('Post title'), 'A') || setweight(to_tsvector('The_quick_brown_fox_jumps_over_the_lazy_dog'), 'B') AS tsvector FROM posts LIMIT 1;
tsvector
-----------------------------------------------------------------------------------
'brown':5B 'dog':11B 'fox':6B 'jump':7B 'lazi':10B 'post':1A 'quick':4B 'titl':2A
(1 row)
As you can see, there's the ability to weight the title more heavily than the body.
Now, to make this work properly, I added another column to the posts
table. I indexed it and created a trigger to make sure that it's automatically updated. Then, to integrate properly with Sequelize, I added all these queries to an afterSync
hook.
=> ALTER TABLE posts ADD COLUMN title_body_tsvector tsvector;
=> UPDATE posts SET title_body_tsvector=setweight(to_tsvector('english', title), 'A') || setweight(to_tsvector('english', body), 'B');
=> CREATE INDEX IF NOT EXISTS title_body_search_idx ON posts USING gin(title_body_tsvector);
=> CREATE OR REPLACE FUNCTION posts_trigger() RETURNS trigger AS $$ begin new.title_body_tsvector := setweight(to_tsvector('english', new.title), 'A') || setweight(to_tsvector('english', new.body), 'B'); return new; end $$ LANGUAGE plpgsql;
=> CREATE TRIGGER posts_update BEFORE INSERT OR UPDATE ON posts FOR EACH ROW EXECUTE PROCEDURE posts_trigger();
See Post.js for more details.
Then, to do a search, I execute a raw query and return it as a promise. The query looks like this:
SELECT id,
title,
ts_rank_cd(title_body_tsvector,$QUERY,1) AS rank,
ts_headline('english', title || ' ' || body,$QUERY, 'MaxWords=100') AS headline
FROM posts
WHERE published AND title_body_tsvector @@ $QUERY
ORDER BY rank DESC, id DESC;
The Javascript looks like this:
function(db, tsquery) {
var query = tsquery.indexOf(' ') == -1 ? "to_tsquery('english','" + tsquery + "')" : "plainto_tsquery('english','" + tsquery + "')";
return db.sequelize.query("SELECT id, title, ts_rank_cd(title_body_tsvector," + query + ",1) AS rank, ts_headline('english', title || ' ' || body," + query + ", 'MaxWords=100') AS headline FROM posts WHERE published AND title_body_tsvector @@ " + query + " ORDER BY rank DESC, id DESC",
{ model: db.Post });
}
Note the ternary operator when defining query
. PostgreSQL tsquery
s allow searches of arbitrary complexity with nesting and various boolean operators. Unfortunately, most people will not have the syntax memorized, so plainto_tsquery
lets one search with more natural syntax. If you get the syntax wrong (e.g., forget to close a parentheses), the error will be handled gracefully thanks to promises.
Try it and let me know what you think!
Building on Tags and Pages, I've added one more navigation tool. Now, if you hover over Blog in the header, you'll get a drop down menu of some popular tags. Since I don't really have any readers, by popular, I mean what I think is important.
Implementation-wise, I store the list of tags that I want to display in Redis as a JSON array. I can edit that list by going to Application Settings without doing a redeploy.
For the front-end, there's no JavaScript. The HTML looks something like
<ul id="header-menu">
<li class="header-link blog"><a href="/" class="header-link">Blog</a>
<ul class="header-dropdown-menu">
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=math" class="header-dropdown-link header-link">Math</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=cooking" class="header-dropdown-link header-link">Cooking</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=algorithm" class="header-dropdown-link header-link">Algorithms</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=stat" class="header-dropdown-link header-link">Statistics</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=meta" class="header-dropdown-link header-link">Meta</a></li>
</ul>
</li>
</ul>
As for the CSS, the key part is
ul.header-dropdown-menu {
position: absolute;
list-style: none;
padding: 0px;
margin: 0px;
overflow: hidden;
max-height: 0px;
transition: max-height 0.5s ease;
background: rgba(254, 254, 254, 0.9);
}
li.header-link:hover > ul {
max-height: 252px;
}
We have that the position
is set to absolute
, so it displays on top of everything without affecting the layout of the rest of the page. We set overflow
to hidden
and max-height
to 0px
, so that it's not displayed. Then, we use a child selector, so when we hover over the parent element, li.header-link
, we select the child ul
and adjust its max-height
. The transition
property just pretties things up.
Here's the full CSS.
ul#header-menu {
align: bottom;
list-style: none;
padding: 0px;
margin: 0px;
}
li.header-link {
display: inline-block;
line-height: 28px;
padding-top: 0px;
padding-bottom: 0px;
margin: 0px;
min-width: 140px;
}
a.header-link {
color: #003b52;
display: block;
margin: 2px;
font-variant: small-caps;
text-decoration: none;
font-size: 125%;
}
a.header-link:hover {
color: #d02027;
outline: 2px solid #d02027;
}
ul.header-dropdown-menu {
position: absolute;
list-style: none;
padding: 0px;
margin: 0px;
overflow: hidden;
max-height: 0px;
transition: max-height 0.5s ease;
background: rgba(254, 254, 254, 0.9);
}
li.header-link:hover > ul {
max-height: 252px;
}
li.header-dropdown-link {
display: block;
width: 100%;
}
a.header-dropdown-link {
outline: 1px solid rgb(34, 34, 34);
}
See how it smoothly expands and contracts. Very cool, right? I think so. Even the animation is done in CSS. Since the number of items in the drop down is dynamic, I would like to set the height
to auto
. Unfortunately, CSS transitions don't work in that case, so I instead set the max-height
to a number large enough. This has the disadvantage that the time of the transition is based on the max-height
, so it will transition faster than desired.
I've made two updates recently to my blog. First, io.js and Node.js finally merged, and so I've upgraded to Node v4.1.1.
Also, in order to make my blog more user-friendly, I've added tags and pages. Now 5 posts are shown per page, and you can click on tags to see posts that belong to the tag. For example, you see all my posts about
- Cooking: http://www.phillypham.com/?tag=cooking
- Math: http://www.phillypham.com/?tag=math
- Algorithms: http://www.phillypham.com/?tag=algorithm
by clicking on the corresponding link. The tags shown below each post are now clickable links, and if you scroll all the way to the bottom, you can find pagination buttons. I'm really starting to like writing tests in Mocha, too. It was pretty easy to make changes and ensure that nothing broke.
For those that are wondering how my life is going, recently, I'm wondering, too. I feel that I don't have much direction or purpose as of late.
Perhaps the funnest part about this blog was writing the commenting system. One may want to comment on a comment. I found a way to support this behaviour with recursion.
In the layout, using Jade mixins, we have something like
mixin commentView(comment)
- var children = comments.filter(function(child) { return comment.id === child.commentId; })
.wmd-preview!= comment.bodyHtml
.children
.inner-children
each child in children
+commentView(child)
We need the inner-children
div to create the smooth transitions when you expand the sub-comments. Apparently, CSS transitions only work when the height is specified, so you can calculate the new height of children
with inner-children
.
We can also expand comments recursively with JavaScript:
function expandParent(node) {
if (node.classList.contains('comments')) return; // we've reached the top level
if (node.classList.contains('children')) {
node.parentNode.querySelector('.reply-expander').classList.add('expanded');
node.classList.add('expanded');
}
expandParent(node.parentNode);
}
I've left some sample comments below to show off the features. Apparently, transitions are only smooth in Chrome and Internet Explorer. Leave a comment below and try it out!
Welcome to my new blog! After about 6 weeks, I've managed to reinvent the wheel in Node.js and have created my own blog. There are a couple reasons why I've decided to do things the hard way instead of using something like WordPress.
- I failed to find an internship this summer, so I have ample time. I figured that it would be a good learning experience.
- I'm very particular about my editor, so I wanted complete customization. Specifically, I wanted my Emacs keybindings that are so deeply ingrained in me.
- Code highlighting and $\LaTeX$ with live preview were a must, too.
- I wanted the ability to save drafts of comments and posts, either because I want to keep them private as a diary, or to write them in pieces.
Writing this blog took me far longer than anticipated, which I mainly attribute to my lack of experience. I perhaps went overboard with testing, and I wrote hundreds of unit, integration, and functional tests. I must say that using Mocha for my test driver and Selenium for my functional tests was a very pleasant experience, however. Customizing Ace with buttons and keybindings and tying it all together with MathJax and marked was not easy, either, but I'm mostly satisified to have it working to my liking. There are a few improvements to make still like adjustable height.
I probably made a mistake in sticking to a low-level framework like Express for a straight-forward create, read, update and delete (CRUD) application and pure JavaScript for much of the front end—jQuery not even once. I had a really hard time with CSS and design, too. My stylesheet is a mess and needs refactoring. Thinking about security hurt my head, too. On the plus side, I was very excited to be able to use recursion for the commenting system. I also loved being able to share server-side and client-side code. For instance, using Browserify, I can use
var Markdown = require('lib/markdown.js');
in both my server-side and client-side code. Thus, if I don't trust the client, I can do the conversion myself on the server. Also, converting Markdown server-side may reduce page loading time for the client.
All the code for the blog can be found here on GitHub. Here are some cool features of the editor, which you can try with comment box below.
Math and SVG
I'm probably the only person in the world that writes in raw SVG. If you do, too, you can draw diagrams like
Now, we can prove the Pythagorean theorem. Note that $c = f + e$. By similar triangles we have that $$\frac{c}{a} = \frac{a}{f} \Rightarrow a^2 = cf,$$ and likewise, $$\frac{c}{b} = \frac{b}{e} \Rightarrow b^2 = ce.$$ Thus, \begin{align*} c^2 &= c(f+e) \\ &= cf + ce \\ &= a^2 + b^2. \end{align*}
The code for the SVG diagram is
<svg class="centered" width="230" height="130">
<defs>
<g id="right-angle-mark">
<path d="M 12 0 L 12 12 L 0 12" style="stroke: black; fill: none;"></path>
</g>
</defs>
<g transform="translate(15,15)">
<g style="stroke: black">
<line x1="0" y1="0" x2="0" y2="100"></line>
<line x1="0" y1="0" x2="200" y2="0"></line>
<line x1="0" y1="100" x2="200" y2="0"></line>
<line x1="0" y1="0" x2="40" y2="80" style="stroke-dasharray: 5 5"></line>
</g>
<g>
<use xlink:href="#right-angle-mark"></use>
</g>
<g transform="translate(40,80) rotate(243.4349)">
<use xlink:href="#right-angle-mark"></use>
</g>
<g style="font-style: italic; text-anchor: middle">
<text x="100" y="0" dy="-3">a</text>
<text x="0" y="50" dx="-7">b</text>
<text x="100" y="50" dx="5" dy="10">c</text>
<text x="20" y="40" dx="7">d</text>
<text x="20" y="90" dy="-5" dx="-3">e</text>
<text x="120" y="40" dy="-5" dx="-3">f</text>
</g>
</g>
</svg>
I had to some basic trigonometry to calculate the coordinates, which mainly involved noting that $\theta = \tan^{-1}(2)$, where $\theta$ is the lower-left angle. Note that you can add a centered
class to center your diagram. One of the annoying things about Mathematics Stack Exchange, from where I drew much inspiration is the lack of ability to center diagrams.
The $\LaTeX$ is
$$\frac{c}{a} = \frac{a}{f} \Rightarrow a^2 = cf$$
and
\begin{align*}
c^2 &= c(f+e) \\
&= cf + ce \\
&= a^2 + b^2.
\end{align*}
Code
Code is automatically highlighted. See this implementation of binary search in C++ using iterators:
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
#include <iterator> // ForwardIterator
namespace phillypham {
template <class ForwardIterator, class T> ForwardIterator
binary_search (ForwardIterator first, ForwardIterator last, const T& val) {
auto mid = first + (last - first)/2;
while (*mid != val && first < last) {
if (*mid > val) {
last = mid; // new upper bound
} else {
first = mid + 1; // *mid < val
}
mid = first + (last - first)/2;
}
std::cout << *mid << std::endl;
// loop ends when val or first and last are equal
while (mid != first && *(mid - 1) == *mid) --mid;
return mid;
}
}
int main (int argc, char *argv[]) {
std::vector<int> v{10,20,30,30,20,10,10,20}; // size 8
std::sort (v.begin(), v.end()); // 10 10 10 20 20 20 30 30
std::vector<int>::iterator it1, it2, it3, it4;
it1 = phillypham::binary_search(v.begin(), v.end(), 20);
it2 = phillypham::binary_search(v.begin(), v.end(), -10);
it3 = phillypham::binary_search(v.begin(), v.end(), 30);
it4 = phillypham::binary_search(v.begin(), v.end(), 1000);
std::cout << "found " << *it1 << " at position " << (it1 - v.begin()) << std::endl;
// 20, 3
std::cout << "found " << *it2 << " at position " << (it2 - v.begin()) << std::endl;
// 10, 0
std::cout << "found " << *it3 << " at position " << (it3 - v.begin()) << std::endl;
// 30, 6
std::cout << "found " << *it4 << " at position " << (it4 - v.begin()) << std::endl;
// undefined, 8
return 0;
}
Languages are auto-detected, but you can also use a hint to help hightlight.js like this
```cpp
// your code here
```
You can also insert code by highlighting your text and clicking the code button or indenting with 4 spaces.
Tables
There is support for GitHub-flavored markdown. You make this table
Item | Value | Qty |
---|---|---|
Computer | $1600 | 5 |
Phone | $12 | 12 |
Pipe | $1 | 234 |
with
| Item | Value | Qty |
| --------- | -----: | :--: |
| Computer | $1600 | 5 |
| Phone | $12 | 12 |
| Pipe | $1 | 234 |