diff --git a/.github/workflows/aws.yaml b/.github/workflows/aws.yaml index 860f401..67f8aa0 100644 --- a/.github/workflows/aws.yaml +++ b/.github/workflows/aws.yaml @@ -21,7 +21,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18 - cache: 'pnpm' + cache: "pnpm" - name: Install dependencies run: pnpm install diff --git a/src/content/algorithms/extrema-circular-buffer.mdx b/src/content/algorithms/extrema-circular-buffer.mdx index 8250f0e..b2f628d 100644 --- a/src/content/algorithms/extrema-circular-buffer.mdx +++ b/src/content/algorithms/extrema-circular-buffer.mdx @@ -4,8 +4,7 @@ date: "30/07/2024" useKatex: true --- -import { Code } from 'astro:components'; - +import { Code } from "astro:components"; # context @@ -15,7 +14,6 @@ While working for [TRB Capital Management](https://trbcap.com/), certain strateg Design a data structure supporting the following operations: - - `build(size_t capacity)` : initialize the data structure with capacity/window size `capacity` The data structure must always hold $\leq$ `capacity` prices. - `void push_back(double value)` diff --git a/src/content/algorithms/models-of-production.mdx b/src/content/algorithms/models-of-production.mdx index 5552192..c7cfe62 100644 --- a/src/content/algorithms/models-of-production.mdx +++ b/src/content/algorithms/models-of-production.mdx @@ -138,7 +138,6 @@ Using both mathematical intuition and manipulating the visualization above, we f - $\bar{A}$ has a positive relationship with steady-state output - Capital is influenced by workforce size, TFP, and savings rate - Capital output share's $\alpha$ impact on output is twofold: - 1. Directly through capital quantity 2. Indirectly through TFP diff --git a/src/content/algorithms/proofs.mdx b/src/content/algorithms/proofs.mdx index f867cea..ad7c189 100644 --- a/src/content/algorithms/proofs.mdx +++ b/src/content/algorithms/proofs.mdx @@ -5,7 +5,7 @@ useKatex: true useD3: true --- -import Pseudocode from '@components/Pseudocode.astro'; +import Pseudocode from "@components/Pseudocode.astro"; A computer science student attempting to learn proofs. @@ -14,16 +14,19 @@ A computer science student attempting to learn proofs. ## E Minimize-Digit-Diff($l, r$): + 1. Initialize $ans=18$, the largest possible value of $f(l, x) + f(x, r)$ with $l \leq r\leq 10^9$ 2. For $i:=1$ to $200$: - - Let $x$ be a random sample from the open interval $[l, r]$ - - Let $cost:=f(l, x) + f(x, r)$, computed in $O(1)$ time - - $ans:=min(ans,cost)$ + +- Let $x$ be a random sample from the open interval $[l, r]$ +- Let $cost:=f(l, x) + f(x, r)$, computed in $O(1)$ time +- $ans:=min(ans,cost)$ + 3. Return $ans$ --- -*Proof.* +_Proof._ We are interested in minimizing the expected probability of failure over $t\leq10^4$ tests. Firstly, consider a single test (i.e. a single $(l,r)$ input): @@ -32,7 +35,6 @@ Thus, a digit has approximately a $1-\frac{1}{5}=\frac{4}{5}$ probability of min It follows that the candidate then has roughly a $(\frac{4}{5})^9\approx13\%$ chance of minimizing the entire score $f(l,x)+f(x,r)$. So, one random sample $x$ has a $100-13\approx87\%$ chance of being a suboptimal candidate. - Consider sampling $n$ times--the probability of all random samples being suboptimal is $0.87^n$. Let's only settle for a probability of failure near $1/10^{10}$ and solve for our number of trials: $$ @@ -57,7 +59,7 @@ Maximize-F(s): --- -*Proof.* +_Proof._ Let $X$ and $Y$ be the number of occurrences of zeroes and ones in the substring $p$ of $s$. Then, $f(p)=max(X, Y)=\frac{X+Y+|X-Y|}{2}$. The goal is to compute: @@ -96,11 +98,12 @@ $\blacksquare$ ## A Count-Pairs($n$): + 1. Return $n-1$ --- -*Proof.* +_Proof._ Suppose $(a,b)\in\mathbb{N}^2$. Because $a=n-b$ and $a\geq1$, it follows that $1\leq b\leq n-1$. Each choice of $b$ yields a unique $a=n-b$, so there are $n-1$ unique solutions. @@ -109,15 +112,17 @@ $\blacksquare$ ## B Mirror-String($s$): + 1. Reverse $s$ 2. For each character $c$ in $s$: - - If $c$ is "w": Print($c$) - - If $c$ is "p": Print($q$) - - If $c$ is "q": Print($p$) + +- If $c$ is "w": Print($c$) +- If $c$ is "p": Print($q$) +- If $c$ is "q": Print($p$) --- -*Proof.* +_Proof._ The string appears fipped on the y-axis from within the score due to the perspective shifting. Structurally, it is read right-to-left. "p"/"q"/"w" appear as "q"/"p"/"w" when flipped on its y-axis. @@ -127,11 +132,12 @@ $\blacksquare$ ## C Seat-Monkeys($a$, $b$, $c$, $m$): + 1. Return $min(m, a)+min(m, b)+min(c, 2\cdot m-(min(m, a) + min(m, b)))$ --- -*Proof.* +_Proof._ Consider an assignment of monkeys $S$ that sits the $a$ and $b$ monkeys in the first and second row and then fills the remaining seats with the $c$ monkeys. @@ -140,11 +146,16 @@ Assume there exists a more optimal assignment of monkeys $S^{'}$. WLOG, assume $ $S^{'}$ can only differ from $S$ as follows: 1. Seats a $c$ monkey in row 1 instead of an $a$ monkey - - $S^{'}$ leaves an $a$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + +- $S^{'}$ leaves an $a$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + 2. Seats a $c$ monkey in row 2 instead of a $b$ monkey - - $S^{'}$ leaves a $b$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + +- $S^{'}$ leaves a $b$ monkey unseated. $S$ seats this monkey instead--the same number of monkeys are seated in $S$. + 3. Does not seat a monkey where $S$ has - - $S$ seats more than $S^{'}$. + +- $S$ seats more than $S^{'}$. In all cases, $S^{'}$ is no better than S, therefore $S$ is optimal. @@ -153,21 +164,26 @@ $\blacksquare$ ## D Construct-B($a$): + 1. Let $b$ be an array of size $n=\#a$ and $X$ be the set of numbers in $a$. 2. For each element $x$ of $a$ at index $i$: + - If $x\in X$: - $b[i]:=x$ - $X:=X \backslash \{x\}$ + 3. Let $Y=\{1,2,\cdots,n\}\backslash X$ 4. For each element $x$ of $b$ at index $i$: + - If $b[i]$ is NIL: - $b[i]:=\text{first-element}(Y)$ - $Y:=Y\backslash\{\text{first-element}{(Y)}\}$ + 5. Return $b$ --- -*Proof.* +_Proof._ Consider the array $b$ from Construct-B. @@ -182,17 +198,21 @@ $\blacksquare$ ## E Count-Pairs($l_1$, $l_2$, $r_1$, $r_2$, $k$): + 1. Let $A:=\lfloor log_k(r_2/l_1)\rfloor$ 2. Let $B:=\lfloor max(0, log_k(l_2/r_1))\rfloor$ 3. Let $\text{total}:=0$ 4. For each $A\leq i\leq B$: + - Let $r=\lfloor r_2/ k^n\rfloor$ - Let $l=\lfloor l_2/k^n\rfloor$ - $\text{total} := \text{total} + max(0, r - l + 1)$ + 5. Return $\text{total}$ + --- -*Proof.* +_Proof._ Each value of $n$ corresponds to a line with slope $k^n$ because $y/x=k^n\leftrightarrow y=x\cdot k^n$. The problem can be visualized as follows: @@ -206,12 +226,13 @@ For each $n_0$ in this range, the smallest $x$ satisfying $y=x\cdot k^n_0$ is $\ 1. Let $A=\sum a$ and $B=\sum b$. 2. For each query with requested beauty $q$: + - If $\exists (i,j)\in(\{1,2,\cdots,n\},\{1,2,\cdots,m\}):(A-a[i])\cdot(B-b[j])=x$, print "YES" - Otherwise, print "NO" --- -*Proof.* +_Proof._ The beauty of the grid equals $B=\sum_i \sum_j M_{i,j}=\sum_i\sum_j a_i\cdot b_j=\sum_i(a_i\cdot \sum_j b_j)=(\sum_i a_i)\cdot (\sum_j b_j)$. @@ -231,14 +252,16 @@ If such $a_i$ and $b_j$ exist, the operation can be performed. 1. Let $G$ be the input graph 2. Let $\text{ans}=0$ 3. For each component $C$ in $G$: - - Let $\text{cycle}$ be the set of nodes in the cycle of $C$ - - Let $d=max_{u\in \text{cycle}}\text{Distance-To-Cycle(C, u)}$ - - $\text{ans}:=max(\text{ans}, d)$ + +- Let $\text{cycle}$ be the set of nodes in the cycle of $C$ +- Let $d=max_{u\in \text{cycle}}\text{Distance-To-Cycle(C, u)}$ +- $\text{ans}:=max(\text{ans}, d)$ + 4. Return $\text{ans}$ --- -*Proof.* +_Proof._ Consider some $v_i\in V$. There must exist an edge $e=(v_i,v_j),i\neq j$. Following this path from $v_i$, the each edge must map to a previously seen node, forming a cycle, or a new node. Because the graph is finite, the path starting any $v_i$ must contain a cycle. Thus, G is a graph of components with one cycle each. @@ -260,18 +283,20 @@ $\blacksquare$ 1. Let $G$ be the input graph 2. Let $\text{ans}=0$ 3. For each component $C$ in $G$: - - Let $U$ be the set of all nodes not in the cycle of $C$ - - Let $\text{count}$ be the number of plushies each spider has - - Let $\text{par}$ be the set of parents for each spider - - Run a multi-source BFS simulating the state transitions on $U$. For each iteration at year $y$: - - $\text{count[u]}:=\text{count[u]}+\sum_{p\in \text{par[u]}}\text{count[p]}$ - - $\text{ans}:=max(\text{ans}, \text{count[u]})$ - - $y:=y+1$ + +- Let $U$ be the set of all nodes not in the cycle of $C$ +- Let $\text{count}$ be the number of plushies each spider has +- Let $\text{par}$ be the set of parents for each spider +- Run a multi-source BFS simulating the state transitions on $U$. For each iteration at year $y$: + - $\text{count[u]}:=\text{count[u]}+\sum_{p\in \text{par[u]}}\text{count[p]}$ + - $\text{ans}:=max(\text{ans}, \text{count[u]})$ + - $y:=y+1$ + 4. Return $\text{ans}$ --- -*Proof.* +_Proof._ Consider some $v_i\in V$. There must exist an edge $e=(v_i,v_j),i\neq j$. Following this path from $v_i$, the each edge must map to a previously seen node, forming a cycle, or a new node. Because the graph is finite, the path starting any $v_i$ must contain a cycle. Thus, G is a graph of components with one cycle each. @@ -295,7 +320,9 @@ $\blacksquare$ - Let $\text{Colwise}$ be the column-wise 2D-prefix coefficient sum matrix of $A$ - Let $\text{Rowwise}$ be the row-wise 2D-prefix coefficient sum matrix of $A$ - Let $\text{Submatrix-Sum}(x_1,y_1,x_2,y_2)$ compute the sum of the submatrix of $M$ bounded by $(x_1,y_1)$ and $(x_2,y_2)$ + 1. For each query $x_1,x_2,y_1,y_2$: + - Let $w=y_2-y_1+1$ - Let $prefix=\text{Submatrix-Sum}(\text{Prefix},x_1,y_1,x_2,y_2)$ - Let $rowsum=\text{Submatrix-Sum}(\text{Rowwise},x_1,y_1,x_2,y_2)$ @@ -304,7 +331,7 @@ $\blacksquare$ --- -*Proof.* +_Proof._ Mathematically formulated: diff --git a/src/content/autonomous-racing/multithreading-a-gui.mdx b/src/content/autonomous-racing/multithreading-a-gui.mdx index 0e5958b..f5b90bd 100644 --- a/src/content/autonomous-racing/multithreading-a-gui.mdx +++ b/src/content/autonomous-racing/multithreading-a-gui.mdx @@ -71,19 +71,19 @@ This lead me to the follow structure: ![multi-threaded-design](/multithreading-a-gui/multi-threaded-implementation.webp) - Three callback groups are triggered at differing intervals according to their urgency on the GUI node - - A thread-safe queue[^2] processes all ingested data for each callback group + - A thread-safe queue[^2] processes all ingested data for each callback group - Every 10ms, the GUI is updated, highest to lowest urgency messages first - The `MainWindow` houses the visualization widgets as before—however, the GUI thread actually performs the update logic - GUI Widgets were re-implemented to be thread-safe with basic locking, a small amount of overhead for safe memory access ## \*actual\* multi-threaded implementation -Sounds good, right? Well, I should've done my research first. The Qt framework has *already internalized* the logic for this entire paradigm of multithreaded code. Turns out all I need are: +Sounds good, right? Well, I should've done my research first. The Qt framework has _already internalized_ the logic for this entire paradigm of multithreaded code. Turns out all I need are: - [Signals/slots](https://doc.qt.io/qt-6/signalsandslots.html) and a `Qt::QueuedConnection` - Running the GUI with `MultithreadedExecutor` -As it turns out, signals and slots *automatically* leverage ROS's internal thread-safe message queue, ensuring deserialization one at a time. +As it turns out, signals and slots _automatically_ leverage ROS's internal thread-safe message queue, ensuring deserialization one at a time. The following (final) design employs two threads: @@ -132,8 +132,8 @@ Elegantly, registering a signal/slot with `Qt::QueuedConnection` does all of the Looking back, this GUI should've been implemented with a modern web framework such as [React](https://react.dev/) with [react-ros](https://github.com/flynneva/react-ros?tab=readme-ov-file). CAR needs high-speed, reactive data, and a QtC++ front-end is simply not meant for this level of complexity. I made it a lot harder than it needed to be with my lack of due diligence, but the single-threaded GUI event loop in ROS is more harm than help. - [^1]: See [the ROS documentation](https://docs.ros.org/en/foxy/How-To-Guides/Using-callback-groups.onhtml) to learn more. The CAR publishes various topic-related data at set rates, so I'm looking to run various groups of mutually exclusive callbacks at a set interval (i.e. `MutuallyExclusive`) + [^2]: The simplest implementation did the job: ```cpp @@ -179,3 +179,4 @@ Looking back, this GUI should've been implemented with a modern web framework su } ... }; + ``` diff --git a/src/content/autonomous-racing/refactoring-a-state-machine.mdx b/src/content/autonomous-racing/refactoring-a-state-machine.mdx index cfccf35..09e778d 100644 --- a/src/content/autonomous-racing/refactoring-a-state-machine.mdx +++ b/src/content/autonomous-racing/refactoring-a-state-machine.mdx @@ -9,7 +9,7 @@ Given the recent switch to the [Marelli flag system](https://www.racecar-enginee # the problem -Our original state machine implementation had grown organically over several racing seasons. What started as a simple finite state machine under a more trivial flag system grew vastly more complex as it maladapted to the new flag system. Specifically, the original flag system only had one flag system for the entire track and a few car-specific ones (i.e. request to retire the car after a rules violation). The Marelli flag system has both flag *and* vehicle flags, each of which enforces different requirements. +Our original state machine implementation had grown organically over several racing seasons. What started as a simple finite state machine under a more trivial flag system grew vastly more complex as it maladapted to the new flag system. Specifically, the original flag system only had one flag system for the entire track and a few car-specific ones (i.e. request to retire the car after a rules violation). The Marelli flag system has both flag _and_ vehicle flags, each of which enforces different requirements. The state machine worked "fine" for its initial use case: @@ -29,7 +29,7 @@ State_Machine: 2. Subsequently, the code was read in and parsed with a [cpp yaml library](https://github.com/jbeder/yaml-cpp). -3. Since all of this was done at runtime, `state_actions`, `entry_condition`, and `next_states` were *manually parsed*, hard-coded in a sort of custom language encoded in a string. Opening up our codebase, I found this set of foundational structs that the data was converted into: +3. Since all of this was done at runtime, `state_actions`, `entry_condition`, and `next_states` were _manually parsed_, hard-coded in a sort of custom language encoded in a string. Opening up our codebase, I found this set of foundational structs that the data was converted into: ```cpp struct Action { @@ -83,9 +83,9 @@ I knew state machines were ubiquitous in software so I explored a few previous a 1. One big switch: nested conditions (i.e. all combinations of vehicle + track flags) seemed a bit nightmarish to look at as we continue to build out the state machine 2. [Boost SMS](https://www.boost.org/library/latest/msm/): this and other existing libraries looked enticing but three things stopped me: - 1. Integrating dependencies into our production code is simple but takes time for other members of the team to vet it - 2. It also bloats our deployment latency and final size because: - 3. It is overkill and writing things from scratch is more fun + 1. Integrating dependencies into our production code is simple but takes time for other members of the team to vet it + 2. It also bloats our deployment latency and final size because: + 3. It is overkill and writing things from scratch is more fun 3. A minimalist and expressive approach: @@ -121,7 +121,6 @@ enum class StateID { State1, State2, ..., Last }; A transition has entry and exit conditions which are compositions of state (i.e. just our bitmask) and an arbitrary action (i.e. a lambda). The entry/exit bits must be set/unset in order to enter/exit the state. - ```cpp struct Transition { VehicleTrackFlags entry; diff --git a/src/content/git/cp.nvim.mdx b/src/content/git/cp.nvim.mdx index b151651..6f3156f 100644 --- a/src/content/git/cp.nvim.mdx +++ b/src/content/git/cp.nvim.mdx @@ -12,12 +12,17 @@ Things have changed since I last documented my competitive programming setup [he After many months of using the aforementioned `make` based setup, I had a few qualms: -- I'm lazy: I grew tired of copying (and mis-copying) inputs, outputs, etc from online judges. -- I'm lazy: I frequently submitted incorrect solutions after erroneously asserting that my outputs matched those of the sample test cases -- External dependencies: it unsettles me that my bare-bones setup required copy-pasting an entire suite of scripts -- Non-native NeoVim experience: while composition and the UNIX philosophy are great, there's only so much you can do with pipes and files. - - Raw I/O files meant I couldn't see colored stdin/stdout - - Fine-grained per-testcase I/O was suspect--isolating and running a subset of test cases required manual intervention +- I'm lazy: I grew tired of copying (and mis-copying) inputs, outputs, + etc from online judges. +- I'm lazy: I frequently submitted incorrect solutions after erroneously + asserting that my outputs matched those of the sample test cases +- External dependencies: it unsettles me that my bare-bones setup + required copy-pasting an entire suite of scripts +- Non-native NeoVim experience: while composition and the UNIX philosophy + are great, there's only so much you can do with pipes and files. - Raw I/O + files meant I couldn't see colored stdin/stdout - Fine-grained per-testcase + I/O was suspect--isolating and running a subset of test cases required manual + intervention The solution was to leverage Neovim's great APIs to give me granular control over every aspect of my problem-solving experience. @@ -25,22 +30,42 @@ The solution was to leverage Neovim's great APIs to give me granular control ove The GitHub page documents the plugin well enough so I'll avoid re-hashing it here. Instead, what's more interesting to document is why I thought this was a worthwhile experience. -1. Making Something Useful for Others: cp.nvim is an opportunity for me to make my first open-source project "right"--not some side project or demo, but a *real*, usable tool that I'll be rolling out to the public soon. I consider the following in my active development of the plugin: +1. Making Something Useful for Others: cp.nvim is an opportunity for me + to make my first open-source project "right"--not some side project or demo, + but a *real*, usable tool that I'll be rolling out to the public soon. I + consider the following in my active development of the plugin: -- Comprehensive continuous integration (*real* testing, linting, and more) +- Comprehensive continuous integration (_real_ testing, linting, and more) - [LuaRocks](https://luarocks.org/) integration (the future of NeoVim package management) - Concise and thorough Vimdoc documentation that communicates effectively - Modern lua tooling: use of [busted](https://lunarmodules.github.io/busted/), [selene](https://kampfkarren.github.io/selene/) and more integrated with the NeoVim lua interpreter - Sensible user defaults & extreme customization - Proper versioning, tagging, and releases -2. The Neovim Community: I'm elated to finally give back to the community (even if no one uses this plugin). [folke](https://github.com/folke), [bfredl](https://github.com/bfredl), and [echasnovski](https://github.com/echasnovski) are my greatest inspirations as an open-source developer and I've had enough of taking without giving back. +2. The Neovim Community: I'm elated to finally give back to the community + (even if no one uses this plugin). [folke](https://github.com/folke), + [bfredl](https://github.com/bfredl), and + [echasnovski](https://github.com/echasnovski) are my greatest inspirations as + an open-source developer and I've had enough of taking without giving back. + - In the coming months I plan to contribute to [NeoVim core](https://github.com/neovim/neovim), including making `:checkhealth` asynchronous and integrating an [mdx](https://mdxjs.com/) parser. -3. Learning Random things: I think this plugin is *really* cool by virtue of its efficacy and the miscellany of knowledge I accrued in the 15k+ LOC as of version v0.3.0. Some things I learned include: +3. Learning Random things: I think this plugin is *really* cool by virtue + of its efficacy and the miscellany of knowledge I accrued in the 15k+ LOC as + of version v0.3.0. Some things I learned include: -- ANSI terminal colors and escape codes: I wrote my own stateful ANSI escape sequence parser to map raw bytes to native NeoVim highlighted text -- Extmarks: NeoVim extmarks (`:h extmarks`) are extremely powerful. Here, I used them to apply dynamic highlighting across various components of the plugin but I also plan to leverage virtual text to catch compile errors in real-time -- VIM filetypes and diffing: Vim is strange and the event-based system is fragile. I faced filetype detection race conditions and odd side effects of functions (such as `:diffthis` resetting `foldcolumn`). -- [LuaCATS](https://github.com/LuaCATS): apparently writing comments is the best way to typecheck in lua... -- The (Neo)Vim event loop: Scraper subprocesses spawned with `vim.system`. Though a powerful API, I often had to obey the event loop and wrap side effects with `vim.schedule` to ensure they ran after jobs finished. This was useful to defer UI updates. +- ANSI terminal colors and escape codes: I wrote my own stateful ANSI + escape sequence parser to map raw bytes to native NeoVim highlighted text +- Extmarks: NeoVim extmarks (`:h extmarks`) are extremely powerful. Here, + I used them to apply dynamic highlighting across various components of the + plugin but I also plan to leverage virtual text to catch compile errors in + real-time +- VIM filetypes and diffing: Vim is strange and the event-based system is + fragile. I faced filetype detection race conditions and odd side effects of + functions (such as `:diffthis` resetting `foldcolumn`). +- [LuaCATS](https://github.com/LuaCATS): apparently writing comments is + the best way to typecheck in lua... +- The (Neo)Vim event loop: Scraper subprocesses spawned with + `vim.system`. Though a powerful API, I often had to obey the event loop and + wrap side effects with `vim.schedule` to ensure they ran after jobs finished. + This was useful to defer UI updates. diff --git a/src/content/software/hosting-a-git-server.mdx b/src/content/software/hosting-a-git-server.mdx index 4383769..c68bf85 100644 --- a/src/content/software/hosting-a-git-server.mdx +++ b/src/content/software/hosting-a-git-server.mdx @@ -83,7 +83,7 @@ Restart=on-failure [Install] WantedBy=multi-user.target -```` +``` 12. I use the name `main` instead of `master` as my default branch (it's less letters, ok?)--set this up on the server too: