<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Notes on Software</title><link>https://nthomas.org/</link><description>Things I&apos;ve learned while chasing problems down the software stack</description><item><title>All My Good Habits Are Bad</title><link>https://nthomas.org/2020-09-03-All-My-Good-Habits-Are-Bad/</link><guid>https://nthomas.org/2020-09-03-All-My-Good-Habits-Are-Bad/</guid><pubDate>Thursday, 03 September 2020 00:00:00 +0000</pubDate><description>HOW fast is the hardware?!</description><content:encoded><![CDATA[<p>During my non-coding time at Recurse Center, I've been digging into videos about high-performance C++ (or generally just high performance software) in preparation for preparing good habits before I start working at Facebook. In the process I've been reflecting on the code I'd write by default, and try to reflect on what I can learn from this. So we'll pair resources to good habits.</p>
<h2>Make life easy for the hardware</h2>
<p>Resources:</p>
<ol>
<li>Mike Acton's talk about <a href="https://www.youtube.com/watch?v=rX0ItVEVjHc">Data Oriented Design</a></li>
<li>Scott Meyer's talk about <a href="https://www.youtube.com/watch?v=WDIkqP4JbkE&amp;ab_channel=NOKIATechnologyCenterWroc%C5%82aw">CPU Caches</a></li>
<li>Carl Cook at <a href="https://www.youtube.com/watch?v=NH1Tta7purM&amp;ab_channel=CppCon">CPPCon</a></li>
</ol>
<p>CPU access patterns aren't as simple as "get the address from the program counter, then look at the memory address for data". It turns out CPUs are a lot smarter than that. A CPU will have various mechanisms to make accessing data as fast as possible, including</p>
<ol>
<li>various levels of caches before hitting main memory</li>
<li>access patterns akin to grabbing blobs of data (called a cache line) and stuffing it into cache</li>
<li>attempting to make predictions on your access patterns and grab data greedily to improve future access</li>
</ol>
<p>A <strong>cache line</strong> is effectively a contiguous blob of memory that the CPU will grab when you access an address. An analogy might be - you're making apple pie. You want to find the 10 best apples at the grocery store. Rather than pick out one by one, you scoop an arm full into your art. Then you start looking at the apples in your cart. If they're all good, yay! If not, you throw away the ones you don't want and grab another armful of apples.</p>
<p>If you treat your cart as <strong>L1 cache</strong> and going back to the apple bin as accessing main memory, it turns out the analogy means that you take 200x as long (see Peter Norvig's <a href="http://norvig.com/21-days.html#answers">numbers</a>)</p>
<p>It turns out people who really care about performance - game programmers - figured this out a long time ago. Bob Nystrom's written a great chapter in Game Programming Patterns about <a href="https://gameprogrammingpatterns.com/data-locality.html">cache locality</a>.</p>
<p>So what can we do?</p>
<ol>
<li>Be judicious with OOP patterns. Making everything an instance of a class and having relationships between them is great, but referencing those instances effectively is chasing a pointer which can nearly guarantee a cache miss</li>
<li>Use monomorphic arrays when it makes sense. If you know you're going to access all the elements of some list often (eg all the values of a map), just use an array. Make it cache friendly.</li>
</ol>
<h2>Big O isn't everything - benchmark!</h2>
<ol>
<li>Eric Lippert's blog post on a very neat <a href="https://ericlippert.com/2020/03/27/new-grad-vs-senior-dev/">assembly instruction</a></li>
<li>The chapter of Crafting Interpreters on <a href="http://craftinginterpreters.com/hash-tables.html#top">writing a hash table</a></li>
</ol>
<p>I expect this is particularly emphasized by the interview process for the FAANG companies, but Big O analysis is more or less second nature to me. But one thing that's worth noting is that sometimes your Big O assumptions miss some truths about realworld numbers.</p>
<p>The idea that a single assembly instruction can do work much faster than the "clever" code I'd write is still kind of wild. Then when thinking about hash tables - of course access is O(1)! But with a caveat.</p>
<p>In the case of a hash collision, your hash table implementation matters a LOT. Are your values a linked list of pointers referencing the next value? If so, you're now chasing pointers again. Which means cache misses. Which means ~200x as long to execute. Could you have done a simple linear scan on an array of data if it were stored in a cache line and held in L1 faster than you could resolve a hash collision? Well, maybe.</p>
<p>In practice, I'm not sure just HOW useful this is. But a key takeaway for me would be <strong>when measuring performance, don't assume your "optimal" algorithm is as fast as it could be</strong></p>
<h2>Higher Order Functions aren't all that fancy</h2>
<ol>
<li>Let's learn about <a href="https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html">monomorphism</a> from this V8 engineer</li>
<li>Some thoughts from <a href="https://twitter.com/BenLesh/status/1237135794634178563">Ben Lesh</a></li>
</ol>
<p>I think this is fallout from the ES6 excitement that led to a ton of "functional-lite" code in the JS ecosystem, but I'm starting to feel a little bit of burnout from it. Consider the following:</p>
<pre><code class="language-javascript">const arrayOfNums = [1, 2, 3, 4, 5];
arrayOfNums.map(x =&gt; x * 2)
	.filter(x =&gt; x % 3 == 0)
	.reduce((obj, num) =&gt; obj[num] = String(num), {})
</code></pre>
<p>Comfortably we can call this linear in time and space. But in practice:</p>
<ol>
<li>we have a function allocation for each element in each callback</li>
<li>We create a ton of arrays in the middle that are thrown away</li>
<li>We end up creating a ton of "shapes" in our reduce unless the function gets inlined away by the compiler.</li>
</ol>
<p>I fully get the code is arbitrary, but at the same time...does this really convey intent better? Maybe - map tells us there's a new array created potentially of a new type, filter tells us we'll get an array of the same type but fewer elements, and reduce tells us...well, not much. But maybe, particularly given that we're running this in a browser where a user might have a dozen or more tabs open (on a phone!) we should be more judicious with our use of higher order functions.</p>
<p>In particular, I've poked around the codebase for React and <a href="https://romefrontend.dev/">Rome</a> and have noticed both <a href="https://twitter.com/sebmarkbage?lang=en">Sebastian Markbåge</a> (and team) and <a href="https://twitter.com/sebmck">Sebastian McKenzie</a> both seem to keep it simple. While loops, for...of iterators, basic data structures (queues, stacks, heaps) and some well-typed classes.</p>
<p>In @sebmarkbage's case in particular, despite his OCaml influence, he's not writing functional-lite JS. Which makes sense! No tail call optimization, different engine under the language runtime, he's playing to the strength of the language. That's something I can take away.</p>
]]></content:encoded></item>
<item><title>Recurse Center Week 4 Day 1</title><link>https://nthomas.org/2020-08-31-Recurse-Center-Week-4-Day-1/</link><guid>https://nthomas.org/2020-08-31-Recurse-Center-Week-4-Day-1/</guid><pubDate>Monday, 31 August 2020 00:00:00 +0000</pubDate><description>🎵🎵 We&apos;re halfway there 🎵🎵</description><content:encoded><![CDATA[<p>Today marks the second half of my Recurse Center batch. It's something I've wanted to do for years, and with the pandemic I had to push off even longer until I felt semi-secure in employment and health insurance options. But wow, am I glad I did it.</p>
<p>Coming into RC, I had a ton of goals. I wanted to write some C. I wanted to write some Rust. I wanted to write a small compiler from scratch. I wanted to contibute to a big open source compiler. I wanted to read a big text book. I wanted to write a ton of blog posts. Looking back, it's kinda silly just how much I thought I could do, or how much I even wanted to get done in 6 weeks.</p>
<p>When I reflect on what I HAVE done in the last 3 weeks, I'm actually very proud of it. However, it's pretty clear to me that what I value (reflected in what I do) is very different than what my goals were (reflected in what I said). My goals were diverse, unfocused, and really about greedily touching as much as I could to sate this need for intellectual stimulation. But it turns out, I didn't really <strong>want</strong> that.</p>
<p>To date I've</p>
<ol>
<li>Written a ton of Rust (a new language for me) by following Crafting Interpreters</li>
<li>Learned more depth about parsing algorithms from Engineering a Compiler and some videos from Dmitry Soshnikov, an engineer / educator I respect a ton</li>
<li>Worked through some Nand2Tetris</li>
<li>Contributed to Rome, an open source Javascript toolchain</li>
</ol>
<p>...and that's it. No distributed systems papers, no Game Boy emulator, just letting my interests and passion guide my focus. That's not to say I'll never switch focus - that Game Boy emulator might be fun in Zig! Maybe I'll spent my last two weeks working towards that.</p>
<p>Two quotes that I like, that might sound a bit too adversarial, are "No plan survives first contact with the enemy" or phrased better by Mike Tyson - "everybody has a plan until they get punched in the mouth". I had a plan coming into RC. I looked at the calendar, chatted with some folk, and fairly promptly threw that all away for serendipity. I just focused on the few things I've wanted to work on for a while...and just did it. For me, for my needs and where I am in my life, this is so much better.</p>
<p>To reflect on values:</p>
<p>Working in web dev for years, and doing a lot of front-end work on business software in particular didn't really allow me the intellectual stimulation or more me towards my goals of <strong>depth over breadth</strong>. RC gave me the space to both realize that this was even a goal of mine - that I preferred a sense of expertise over loosely touching many topics - and the time to just dig deep into topics. I'm grateful for the opportunity and the financial privilege, and I think the largest growth opportunity I've even had is this realization of my values.</p>
]]></content:encoded></item>
<item><title>An Overview of Parsing Algorithms</title><link>https://nthomas.org/2020-08-26-An-Overview-Of-Parsing-Algorithms/</link><guid>https://nthomas.org/2020-08-26-An-Overview-Of-Parsing-Algorithms/</guid><pubDate>Wednesday, 26 August 2020 00:00:00 +0000</pubDate><description>Notes from Engineering a Compiler</description><content:encoded><![CDATA[<hr />
<p>These notes are supplemented by Dmitry Soshnikov's videos on <a href="https://www.youtube.com/playlist?list=PLGNbPb3dQJ_6aPNnlBvXGyNMlDtNTqN5I">parsing</a></p>
<hr />
<h2>Introduction</h2>
<p>Compiler parsers serve as the second stage of the "front end", after scanning. A scanner takes a set of source text and effectively annotates them into tokens, or small structs with a classification. For example <code>if (true) {print("hello")}</code> might yield <code>[{type: IF}, {type: LEFT_PAREN}, {type: TRUE}, {type: RIGHT_PAREN}, {type: LEFT_BRACE}, {type: IDENTIFIER, value: "print"}, {type: LEFT_PAREN}, {type: STRING, value: "hello"}, {type: RIGHT_PAREN}, {type: RIGHT_BRACE}, {type: EOF}]</code>. A parser receives these input tokens as a stream and tries to determine syntactic structure based on some <strong>grammar</strong>, or rules of a valid language. Parsers typically use a <strong>context-free grammar</strong>, which Cooper/Torczon define as "a set of rules that describe how to form sentences."</p>
<p>Formally, a CFG is defined as the tuple (T, NT, S, P) where T is the set of terminal values (that is, as you follow the rules, you cannot substitution any deeper rule), NT is the set of non-terminal values (variables, a set of abstractions), S is the <strong>goal symbol</strong> or <strong>start symbol</strong>, and P is the set of <strong>productions</strong> or rules for rewrites / substitutions. That is, any non-terminal should be rewriteable as a string of other symbols in the grammar.</p>
<p>We use <strong>Backer-Naur Form</strong> traditionally to describe our grammar. For example, an incomplete grammar for math might look like</p>
<pre><code class="language-bnf">1) Expr -&gt; Expr + Expr
2)       | Expr * Expr
3)       | number
</code></pre>
<p>When we create a <strong>derivation</strong>, a sequence of rewriting steps following the rules of the grammar, we generate <strong>sentential forms</strong> which are sentences that can occur in one step of a derivation. As we do so, we generate a <strong>parse tree</strong>. For example, if we used our above rules and tried to parse <code>2 + 5 * 10</code> we might get:</p>
<table><thead><tr><th>Rule</th><th>Sentential Form</th></tr></thead><tbody>
<tr><td></td><td>Expr</td></tr>
<tr><td>1</td><td>Expr + Expr</td></tr>
<tr><td>3</td><td>2 + Expr</td></tr>
<tr><td>2</td><td>2 + Expr * Expr</td></tr>
<tr><td>3</td><td>2 + 5 * Expr</td></tr>
<tr><td>3</td><td>2 + 5 * 10</td></tr>
</tbody></table>
<p>In doing this, we also followed <strong>leftmost derivation</strong> where we rewrote the leftmost non-terminal in each step. There also exists <strong>rightmost derivation</strong>.</p>
<p>If it is possible that following leftmost (or rightmost) derivation can result in different parse trees, we call the grammar <strong>ambiguous</strong>. One way to remove ambiguity is to add a new nonterminal that embodies the repeated logic. The book uses the example of</p>
<pre><code class="language-bnf">Statement → if Expr then Statement else Statement 
			| if Expr then Statement
			| Assignment
</code></pre>
<p>Which is ambiguous for "if expr1 then if expr2 then assign1 else assign2" (which arm does the else belong to?) and corrects it to</p>
<pre><code class="language-bnf">Statement → if Expr then Statement
			| if Expr then WithElse else Statement
			| Assignment

WithElse → if Expr then WithElse else WithElse
			| Assignment

</code></pre>
<p>This forces the else clause to always be associated with the correct arm. We also assert that the closer to the start symbol a rule is, the lower its precedence. That is, more important / more binding power elements will appear <strong>lower</strong> in the parse tree usually.</p>
<h2>Top Down Parsing</h2>
<p>Top-Down parsers start with the root of a parse tree and build downwards, continuing until all leaves of the tree are terminal values and the input stream of tokens is exhausted (or, in case of error). In the case of an error, a parser will assume it applied some wrong production and will <strong>backtrack</strong> and unwind the set of changes it made to its last branching choice, try the next one and continue. Backtracking isn't mandatory for a grammar, however. A good set of pseudocode for a backtracking algorithm</p>
<pre><code>let node = root
let word = scanner.nextWord()
let testingStack = []
while true
	if node is a variable
		// look at all rules for node
		for 1 to node.rules.length
			testingStack.push(node.rules[idx])
		node = node.rules[0]
	else if word == node
		word = scanner.nextWord()
		node = testingStack.pop()
	else if word == EOF and we have no more nodes to look at
		return root
	else
		backtrack
		
</code></pre>
<p>What happens in the case of a <strong>left-recursive grammar</strong> with a top-down parser? Think of our above math</p>
<pre><code class="language-bnf">1) Expr -&gt; Expr + Expr
2)       | Expr * Expr
3)       | number
</code></pre>
<p>We would end up with an infinite cycle of testing the leftmost Expr with the first substitution rule. So, in order to change this, we would need a <strong>right-recursive grammar</strong> by rewriting the productions.</p>
<pre><code class="language-bnf">Expr -&gt; Term Expr'

Expr' -&gt; + Expr'
       | * Expr'
	   | Term
	   
Term -&gt; number
</code></pre>
<p>We need to watch out for direct left recursion as well as <strong>indirect left recursion</strong>.</p>
<p>Once we've done this, we can introduce a <strong>lookahead symbol</strong> which allows us to peek at the first symbol in each rule and match it to the upcoming token in order to predict the next applicable rule. We call the set of valid lookahead symbols the <strong>FIRST set</strong>, which is defined as "the set of terminals can that appear at the start of a sentence derived from some rule a'" .</p>
<p>That is, for Expr', the FIRST set includes Term, + and *</p>
<p>We can also define a <strong>FOLLOW</strong> set as all the words that can come after a nonterminal. That is, for Expr', that would be eof</p>
<p>We also introduce <strong>left-factoring</strong> that rewrites ambiguity in left terms by isolating common prefixes into their own set of productions. That is</p>
<pre><code class="language-bnf">Expr -&gt; T + F
     | T - F
	 | T
</code></pre>
<p>We see a common prefix of T, isolate and move the rest</p>
<pre><code class="language-bnf">Expr -&gt; T Expr'

Expr' -&gt; + F
      |  - F
	  |  ε
</code></pre>
<p>The pseudocode to use Expr' looks like</p>
<pre><code>function exprPrime() {
	if word == + or word == -
		word = scanner.nextWord()
		if isF(word)
			return true
		else return false
	else if word == "" or word == EOF // epsilon case
		return true
	else
		reportSyntaxError()
		return false
}
</code></pre>
<p>A topdown parser that uses FIRST, FOLLOW, and FIRST+ sets to avoid backtracking is called <strong>LL(1)</strong>. That is, we scan <strong>l</strong>eft to right on input, working on <strong>l</strong>eftmost derivation, with a lookahead of <strong>1</strong> token. As long as the grammar is right-recursive and backtrack free, LL(1) is an option.</p>
<h2>Bottom-Up Parsing</h2>
<p>Like the name suggests, a bottom-up parser finds the leaves (terminals) of the parse tree and builds non-terminals on top. The parser "extends the frontier upwards" by looking for some production <code>a' -&gt; b'</code> - that is, if b' exists at some rightside position k, we can replace b' with a'. If that production yields a valid derivation, we call this the <strong>handle</strong>, which is the pair (a' -&gt; b', k). The process of replacing b' with a' in the frontier is called a <strong>reduction</strong>.</p>
<p>The scanner still runs with left to right input, but our parser works on rightmost derivation. However, we reverse the order to make sure we handle the leftmost leaf first. As a result, these parsers are <strong>LR</strong> parsers - left to right input, reverse rightmost derivation.</p>
<p>The <strong>LR(1)</strong> parsing algorithm uses a single lookahead symbol. It uses two tables, an <strong>Action</strong> table and a <strong>Goto</strong> table to find the next handle for the derivation. It does this by holding a stack of the current upper frontier (the layer of the parse tree it's building) interleaves with states from the handle-finding steps to figure out how to make a reduction.</p>
<p>A <strong>shift</strong> is just an advancement of the scanner stream - the equivalent of consuming a token. The shifted symbol becomes the new parse tree state.</p>
<p>Pseudocode</p>
<pre><code>push $
push starting state s0
word = scanner.nextWord()

while true
	let state = stack.pop()
	if Action[state.word] == reduction of a' to b'
		cardinality = b'.length
		pop cardinality times
		state = pop()
		push(a')
		push Goto[state, A]
	else if Action[state.word] == "shift state si"
		push word
		push si
		word = scanner.nextWord()
	else if Action[state.word] == accept
		break
	else
		failure()

return success
</code></pre>
<p>Action tables are a mapping of states and words to steps - either shifts or reduces. Goto tables are a mapping of handles (functions to replace some b' with a') and state transitions.</p>
<p>In order to build these tables, we need to be able to represent handles and potential handles alongside the lookahead symbols. Each potential handle is presented by an <strong>LR(1) item</strong> which looks like <code>[a' -&gt; b' •  y. a]</code> where <code>a' -&gt; b' y</code> is a grammar production, • represents the position of the stacktop, and a is a terminal symbol. The set of items is called the <strong>canonical collection</strong>. Where we place • means different things.</p>
<ol>
<li>[a' -&gt; • b'y, a] = a' would be valid, if we find a b' next then we can discover a'. This item is called a <strong>possibility</strong> - it represents a possible completion for an input seen.</li>
<li>[a' -&gt; b'• y, a] = the parser has progressed from the state before and has recognized b'. This item is <strong>partially complete</strong></li>
<li>['a -&gt; b'y• , a] = the parser has found some b'y such that a' followed by a would be valid. If the lookahead symbol is a, then the item is a handle and the parser can reduce b'y to a'. This is a <strong>complete</strong> item.</li>
</ol>
<p>To build the canonical collection, a parser states with an initial state [Goal -&gt; • SomeExpr, eof] that is, the first handle that can be reduced such that the lookahead symbol is the end of the file. We then find every potential state change as a set. The two operations that can occur are taking a closure and computing a transition.</p>
<ol>
<li>A <strong>closure</strong> completes a state. It adds to the set of items any related implications. That is, anywhere <code>Goal -&gt; List</code> is valid, we can also add the productions of ways to derive <code>List</code>. So if we see <code>[Goal -&gt;•List, eof]</code>, we can say that both <code>[List -&gt; • List Pair, eof]</code> and <code>[List -&gt; • Pair, eof]</code> are valid.</li>
<li>To compute a transition on some symbol x, the algorithm should find the item of items that has •  before x, and pick the set of items that would transition •  AFTER x. This is the <strong>goto</strong> procedure.</li>
</ol>
<p>Pseudocode for closure</p>
<pre><code>function closure(s)
	while s is different from lastSeenS
		for each item in s where [a' -&gt; b' • nonTerminalC, a]
			for each production for nonTerminalC -&gt; y in ProductionList
				for each lookAhead in FIRST for someState
					s = union(s, ([nonTerminalC -&gt;• y, lookAhead]))
	
	return s
</code></pre>
<p>So if we had some grammar for math, we might start with <code>[Goal -&gt; • Expr, eof]</code> and the closure operator might add to the set</p>
<pre><code>[Expr -&gt; • Num, eof], [Expr -&gt; • Num, +], [Expr -&gt; + Expr, eof]
</code></pre>
<p>The goto procedure takes some set CCi in the canonical collection, a grammar symbol c, and computes the states that would be made if x was accepted in state i.</p>
<pre><code>goto(set, symbol)
	moved = null
	for item in set
		if item matches some [a' -&gt; b'• xAndExtra, a]
			moved = union(moved, [a-&gt;b'x• theExtra, a])
	return closure(moved)
		
</code></pre>
<p>It tests to see if it can move the acceptance past the lookahead symbol , and if it does adds the moved state into a total collection, then finds the closure over that.</p>
<h2>Conclusion</h2>
<p>This is a very high-level overview, ignoring a lot of error management. There are many other parsing algorithms, such as SLR and LALR. I handwaved over a couple of details in the parsing pseudocode for brevity, but I have found some notes / books that can help.</p>
<ol>
<li>https://tomassetti.me/guide-parsing-algorithms-terminology/#parsingAlgorithms</li>
<li>https://www.amazon.com/Parsing-Techniques-Practical-Monographs-Computer/dp/1441919015</li>
</ol>
]]></content:encoded></item>
<item><title>Recurse Center Day 9</title><link>https://nthomas.org/2020-08-20-Recurse-Center-Day-9/</link><guid>https://nthomas.org/2020-08-20-Recurse-Center-Day-9/</guid><pubDate>Thursday, 20 August 2020 00:00:00 +0000</pubDate><description>Week 2, Day 4 - A lighter day</description><content:encoded><![CDATA[<p>The Rust virtual machine works on binary and unary operations for double math, with grouping and precedence, from stdin OR reading from a file.</p>
<p><img src="./vm.gif" alt="Virtual Machine in action" /></p>
<p>I'm really excited with this. The steps are:</p>
<ol>
<li>Read into a String, which is a heap-allocated type in Rust.</li>
<li>The entry point hands the string over to the virtual machine which allocates a "chunk" and gives the chunk to the compiler.</li>
<li>The compiler loads that into a scanner and starts to parse token by token</li>
<li>When the compiler calls <code>advance</code>, the scanner grabs a token, hands the token back to the compiler and the compiler parses it - if it's a number, it creates a constant, if it's a parenthesis, it assumes grouping, etc.</li>
<li>The compiler writes these opcodes to the chunk</li>
<li>When the compiler is done grabbing tokens and turning into opcodes, it finishes and returns a compiler <code>InterpretResult</code></li>
<li>When the VM sees the compiler is done, the chunk is full of instructions</li>
<li>The VM then walks the instructions and works with an internal stack and instruction pointer which is the index of the next instruction to run</li>
</ol>
<p>Overall we are successful. Next step in the book is to add more types of values (bools, for example).</p>
<p>I also got Nand2Tetris week 2 done, which is really neat. Making an ALU from basic chips like Mux was great. I only got stumped when I forgot that a chip can have multiple outputs. Excited to try and get all the hardware stuff done during RC.</p>
<p><a href="http://dmitrysoshnikov.com/">Dmitry Soshnikov's</a> class on finite automata is also going well - I need to try and finish it this week, so I might make that my goal for tomorrow. Additionally, I need to finish the parsing chapter from Engineering a Compiler. Whew, lots of stuff to do and such little time!.</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 7</title><link>https://nthomas.org/2020-08-20-NYU-Tandon-Bridge-Week-7/</link><guid>https://nthomas.org/2020-08-20-NYU-Tandon-Bridge-Week-7/</guid><pubDate>Thursday, 20 August 2020 00:00:00 +0000</pubDate><description>C++ Pointers, dynamic arrays</description><content:encoded><![CDATA[<h2>Problem Solving with C++, 9.1</h2>
<p>A <strong>pointer</strong> is the memory address of a variable. If we declare <code>int x = 6;</code> a few things happen. The number 6 is created somewhere in memory (stack), then we assign a name to identify it. That name in a human way to read the location of that value. Calling a variable is the same as telling the machine "please go to this location and tell me what is stored there".</p>
<p>A pointer can be stored directly in a variable, known as a <strong>pointer variable</strong>. We declare these with the star sign, such as <code>int *p</code> - which is, p is the memory address of an integer. When we assign values to a pointer variable we do so by getting the address of another variable using the <strong>address-of</strong> operator.</p>
<pre><code class="language-cpp">// declare a pointer variable for an int

int &amp;p;

// declare a variable 
int x = 5;

// tell p to point  to the location of x
p = &amp;x;
</code></pre>
<p>Pointers would be useless if all they could do is point to a location. We'd like to tell the computer "please go to this location and get me the variable there". We call this <strong>dereferencing</strong> and it is called with the * operator again.</p>
<pre><code class="language-cpp">
int x = 0;
int *p = &amp;x;

*p = 42; // now we told the computer "go to the location at p and change the value to 42

cout &lt;&lt; *p &lt;&lt; endl; // this is now 42
cout &lt;&lt; x &lt;&lt; endl; // this is now 42
</code></pre>
<p>Additionally,  pointers can point to pointers which follow the memory address. That is</p>
<pre><code class="language-cpp">int *p1, *p2;
int v1 = 0;

p1 = &amp;v1;
p2 = p1; // p2 now points to v1's address
</code></pre>
<p>We don't actually need to even create a variable to point to in order for a pointer to be valid. We can use the <code>new</code> keyword to assign a pointer to a location holding an int. When we use the new keyword, we are creating a <strong>dynamic variable</strong></p>
<pre><code class="language-cpp">int *p = new *int;
</code></pre>
<p>Dynamic variables need to be held somewhere. That portion of unallocated memory is called the <strong>heap</strong> or <strong>freestore</strong>. It is possible to fill the heap with too many variables, which will cause the next call to <code>new</code> to crash the program. In order to prevent this crash, we use the <code>delete</code> keyword to free memory used by a variable.</p>
<pre><code class="language-cpp">int *p = new int;

*p = 10;

cout &lt;&lt; *p &lt;&lt; endl; //prints 10

delete p; // now the value of p is nothing
</code></pre>
<p>Note that we delete the pointer, not the dereferencing. Once a pointer is deleted, it is called a <strong>dangling pointer</strong> - don't dereference these!</p>
<p>The opposite of a dynamic program, one that has its lifetime handled by the compiler, is an <strong>automatic variable</strong> or <strong>ordinary variable</strong>.</p>
<p>We can use <code>typedef</code> to alias types to apply reader-semantics or domain-information to a type. That is <code>typedef double Kilometer</code> would allow us to then say <code>Kilometer distance = 123.45;</code></p>
<p>This also works for pointers. If we wanted to say that, for example, this pointer is pointing to an int on the heap, we could say <code>typedef int* IntPtr;</code> This also lets you declare multiple variables of pointer type easily, such as <code>IntPtr p1, p2;</code></p>
<p>If you want to pass a pointer into a function by reference, you need the &amp; sign to determine reference and * to annotate pointer type.</p>
<pre><code class="language-cpp">void functionthing(&amp;*int ptr);

// or
typedef *int IntPtr;

void otherfn(&amp;IntPtr ptr);
</code></pre>
<h2>Problem Solving with C++, 9.2</h2>
<p>A <strong>dynamic array</strong> is one that is heap-allocated and can grow and shrink in size according to the number of elements in the array.</p>
<p>We can think of the variable of an array as a pointer since it refers to the address of the first element in the array, but it CANNOT be reassigned to a pointer (unlike a real pointer). That is</p>
<pre><code class="language-cpp">int a[10];
typedef *int IntPtr;
IntPtr p;

p = a; // valid! P can point to a

a = p; // invalid!! a is not of type *int
</code></pre>
<p>To allocate an array on the heap, we use the <code>new</code> keyword like</p>
<pre><code class="language-cpp">double *p = new double [10];

// do stuff with array

delete [] p; // array notation to delete an array pointer
</code></pre>
<p>Note that deleting a pointer to an array must be <code>delete [] ptr;</code> - this tells the system to delete the entirety of the length of the array, rather than the first element.</p>
<p>We can use <strong>pointer arithmetic</strong> to index an array, that is</p>
<pre><code class="language-cpp">
double *p = new double [10];

for (int i = 0; i &lt;10; i++ ) {
	// go to location p, adds i * sizeof(double) and get value
	cout &lt;&lt; *(p + i) &lt;&lt; endl; 
} 


</code></pre>
]]></content:encoded></item>
<item><title>Recurse Center Day 6</title><link>https://nthomas.org/2020-08-17-Recurse-Center-Day-6/</link><guid>https://nthomas.org/2020-08-17-Recurse-Center-Day-6/</guid><pubDate>Monday, 17 August 2020 00:00:00 +0000</pubDate><description>Week 2, Day 1 - Pratt Parsers and lots o&apos; Rust</description><content:encoded><![CDATA[<p>Kicking off a new week! It's open source week at RC and I'm both excited at nervous. There are so many projects I'd want to work on, and so many people giving talks about projects. How am I going to make time to work on something? I've decided to attend a ton of talks, just to learn, but try to focus on the <a href="https://github.com/mirage/mirage">MirageOS</a> project. My OCaml is...not terrible, so hopefully I can find a small ticket or two and get a PR open.</p>
<p>My <a href="https://github.com/nt591/lox-rust/tree/master/src">Rust VM</a> is going well. I'm up to the tricky part, the Pratt parser. If I understand correctly, the way a Pratt parser works (more or less) is:</p>
<pre><code>Given tokens = a list of tokens
startingPrecedence = 0
rules = table mapping tokentype to prefixFunction, infixFunction, precedenceValue


function parsePrecedence(precedence) =
  while token in tokens
    kick off parsing with precedence argument
      get first token
      Look up the token's prefixFunction in the rules
        that is, it must be prefix because it's the first thing in the list
        if the first token is a + sign, that would be an error
        if that token is a number, for example, that will be valid since numbers can be considered prefix values
      run that prefixFunction (eg, if token is a number, run the compileNumberToken function, if the token is a minus, run the compileUnaryToken function)

      as long as our function precedence is &lt;= the next token's precedence
        consume next token
        get that token's infixFunction from our lookup table
        run infixFunction()

parsePrecedence(0)
</code></pre>
<p>note that prefixFunction, infixFunction, etc will ALSO call parsePrecedence with slightly higher precedences - that is, the infixFunction for multiplying might call parsePrecedence(20)</p>
<p>This means too that when when call parsePrecedence(20) for our multiplier it will say something like</p>
<ol>
<li>go get tokens, and keep generating compiler output as long as those are MORE important than me, since it's a recursive call</li>
<li>when you're done, eg when we see an addition, exit and let the calling function continue its lower precedence work</li>
</ol>
<p>So the workflow for</p>
<p>1 + 2 * 3 + 4</p>
<p>looks like</p>
<pre><code>get token 1
call makeNumber(1)
get precedence 0

look at next token, addition
  call addition infix which enters higher precedence
    get token 2
    call makeNumber(2)
    next token is *, which is higher predecence
      get token 3
      call makeNumber(3)
      next token is +, which is lower precendece - exit
    emit *
  emit +
look at next token, which is +
  call addition infix with which is higher precedence
    capture 4
    call makeNumber(4)
    done
    pop call stack
  emit +
done

</code></pre>
<p>If we were emitting opcodes for a stack VM, that might  end up looking like</p>
<p>[1, 2, 3, *, +, 4, +]</p>
<p>And that is, in an ugly nutshell, notes on a Pratt Parser.</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 5 part 2</title><link>https://nthomas.org/2020-08-15-NYU-Tandon-Week-6/</link><guid>https://nthomas.org/2020-08-15-NYU-Tandon-Week-6/</guid><pubDate>Saturday, 15 August 2020 00:00:00 +0000</pubDate><description>C++ Strings and Arrays; Probability, conditional probability, random variables, Bernoulli trials</description><content:encoded><![CDATA[<h2>Module 9 - Arrays</h2>
<p>The basic properties of arrays</p>
<ol>
<li>Arrays are stored continuously in memory (one uninterrupted section)</li>
<li>Elements are all of the same type</li>
<li>We access elements via 0-based index</li>
</ol>
<p>The compiler uses the index as an offset, so the math is</p>
<p>addressOfElementAtIdxI = addressOfStart + i * sizeOfEachElement</p>
<p>This downsize of this math is that we can access <strong>arbitrary memory</strong> by passing any index in - if i is out of bounds, it'll basically say "go to the start of the array, go to i * size and get what's there" EVEN if i is not in the array. Security flaw in business logic.</p>
<p>These arrays are technically <strong>static arrays</strong> (we'll cover dynamic arrays later) - static arrays are stack allocated. As a result</p>
<p><strong>Array size MUST be a compile-time constant and given at declaration</strong></p>
<p>So how would we calculate the average of a list of grades AND the number of grades above the average?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

const int MAX_SIZE = 60;
int main() {
	int numberOfStudents;
	cout &lt;&lt; "Enter the number of students in the grade (max " &lt;&lt; MAX_SIZE &lt;&lt; "): ";

	cin &gt;&gt; numberOfStudents;

	if (numberOfStudents &gt; MAX_SIZE) {
		//error
	}

	int grades[MAX_SIZE];
	int sum;

	for (int i = 0; i &lt; numberOfStudents; i++) {
		int grade;
		cin &gt;&gt; grade;
		sum += grade;
		grades[i] = grade;
	}

	double average = (double)sum / numberOfStudents;
	cout &lt;&lt; "the class average is " &lt;&lt; average;
	cout &lt;&lt; " and the grades above that are ";

	for (auto grade: grades) {
		if (grade &gt; average) {
			cout &lt;&lt; grade &lt;&lt; " ";
		}
	}

	return 0;
}
</code></pre>
<h2>Problem Solving with C++, 7.1</h2>
<p>An <strong>array</strong> provides an interface over a finite collection, or list of elements that are all of the same type T. For example, if we want to see a list of all the receipts I have from Ample Hills (oh no), we might declare <code>float receipts[12];</code></p>
<p>Our syntax declares the TYPE of the element, the name of the collection, and the size of the array. Arrays are fixed size in C / C++ due to stack allocation, and needing the size to be known at compile time.</p>
<p>We can access variables by their <strong>index</strong>, or position by order, starting from 0 as the first element. That is, the first expense I have at Ample Hills would be <code>receipts[0]</code> and the last <code>receipts[11]</code></p>
<p>We can declare arrays alongsize other variables with something like <code>int height, sandwiches[5], weight</code>. Array index accessors also allow us to write to an array, which looks like <code>sandwiches[5] = 100;</code></p>
<p>We can read and write from arrays if we know the length ahead of time. That might look like</p>
<pre><code class="language-cpp">const int NUMBER_OF_ICE_CREAM_FLAVORS = 10;
float iceCreamCosts[NUMBER_OF_ICE_CREAM_FLAVORS];

for (int i = 0; i &lt; NUMBER_OF_ICE_CREAM_FLAVORS; i++) {
	cout &lt;&lt; "The ice cream at " &lt;&lt; i &lt;&lt; " is $" &lt;&lt; iceCreamCosts[i];
}
</code></pre>
<p>How does array access really work?</p>
<p>Imagine the following declaration</p>
<p><code>int arrayThing[5];</code></p>
<p>This tells the C++ compiler "reserve space in memory large enough for 5 integers and tell me where the first element is is memory". That is, the array value is really a <strong>memory address</strong>; a location in memory where the array starts. Thus, saying <code>arrayThing[0]</code> is the same as "to go the location at arrayThing and tell me what's there". But how does <code>arrayThing[3]</code> work? This is the same as telling the compiler "Go to the location of arrayThing, find out how many bytes is the size of 3 of the base type int, then add that value"</p>
<p>That is, if <code>arrayThing</code> is located at 100, and an int is 4 bytes large, this tells the compiler "Go to 100 + 3 * 4 and get me that value".</p>
<p>Out of range accessing values is <strong>illegal</strong> and can return garbage values, or allow bug exploitation.</p>
<p>In order to be safe, if we ask a user to tell us where in the array their element is we must validated, to avoid giving them access to arbitrary memory.</p>
<p>We can declare arrays with size and elements by using braces and commas like <code>int children[3] = {2, 12, 1}</code></p>
<p>C++ introduces a range based for loop with</p>
<pre><code class="language-cpp">for (int child: children) {
	cout &lt;&lt; child;
}
</code></pre>
<p>That is, we declare the data type and the name of the variable to identify the element in the array. Note that the variable is pass-by-value - you cannot change the array. You CAN declare it with a pass-by-reference style such as</p>
<pre><code class="language-cpp">for (int &amp;child: children) {
	cout &lt;&lt; child;
}
</code></pre>
<p>C++ also allows the <code>auto</code> keyword to infer type.</p>
<pre><code class="language-cpp">for (auto child: children) {
	cout &lt;&lt; child;
}
</code></pre>
<h2>Problem Solving with C++, 7.2</h2>
<p>Sending an array-index-accessor into a function is valid. That is</p>
<pre><code class="language-cpp">bool isOne(int num) {
	return num == 1;
}

int arr[5] = {1, 2, 3, 4, 5};

isOne(arr[0]);
</code></pre>
<p>Is syntactically correct. The index accessor will be evaluated prior to function call.</p>
<p>We can also pass in entire arrays as a function argument. They end up behaving similar to pass-by-reference (that is, functions can write to them)</p>
<p>A function parameter can be an array of some type T, and the function can also take array size for iteration</p>
<pre><code class="language-cpp">void writeToArray(int arr[], int size) {
	for (int i = 0; i &lt; size; i++) {
		arr[i] = 100;
	}
}
</code></pre>
<p>Due to the way arrays are stored in memory, you need to tell a function how many elements are in the array. Specifically, when you pass an array into a function technically all you're doing is telling the function where in memory the array starts, and how large each element is in bytes. You don't pass the third piece of array-information, the size. So you must do that explicitly.</p>
<p>Since functions can modify arrays passed as arguments, we might want to prevent the code from doing so for compiler-level safety. To do that, we use the <code>const</code> keyword to declare a <strong>constant array parameter</strong>. That might look like</p>
<pre><code class="language-cpp">void readArray(const int arr[], int size) {
	for (int i = 0; i &lt; size; i++) std::cout &lt;&lt; arr[i];
}

readArray(someArray, 10);
</code></pre>
<h2>Problem Solving with C++, 7.3</h2>
<p>Sometimes we don't know at compile time exactly how much memory we need. So we allocate a massive space, one that would cover more than the use case. In this case, we end up with <strong>partially filled arrays</strong>. When this happens, the unused space (that is, the end range that we don't populate, the excess) is garbage. We must be careful to NOT access that, by tracking and only using the real value that the user put in.</p>
<h2>Module 10 - Strings</h2>
<p>C++ has its own string type, which we can use with the directive <code>#include &lt;string&gt;</code>. Strings are double quoted and respond to concatenation with the plus operator. We can read strings with <code>std::cin</code> and write to stdout with <code>std::cout</code>. <code>cin</code> by default only reads up to a space, so keep that in mind.</p>
<p>If we want to capture an entire line, we use</p>
<pre><code class="language-cpp">int main() {
	string str;
	cout &lt;&lt; "enter your name: ";
	getline(cin, str);
	cout &lt;&lt; "Your name is: " &lt;&lt; str;
	return 0;
}

</code></pre>
<p>Strings can be read with index-accessors like arrays. A string is effectively a sequence of characters, so each element is a <code>char</code>.</p>
<p>We can also slice (find substrings) with a start (inclusive) and length.</p>
<p><code>str.substr(startIdx, len);</code></p>
<p>Strings also have a length method on the class, such as <code>str.length()</code></p>
<p>How can we print a string backwards?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

int main() {
	string str;
	cout &lt;&lt; "Enter your name: ";
	getline(cin, str);
	int len = str.length();

	for (int i = len-1; i &gt;= 0; i--) {
		cout &lt;&lt; str[i];
	}
	return 0;
}
</code></pre>
<p>Strings have lexicographic order based on ASCII code from index 0 to length. That is</p>
<pre><code>// pseudocode

for i in min(str1.len, str2.len)
	if str1[i] &lt; str2[i]
		return str1 is less
	else if str1[i] &gt; str2[i]
		return str2 is less
	else
		continue loop

if not yet returns
	shorter string is less, return that
</code></pre>
<p>Strings also have a <code>find</code> method to see if the substring of a string exists and where the starting index is (first appearance, in case of duplicates).</p>
<pre><code class="language-cpp">	string test = "bananananna"
	int startingIndex = test.find("anna");
	bool notFound = test.find("notInside") == string::npos;

</code></pre>
<p>Find can also take an optional starting index as the second param, which will start looking AFTER the second param such as <code>str.find(sample, idxToStart);</code></p>
<h2>Problem Solving with C++, 8.2</h2>
<p>Strings are arrays of characters terminated with the null byte <code>'\0'</code>. We include strings via the string directive, which is in the std library so we'll need to add the namespace std, or access via <code>std::string</code></p>
<p>We can read all input from a user into a string with the <code>getline</code> function, which stops reading when it hits a newline <code>'\n'</code> We can change that to read until some OTHER character, such as <code>getline(std::cin, stringVar, '?')</code></p>
<p>String accessors can be protected at runtime with <code>str.at(idx)</code> which throws a runtime error rather than reading garbage values with bracket notation. It also allows you to write with <code>str.at(idx) = 'a'</code>;</p>
<h2>Discrete Math, 6.1</h2>
<p>Language:</p>
<p><strong>Experiment</strong> - a procedure that results in one out of a number of possible <strong>outcomes</strong></p>
<p>The set of all outcomes is the <strong>sample space</strong>.</p>
<p>A subset of the sample space is an <strong>event</strong>.</p>
<p>That is, an experiment could be flipping two coins. The set of opttions, the sample space, is {HH, HT, TH, TT}. The event of at least one head is {HH, HT, TH}.</p>
<p><strong>Discrete probability</strong> is concerned with experiments where the sample space is countably finite or countability infinite. What does the latter mean?</p>
<p>A set is <strong>countably infinite</strong> if there's a one-to-one correspondence between the elements of the sets and the integers. That is, it's possible to assign every element in a set to an integer and eventually reach some value by counting, no matter how long it takes. Think about binary strings. We can assign 0=0, 1=1=, 10=2, 11=3, ... so on and so forth and, as long as we progress in the set, can reach some integer N.</p>
<p>Probability is concerned with likelihood. Every event has a probability, and the sum of all event probabilities is 1.</p>
<p>A <strong>uniform distribution</strong> occurs when the probability of every outcome is equal (a dice roll, for example). In a uniform distribution, if every outcome is equally weighted, then the probability of an event is the number of outcomes in the event divided by the total number of events in the sample space.</p>
<p>That is, the probably of 2 or lower when rolling a die is</p>
<p>|{1, 2}| / |{1, 2, 3, 4, 5, 6}| = 2/6 = 1/3</p>
<h3>6.1.1</h3>
<p>b) {hhtt, thht, tthh} = 3 outcomes / 16 sample = 3/16</p>
<p>c) {thhh, thht, tthh} = 3 outcomes = 3/16</p>
<h3>6.1.2</h3>
<p>b) There are n! total ways to order the group. If Ceiia is first and Felicia is last, there are (n-2)! possible middle-orderings. Thus, 1 / (n(n-1))</p>
<h3>6.1.3</h3>
<p>a) There are 2n total socks. We need to choose two. thus 2n choose 2, or n(2n - 1)</p>
<p>b) The ways to pick 2 white socks is n choose 2. The ways to pick 2 black socks is n choose 2. Thus 2(n choose 2) or n (n-1)</p>
<p>c) The total number of ways to pick socks is n(2n -1). The ways to pick matching pairs is n(n-1). Thus, the probability of matching pairs is (n-1) / (2n-1)</p>
<h2>Discrete Math, 6.2</h2>
<p>If two events have no overlap, we call them <strong>mutually disjoint</strong>. That is, with two coin flips, the events where we have exactly one head and exactly two heads. Then we can add these probabilities to find the probability of at least one head over two coin flips. This ONLY works when the intersection of events is zero. That is</p>
<p>The probability of exactly one head over two coin flips is .5 {TH, HT}. The probability of exactly two heads is .25 {HH}. The sum is .75</p>
<p>We can formalize as</p>
<p>P(E1 or E2) = P(E1) + P(E2)</p>
<p>If two events are NOT mutually exclusive we can use the <strong>Inclusion-Exclusion</strong> principle as</p>
<p>P(E1 or E2) = P(E1) + P(E2) - P(E1 and E2)</p>
<p>(this is the same as the above, except in a mutually disjoint case, P(E1 and E2) = 0)</p>
<p>Often times, we can use the <strong>complement</strong> of an event to determine our probability - that is, it's easier to reason about the probability an event DOESN'T happen. Then we can calculate</p>
<p>P(E') = 1 - P(E)</p>
<h3>6.2.1</h3>
<p>a) Probability at least n-1 heads in n coin flips? Same as odds of no tails + odds of exactly one tails. Odds of no tails = 1/(2^N) and odds of exactly one tails = n / (2^n) -&gt; sum = (n+1) / (2^n)</p>
<p>b) Odds at least two consecutive coin flips are the same? What are odds no consecutive coin flips are the same? 2 / 2^n (HTHTHTH... or THTHTH....). So complement is 1 - (2 / 2^n)</p>
<h3>6.2.2</h3>
<p>b) Prob of Celia first = 1/n. Prob of Felicity last = 1/n. Prob of Celia first AND Felicity last = (n-2)! / n! = 1 / n(n-1). Make Celia  and Felicity n-1 / n(n-1), sum to 2(n-1)/n(n-1) - 1 = 2n-3 / n(n-1)</p>
<h2>Discrete Match, 6.3</h2>
<p>Sometimes we want to discuss the probability of a thing happening predicated on another event. That is, two coin flips - what are the odds of 2 heads? 1/4. What are the odds of 2 heads, given we flipped one coin and got heads already? 1/2.</p>
<p>We use <strong>conditional probability</strong> to denote the probability of event F happening given some event E happening, that is p(E|F) - or given E, F.</p>
<p>The formula is</p>
<p>p(E |F) = p(E and F) / p(F)</p>
<p>Which can be simplified to</p>
<p>|E and F| / |F|</p>
<p>For our coin flips, the event E = two coin flips are {HH}. F = first coin is heads. |E and F| = 1 ({HH}. |F| = 2 {HH, HF}. Therefore, 1/2</p>
<p>Another example, let E and F = two dice rolls, the first is 5 and the sum is at least 11. F = event that the first die is 5.</p>
<p>|F| = all the possible two-die rolls where the first is 5. That is, (5,1), (5,2)... = |F| = 6</p>
<p>|E and F| = all the possible ways to sum to 11 or higher, given one die is 5 =&gt; 1</p>
<p>1/6</p>
<p>Essentially, we are narrowing down probabilities given some event F happening. Like...the probability I get 100 on an exam AND get accepted into a masters / probability I get 100 on an exam</p>
<p>Two events are <strong>independent</strong> if conditioning on one event does not change the probability of the other. Think about two dice rolls. Probabiliy both are the same? 6/36 = 1/6. Probability both are same, given first is 5? Well if first is 5 the second must be 5 -&gt; 1, 1/6. These rolls are <strong>independent events</strong>. Specifically, the odds of the die rolling a 5 on the first roll does not impact the odds of a 5 on the second roll.</p>
<p>If two events are independent then the probabiliy of both happening is the product of the probabiliyt of each happening.</p>
<p>p(X and Y) = p(X) * p(Y)</p>
<p>If EVERY event in a set are indepedent from each other, we say they are <strong>mutually independent</strong> and calculate</p>
<p>p(A1 and A2 and ... and An) = p(A1) * p(A2) * ... * p(An)</p>
<h3>6.3.1</h3>
<p>a)</p>
<p>p(A) = 1/2</p>
<p>p(B) = {46, 55, 56, 64, 65, 66} = 6/36 = 1/6</p>
<p>p(C) = 1/6</p>
<p>b) |A and C| = |{55, 53, 31}| = 3. |C| = 6. 3/6 = 1/2</p>
<p>c) |B and C| = |{55, 56}| = 2. |C| = 6. 2/6 = 1/3.</p>
<p>d) |A and B| = |{46, 55, 64, 66}|. |B| = |{46, 55, 56, 64, 65, 66}|. 4/6 = 2/3</p>
<h3>6.3.3</h3>
<p>a) There are 8! total ways to line up the party. To have bride and groom next to each other, we have 7! total variations, times 2 for bride before groom or vice versa. = 2 / 8 = 1/4</p>
<p>b) There are 7! ways to arrange a group with MoH on the left. There are 8! total arrangements. 1/8</p>
<p>c) p(A) * p(B) = 1/32. P(A and B) = 2 * 6! total arrangements of MoH on left and BG together / 8! total. 2 / (8 * 7) = 1 / 28. Not independent.</p>
<h3>6.3.5</h3>
<p>Total number of hands is 52 choose 5.</p>
<p>A - Total number of ways to pick a 4-of-a-kind is equal to (13 choose 1) * 48 - picking one rank and 1 of the leftover cards.</p>
<p>B - Ways to pick from no aces = 48 choose 5, at least one ace = 1 - 48 choose 5.</p>
<h2>Discrete Math, 6.4</h2>
<p><strong>Bayes' Theorem</strong> is a way of reasoning about probability based on the evidence of outcomes.</p>
<p>Specifically, Bayes's theorem lets us theorize about p(F | X) if we know p(X | F), p(X | F'), and p(F).</p>
<p>Assuming F = a fair die is selected, and F' = an unfair die loaded with 2/7 probability of rolling a 6, and X = a die rolls 6.</p>
<p>What is the probability of selected a fair die and rolling a 6 given rolling a 6?</p>
<p>Well, if we know the probability of rolling a 6 and selecting a fair die given a fair die (1/6), the probability of rolling a 6 and selecting an unfair die given an unfair die (2/7), and the probability of a fair die (1/2).</p>
<p>we can calculate</p>
<p><img src="./bayes.png" alt="Bayes Theorem" /></p>
<p>the numerator = 1/6 * 1/2 = 1/12</p>
<p>The denominator = 1/12 + (2/7) * (1/2)</p>
<p>The probability then = ~0.37</p>
<p>5/72 / (5/72 + 10/98) = 49/121</p>
<h3>6.4.1</h3>
<p>a)</p>
<p>Let F = biased coin</p>
<p>p(B) = 1/2. P(X | B) = (3/4)^7 * (1/4) ^ 3.. P(X | B') = (1/2) ^ 10</p>
<p>( ((3/4)^7 * (1/4) ^ 3) * 1/2) / ((3/4)^7 * (1/4) ^ 3 * 1/2) + 1/2 * (1/2) ^ 10)</p>
<p>0.00104 / (0.00104 + 0.00048)</p>
<p>0.6842</p>
<h3>6.4.2</h3>
<p>Let F = fair die</p>
<p>p(F) = 1/2</p>
<p>p(X | F) = (1/6)^6</p>
<p>p(X | F') = 0.25^2 * 0.15^4</p>
<p>(1/6)^6 * 1/2 / ((1/6)^6 * 1/2 + 0.25^2 * 0.15^4 * 1/2)</p>
<p>0.4038</p>
<h2>Discrete Math, 6.5</h2>
<p>A <strong>random variable</strong> X is a function from the sample space S to an experiment of real numbers, where X(S) is the range of the function X.</p>
<p>If two dice are rolled, the random variable D is the sum of the rolls, so D(x, y) = x + y</p>
<p>If X is a random variable and r is a real number, then X = r is an event. That is, for rolling two dice, if D(x+y) = 5, then that's an event with the subset of {(1, 4), (3, 2), (4, 1)}</p>
<p>The <strong>distribution</strong> of a random variable is the set of all pairs (r, p(X = r)) for all r in X(S)</p>
<h3>6.5.1</h3>
<p>a) {1, 2, 3, 4, 5, 6, 4, 8, 10, 12, 9, 15, 18, 16, 20, 24, 25, 30, 36}</p>
<p>b) 4 / 36 = 1/9</p>
<h2>Discrete Math, 6.6</h2>
<p>The <strong>expected value</strong> of a random variable is defined as</p>
<p>E[X] = the sum of all X(s) * p(x) for all s in the sample space.</p>
<p>That is, the expectation of a random variable is equal to the sum of all elements in the range * the probability we will see them.</p>
<p>If X = winning 100_000_000 dollars in the lottery, and only one ticket can win, and the number of possible tickets are 50^6</p>
<p>then E[X] = 100_000_000 * (1 /  50^6) + 0 * (1 - 1 /  50^6) = ~0.0064</p>
<p>This can also be calculated as</p>
<p>E[X}] = r * p(X = r) for all r in X(S).</p>
<h3>6.6.1</h3>
<p>Let |S| = 10 choose 2 = 45</p>
<p>Number of ways to pick two girls = 7 choose 2 = 21</p>
<p>Number of ways to pick 1 girl = 7 choose 1 * 3 choose 1 = 21</p>
<p>E[G] = 2 * (21/45) + 1 * (21/45) = 63/45 = 21/15 = 7/5</p>
<h3>6.6.2</h3>
<p>E[D] = 2 * 1/6 + 1 * 1/6 + -1 * 4/6 = -0.17</p>
<h2>Discrete Math, 6.7</h2>
<p><strong>Linearity of expectations</strong> says the expectation of the sum of two random variables is equal to the sum of the expectations.</p>
<p>If X and Y are two random variables in S, and c is some real number constant</p>
<p>E[X + Y] = E[X] + E[Y]</p>
<p>E[cX] = cE[X]</p>
<p>From the book</p>
<p>Two friends run a lemonade stand over the summer. It takes 10 lemons to make a batch of lemonade. Usually, lemons are 50 cents apiece, but with probability 1/4, they are on sale for 40 cents apiece. If there is a baseball game at the park where they put up their stand, they will sell $20 worth of lemonade. If there is no baseball game, they will sell $10 worth of lemonade. On a given day, there is a baseball game with probability 1/2. What is the expected profit?</p>
<p>E[P] is the same as E[R - C] where R is revenue and C is cost</p>
<p>E[R - C] = E[R] - E[C]</p>
<p>E[R] = 0.5 * 20 + 0.5 * 10 = 15</p>
<p>E[C] = 3/4 * 5 + 1/4 * 4 = 4.75</p>
<p>15 - 4.75 = 10.25</p>
<h2>Discrete Math, 6.8</h2>
<p>A <strong>Bernoulli trial</strong> is an experiment wiht two outcomes - <strong>success</strong> and <strong>failure</strong>.</p>
<p>In a sequence of trials, called a <strong>Bernoulli process</strong>, the outcomes of the experiments are assumed to be mutually independent and have the same probability of success and failure. Success denoted by p and failure as 1-p</p>
<p><strong>The probability of exactly k successes in a sequence of n independent Bernoulli trials, with probability of success p and probability of failure q = 1 - p is</strong></p>
<p><img src="./bernoulli.png" alt="Bernoulli" /></p>
<p>The distribution over the random variable defined by the number of successes in a sequence of independent Bernoulli trials is called the <strong>binomial distribution</strong>. The probability that the number of successes is k in a sequence of length n with probability of success p is denoted by b(k; n, p).</p>
]]></content:encoded></item>
<item><title>Recurse Center Day 3</title><link>https://nthomas.org/2020-08-12-Recurse-Center-Day-3/</link><guid>https://nthomas.org/2020-08-12-Recurse-Center-Day-3/</guid><pubDate>Wednesday, 12 August 2020 11:24:07 +0000</pubDate><description>A whole lotta inspiration</description><content:encoded><![CDATA[<p>Had a really condensed and productive day today. The Hack n Tell model is really perfect for these short bursts of focused energy, but now my brain is tired.</p>
<p>Spent the morning just reading lightly. Nothing too major. There was the 11:00am check in, then a 11:30 compilers study group planning session. We're going to read Engineering a Compiler as two chapters a week, and there will also be a 1x a week meeting on Tuesdays just to discuss open source projects to contribute to. LLVM sounds really interesting, I may try to get my hands dirty in a week or two.</p>
<p>During the first Hack n Tell sprint, I managed to write some more Rust and finish chapter 15 of Crafting Interpreters. We have a very small stack based virtual machine. I'm hesitant to call it bytecode, as I'm using Rust's typesystem to capture constants as a type rather than pushing two ops into an array. But it's the spirit of the law, I guess. Overall I'm happy with the work. Rust is slowly starting to become syntactically natural with practice. Admittedly I'm sure it's not all idiomatic, but that's okay. Diff <a href="https://github.com/nt591/lox-rust/commit/71c188828c83ce425f1b0f6aa03d13083216ed3f">here</a>.</p>
<p>For the second Hack n Tell, I hit a wave of inspiration from the compiler study group meeting (inspiration from community is the best part of RC, it seems). I remembered <a href="https://romefrontend.dev/">Rome</a> - I've got a lot of respect for @sebmck, and figured if I want to get my hands in a big project that's a developer toolchain application, why not Rome? It's early, they'll need help. Luckily there's a LOT of missing tests, so after hopping in the Discord I opened up a <a href="https://github.com/romefrontend/rome/pull/1047">pull request</a>. I'm hoping to make 4-5 contributions during RC. Ideally not all tests, but it at least lets me navigate the code base a bit.</p>
<p>Tomorrow I'll probably chill a bit - I did the reading for Nand2Tetris week 1 today and want to watch the videos. The calendar looks less hectic so I'll probably just consume some learning, MAYBE another test case.</p>
]]></content:encoded></item>
<item><title>Recurse Center Day 2</title><link>https://nthomas.org/2020-08-11-Recurse-Center-Day-2/</link><guid>https://nthomas.org/2020-08-11-Recurse-Center-Day-2/</guid><pubDate>Tuesday, 11 August 2020 11:24:07 +0000</pubDate><description>Making a little progress</description><content:encoded><![CDATA[<p>Today I spent the majority of my day reading and coding. After tweeting a <a href="https://twitter.com/nikhilthomas90/status/1293007737048236033">few folk</a> I decided to switch my textbook over to <a href="https://www.oreilly.com/library/view/engineering-a-compiler/9780080916613/">Engineering a Compiler</a> by Keith Cooper and Linda Torczon. I've read the first two pages, but admittedly scanner theory is a little tough. I'll need to write a blog post on finite automata to really drill it in.</p>
<p>I also began working on Crafting Interpreters, specifically the bytecode VM, in Rust. Repo <a href="https://github.com/nt591/lox-rust">here</a>. This will give me something to fall back to just to make sure I'm writing code regularly. I'd like to finish chapter 15 this week (introducing the virtual machine), and then also kick off Nand2Tetris. I DO have an exam Thursday, so I may take a bit easier just to study.</p>
<p>If I can go pretty deep into compiler stuff in the first 3-4 weeks, I can use the last couple of weeks to read some papers and MAYBE learn some Go along the way.</p>
<p>Having fun though!</p>
]]></content:encoded></item>
<item><title>Recurse Center Day 1</title><link>https://nthomas.org/2020-08-10-Recurse-Center-Day-1/</link><guid>https://nthomas.org/2020-08-10-Recurse-Center-Day-1/</guid><pubDate>Monday, 10 August 2020 11:24:07 +0000</pubDate><description>A life check in</description><content:encoded><![CDATA[<p>I'm attending the Fall 1 2020 half batch of <a href="http://recurse.com/">Recurse Center</a>. I'm taking a break between jobs before joining Facebook, and I've been meaning to find time to nerd out for fun rather than money.</p>
<p>I'm interested in the idea of "developer infrastructure" organizations and teams, so I'll be digging into projects and lectures related. Specifically, I'd like to</p>
<ul>
<li>Implement most of <a href="https://www.cs.princeton.edu/~appel/modern/ml/">Modern Compiler Implementation in ML</a> in OCaml</li>
<li>Read a ton of distributed systems papers from this MIT <a href="https://pdos.csail.mit.edu/6.824/schedule.html">syllabus</a></li>
<li>Keep working through the CS-537 <a href="http://pages.cs.wisc.edu/~remzi/Classes/537/Spring2018/Discussion/videos.html">videos</a> and learn more about operating systems</li>
<li>Maybe implement some of those scheduler algorithms</li>
</ul>
<p>I've got 6 weeks to get through this, PLUS embrace all the serendipity of being around new people who are also curious. I'll be nice to myself if I don't make it through it all, but I think just the act of learning and being out of my comfort zone will do a lot for me. Plus - there's a lot of RC alum I respect a ton like <a href="https://danluu.com/">Dan Luu</a> and <a href="https://jvns.ca/">Julia Evans</a>. I'm a pretty big believer that if you want to be where someone is, you should do what they did when they were in your shoes. So, here's hoping we learn something.</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 5 part 2</title><link>https://nthomas.org/2020-08-05-NYU-Tandon-Bridge-Week-5-Redux/</link><guid>https://nthomas.org/2020-08-05-NYU-Tandon-Bridge-Week-5-Redux/</guid><pubDate>Wednesday, 05 August 2020 11:24:07 +0000</pubDate><description>Functions - analysis, runtime stack, abstractions; counting - combinatorics, permutations and combinations</description><content:encoded><![CDATA[<h2>Module 7 - Functions</h2>
<p>We introduce the <strong>k-combinations</strong> problem, that is, for some non-negative numbers n and k where n is the size of the collection (number of cards in a deck, number of dishes in a buffet line) and k is the size of the combination and n &gt;= k, we define the number of possible unordered ( aka "red blue green" and "blue green red" are unique) combinations - <strong>n choose k</strong>  - as (n! / (k! * (n-k)!)),</p>
<p>For example, if we have a collection of n = 5 objects and want to know all the number of k = 3 possible combinations, we calculate (5! / (3! * 2!)) or 120 / 12 = 10 possible combinations.</p>
<p>The mathematical notation looks like:</p>
<p><img src="./n-choose-k.png" alt="n-choose-k formula" /></p>
<p>If we wanted to do this with while loops, we can formulate as</p>
<pre><code class="language-cpp">int main() {
	int n, k;

	cout &lt;&lt; "enter n and k"
	cin &gt;&gt; n &gt;&gt; k
	int nFact = 1;
	for (int i = 1; i &lt;= n; i++) nFact *= i;

	int kFact = 1;
	for (int i = 1; i &lt;= k; i++) kFact *= i;

	int nkFact = 1;
	for (int i = 1; i &lt;= (n-k); i++) nkFact *= i;

	cout &lt;&lt; "the answer is " &lt;&lt; (nFact / (kFact * nkFact);
	return 0;
}
</code></pre>
<p>There's repeated logic here that distracts us and doesn't add expressivity in the factorial logic. We now learn how to make our own functions.</p>
<p>We need a function we'll call factorial that takes an int that represents the number we want the factorial of, and return an int as the answer. So we define a header which looks like</p>
<pre><code class="language-cpp">/*
	RETURN_TYPE FUNCTION_NAME(ARG_TYPE ARG_NAME, ...) {
		FUNCTION BODY
		RETURN
	}
*/

int factorial (int num) {
	int result = 1;
	for (int i = 1; i &lt;= num; i++) {
		result *= i;
	}
	return result;
}
</code></pre>
<p>Using these functions, we can clean up our main function as</p>
<pre><code class="language-cpp">int main() {
	int n, k;

	cout &lt;&lt; "enter n and k"
	cin &gt;&gt; n &gt;&gt; k
	int nFact = factorial(n)

	int kFact = factorial(k)

	int nkFact = factorial(n - k)

	cout &lt;&lt; "the answer is " &lt;&lt; (nFact / (kFact * nkFact);
	return 0;
}
</code></pre>
<p>Function calls then can be considered their own control flow, by jumping to a position we've been in (the function block) and return later.</p>
<p>Using the <strong>runtime stack</strong> model, we can understand the execution of this function. Our main program will exist in a <strong>frame</strong> on the stack with all local variables (nFact, kFact, etc). So n=5 and k=3 would get stored in the frame, then the calls to factorial need to be <strong>evaluated</strong> before nFact can be assigned a value. So we create another frame for factorial with local variables, along with the <strong>return address</strong> of where we go to when the factorial function is done. Then we associate the arguments of the function with the parameters - that is, <code>factorial(5)</code> should tell the frame that <code>num = 5</code>.  During evaluation, the frame only has access to its own variables, it CANNOT access variables in another frame. After evaluation, we <strong>pop</strong> the frame off the stack and jump back to the return address with the value of the function.</p>
<p>This chapter of <a href="https://craftinginterpreters.com/calls-and-functions.html">Crafting Interpreters</a> is a great but detailed explanation of an implementation.</p>
<p><strong>Note</strong>: C++ functions must be declared before usage. So you cannot write the main function at the top of a file and have functions below. Either write in another file and include, or just define it above the main function. We can also just declare the header inline ahead, something like</p>
<pre><code class="language-cpp">int factorial(int num); // no body, only header

int main() {
	// using factorial
}

int factorial(int num) {
	// actual implementation
}
</code></pre>
<p>Aside: a function that does not return (only does I/O) has a return type of <code>void</code>.</p>
<p>When we pass variables into functions, it is default <strong>pass by value</strong> (sometimes called <strong>call by value</strong>). That is, a function that receives variables cannot <strong>mutate</strong> those variables. It can only act with the values.</p>
<p>If we want to pass parameters into functions, we can also use <strong>pass by reference</strong> or <strong>call by reference</strong> with an <strong>address-of</strong> operator in the function, eg <code>void func(int &amp;x)</code> . So, if we wanted a function that swapped the values of two parameters:</p>
<pre><code class="language-cpp">void swap(int &amp;a, int &amp;b) {
	int temp = a;
	a = b;
	b = temp;
}
int main() {
	int a = 1;
	int b = 2;
	swap(a, b);
	return 0;
}

</code></pre>
<p>In this example, the <code>swap</code> stack frame hold <strong>references</strong> to a and b rather than the values. This means that when swap makes changes, it changes the variables in the <code>main</code> stack frame rather than its local stack frame.</p>
<p>Okay, new program. Take a positive integer <code>num</code> and write the number of digits and sum of digits. We can't just have one function that returns TWO values (number of digits and sum of digits). So instead we can use call-by-reference to pass in the values and update in the function</p>
<pre><code class="language-cpp">
void analyzeDigits(int num, int &amp;count, int &amp;sum);

int main() {
	int num, count, sum;
	cout &lt;&lt; "enter a positive integer: ";
	cin &gt;&gt; num;

	analyzeDigits(num, count, sum);
	cout &lt;&lt; "the number of digits in " &lt;&lt; num &lt;&lt; " is ";
	cout &lt;&lt; count;
	cout &lt;&lt; " and the sum is ";
	cout &lt;&lt; sum;

	return 0;
}

void analyzeDigits(int num, int &amp;count, int &amp;sum) {
	count = 0;
	sum = 0;
	while (num &gt; 0) {
		int digit = num % 10;
		num /= 10;
		count++;
		sum += num;
	}
}

</code></pre>
<h2>Problem Solving with C++, Chapter 4</h2>
<p>The <strong>top-down design</strong> or <strong>stepwise refinement</strong> of a problem it to divide it into smaller and smaller subproblems that become trivial to implement, and then stitch those steps together. In C++, we can separate these subproblems into <strong>functions</strong>.</p>
<p>C++ has built in libraries with predefined functions that we've used before, like <code>sqrt</code> from <code>cmath</code>. A function has <strong>arguments</strong> attached to <strong>parameters</strong> and also (often) has a <strong>return value</strong>. When we use a function, we <strong>call</strong> or <strong>invoke</strong> it, such as <code>int root = sqrt(3.0)</code></p>
<p>When we bring a function into a file, we can add it via <strong>include directives</strong> a la <code>#include &lt;cmath&gt;</code>. The value in the angled brackets is the name of a file called the <strong>header file</strong> which dictates the structure of a function - return type, function name, parameter types and names. Header files do NOT include implementation.</p>
<p>Include directives are handled by the <strong>preprocessor</strong> in a step that looks like copy-pasting the code into the file including the library. You can see what it looks like with <code>clang++ -E FILE_TO_COMPILE</code></p>
<p>Another function is type casting via <code>static_cast</code> - for example</p>
<pre><code class="language-cpp">static_cast&lt;double&gt;(9);
</code></pre>
<p>We can also <strong>declare</strong> and <strong>define</strong> our own functions. Function declaration (or the <strong>function prototype</strong>) describes how a function is CALLED (the header). Function definition describes what a function DOES. Declarations must happen before a function is invoked, but definition can occur later. In a declaration, we have <strong>formal parameters</strong> to stand in for the values that a function will actually be called with. Note: formal parameters are optional but types are mandatory, yet we will use parameter names for clarity.</p>
<p>When a function is invoked, by default we plug in values for the parameters in a <strong>call by value</strong> mechanism (see above notes).</p>
<p>Arguments are invoked by order! Cannot mix and match the order between declaration and invocation.</p>
<p>We use functions as <strong>black boxes</strong> to <strong>hide information</strong> about implementation. A programmer may need to know what a function does without how it does it. Implementation may change but function APIs may not - think about a more efficient search algorithm. Writing and using functions as if they were black boxes is also called <strong>procedural abstraction</strong>.</p>
<p>When variables are declared inside a function they are <strong>locally scoped</strong> and do not leak outside. For a value that is needed by many functions, we might use a <strong>globally scoped</strong> constant by defined it outside all functions.</p>
<p>Technically, everything exists in some <strong>block scope</strong> where blocks are defined within curly braces, and global scope is the global block.</p>
<p><strong>Note</strong>: Our using directives can be block scoped as well if we want to open a namespace locally to a function. This can help avoid clobbering.</p>
<p>C++ offers <strong>function overloading</strong> where we can define functions with different <strong>arity</strong> or <strong>parameter types</strong> and the same function name. We've seen examples with the division operator that can work on doubles, floats and ints in different ways. For example:</p>
<pre><code class="language-cpp">int add(int a, int b);
int add(int a, int b, int c);
double add(double a, double b);
</code></pre>
<p>When you overload a function name, the function declarations for the two different definitions must differ in their formal parameters. <em><strong>You cannot overload a function name by giving two definitions that differ only in the type of the value returned.</strong></em></p>
<h2>Problem Solving with C++, Chapter 5</h2>
<p>Functions can return <code>void</code>. For void functions, we can omit the final return. This is useful for I/O like writing to terminal, or <strong>call by reference</strong> mutation. Defining a function that calls by reference just adds an ampersand before the reference variable, eg <code>void example(int &amp;x)</code>.</p>
<p>Since program variables are implemented as memory locations, a reference is actually a variable whose value is the memory location. This allows us to circumvent scope limitations. We are also allowed to mix call-by-value and call-by-reference in function parameters.</p>
<p>When we write function declarations, we can add comment blocks to describe <strong>preconditions</strong> (what we assume to be true) and <strong>postconditions</strong> (the effect of the function).</p>
<p>We can write <strong>driver programs</strong> to test our functions. These a small, temporary tools (often loops) to test various inputs</p>
<pre><code class="language-cpp">int main() {
	do {
		functionStuff()
	} while (inputIsValidRunner)

}
</code></pre>
<p>We can also add <strong>stubs</strong> that just replace a function dependency with an output value so we don't test more than the one function we care about. Example:</p>
<pre><code class="language-cpp">// stub
int taxableIncome(int salary) {
	return 100;
}

int totalSavings(int salary) {
	// our function we want to test
}

int main() {
	int income = taxableIncome();
	return totalSavings(income);
}
</code></pre>
<p>We can use the <code>assert</code> macro with a boolean expression as a precondition</p>
<pre><code class="language-cpp">#include &lt;cassert&gt;
int function() {
	assert(booleanExpression);
	// do stuff
}
</code></pre>
<p>To disable assertions, add <code>#define NDEBUG</code> above the include directive.</p>
<h2>Data structures and Algorithms in C++, Chapter 2</h2>
<p>We're now into algorithm stuff. Some rules:</p>
<p><strong>Definition 2.1</strong>: T(N) = O(f(N)) if there are positive constants c and n0 such that T(N) ≤ cf(N) when N ≥ n0. My note - this is the upper bound</p>
<p><strong>Definition 2.2</strong>:  T(N) = Ω(g(N)) if there are positive constants c and n0 such that T(N) ≥ cg(N) when N ≥ n0. My note - omega is the lower bound</p>
<p><strong>Definition 2.3</strong>: T(N) = θ(h(N)) if and only if T(N) = O(h(N)) and T(N) = Ω(h(N)). - Theta is the actual value, so time is equal to lower bound and upper bound</p>
<p><strong>Definition 2.4:</strong> T(N) = o(p(N)) if, for all positive constants c, there exists an n0 such that T(N) &lt; cp(N) when N &gt; n0. Less formally, T(N) = o(p(N)) if T(N) = O(p(N)) and T(N)=/= θ(p(N)). 51</p>
<p>We are concerned with <strong>relative rates of growth</strong> that help us determine which functions take more time than others. We throw away constants and note that this is about eventuality so: 1000N vs N^2 - N^2 for some value of N will eventually overtake 1000N, thus we say that N^2 is the limiting factor.</p>
<p>We call the first definition <strong>Big-O</strong> notation.</p>
<p>We ignore constants and lower order - eg we don't write O(N^2 + N) becaues N^2 is the dominant element.</p>
<p><strong>Note</strong>: These are algorithmic analyses. We cannot, in the real world, ignore caching, memory locality, disk I/O time, etc. However, we do work with massively large numbers of inputs in programming so these analyses become useful tools.</p>
<h4>Rules</h4>
<ol>
<li>For loops - the running time is at most the number of elements of the loop</li>
<li>Nested loops - analyze inside out. The running time is the inner loop's runtime multiplied by the number of times it is executed, the number of elements in the outside loop</li>
<li>Consecutive work - it's additive. So two sequential for loops become O(N) + O(N) = O(2N), and we throw away constants so it's O(N)</li>
<li>If/else - the running time is never more than the running time of the condition plus the larger of the work in the if/else branches</li>
</ol>
<p>Logarithmic algorithms might be confusing but it is defined as:</p>
<p>O(logn) if it is constant time to cut the problem size by a fraction (like divide and conquer algorithms like binary search)</p>
<h2>Module 8 – Algorithm Analysis</h2>
<p>We'll look at the problem of seeing if a number is prime or not, and learn how to talk about runtime complexity.</p>
<pre><code class="language-cpp">
bool isPrime (int num) {
	// count the number of integers that divides cleanly into num
	// aka, num % i == 0
	// only correct answer is 1 and num for a prime
	int countDivs;

	for (int i = 1; i &lt;= num; i++) {
		if (num % i == 0) countDivs++;
	}

	return countDivs == 2;
}
</code></pre>
<p>We know we can cut the problem space in half: let k be some int between num/2 and num - there is no complementary divider that is an integer. That is, d = num / k and if k &gt; num/2 then d &lt; 2</p>
<pre><code class="language-cpp">
bool isPrime (int num) {
	// count the number of integers that divides cleanly into num
	// aka, num % i == 0
	// make sure only number in first half is 1
	int countDivs;

	for (int i = 1; i &lt;= num/2; i++) {
		if (num % i == 0) countDivs++;
	}

	return countDivs == 1;
}
</code></pre>
<p>There's a third version! Instead we can look up to sqrt(num). Think about 100 - the largest unique divisor is up to sqrt(100) = 10. 20 divides into 100, but 5 times which we checked up to sqrt. That is, every number after the sqrt has a complementary divider LESS than sqrt.</p>
<p>Proof by contradiction: let k and d be complementary dividers of num (k * d == num). Assume BOTH are greaterr than sqrt(num). we can then say k * d &gt; sqrt(num) * sqrt(num) or num &gt; num. Contradiction, implying that both k and d cannot be greater than sqrt. Thus at least one must be greater.</p>
<pre><code class="language-cpp">#include &lt;cmath&gt;

bool isPrime (int num) {
	// count the number of integers that divides cleanly into num
	// aka, num % i == 0
	// make sure only number in first half is 1
	int countDivs;

	for (int i = 1; i &lt;= sqrt(num); i++) {
		if (num % i == 0) countDivs++;
	}

	return countDivs == 1;
}
</code></pre>
<p>Runtime analysis:</p>
<p>Time 1 (T1), Time 2 (T2), Time 3 (T3). We know the running time depends on the size of the integer. Let n be the size of the input. Let us parameterize the running time over the size of the input. Running time depends on operators, so we ignore machine-dependent constants and count all primitive operators (addition, multiplication) as 1. We also only care about <strong>asymptotic analysis</strong> - we care about asymptotic order of the input. So drop lower order terms and constants.</p>
<p>T1(n): O(n) = O(n)</p>
<p>T2: O(n/2) = O(n)</p>
<p>T3: O(sqrt(n))</p>
<h3>Formal definition</h3>
<p>Asssume two functions f(n) and g(n) mapping positive integers to positive g(n)</p>
<p>We say f(n) = O(g(n)) if there exists to real constants c1 and c2 and a positive integer n0 such that c2 * g(n) &lt;= f(n) &lt;= c1 * g(n) for all n &gt;= n0.</p>
<p>Proof: 3n^2 + 6n - 15 = O(n^2)</p>
<h4>Find upper bound:</h4>
<p>Drop leading constants</p>
<p>3n^2 + 6n - 15 &lt;= 3n^2 + 6n</p>
<p>Find an upper bound by adding a 6n</p>
<p>3n^2 + 6n - 15 &lt;= 3n^2 + 6n &lt;= 3n^2 + 6n^2 = 9n^2</p>
<p>we can then say c1 = 9</p>
<h4>find lower bound:</h4>
<p>3n^2 &lt;= 3n^2 + 6n - 15 &lt;= 3n^2 + 6n &lt;= 3n^2 + 6n^2 = 9n^2</p>
<p>if 3n^2 &lt;= 3n^2 + 6n - 15</p>
<p>then  6n - 15 &gt;= 0</p>
<p>n &gt;= 2.5</p>
<p>Then we can say n0 = 3</p>
<p>Then we can drop lower order constant 6n</p>
<p>3n^2 &lt;= 3n^2 + 6n - 15 &lt;= 9n^2</p>
<p>Then we can say  c2 = 3</p>
<p>Therefore 3n^2 + 6n - 15 = O(n^2)</p>
<h2>Discrete Math Section 5.1</h2>
<p>How do we count combinations of numbers? We have two rules we'll start with.</p>
<p><strong>Product rule</strong> - The product rule says that for N number of sets, the cardinality of the Cartesian product is the product of the cardinality of each set. That is, |A1 x A2 x .. x An| = |A1| * |A2 * ... * |An|. For example, if we wanted to know all combination of breakfasts given:</p>
<p>main: {eggs, pancakes, waffles}</p>
<p>sides: {toast, bacon}</p>
<p>drinks: {coffee, water, orange juice}</p>
<p>The cardinality of set of all combinations is 3 * 2 * 3 = 18</p>
<p>This works for strings too - for example, If Σ is a set of characters (called an alphabet) then Σ^n is the set of all strings of length n whose characters come from the set Σ. For binary, for binary strings of length 5, we know there are 2^5 = 32 possible combinations.</p>
<p><strong>Sum rule</strong> - The sum rule says that, for N number of sets, if they are mutually disjoint then the union of |A1 u  A2 u ... u An| = |A1| + |A2| + .. + |An|</p>
<p>That is, if you know all options and only pick <strong>one</strong> element from the sets, then the number of options is equal to the sum of the cardinalities. Given our above menu of mains, sides and drinks, and user can only pick one, the sum rule states that the number of options is 8.</p>
<p>The book uses passwords as an example to merge these two.</p>
<p>Assume that L is the letters of an alphabet (all lower case) and D are the digits. |L| = 26, |D| = 10. The set of all characters is C = L union D, so the size of the set C is |C| = |L| + |D| = 36. Assume we must have passwords between 6 and 8 characters. The sum rule, since each letter combination is disjoint, says |C6 union C7 union C8| = |C6| + |C7| + |C8|. For each, we can apply the product rule and get 36^6 + 36^7 + 36^8.</p>
<h2>Discrete Math Section 5.2</h2>
<p>The <strong>bijection rule</strong> says that if there is a bijection from one set to another then the two sets have the same cardinality. This allows us to take a set of unknown cardinality, find a bijection to a set of known (or easily counted) quantity, then assert the count of the first set.</p>
<p>For example, if you have a movie theater and want to count the number of people who attended in a day, you can define a bijection as the number of tickets at the end of the day. You then have a <strong>well defined inverse</strong>.</p>
<p>We can also use this to count the number of elements in a power set of a finite set X. If |X| = n, then we can say that f is a bijection:|{0, 1} ^n| = |P(x)| where a bit being 0 is the absense of an element in the set and 1 is the presence.</p>
<p>The <strong>k-to-1 correspondence</strong> defines a relationship of K elements in set X for every element in set Y - that is, if each guest has two shoes then the number of shoes should be 2x the number of guests, thus k = 2.</p>
<p>Formally - Let X and Y be finite sets. The function f:X→Y is a k-to-1 correspondence if for every y ∈ Y, there are <strong>exactly</strong> k different x ∈ X such that f(x) = y.</p>
<h2>Discrete Math Section 5.3</h2>
<p>The <strong>generalized product rule</strong> says that in selecting an item from a set, if the number of choices at each step does not depend on previous choices made, then the number of items in the set is the product of the number of choices in each step.</p>
<p>An example - I go to the same restaurant every day for lunch. They have 10 lunch specials. I want to try something new every day this work week. On Monday I have 10 options. On Tuesday I must eliminate yesterday's choice, giving me 9 options. On Wednesday I lose another. The number of total combinations is 10 * 9 * 8 * 7 * 6.</p>
<h2>Discrete Math Section 5.4</h2>
<p>Building off the generalized product rule, an <strong>r-permutation</strong> is a sequence of r items with no repetitions, all taken from the same set. In this case, order does matter so the sequences ()"red", "blue", "green") and ("blue", "green", "red") are different permutations.</p>
<p>The number of r-permutations in a set of length n where r &lt;= n, called P(n, r), is n! / (n-r)!</p>
<p>That is, if n = 5 and r = 3 then (5 * 4 * 3 * 2 * 1) / (2 * 1) = 5 * 4 * 3</p>
<p>A <strong>permutation</strong> (without the parameter r) is a sequence that contains each element of a finite set exactly once. You can think of it as an r-permutation where r = the number of elements in the set. The number of options is n! (n-factorial)</p>
<p>Example - imagine I have 4 people at a dinner party (Alice, Bob, Carol, Dan). I want to seat them at a bench, but I want Carol and Dan to sit next to each other. How many options do I have?</p>
<p>First, Carol and Dan - I have two options to sit them next to each other (Carol then Dan, or Dan then Carol). Then I need to figure out the number of ways to seat Alice, Bob and the Carol/Dan pair. That's 3! (three discrete elements) times 2 for the number of ways I can organize the pair, for 12 options.</p>
<p><strong>Question</strong>: A wedding party consisting of a bride, a groom, two bridesmaids, and two groomsmen line up for a photo. How many ways are there for the wedding party to line up so that the bride is next to the groom?</p>
<p><strong>Answer</strong>: 240. First decide whether the bride is to the left or right of the groom (2 choices). Then glue the bride and groom together and there are 5! ways to permute the five items. By the product rule, the number of line-ups with the bride next to the groom is:</p>
<h2>Discrete Math Section 5.5</h2>
<p>An <strong>r-combination</strong>, sometimes called an <strong>r-subset</strong>, is a way of counting order-independent combinations of elements from a set. If you care about which three people from an election got the most votes and win representative seats, order doesn't matter so {Alice, Bob, Carol} and {Carol, Bob, Alice} are the same - note the set notation.</p>
<p>To calculate the number of r-combinations in a set, you start with the number of permutations and divide by r!.</p>
<p>The notation is the same as the above n-choose-k example, but we can also say C(n, r) for n-choose-r</p>
<p>That is</p>
<ul>
<li>P(n, r) / r!</li>
<li>n! / ((n-r)! * r!)</li>
</ul>
<p>An equation is called an <strong>identity</strong> if the equation holds for all values for which the expressions in the equation are well defined. We can say C(n, r) = C(n, n - r) is an identity because it holds true for all non-negative n and any r from 0 to n.</p>
<h2>Discrete Math Section 5.6</h2>
<p>One of the examples here specifies words / phrases to look for that hint at combinations vs permutations. For example, given 10 people and 4 prizes, how many ways can you allocate them if</p>
<ul>
<li>the prizes are all the same? Order doesn't matter so it's combination, C(n, r) or (10! / (6! * 4!))</li>
<li>The prizes are all different? Order does matter, so it's permutation, or P(n, r) or 10! / 6!</li>
</ul>
<p>Review book for more examples of non-mathematical terms. eg if a waiter puts dishes in the center of a table, the number of ways to order from the menu is the combination (subsets) - if everyone gets a dish it becomes permutations (order counts).</p>
<h2>Discrete Math Section 5.7</h2>
<p><strong>Counting by complement</strong> is a strategy of counting the number of elements in a set S that have some property P by removing the elements that do not have P. That is</p>
<p>|P| = |S| - |P'|</p>
<h2>Discrete Math Section 5.8</h2>
<p>A <strong>permutation with repetition</strong> is an ordering of a set of items in which some of the items may be identical to each other. That is, how do you find the r-permutation of something that has repeats, like the string "MISSISSIPPI"?</p>
<p>In order to do so, you need to find the r-subsets for EACH repeated element where N is the number of remaining spots and R is the count of that element, then use the product rule to determine the answer.</p>
<p>Mississippi has 4 s, 2 p, 3 i, 1 m - we can then do 11-choose-4 * 7-choose-2 * 4-choose-3 * 1-choose-1.</p>
<p>There's an easier way!</p>
<p>The formula is defined as n! / (n1! * n2! * n3! ... nk!) where k is the number of elements, and nk is the count of element k.</p>
<p>So for Mississippi it becomes 11! / (4! * 2! * 3! * 1!)</p>
]]></content:encoded></item>
<item><title>CS-537 Introduction to Operating Systems - Lecture 2</title><link>https://nthomas.org/2020-08-03-cs-537-lecture-2/</link><guid>https://nthomas.org/2020-08-03-cs-537-lecture-2/</guid><pubDate>Monday, 03 August 2020 11:24:07 +0000</pubDate><description>Scheduling policies and strategies</description><content:encoded><![CDATA[<h2>Resources</h2>
<p>Link: http://pages.cs.wisc.edu/~remzi/Classes/537/Spring2018/Discussion/videos.html</p>
<h2>Intro</h2>
<p><strong>Scheduling</strong> takes its origins from operations research and concerns itself with making sure that resources are utilized most effectively given a set of metrics. The most obvious metric is time to completion, aka how long does it take to finish a set of work, but we'll introduce other metrics and strategies for scheduling. In our case, we're asking the question of "how do we utilize the CPU most effectively to share resources across many jobs of various lengths and CPU use needs?"</p>
<p>The textbook makes some very clear but incorrect assumptions about <strong>workload</strong>, the processes in the system, in order to simplify our starting point. We then remove those assumptions in order to understand the limitations of a proposed solution.</p>
<p>We start with the following assumptions about our <strong>jobs</strong>:</p>
<ul>
<li>Each job runs for the same amount of time</li>
<li>All jobs arrive at the same time</li>
<li>Once started, all jobs run to completion</li>
<li>All jobs only use CPU (no disk or network IO)</li>
<li>The runtime of each job is known ahead of time</li>
</ul>
<p>We also need to define <strong>scheduling metrics</strong>, a measurement of SOMETHING in our system. The book starts with <strong>turnaround time</strong>, which is defined at the time a job completes minute the time a job arrived in the system. That is, the turnaround time of me at McDonalds is the time I get my food minus the time I walk in the door.</p>
<p>Since we are currently assuming all jobs arrive at the same time, we can say that turnaround time = completion time, and arrival time = 0.</p>
<p>Turnaround time is a <strong>performance metric</strong>, but we could also measure <strong>fairness</strong>. Something that is high performance may not be very fair, and vice versa.</p>
<h2>First In, First Out (FIFO)</h2>
<p>The most basic strategy is FIFO, implementing a queue structure. FIFO works for our assumptions, particularly when we say that all jobs are CPU-only, and arrive at the same time.</p>
<p>The book's example takes 3 jobs that need 10 seconds a piece, called A, B and C. In a FIFO strategy, we do job A and complete it at time = 10, job B and complete at time = 20, then job C and complete at time = 30. Thus making the <strong>average turnaround time</strong> 20 seconds (10 + 20 + 30 / 3).</p>
<p>We now relax assumption 1 - not every job runs for the same amount of time. What happens when A needs 100 seconds, then B and C need 10 seconds each? We complete A at time = 100, B at time = 110 and C at time = 120. Thus making the average turnaround time 110 seconds - much worse as a result of simply being FIFO.</p>
<p>This effect of low-resource-needs jobs getting stuck behind a high-resource-needs job is called the <strong>convoy effect</strong>.</p>
<h2>Shortest Job First (SJF)</h2>
<p>Given the one issue we have now identified, perhaps there's an obvious solution - we always take the shortest job first, leaving the resource-intensive ones for last. Assuming our last case (What happens when A needs 100 seconds, then B and C need 10 seconds each?) - we now finish B at time = 10, C at time = 20, then A at time = 120, giving us an average turnaround time of 50 seconds for the same set of jobs.</p>
<p>We now relax assumption number 2 - not every job arrives at the same time, and in fact they arrive at various times. What happens now?</p>
<p>Well, what if job A arrives before job B and C? Specifically, what if B and C arrive at time = 10?</p>
<p>In that case, our time looks like (100 + (110-10) + (120 - 10)) / 3 = ~103 seconds. Not good!</p>
<h2>Shortest Time-to-Completion First (STCF)</h2>
<p>We can relax another assumption - we no longer need jobs to run to completion. We've discussed in virtualization the idea of <strong>timer interrupts</strong> and <strong>context switching</strong> - we can borrow that same concept here. We will introduce <strong>preemptive scheduling</strong>, where STCF is the preemptive version of the non-preemtive SJF. We also call STCF <strong>Preemptive Shortest Job First (PSJF)</strong>.</p>
<p>Our STCF scheduler, whenever a new job enters the system, will look to see which of the jobs needs the LEAST amount of time left and run that first. So, given our above job where A needs 100 seconds and arrives at time=0, B and C need 10 seconds and arrive at time = 10:</p>
<p>The scheduler will run A for 10 seconds, then see that B and C have arrived. It will switch to B, finish B at time = 20, then C at time = 30, the finish the remaining 90 seconds of A at time = 120 - thus giving us our turnaround time of (120 - 0) + (20 - 10) + (30 -10) / 3 of 50 seconds. Less than half the time of SJF just by introducing preemptive scheduling.</p>
<h2>Response Time</h2>
<p>Given our current assumptions we are happy. However, we're now going to formalize a new metric called <strong>response time</strong>  - defined as the time a job arrives in a system to the time it's scheduled. Metaphorically, the time I walk in the door at McDonalds vs the time my order is taken. So the response time for our STCF case ( A needs 100 seconds and arrives at time=0, B and C need 10 seconds and arrive at time = 10) is 0 for A, 0 for B, and 10 for C for an average of 3.33 seconds of response time.</p>
<p>Since all our previous strategies, including STCF, try to run short jobs to completion, they are not necessarily good for response time. If I'm the person that submitted job C, I'd be waiting 10 seconds for B to complete prior to a response from the system. So, to improve response time, we introduce new strategies.</p>
<h2>Round Robin</h2>
<p>If we want to improve response time, we need a strategy to...respond faster. <strong>Round robin</strong> aims to handle that. Instead of running jobs to completion, we have a <strong>time slice</strong> (or a <strong>scheduling quantum</strong>), a unit of time dedicated to working on one job at which point we switch to the next time. We switch jobs every slice until all jobs are done. We can also call this strategy <strong>time slicing</strong>. The length of a time slice must be a multiple of the timer interrupt period, that is if the timer interrupt is every 10 ms, then a time slice cannot be 25ms, but can be 20 or 30 ms.</p>
<p>Imagine a system where jobs A, B and C all arrive at time = 0 and need 5 seconds to run. In our SJF or SJTF model, we run A at time = 0, B at time = 5, and C at time = 10 for an average response time of 5 seconds.</p>
<p>In round robin, with a time slice length of 1, we run A at time = 0, switch to B at time = 1, then switch to C at time = 2 for an average response time of 1 second - a greatly improved number.</p>
<p>This switching between jobs has a cost, including loading and saving register state, CPU caches being blown away, branch predictors, etc all flushing. As a result, we may want to limit our switching frequency in order to <strong>amortize</strong> the switching cost and pay it off over a longer period of time.</p>
<p>So if round robin is great for response time, how does it do with average turnaround time?</p>
<p>The turnaround time of our SJF model would be (5 + 10 + 15) / 3 = 10 seconds. However, in round robin we don't finish A until 13 seconds in, B until 14 and C until time = 15 seconds for an average turnaround time of 14 seconds.</p>
<p>The book notes that any <strong>fair</strong> policy (that is, spreading CPU resources evenly across processes over a small unit of time) will do well on response time at a tradeoff of turnaround time.</p>
<h2>Incorporating I/O</h2>
<p>We now relax assumption 4 and allow processes to perform IO - Chrome making a network request, VS Code reading / writing to disk. When a process does so, it is <strong>blocked</strong> on waiting for IO completion. The resource (disk, network) needs some number of milliseconds to perform work so the CPU can and should move onto another task in the mean time.</p>
<p>When a block, an interrupt is raised and the OS can move the job from blocked to the ready state.</p>
<p>Let's use two jobs as example. Jobs A and B need 50 ms of CPU. Job A runs for 10ms, then switches to IO and blocks (assume IO takes 10 ms) while B only does CPU work for the whole 50 ms.</p>
<p>In a naive, SJF world, A would run for 10 ms, block for 10 ms, run for 10 ms, block, run, block, run again for 50ms of CPU and 40ms of IO and B won't start until 90ms have passed and won't finish until time = 140.</p>
<p>A better system would treat each 10ms sub-job of A as an independent job. So then the system sees a 10ms job of A, and a 50ms job of B. So what does it do?</p>
<p>Schedule A for 10ms, then while that does IO the scheduler works on B for 10ms. Then the next sub-job of A comes in, so the scheduler switches back and works on A again for 10ms - this switching back and forth means that A will complete at time = 90 again, but B will have already done 40ms of work in the gaps so B will complete at time = 100.</p>
<p>Thus, the CPU can treat every discrete "burst" of CPU-utilization as its own job and when they interact with other resources, the CPU can switch over to another CPU-intensive job.</p>
<h2>Multi-Level Feedback Queue</h2>
<p>We are now giving up our last assumption - we no longer know ahead of time how long a job will take to run. We introduce a new approach to scheduling, called the <strong>Multi-Level Feedback Queue</strong> which tries to optimize turnaround time while also minimizing response time.</p>
<p>The book uses an implementation of MLFQ that has a number of distinct <strong>queues</strong>, each of which are assigned a different <strong>priority level</strong>. The scheduler will choose to run jobs with highest priority first, then will move down the priority list. Formally:</p>
<ul>
<li>Rule 1: If Priority(A) &gt; Priority(B), A runs and B does not</li>
<li>Rule 2: If Priority(A) == Priority(B), A and B run in round-robin</li>
</ul>
<p>How do we set priority? Well, it depends on the observed behavior of the work. If a job tends to often relinquish CPU in order to do something else, the MLFQ will identify that as a high priority job as it seems like an interactive, user-blocking task. If something tends to run its entire time-slice on CPU, the scheduler will slowly decrease its priority as that works akin to long batch jobs. Thus, we use previous behavior to predict future behavior.</p>
<p>Are there some flaws? Yes! Imagine we have three priority queues (high, medium, low) - with two high priority jobs A and B, a medium priority job C and a low priority job D. A and B will run in round-robin, starving our C and D jobs of resources. Thus, we need strategies to change priority.</p>
<h4>Attempt 1</h4>
<p>Since our workload is a blend of CPU intensive and interactive jobs, we need to identify a way to adjust priorities. Here's one try</p>
<ul>
<li>Rule 3: When a job enters the system, it's placed at the highest priority</li>
<li>Rule 4a: If a job uses its entire time slice while running, we move it down a priority level</li>
<li>Rule 4b: If a job gives up CPU before time slice is up, it stays at the same priority.</li>
</ul>
<p>Imagine for example a very basic job. Job A takes 200ms. We run it at high priority (what we call Q2, or queue 2) for 10ms, then move it down to a lower priority (Q1) for 10ms, then down to Q0 for the remainder.</p>
<p>Easy!</p>
<p>Let's make a change - we introduce job B, a short interactive job. B arrives at time = 100 in Q2 and runs for 20 ms. Now we, at time = 100, run B for 10 ms from, move it from Q2 to Q1, run it for 10ms again, then we go back to Q0 to finish our low priority job A.</p>
<p>In other words, we approximate SJF by assuming ANY new job could be short, high priority work and adjust behavior according to how it operates in the real world. If it is short, we complete it soon. If it's not, we round-robin with other lower priority work.</p>
<p>What if B runs for 1ms and then does I/O for 1ms?</p>
<p>Well then at time = 100, we've moved A to Q0 (lowest priority) and now have B. We work on B for 1ms, then while that's blocked we look for the next job in Q2, Nothing - go to Q1 - nothing. So while B is blocked, we work on low priority A and keep B in highest priority since it gave up CPU before its time slice. We then alternative back and forth between high priority B and low priority A until B completes.</p>
<p>BUT WE HAVE A PROBLEM!</p>
<p>What if we have a lot of interactive tasks? Our low-priority A will be <strong>starved</strong> of resources since high priority tasks will always be running.</p>
<p>What if someone is malicious and wants to game the system? What if someone wrote a process B that always gave up CPU right before its time slice ended? The scheduler would always treat that as high priority work, and it would never demote priority thus allowing a user to stay in high priority even those, in reality, it's CPU work. Imagine if they just did a file read for no reason to force disk I/O at 9ms in. Not good!</p>
<p>Finally, what if a process just changes behavior over time? How can something move from low priority to high? If it starts very CPU bound but eventually becomes interactive (a test runner with suggested fixes?) it will get demoted to low and then its I/O phase will stay on the lowest priority.</p>
<h4>Attempt 2</h4>
<p>If we know somethnig can get stuck on a low priority, and if we know that CPU-bound jobs can starve or not make much progress, what can we do?</p>
<ul>
<li>Rule 5: After some time period S, move all jobs in the system back to the highest priority queue</li>
</ul>
<p>We call this <strong>boosting</strong> priority.</p>
<p>This will take our CPU bound jobs, and if they're been stuck waiting, will get a little bit of CPU resources. Additionally, if our CPU-bound job became interactive it'll get a chance to do IO and give up a time slice again. So, hypothetically, every 50ms or so, we flush all queues into the highest priority queue and let tasks settle back down into lowest priority as needed.</p>
<h4>Attempt 3</h4>
<p>We still allow people to game our system - giving up CPU just before a time slice ends to stay in high priority. Thus we rewrite rules 4a and 4b into</p>
<ul>
<li>Rule 4: once a job uses up its time allotment (regardless of how many times it released the CPU) we move it down a priority.</li>
</ul>
<p>This new rule change means that even if we give up CPU and switch to I/O, we'll eventually run out our time slice and be forced down a priority queue.</p>
<h2>Tuning MLFQ</h2>
<p>The book makes points - how do we <strong>parameterize</strong> our scheduler? Number of queues? Length of time slice? Length of time period before we boost priority?</p>
<p>The short answer is...trial and error. We want to be able to monitor and benchmark our workloads and test our assumptions accordingly. We can start with some default values and tweak them according to desired turnaround and response time.</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 5</title><link>https://nthomas.org/2020-08-02-NYU-Tandon-Bridge-Week-5/Untitled/</link><guid>https://nthomas.org/2020-08-02-NYU-Tandon-Bridge-Week-5/Untitled/</guid><pubDate>Sunday, 02 August 2020 11:24:07 +0000</pubDate><description>Functions in discrete mathematics</description><content:encoded><![CDATA[<h2>Discrete Math Section 4.1</h2>
<p>We understand sets, which are unordered unique collections of items. A <strong>function</strong> is a mapping from one set to another. Common functions in mathemetics are concerned with numbers, such as the function x^2 which takes a set of integers and maps each value to another value in a set of integers. We can also think about sets of tasks, or abstractions in general. For example, if we were to take the set of all students and assign them to study groups, that would be a function.</p>
<p>A function from X to Y can also be viewed as a subset of X x Y (that is, the Cartesian product) since a function will give is pairs of values from X and Y. We can more formally define this as X × Y: (x, y) ∈ f if f maps x to y. If X and Y are the same set, then f is a subset of X x X.</p>
<p>Strictly, for a function f that maps X to Y, for every element x ∈ X there is EXACTLY ONE y ∈ Y for which (x, y) ∈ f. Another way, functions cannot map a value in X to many Ys. If the function is x^2, 3^2 cannot have two or more possible values.</p>
<p>We use the notation f: X → Y where we call X the <strong>domain</strong> and Y the <strong>target</strong>. We can also denote as f(x) = y</p>
<p>For a function f mapping X to Y and a finite set X, the function f can be specified as the set of pairs mapping X to Y. We can also use a graphical representation called an <strong>arrow diagram</strong> which just draws lines from a left column showing all values of X to a right column showing all values of Y. A line will be drawn from  x ∈ X to some y ∈ Y only if (x, y) ∈ f. This also means only one arrow can be drawn OUT of an element in the domain.</p>
<p>We say that an element in y is in the <strong>range</strong> of the function if and only if there is a line drawn from some value x to that element. Put another way, every y in the set of f is in range, but not every element in Y is in range. How?</p>
<p>Imagine the set X = {-1, 0, 1} and Y = {-1, 0, 1} and the function x^2. If you square every number in X, you get the set {0, 1} (-1 squared is 1). Therefore, the range of the function is smaller than the target of the function.</p>
<p>Concretely: Range of f = { y: (x, y) ∈ f, for some x ∈ X }</p>
<p>A function definition is not just its behavior. A definition is not complete until the domain of f is specified</p>
<p>f(x) = x^2 <strong>incomplete</strong></p>
<p>f: <strong>N</strong> -&gt; <strong>N</strong>, where f(x) = x^2 <strong>complete</strong></p>
<p>We say that two functions f and g are <strong>equal</strong> or have <strong>function equality</strong> if and only if f and g have the same domain and target, AND for every x ∈ X, f(x) = g(x). If this holds then we say f = g.</p>
<h2>Discrete Math Section 4.2</h2>
<p>A function is called <strong>one-to-one</strong> or <strong>injective</strong> if, formally, x1 ≠ x2 implies that f(x1) ≠ f(x2). An example, f(x) = x + 1. For any two values of x called <strong>a</strong> and <strong>b</strong>, f(a) ≠ f(b). That is, f maps different elements in X to different elements in Y.</p>
<p>A function is called <strong>onto</strong> or <strong>surjective</strong> if, formally, the range of f is equal to the target Y. That is, for every y ∈ Y, there is an x ∈ X such that f(x) = y. An example, f: <strong>R</strong> -&gt; <strong>R</strong>, where f(x) = x + 1. For EVERY real number, there exists a number x such that x + 1 = y.</p>
<p>If a function is both injective and surjective, we call it <strong>bijective</strong>. To repeat, a bijective function is one where both no two x values map to the same y AND every y in the target is reached by some x. A bijective function is also called a <strong>bijection</strong> or a <strong>one-to-one correspondence</strong>.</p>
<p>If we know whether a function is one-to-one or onto, then we can make inferences about the relative sizes of the domain and targets.</p>
<p>If f: D → T is onto, then for every element in the target, there is at least one element in the domain: |D| ≥ |T|. That is, if every element in T is reachable, we know that at the least ONE element in D must be able to reach it. Therefore, the cardinality of D must be at LEAST equal to T. It's possible multiple elements in D reach an element in T, so |D| ≥ |T|.</p>
<p>If f: D → T is one-to-one, then for every element in the domain, there is at least one element in the target: |D| ≤ |T|. That is, if every element in D has a unique mapping in T, then there must be at LEAST the same number of elements in both. Since it's possible there are unreachable elements in T in a one-to-one function, it's possible that the cardinality of T is greater. Thus |D| ≤ |T|</p>
<p>If f: D → T is a bijection, then f is one-to-one and onto: |D| ≤ |T| and |D| ≥ |T|, which implies that |D| = |T|.</p>
<p>If we know these rules, then we can make other inferences. For example, if we have a set of unknown size, and we want to count the elements we can do so by defining a bijection between this set and another set that we DO know the size of.</p>
<h2>Discrete Math Section 4.3</h2>
<p>If a function f: X -&gt; Y is a bijection (a function that has a unique y for every x and every y value in Y is reached) then the <strong>inverse</strong> of f is obtained by exchanging the first and second entries of each pair in f. We denote the inverse of f as f^-1 (wow that's annoying).</p>
<p>f^-1 = { (y, x) : (x, y) ∈ f }</p>
<p>Or, the inverse function f^-1 is the set of all (y, x) such that (x, y) exists in f.</p>
<p>Reversing each pair does not always result in a well-defined function (no x has multiple outputs, the domain and target are known). Therefore some functions just don't have an inverse. A function f: X → Y has an inverse if and only if reversing each pair in f results in a well-defined function from Y to X. f^-1 is a well-defined function if every element in Y is mapped to exactly one element in X.</p>
<p>Put another way, if the range of f has elements with multiple incoming arrows (that is, the function f is not one-to-one) then the inverse is not valid.</p>
<p><strong>A function f has an inverse if and only if f is a bijection.</strong></p>
<h2>Discrete Math Section 4.4</h2>
<p>The process of applying a function to the result of another another function is called <strong>composition</strong>. For example. Let f be the function that determines what a person orders at Mcdonalds. Let g be the function that determines how long it takes to eat that order. g(f(Nikhil)) is how long it takes me to each 10 chicken McNuggets.</p>
<p>We use the character <code>o</code> to denote composition. That is if f: X --&gt; Y and g: Y --&gt; Z, (g o f): X --&gt; Z. Note that composition is right to left, since it's akin to unwrapping parentheses. For all x in X, (g o f) (x) = g(f(x)).</p>
<p>We can compose multiple functions.</p>
<p>The <strong>identity function</strong> is the function defined f(x) = x, or the function that just returns the same element given. The identity function on A, denoted Ia: A → A, is defined as Ia(a) = a, for all a ∈ A. (Ia is I subscript A).</p>
<p>If a function f has an inverse, then composing f and its inverse yields the identity function. That is, if f(x) = x + 1, the inverse is f^-1(x) = x-1. Therefore (f o f^-1)(x) = x</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 4</title><link>https://nthomas.org/2020-07-27-NYU-Tandon-Bridge-Week-4/</link><guid>https://nthomas.org/2020-07-27-NYU-Tandon-Bridge-Week-4/</guid><pubDate>Monday, 27 July 2020 11:24:07 +0000</pubDate><description>C++ Control Flow - Iterative Statements (for loops, while loops)</description><content:encoded><![CDATA[<h2>Module 6 - Iterative Statements</h2>
<p>To recap, we've covered data, expressions, and basic control flow.</p>
<p>Now we're going to try to count the numbers from 1 to n where n is an input number. So for n = 4,  	return "1 2 3 4". We have no sequential behavior for this. We also can't if-else our way to the solution. Unless we try to make it work for all inputs in the universe (if n = 9999, print 9999,  n -= 1)</p>
<p>We can use a <strong>while</strong> statement to <strong>iterate</strong> over behavior and repeat for some condition. The syntax is</p>
<pre><code class="language-cpp">while (CONDITION IS TRUE) {
  // behavior
  // potentially change condition to false
}
</code></pre>
<p>Let's try our problem again. Given an input positive integer N, cout from 1 to N. How would we do this? Maybe some counter. Start it as 1. Begin looping logic: Is counter &gt; N? Print, increment, repeat.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
int main () {
	int max;
	cout &lt;&lt; "Input a positive integer";
	cin &gt;&gt; max;
	
	int counter = 1;
	while (counter &lt;= max) {
		cout &lt;&lt; counter &lt;&lt; endl;
		counter++;
	}
	return 0;
}
</code></pre>
<p>We also <strong>for loops</strong> with a different syntax that looks something like</p>
<pre><code class="language-cpp">for (INITIALIZE_VARIABLE; CONDITION_CHECK; CHANGE_VARIABLE_VALUE) {}
</code></pre>
<p>The module calls the first position "increment" but we could add, subtract, divide by 2 (eg binary search) so better to just consider it as a value change.</p>
<p>Now let's solve the same problem:</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
int main () {
	int max;
	cout &lt;&lt; "Input a positive integer";
	cin &gt;&gt; max;
	
	for (int counter = 1; counter &lt;= max; counter++) { 
		cout &lt;&lt; counter &lt;&lt; endl;
	}
	return 0;
}
</code></pre>
<p>Because for-loops have their variable changing logic declared at the top, the module makes the case it is easier for a programmer to read.</p>
<p>Let's try taking a positive integer, then summing the digits and returning number of digits and sum. eg 375 = sum 15, count 3.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
int main () {
	int num;
	cout &lt;&lt; "Input a positive integer";
	cin &gt;&gt; num;
	int tmp = num;
	int sum = 0;
	int digitCount = 0;
	
	while (num &gt; 0) {
		int onesDigit = tmp % 10;
		sum += onesDigit;
		digitCount++;
		tmp /= 10;
	}
	
	cout &lt;&lt; "the sum of the digits of " &lt;&lt; num &lt;&lt; " is " &lt;&lt; sum;
	cout &lt;&lt; " and the number of digits is " &lt;&lt; digitCount &lt;&lt; endl;
	
	return 0;
}
</code></pre>
<p>Now we can try to average some numbers. Assume user inputs a sequence of grades, return the average. Inputs are number of users, then space-separated grades, then return class average.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
int main () {
	int studentCount;
	cout &lt;&lt; "Please enter the number of students: "
	cin &gt;&gt; studentCount;
	
	float sum = 0f;
	cout &lt;&lt; "Please enter a space-separated list of grades";
	for (int i = 0; i &gt; studentCount; i++) {
		float grade;
		cin &gt;&gt; grade;
		sum += grade;
	}
	
	float average = sum / studentCount;
	
	cout &lt;&lt; "The average grade of the class is: " &lt;&lt; average &lt;&lt; endl;
	return 0;
}
</code></pre>
<p>Let's enter a caveat. Rather than ask for number of students, lets take a stream of integers and use the value -1 as a <strong>terminal value</strong> to denote that it's over. The module uses this idea of a <strong>flag variable</strong> initialized to the <strong>down</strong> (off) position and <strong>raise</strong> it when a condition is hit</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
int main () {
	int studentCount;
	float sum = 0f;
	bool seenTerminalValue = false;
	cout &lt;&lt; "Please enter a space-separated list of grades and finish with -1";
	
	while (!seenTerminalValue) {
		cin &gt;&gt; currDigit;
		if (currDigit == -1) {
			seenTerminalValue = true;
		} else {
			sum += currDigit;
			studentCount++;
		}
	}
	
	float average = sum / studentCount;
	
	cout &lt;&lt; "The average grade of the class is: " &lt;&lt; average &lt;&lt; endl;
	return 0;
}
</code></pre>
<h2>Problem Solving with C++ Section 2.4 - 2.5</h2>
<p>A <strong>while loop</strong> has a <strong>condition</strong> and a <strong>body</strong>. The body is a (series of) statement(s) that get executed <em>while</em> the condition is true. Therefore there needs to be some adjustments that could change the condition to false, or get closer to false. That is, "print x while x is less than 4" means we need to make changes to x on each iteration to get closer to 4. Each repetition is called an <strong>iteration</strong>. Sample code</p>
<pre><code class="language-cpp">while (countDown &gt; 0) {
	std::cout &lt;&lt; countDown &lt;&lt; std::endl;
	countDown--;
}
</code></pre>
<p>If a loop body has a single statement, braces can be omitted;</p>
<pre><code class="language-cpp">while (countDown &gt; 0)  countDown--;
</code></pre>
<p>I just introduced the <strong>unary</strong> (that is, one argument) operator for decrementing. There are unary increment and decrements. The book doesn't cover this yet, but they can be <strong>prefix</strong> (<code>--count</code>) or <strong>postfix</strong> (<code>count++</code>) which changes precedence and execution time. Postfix has lower precedence than almost everything else, so you can do stuff like</p>
<pre><code class="language-cpp">	sum += counter++;
</code></pre>
<p>Which says "add counter to sum, then increment counter"</p>
<pre><code class="language-cpp">	sum += ++counter;
</code></pre>
<p>says "increment counter, then add to sum"</p>
<p><strong>Pitfall</strong> - since a while loop doesn't terminate until the condition is false, and the user is on the hook for condition adjustments, it's easy to forget to change a variable or condition, and get stuck in an infinite loop.</p>
<p>Let's write a program to return how many periods of time it takes for a balance of 50 dollars and 2% interest to hit 100 dollars or more</p>
<pre><code class="language-cpp">int main() {
	double balance = 50.0;
	int periods = 0;
	
	while (balance &lt; 100) {
		balance = balance * 1.02;
		periods++;
	}
	
	std::cout &lt;&lt; periods;
	return 0;
}
</code></pre>
<p>Indenting - do it in loops to express semantics and add readability. I'm using braces a la K&amp;R style (that is, open brace on same line as condition).</p>
<p>Comments - line comments with <code>//</code> and block comments with <code>/* */</code></p>
<p>Constants  - all caps,  declared as <code>const</code> please and thank you.</p>
<h2>Problem Solving with C++ Section 3.3</h2>
<p>A <strong>loop</strong> is a construct to repeat a sequence of statements. We also introduce <code>do...while</code> loops which will FIRST execute the loop, THEN check condition. This changes the behavior to make sure the loop runs at least once.</p>
<pre><code class="language-cpp">// first
int count = 0
while (count != 0) doStudd; // never runs

do {
	doStuff
} while (count != 0) // runs once
</code></pre>
<p>Since our condition is checking for some match, it must be a boolean expression. Something that can be evaluated as false, otherwise we get stuck in a loop forever.</p>
<p>We introduce <strong>postfix</strong> vs <strong>prefix</strong> incrementing - see above for clarity.</p>
<p>For problems where we need sequential integers or values (eg summing nums from 1 to N, array access) we can use a <strong>for-loop</strong></p>
<p>The book refers to the structure of a for-loop as <strong>(Initialization_Action; Boolean_Expression; Update_Action)</strong> - the language of Update_Action is clearer that it's not just incrementing, but some sort of variable adjustment.</p>
<p>The initialized variable has block scope, so it isn't accessible outside of a loop. Additionally any variables declared inside the body will be inaccessible outside. As a result, if your loop needs to impact the outside world you'll need to declare it outside the loop.</p>
<p>Be careful of semis</p>
<pre><code class="language-cpp">for (int x = 0; x &lt; 10; x++);
	doStuff()
</code></pre>
<p>This will terminate before doStuff happens in the loop so it'll only run once. It makes our for-loop a <strong>null statement</strong> which does nothing.</p>
<p>Choose for loops when the variable will change by a fixed amount. Else, a while loop might be easier to read.</p>
<p>We can use the <code>break</code> keyword to end a loop when we reach some terminal value or error state.</p>
<pre><code class="language-cpp">
int main() {
	std::cout &lt;&lt; "please enter a list of positive values followed by -1" &lt;&lt; endl;
	int x;
	
	while (true) {
		std::cin &gt;&gt; x;
		if (x &lt; 0) break;
		
		std: cout &lt;&lt; "You entered: " &lt;&lt; x &lt;&lt; endl;
	}
	
	return 0;
	
}
</code></pre>
<p>A break will only break the innermost (nearest) loop. So for nested loops we can run back up</p>
<pre><code class="language-cpp">for (int x = 0; x &lt; 10; x++) {
	for (int y = 0; y &lt; 10; y++) {
		if (x == y) break;
		std::cout &lt;&lt; y;
	}
}
</code></pre>
<h2>Problem Solving with C++ Section 3.4</h2>
<p>When we design a loop we need to handle:</p>
<ul>
<li>The body</li>
<li>The initialization</li>
<li>The terminating condition</li>
</ul>
<p>A for loop can be expressed in pseudocode as</p>
<ul>
<li>repeat the following THIS_MANY times
<ul>
<li>take a new value</li>
<li>do something</li>
</ul>
</li>
</ul>
<p>We can terminate with the following conditions</p>
<p><strong>List headed by size</strong> - take some value N and run N times from 1 to N</p>
<p><strong>Ask before iterating</strong> - take an input on each loop run and determine if we keep going or stop</p>
<p><strong>Sentinel value</strong> - a value marked as the flag to terminate. AKA "give me a list of numbers and when you're done, send me -1". Reading from files sends a terminal EOF to determine that the file is done.</p>
<p><strong>Count-controlled loop</strong> - just run COUNT times</p>
<p><strong>Flag condition</strong> - set a flag to false, loop until you have a reason to set to true. The trap is that you might never toggle the flag.</p>
<p><strong>Nested loops</strong> - sometimes each value in a list needs its own looping behavior. Imagine the following pseudocode:</p>
<pre><code class="language-cpp">for birdWatcher in birdWatchers
	for bird in birdWatcher.photoAlbum
		cout &lt;&lt; bird
</code></pre>
<p><strong>Debugging</strong> - gotta be careful of <strong>off by one</strong> errors, <strong>infinite loops</strong>, <strong>failed equality</strong> (eg comparing two doubles, since approximations can introduce rounding error).</p>
<p>When we test, we should use values that can invalidate our assumptions about the world in order to handle gracefully. eg if we ask a user for a list of positive ints and they send a negative, does this break our looping exit condition?</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 3</title><link>https://nthomas.org/2020-07-20-NYU-Tandon-Bridge-Week-3/</link><guid>https://nthomas.org/2020-07-20-NYU-Tandon-Bridge-Week-3/</guid><pubDate>Monday, 20 July 2020 11:24:07 +0000</pubDate><description>C++ Branching and Control Flow, Discrete Math Sets (basics, operations, identities)</description><content:encoded><![CDATA[<h2>Module 5.1 - Branching Statements Part 1</h2>
<p>Let's start with a program that reads an int from stdin and returns the absolute value to stdout. Well, depending on the user input we need to do different things. Did the user send a positive? Negative? Our current understanding of the world is <strong>sequential flow</strong> or executing lines of code in the order we see them. In order to get more granularity we'll need <strong>branching flow</strong>, where we have different cases to execute based on some <strong>predicates</strong>.</p>
<p>We'll introduce the <strong>if-statement</strong> for control. A <strong>one-way</strong> if-statement is the most basic. Introducing the syntactic rules:</p>
<pre><code class="language-cpp">// code goes here
if (CONDITION) {
  // consequent code goes here.
  // often called the body
}

// more code
</code></pre>
<p>Now let's consider implementing absolute value with conditionals.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {
	cout &lt;&lt; "Please input an integer" &lt;&lt; endl;
	int input;
	cin &gt;&gt; input;

	if (input &lt; 0) {
		input *= -1;
	}

	cout &lt;&lt; "Your absolute value is: " &lt;&lt; input &lt;&lt; endl;
	return 0;
}
</code></pre>
<p>C++ supports arithmetic operation and assignment with the shorthand <code>variable *= operand</code> where the variable will be multiplied (we can use addition, division, etc) by the operand and reassigned.</p>
<p>We use curly braces after the condition to group together a set of statements.</p>
<p>Let's try another example. Let's determine if an input integer is even or odd. We have the one-way if statement but a <strong>two-way</strong> if-statement would be more powerful, using <code>if-else</code> commonly known as the <strong>Condition-Consequent-Alternative</strong> model  - [wiki](https://en.wikipedia.org/wiki/Conditional_(computer_programming).</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {
	cout &lt;&lt; "Please input a positive integer" &lt;&lt; endl;
	int input;
	cin &gt;&gt; input;

	if (input % 2 == 0) {
		cout &gt;&gt; "Your input is even" &lt;&lt; endl;
	} else {
		cout &gt;&gt; "Your input is odd" &lt;&lt; endl;
	}

	return 0;
}
</code></pre>
<p>We could also have done this with two independent one-way ifs, since mod-2 is a boolean domain.</p>
<pre><code class="language-cpp">// SNIPPED
	if (input % 2 == 0) {
		cout &gt;&gt; "Your input is even" &lt;&lt; endl;
	}
	if (input % 2 == 1) {
		cout &gt;&gt; "Your input is odd" &lt;&lt; endl;
	}

// SNIPPED
</code></pre>
<p>The two-way syntax makes it very obvious that only one of two conditions can run at the same time. However, if we had some complex multi-branch logic or a possibility that both states run (number is even AND number is positive?) then it's possible to <em>fall through</em> into multiple ifs.</p>
<p>If the condition is not explicitly true or false (ex <code>if (val = 0)</code> assignment), C++ will <strong>cast</strong> to boolean. In this case, <code>val = 0</code> returns 0, which is cast to false.</p>
<h2>Module 5.1 - Branching Statements Part 2</h2>
<p>Now that we have some logic, we can write a program that takes an input character and classifies it as a lower case, upper case, digit, or non-alphanumeric. Before looking at the video, I decided to try it with direct character comparison rather than ASCII code conversions.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {
	char input;
	cout &lt;&lt; "Please input a single character" &lt;&lt; endl;
	cin &gt;&gt; input;

	if (input &gt;= 'A' &amp;&amp; input &lt;= 'Z') {
		cout &gt;&gt; input &gt;&gt; " is an upper case" &lt;&lt; endl;
	} else if (input &gt;= 'a' &amp;&amp; input &lt;= 'z') {
		cout &gt;&gt; input &gt;&gt; " is a lower case" &lt;&lt; endl;
	} else if (input &gt;= '0' &amp;&amp; input &lt;= '9') {
		cout &gt;&gt; input &gt;&gt; " is a digit" &lt;&lt; endl;
	} else {
		cout &gt;&gt; input &gt;&gt; " is non-alphanumeric" &lt;&lt; endl;
	}

	return 0;
}
</code></pre>
<p>How about converting 24-hour time to 20-hour time?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

int main() {
	int hours;
	int minutes;
	string period;
	int hourConverted, minuteConverted;
	char tmp;

	cout &lt;&lt; "Input a 24 hour time" &lt;&lt; endl;
	cin &gt;&gt; hours &gt;&gt; tmp &gt;&gt; minutes;

	minuteConverted = minutes;
	if (hours &gt;= 0 &amp;&amp; hours &lt;= 11 ) {
		period = "am";
		if (hours == 0) {
			hourConverted = 12;
		} else {
			hourConverted = hours;
		}
	} else {
		period = "pm";
		if (hours == 12) {
			hourConverted = 12;
		} else {
			hourConverted = hours - 12;
		}
	}

	cout &lt;&lt; "The time is: " &lt;&lt;hourConverted &lt;&lt; ":" &lt;&lt; minuteConverted;
	return 0;
}

</code></pre>
<p>We've now introduced the <strong>multi-way if</strong>. We have another way to handle this, the <strong>switch statement</strong>. A switch condition is a numeric expression, and each branch is a <code>case</code> that matches a constant and executes some code. Each branch must end with a <code>break</code> to avoid falling through.</p>
<p>Now let's read a mathematic expression (basic operators) nd return the value. Input is <code>arg op arg</code>.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {
	double arg1, arg2;
	char operator;

	cout &lt;&lt; "Please enter an integer, operator and integer separated by spaces" &lt;&lt; endl;

	cin &gt;&gt; arg1 &gt;&gt; operator &gt;&gt; arg2;

	switch(operator) {
		case '*':
			cout &lt;&lt; arg1 * arg2 &lt;&lt; endl;
			break;
		case '+':
			cout &lt;&lt; arg1 + arg2 &lt;&lt; endl;
			break;
		case '-':
			cout &lt;&lt; arg1 - arg2 &lt;&lt; endl;
			break;
		case '/':
			if (arg2 != 0) {
				cout &lt;&lt; arg1 / arg2 &lt;&lt; endl;
			} else {
				cout &lt;&lt; 'Invalid divisor' &lt;&lt; endl;
			}

			break;
		default:
			cout &lt;&lt; "Error: invalid operator" &lt;&lt; endl;
			break;
	}

	return 0;
}
</code></pre>
<p>A switch statement is considered to be less powerful than multi-way if statements (only can evaluate one condition). Also the condition must be of type int, char or bool. We also need that default branch, or more accurately if we don't have a default branch then nothing happens.</p>
<h2>Problem Solving with C++ - Section 2.4</h2>
<p>The order of execution of a program is often called the <strong>flow of control</strong>. We need some language to tell a computer to choose one of two alternatives based on some input. For example, how much do we pay a worker? Well, if they worked overtime we pay 1.5 times their rate for overtime. Else, we pay the standard rate. C++ affords us this logic with the <strong>if-statement</strong>.  The structure of the statement is the <strong>Boolean Expression</strong>, the <strong>Yes Statement</strong> and the optional <strong>No Statement</strong> (ex if this thing is true, do the yes, optionally if it's not true do the no).</p>
<p>Sometimes our boolean expression needs to be semi-complex, or based on multiple predicates (I can hire this person if they're 18 or older AND either has job experience OR has a good referral). We can join these predicates with boolean operators like &amp;&amp; or || to return a single boolean value.</p>
<p>A boolean can also be negated with a ! sign (ex I can hire this person if they are not under 18).</p>
<p>Note that comparisons are <strong>binary expressions</strong>, that is, they operator on two values, so the statement <code>if (18 &lt;= employeeAge &lt; dead)</code> is invalid. We would need two comparisons joined by &amp;&amp;. The book skips over why but the tldr is is parsing and compilation, where we'll go from <code>if (18 &lt;= employeeAge &lt; dead)</code> to <code>if (true &lt; dead)</code> because we'll have to evaluate step by step.</p>
<h2>Problem Solving with C++ - Section 3.2</h2>
<p>When a program chooses from a number of options, we call this a <strong>branching mechanism</strong>. The if-else statement is an introductory one, but there are others. We have <strong>nested statements</strong> which look like</p>
<pre><code class="language-cpp">	if (this happens) {
		if (something else is true) {
			// do a thing
		} else {
			// do another thing
		}
	} else {
		//body
	}

</code></pre>
<p>The book has some gotchas around no braces and indenting nested ifs. Just add the damn braces. It's free.</p>
<p>We also have <strong>switch statements</strong> which will sequentially evaluate branches (called <strong>cases</strong>) and potentially fall through multiple branches unless you <code>break</code> out.</p>
<pre><code class="language-cpp">switch (grade) {
	case 'A':
		// do a thing
		break;
	case 'B':
		// do a thing
		break;
	default:
		// for everyone else
		break;
}
</code></pre>
<p>The choice of which branch runs is determined by a <strong>controlling expression</strong> (the condition in the parentheses). Each case has  <strong>label</strong> which must be a constant (a literal, or a predefined <code>const</code>). That means we cannot do this</p>
<pre><code class="language-cpp">// INVALID!!!
switch (age) {
	case age &lt; 18:
		// no job for you friend
		break;
	default:
		// hiring you
		break;
}
</code></pre>
<p>We CAN have fall throughs on purpose by skipping breaks</p>
<pre><code class="language-cpp">switch (grade) {
	case 'A':
	case 'a':
		// do a thing
		break;

	case 'B':
	case 'b':
		// do a thing
		break;
	default:
		// for everyone else
		break;
}
</code></pre>
<p>When we create if-else statements (or even switches) we can use <strong>blocks</strong>, which are blobs of code bounded by curly braces. This allows us <strong>local variables</strong>, or the ability to define something that doesn't exist outside of the curlies. We call this <strong>block scope</strong>. We will refer to these blocks as <strong>statement blocks</strong> to separate them from functions.</p>
<h2>Discrete Math Section 3.1</h2>
<p>A <strong>set</strong> is a collection of objects. The objects in a set are called <strong>elements</strong> (just like domains). A set does not need to be all of the same type, ex a set could be the set of a strawberry, the number 2, and a monkey.</p>
<p>When a set is small, we can use <strong>roster notation</strong> to define a set as the elements separated by commas and wrapped in curly braces</p>
<p>ex A = {2, 4, 6, 8, 10}</p>
<p>Order is unimportant in sets.</p>
<p>We have two symbols to determine if an element is a member of a set.</p>
<ul>
<li>∈ is the symbol to indicate presence, ex 2 ∈ A</li>
<li>∉ is the symbol to indicate no presence, ex 5 ∉ A</li>
</ul>
<p>We can also use variables to indicate that some variable x is a member of a set. So if we say a ∈ A, we're telling the reader "a is a value that exists in set A, but it could be any of them".</p>
<p>Sometimes it's unwieldy to use roster notation to indicate all elements of a set. If that's the case, we show enough elements to indicate a pattern, then ellipsis, then the last value. For example, if I said "Let A be the set of all numbers from 1 to 100" we could write A = {1, 3, 5, ..., 99}</p>
<p>We can also have an <strong>infinite set</strong> rather than a <strong>finite set</strong>, and we indicate the infinite set without an ending element (because...it's infinite). So if we say "Let C be the set of all positive multiples of 3" we could write C = {3, 6,  9, 12, ...}</p>
<p>If a set has no elements, we call it the <strong>empty set</strong> and write it as ∅. We can also call it the <strong>null set</strong> and indicate with empty braces {}. Since an empty set has no elements, for any a, a ∈ ∅ is true.</p>
<p>We have the idea of <strong>cardinality</strong> which is the size of a finite set. We denote the cardinality of a set as |A|. So for example for A = {1, 3, 5, 7}, |A| = 4. The cardinality of the empty set is zero.</p>
<p>Two sets are the same if and only if they have the exact same elements (remember, order doesn't matter).</p>
<h4>Common mathematical sets</h4>
<table><thead><tr><th>Set</th><th>Symbol</th><th>Example</th></tr></thead><tbody>
<tr><td><strong>N</strong> is the set of <strong>natural numbers</strong>, which includes all integers greater than or equal to 0.</td><td><strong>N</strong></td><td>0, 1, 2, …</td></tr>
<tr><td><strong>Z</strong> is the set of all integers</td><td><strong>Z</strong></td><td>…, -2, -1, 0, 1, 2, …</td></tr>
<tr><td><strong>Q</strong> is the set of <strong>rational numbers</strong>, which includes all real numbers that can be expressed as a/b, where a and b are integers and b ≠ 0.</td><td><strong>Q</strong></td><td>0, 1/2, 5.23, -5/3</td></tr>
<tr><td><strong>R</strong> is the set of real numbers.</td><td><strong>R</strong></td><td>0, 1/2, 5.23, -5/3, π, sqrt(2)</td></tr>
</tbody></table>
<p>We can use <strong>superscripts</strong> to denote the positive or negative elements of a set. For example, the set R+ is the set of all positive real numbers. The set Z- is the set of all negative integers. Since 0 is neither positive nor negative, these superscripts exclude 0.</p>
<p>We have another notation called <strong>set builder notation</strong> that basically says "This set is all the elements of another set that pass a predicate." In practice this looks something like</p>
<p>A = { x ∈ S : P(x) }</p>
<p>Put another way A is the set of elements of all x in S such that P(X)</p>
<p>S is often one of our mathematical sets. So we might way to say something like "all the integers such that x is in between 0 and 100 and is prime", or</p>
<p>C = { x ∈ Z : 0 &lt; x &lt; 100 and x is prime}</p>
<p>The <strong>universal set</strong> usually denoted by the variable U is the set that contains all elements mentioned in a particular context. For example, if we're having a conversation about grades in the Bridge, we can assume the universal set is the set of all students in the bridge.</p>
<p>When we visualize sets, a <strong>Venn diagram</strong> is useful. We can define the universal set as a box outside, then sets inside as circles (with or without overlaps) and even write sample elements in or out of the sets.</p>
<p>If every element of A is an element of B, we call A a <strong>subset</strong> of B (the students of the Bridge in NY are a subset of the students in the Bridge). We denote this as A ⊆ B.</p>
<p>If at least one element in A is not a member of B, we can say A is not a subset of B. We can denote this as A ⊈ B.</p>
<p>We can chain these operations, to express something like "The empty set is a subset of A and A is a subset of the universal set" as ∅ ⊆ A ⊆ U</p>
<p>If two sets are subsets of each other they must be equal (that is, every element from one exists in the other).</p>
<p>If A is a subset of B and B is not a subset of A (that is, The students that get an A vs the students that get As and Bs) we call this a <strong>proper subset</strong> denoted as A ⊂ B. This is only true when A is not equal to B but A IS a subset.</p>
<h2>Discrete Math Section 3.2</h2>
<p>It's possible that elements of a set are in themselves sets. For example, consider from the book</p>
<p>A = { { 1, 2 }, ∅, { 1, 2, 3 }, { 1 } }</p>
<p>A is a set that has a cardinality of 4. Its members are each sets. We cannot say that A contains the integer 1, so 1 ∉ A, but we CAN say that the set of { 1 } is a member of A, so { 1 } ∈ A.</p>
<p>We can also not say that { 1 } is a subset of A, since 1 ∉ A therefore { 1 } ⊈ A.</p>
<p>The book notes that ∅ is not the same as  { ∅ } - the former is the empty set, and the latter is a set of one element (the empty set).</p>
<p>The <strong>power set</strong> of set A, denoted P(A), is the set of all subsets of A. Or in other words, if a set has three elements then the power set is every combination of subsets from the empty set to the full set - didn't I do <a href="https://leetcode.com/problems/subsets/">this before</a>? Concretely if A = {1, 2, 3} then P(A) = { ∅, { 1 }, { 2 }, { 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 1, 2, 3 } }. The cardinality of a power set is 2^n where n is the cardinality of the set.</p>
<h2>Discrete Math Section 3.3</h2>
<p>We can introduce <strong>set operations</strong> which are behaviors on sets that can define new sets.</p>
<h4>Set intersection</h4>
<p>The <strong>intersection</strong> of sets A and B is denoted by A ∩ B and read "A intersect B", and is the set of all elements in both A and B. That is, the set of desserts I like is { cookies, brownies, ice cream }. The set of desserts my dog likes is { ice cream (please no) , chicken bones }. A ∩ B would be { "ice cream" }.</p>
<p>This can also apply to infinite sets, from the book:</p>
<p>A = { x ∈ Z: x is an integer multiple of 2 }</p>
<p>B = { x ∈ Z: x is an integer multiple of 3 }</p>
<p>A ∩ B = { x ∈ Z: x is an integer multiple of 6 }</p>
<h4>Set union</h4>
<p>The <strong>union</strong> of two sets A and B, denoted A ∪ B and read "A union B", is the set of elements in either A or B <em>inclusive</em> (not XOR). That is, using the same dessert example, A ∪ B = { cookies, brownies, ice cream, chicken bones }</p>
<p>Set operations can be joined and chained but must use parentheses to define them when mixing union and intersection. That is, if we're all unions then order doesn't matter (commutative and associative properties) and if we're all intersections then order doesn't matter, but once we mix then we need to group.</p>
<p>There is a special notation for long sequences of sets A1 to An. We can say in english "From i = 1 to n, the set Ai is the set of all x such that x exists in some A" or "for i = 1 to n, the set is the set of all X such that x exists in ALL A"</p>
<p><em>source: Discrete Math, Rosen</em></p>
<p><img src="./union.png" alt="Union image" /></p>
<p><img src="./intersection.png" alt="Intersection image" /></p>
<h2>Discrete Math Section 3.4</h2>
<p>We can also describe the <strong>difference</strong> betwen two sets, denoted as A - B, which is the set of elements in A but not B. That is, the extra elements in A. So if A = { 1, 2, 3, 4, 5} and B = {4, 5, 6}, A - B = {1, 2, 3} and B - A = { 6 }</p>
<p>We also have the <strong>symmetric difference</strong> - that is, the set of elements that are present in exactly one but not both sets. ex since A - B doesn't necessarily equal B - A, we need an operator for this. In this case, we use the XOR A ⊕ B. We can also define this as the union of the two differences, or (A - B) ∪ (B - A)</p>
<p>This far, all we've needed is a definition of A and B. We haven't really cared to define the universal set U. We're about to need this. How do we describe the set of all elements NOT in A? We need a domain, or the universal set. For this we use the <strong>complement</strong> denoted of set A denoted A' ( <a href="http://web.mnstate.edu/peil/MDEV102/U1/S6/Complement3.htm#:~:text=Complement%20of%20a%20Set%3A%20The,U%20%3A%20x%20%E2%88%89%20A%7D.&amp;text=Example%3A%20U&#x27;%20%3D%20%E2%88%85%20The,universe%20is%20the%20empty%20set.">link</a>) (or A with a bar over it). A' can be defined as U - A, or the entirety of the universe minus the elements of set A.</p>
<p>For example, if U = Z (the set of all integers), and A = { x ∈ Z: x is odd } (aka the set of all odd integers), then A', the complement of A, is the set of all even integers.</p>
<p>Table time.</p>
<table><thead><tr><th>Operation</th><th>Notation</th><th>Description</th></tr></thead><tbody>
<tr><td>Intersection</td><td>A ∩ B</td><td>{ x : x ∈ A and x ∈ B }</td></tr>
<tr><td>Union</td><td>A ∪ B</td><td>{ x : x ∈ A or x ∈ B or both}</td></tr>
<tr><td>Difference</td><td>A - B</td><td>{ x : x ∈ A and x ∉ B }</td></tr>
<tr><td>Symmetric Difference</td><td>A ⊕ B</td><td>{ x : x ∈ A - B or x ∈ B - A }</td></tr>
<tr><td>Complement</td><td>A'</td><td>{ x : x ∉ A}</td></tr>
</tbody></table>
<h2>Discrete Math Section 3.5</h2>
<p>An <strong>ordered pair</strong> of items is denoted by (x, y). The first <strong>entry</strong> is x and the second y. Parentheses indicate that order matters, unlike the curly braces of set denotation. This means that (x,y) does not necessarily equal (y, x).</p>
<p>For two sets A and B, the <strong>Cartesian product</strong>, denoted A x B, is tthe set of all ordered pairs in which the first entry is in A and the second in B. This means that A x B isn't necesssarily B x A. An alternative explanation is A x B = { (a, b) : a ∈ A and b ∈ B }. The cardinality of the Cartesian product is |A| * |B|.</p>
<p>We call (x, y, z) an <strong>ordered triple</strong> and refer to an ordered list of length &gt;= 4 as an <strong>ordered n-tuple</strong>, ex (w, x, y, z) is an ordered 4-tuple. The Cartesian product of n number of sets contains tuples of n length. That is, two sets yields ordered pairs, 3 sets yields ordered triples, 10 sets yields ordered 10-tuples.</p>
<p>We can denote the Cartesian product of set A with itself, A x A, as A^2. More generally, A^k = A x A x A x... x A  (k times). Examples, R^2 is the set of all pairs (x, y) such that x and y are real numbers.</p>
<p>When A is a set of symbols or characters, we can list the elements of A^n without parentheses or commas. For example, if A = { x, y } then A^2 = {xx, xy, yx, yy}. A sequence of characters is a <strong>string</strong> and the set of characters used by the strings is called the <strong>alphabet</strong>. The <strong>length</strong> of a string is the number of characters in the string. That is, the length of xyyx = 4.</p>
<p>A <strong>binary string</strong> is a string made from the alphabet {0, 1}. A <strong>bit</strong> is a character in a binary string. A string of length n is also called an <strong>n-bit string</strong>. The set of binary strings of length N is {0, 1} ^ n.</p>
<p>The <strong>empty string</strong> is a string of length 0 and is denoted by the lambda λ. Since {0, 1}0 is the set of all binary strings of length 0, {0, 1}0 = {λ}.</p>
<p>If two strings s and t are joined, the <strong>concatentation</strong> is denoted st is a longer string made up joining s and t together. If s = 010 and t = 110 then st is 010110. We can also join with symbols, so t0 = 1100 where t = 110.</p>
<h2>Discrete Math Section 3.6</h2>
<p>We can define set operations with logical identiies. That is x ∈ A ∩ B  ↔  (x ∈ A) ∧ (x ∈ B), or A intersect B is the same as in A AND in B.</p>
<p>The universal set U corresponds to true and the empty set to false</p>
<p>x ∈ ∅   ↔   F
x ∈ U   ↔   T</p>
<p>Given these, and laws of propositional logic, we can derive set <strong>identities</strong>. A set identity is an equal that is true regardless of the contents in the set. For example, we can use De Morgan's law.</p>
<p>Table</p>
<table><thead><tr><th>NAME</th><th>Identity 1</th><th>Identity 1</th></tr></thead><tbody>
<tr><td>Idempotent Laws</td><td>A ∪ A = A</td><td>A ∩ A = A</td></tr>
<tr><td>Associative Laws</td><td>(A ∪ B) ∪ C = A ∪ (B ∪ C)</td><td>(A ∩ B) ∩ C = A ∩ (B ∩ C)</td></tr>
<tr><td>Commutative Laws</td><td>A ∪ B = B ∪ A</td><td>A ∩ B = B ∩ A</td></tr>
<tr><td>Distributive Laws</td><td>A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C)</td><td>A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C)</td></tr>
<tr><td>Identity Laws</td><td>A ∪ ∅ = A</td><td>A ∩ U = A</td></tr>
<tr><td>Domination Laws</td><td>A ∩ ∅ = ∅</td><td>A ∪ U = U</td></tr>
<tr><td>Double Complement Law</td><td>A'' = A</td><td></td></tr>
<tr><td>Complement Laws</td><td>A ∩ A' = ∅ or U' = ∅</td><td>A ∪ A' = U or ∅' = U</td></tr>
<tr><td>De Morgan's Laws</td><td>(A ∪ B)' = A' ∩ B'</td><td>(A ∩ B)' = A' ∪ B'</td></tr>
<tr><td>Absorption Laws</td><td>A ∪ (A ∩ B) = A</td><td>A ∩ (A ∪ B) = A</td></tr>
</tbody></table>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 2</title><link>https://nthomas.org/2020-07-11-NYU-Tandon-Bridge-Week-2/Index/</link><guid>https://nthomas.org/2020-07-11-NYU-Tandon-Bridge-Week-2/Index/</guid><pubDate>Saturday, 11 July 2020 11:24:07 +0000</pubDate><description>C++ data types and expressions, representation internally, mathematical proving techniques</description><content:encoded><![CDATA[<h2>Module 3: Hello Word</h2>
<p>Process of executing a program - we'll focus on CPU and main memory (RAM), alongside secondary memory. Both memory devices contain a lot of 0s and 1s, or <strong>bits</strong>. These bits encode information and are collected into collection of 8 bits, called a <strong>byte</strong>. The first byte is located at a physical address of 0, then 1, 2, etc. A sequence of bytes will capture a program. What happens when we execute a program? Program is currently stored in secondary memory. First it gets copied into main memory for faster access to CPU. Then instructions get fetched by CPU one at a time. The CPU has a <strong>program counter</strong> (PC) instruction, initialized to location in memory where program begins. CPU fetches, decodes, executes steps, then increments PC.</p>
<p>We don't program with 0s and 1s, or <strong>machine language</strong>. We use a higher level language like C++, Java, Python, etc etc. We need a way to express closer to human thought and get it translated to machine code. This process of translation is called <strong>compilation</strong> or a build process. This is automated by tools like clang, gcc, javac.</p>
<p>A sample program that reads two numbers from user and prints the sum:</p>
<pre><code class="language-cpp">/*
	intro comments that express the program task eg
	this program takes two numbers from stdin, finds the sum and returns it to stdout
*/
#include &lt;iostream&gt;
using namespace space;

int main() {
	int num1; // holds first input
	int num2; // holds second input
	int sum; // holds sum

	cout &lt;&lt; "Please enter two numbers separated by a space:" &lt;&lt; endl;
	cin &gt;&gt; num1 &gt;&gt; num2;

	sum = num1 + num2;

	cout &lt;&lt; num1 &lt;&lt; " + " &lt;&lt;num2 &lt;&lt; " = " &lt;&lt; sum &lt;&lt; endl;

	return 0;
}
</code></pre>
<h2>Problem Solving with C++ sections 1.1-1.4</h2>
<p>A set of instructions is called a program. A collection of programs for a computer is what we call the <strong>software</strong> for that computer (OS, Microsoft Office, VS Code). The physical parts are what we call <strong>hardware</strong>. Hardware is "conceptually very simple" but the breadth and complexity of software in a working system is what makes computers complicated and powerful.</p>
<h4>Hardware</h4>
<p>Three classes of computers - PCs, workstations and mainframes. A <strong>PC (personal computer</strong> is what it sounds like - relatively small, designed for one person at a home. A <strong>workstation</strong> is an industrial-strength PC (super powerful Dells). A <strong>mainframe</strong> is even larger that is shared between users and typically requires a support staff.</p>
<p>A <strong>network</strong> is a connection of computers that share resources (printers, for example) and even information). A work network could have multiple PCs, a few mainframes for shared compute, access to shared printers, etc.</p>
<p>The book classifies hardware into <strong>input devices</strong>, <strong>output devices</strong>, <strong>processors</strong>, <strong>main memory</strong> and <strong>secondary memory</strong>. CPU + main memory = integrated compute unit. Everything else connects to those two and operate under their direction.</p>
<p>Input devices - keyboards, mice, maybe voice-operated equipment?</p>
<p>Output devices - monitors, printer, something that CPU can write to externally. Keyboard + monitor = <strong>terminal</strong> (kinda)</p>
<p>Memory - two forms. <strong>Main memory</strong> is a long list of numbers locations called <strong>memory locations</strong> (the number or index is the <strong>address</strong>), each holding a string of <strong>bits</strong>. 8 bits = 1 <strong>byte</strong>. You can store data in a location, then find it by its address. A consecutive chain of bytes can form data types like numbers and letters. One concern - 01000001 is both the letter A and the number 65. How does the computer know what type it is? The book skips over this. We call main memory <strong>Random Access Memory</strong> since randomly accessing a byte location takes constant time.</p>
<p><strong>Secondary storage</strong> is for permanent (non volatile) storage. Writing to disk (HDD, SSD, etc) is for <strong>file</strong> storage. Memory access is usually sequential (that is, is this location X? No, go to next place. Is this location A? No, go to next place).</p>
<p><strong>Processor</strong> (our central processing unit) is the brain of the computer. The <strong>chip</strong> on the actual hardware item is the processor (buying a Ryzen 3600 means the chip is that, the rest is supportive hardware). The CPU can interpret instructions but those instructions are typically very simple, eg ADD two numbers and store the result somewhere, MUL two nums, MOV an item, JMP to another location.</p>
<h4>Software</h4>
<p>We communicate to hardware via an <strong>operating system</strong> interface (go see my notes from the CS class from Wisconsin). If you tell the computer "run Steam" what really happens is you tell the operating system, which is in charge of coordinating with the hardware to find the file, load into memory, etc.</p>
<p><strong>Program</strong> = set of instructions. <strong>Data</strong> = conceptualized input to program. Niklaus Wirth said "Algorithms + Data Structures = Programs".</p>
<p>High level languages vs machine language = covered above. We write closer to human-speak, but computers speak computer-speak. What do we do? We <strong>compile</strong> programs. A compiler takes source code and returns <strong>object code</strong> (a.out, main.o). A <strong>linker</strong> takes your programs object code, combines it with other code needed for some routines (like input/output) and returns a bundled version. Workflow is</p>
<p>C++ program --&gt; Compiler --&gt; Object Code  --&gt; linker --&gt; Machine code</p>
<p>Object code for other routines ---------------- ^</p>
<p><strong>Algorithm</strong> a series of precise instructions. A program lays out an algorithm, that is "get a list of names from the user, validate that there is at least 1, initialize a counter, for each name in list increment counter if name starts with X, then return counter."</p>
<p><strong>Program design</strong> can be broken down into two phases. There is the <em>problem solving phase</em> (what data structures, what steps, what algorithms, what optimizatoins) and the <em>implementation phase</em> (the writing of code). Quote: "Experience has shown that the two-phase process will produce a correctly working program faster."</p>
<p><strong>Object oriented programming</strong> (or OOP) is a method to model your problem domain as a set of interacting objects. Each object can have their own internal algorithm for behavior. We care about OOP because it affords <strong>encapsulation, inheritance and polymorphism</strong> via <strong>classes</strong> (a combination of data and behavior / algorithm).</p>
<p>The <strong>software life cycle</strong> (SLC or SLDC if we insert <em>development</em>) is a set of 6 phases:</p>
<ul>
<li>Problem definition - analyzing the task</li>
<li>Object and algorithm design</li>
<li>Implementation</li>
<li>Testing</li>
<li>Maintenance and evolution</li>
<li>Obsolescence</li>
</ul>
<p>In C++ a <em>return statement</em> is a way of identifying that a program ends, and returning 0 is the usual way of returning successfully. We have <strong>variable declaration</strong> with prefix type annotations in C++ - <code>int bearCount;</code>C++ uses <code>cin</code> and <code>cout</code> for interacting with stdin and stdout.</p>
<p>We often start a C++ program with <code>#include &lt;iostream&gt;</code> - this is called an <strong>include directive</strong>. iostream is joined by the linker at compile time. Directives always begin with the hash #.  C++ allows us to open a namespace with, for example  <code>using namespace std</code> which makes all methods in file scope.</p>
<p>We can compile with <code>g++</code> or <code>clang++</code> and specify a language standard with the <code>--std</code> flag, eg <code>--std=c++17</code>.</p>
<h2>Module 4 : Data Types and Expressions - Part 1</h2>
<p>We care about <strong>data, expressions and control flow</strong>. Data = types, classes, etc. Expressions = Arithmetic, IO, etc. Control flow = if/else, while, functions, etc. In the above C++ code from module 3, <code>int num1</code> is an example of data. <code>cin</code> and <code>cout</code> are expressions, and we had no control flow. Lines are default ordered sequentially. Everything has a type, because C++ is <strong>strongly typed</strong>.</p>
<p>The <code>int</code> type holds integers. We fix the size to 4 bytes (32 bit ints), and in memory the address of the integer points to the first of the bytes. 32 bit ints means we can store 2^32 or a little north of 2 billion. Can't represent all ints. Numbers are stored in bytes presented in <em>Two's Complement</em>.</p>
<p>Two forms of data - variables and constants. <code>int x</code> declares a variable. <code>6</code> is a constant, since it cannot be redefined. We call built-in constants <strong>literals</strong> (6 is a literal, we don't need to define it). User-defined or programmer-defined constants look more like <code>const int MAX = 5;</code></p>
<p>Operators are type-constrained. Arithmetic operators like + require ints or floats. Note that division is integer-division, eg 5 / 2 = 2. If we want remainder, we need the <strong>modulo</strong> (or mod) operator. In C++ that would be <code>5 % 2</code>.</p>
<p>We can do multiple assignment with <code>y = x = 7</code> since the return value of assignment is the righthand side value.</p>
<p>Conventions - camelCase or snake_case? camelCase is more common for C++ so I'll go with that.</p>
<h2>Module 4 : Data Types and Expressions - Part 2</h2>
<p>Floats and doubles are for real numbers (potential fractionals). Also fixed size, floats are 4 bytes and doubles are 8 bytes. Doubles allow us to represent up to 2^64. How do we represent the decimal place? Just gotta learn from <a href="https://fabiensanglard.net/floating_point_visually_explained/index.html">Fabien Sanglard</a> - the IEEE-754 spec. We call it floating point because the decimal point can "float around".</p>
<p>We need to represent floats as <code>6.0f, 0.85f, 3.1415f</code> etc.  To mark as a float we add an <code>f</code>, otherwise it'll be a double.</p>
<p>Write a program that reads from user the radius of a circle, then calculates and prints the area (radius * radius * PI).</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;cmath&gt;
using namespace std;

int main() {
 cout &lt;&lt; "Enter the radius of your circle: " &lt;&lt; endl;

 double radius;
 cin &gt;&gt; radius;
 double area = radius * radius * M_PI;

 cout &lt;&lt; "the area of your circle is " &lt;&lt; area &lt;&lt; endl;

 return 0;
}

</code></pre>
<p>Sometimes we need <strong>type casting</strong> - when we mix types we need to maintain one. We need to convert the data's representation from one type to another. We type cast with the syntax, <code>VARIABLE = (newType)VALUE</code> eg <code>double y = (double) 6;</code> or <code>int x = (int) 3.14f</code>;</p>
<p>If we mix types in an expression, like <code>5 / 3.0</code> then the compiler will try to cast both operands to an appropriate type. This is an <strong>implicit cast</strong> and maintains accuracy, that is int -&gt; double is safe but double -&gt; int is not okay.</p>
<h2>Module 4 : Data Types and Expressions - Part 3</h2>
<p>The <strong>char</strong> type is for representing characters. It's stored as 1 byte, since 2^8 (256) values is enough to represent lower case, upper case, digits, symbols. We call these representations the <strong>ASCII values</strong>. ASCII numbers are base-ten values that get converted to binary for storage.</p>
<p>Write a program that takes a char and returns the ASCII value.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {
  char letter;
  cout &lt;&lt; "Please input one character." &lt;&lt; endl;

  cin &gt;&gt; letter;

  cout &lt;&lt; "The ASCII value is " &lt;&lt; (int) letter &lt;&lt; endl;

  return 0;
}

</code></pre>
<p>Char literals for C++ are single-quote letters, like <code>char x = 'a';</code>. Double quotes are reserved for strings (<code>std::string</code>). We can use backslash before chars for special characters, like <code>\n</code> for newline. The backslash is called the escape. We can use arithmetic to get the next char like <code>(char) ('a' + 1)</code>. We can convert to uppercase as well. Write a char that takes a letter (assume lower case) and returns its upper case letter.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {

  char lowerCase;

  cout &lt;&lt; "Please enter a letter. " &lt;&lt; endl;
  cin &gt;&gt; lowerCase;

  int asciiCode = lowerCase - 32;
  cout &lt;&lt; asciiCode &lt;&lt; endl;
  char upperCase = (char)asciiCode;

  cout &lt;&lt; "The upper case of your letter is " &lt;&lt; upperCase &lt;&lt; "." &lt;&lt; endl;
  return 0;
}

</code></pre>
<p>The video uses offset, closer to</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

int main() {

  char lowerCase;

  cout &lt;&lt; "Please enter a letter. " &lt;&lt; endl;
  cin &gt;&gt; lowerCase;

  int offset = 'A' - (int) lowerCase;

  char upperCase = (char) ('A' + offset);

  cout &lt;&lt; "The upper case of your letter is " &lt;&lt; upperCase &lt;&lt; "." &lt;&lt; endl;
  return 0;
}

</code></pre>
<p>The <code>string</code> class is not built into C++, but needs the <code>#include &lt;string&gt;</code> directive. Representation is a sequence of characters. Literals are defined with double quotes. We can declare <code>std::string x = "Hello " + "world!"</code></p>
<h2>Module 4 : Data Types and Expressions - Part 4</h2>
<p>The <code>bool</code> data type represents true or false, or boolean logic. Takes 1 byte (not 1 bit) so any non-zero value is true. That is <code>10000000</code> and <code>00001000</code> and <code>0110000</code> are all true, only all-zeroes will be false. Only operators are our usual logic operators. First is <strong>not</strong> or <code>!</code>. Next is <strong>conjunction</strong> or <strong>and</strong>, with binary operator <code>&amp;&amp;</code>. Last is <strong>disjunction</strong> or <strong>or</strong>, with binary operator <code>||</code>.</p>
<p><strong>Atomic boolean expressions</strong> are true or false. We can use operators to make <strong>compound boolean expressions</strong>. We have arithmetic expressions compared with relational operators: <code>&gt;, &gt;=, &lt;, &lt;=, ==</code>.</p>
<h2>Problem Solving with C++ sections 2.1 - 2.3</h2>
<h4>Variables and assignment</h4>
<p>We use variables to name and store data. A C++ variable can hold all types of data, from <code>int</code>s to <code>bool</code>s to custom classes. The data itself is called the variable's <strong>value</strong>. <code>cin &gt;&gt; variable_name</code> takes input from stdin and assigns it to the variable on the right side of <code>&gt;&gt;</code>. In practice, variables are implemented as memory locations by the compiler.</p>
<p>The name of the variable is called the <strong>identifier</strong> (we learned this from <a href="https://craftinginterpreters.com/statements-and-state.html">Bob Nystrom</a>). Identifiers MUST start with a letter or underscore, and the rest of the characters can be letters, digits or underscore.</p>
<pre><code class="language-cpp">int _ignored; // valid
std::string firstName; // valid
bool 2_fast_2_furious; // invalid
</code></pre>
<p><strong>Keywords</strong> or <strong>reserved words</strong> are words disallowed for variable use, eg <code>class</code>.</p>
<p>Every variable must be <strong>declared</strong>, and you can declare multiple variables with the same type with commas, like <code>std::string firstName, lastName;</code>. The first part of a declaration is a <strong>type name</strong>. Variable declarations are used to let the compiler know how to encode the data and how much memory to allocate.</p>
<p>To give a variable a value, we use <strong>assignment statements</strong>. The parts are the type name, variable identifier, assignment operator, value, and then a semi colon. For example, <code>int age = 30;</code>. The value can also be an expression which will get evaluated prior to assignment. For example, <code>int moonWeight = earthWeight / earthGravity * moonGravity;</code>. Some values cannot change, like ints. We call these <strong>constants</strong>.</p>
<p>A variable without an assigned value is called an <strong>uninitialized</strong> variable. When variables are uninitialized, their values will be whatever value was in that memory location prior to the program running. That is, if Microsoft Word stuffed some file into that memory location and my uninitialized variable is assigned that location, its value will be a set of bits that correspond to the file data.</p>
<p>An <strong>input stream</strong> is...the stream of input flowing into the program. Okay not a good definition. But specifically, we use the word stream because we don't worry about the source and only act on the incoming data itself. We also have <strong>output streams</strong>.</p>
<p><code>cout</code> uses the insertion operator <code>&lt;&lt;</code>.</p>
<p>An <strong>include directive</strong> looks like <code>#include &lt;LIBRARY&gt;</code> and tells the system to use code from that file. Akin to copying the code over into the current file.</p>
<p>C++ also has <strong>namespaces</strong> and we can open a namespace for local resolution with a <strong>using directive</strong> like <code>using namespace std;</code>. Namespaces exist to let methods with the same name not clash.</p>
<p>The backslash operator <code>\</code> is to let users enter special characters in strings, such as <code>\n</code> for a newline or <code>\r\n</code> for "carriage return line feed".</p>
<p><code>double</code> and <code>float</code> types allow decimal points but they must be neither the first nor last character (0.6, 1.0, 3.14f). There is a way to format <code>cout</code> to return for example 2 decimals with</p>
<pre><code class="language-cpp">cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
</code></pre>
<p>When using <code>cin</code> to take in multiple inputs, like <code>cin &gt;&gt; var1 &gt;&gt; var2</code> the input from stdin must be separated by at least one whitespace.</p>
<p>It's considered good form to <strong>echo the input</strong> or write the input to stdout at some point before the program terminates. This allows the user to validate their input in case of weird or unexpected behavior.</p>
<p>Since doubles and floats have finite space, their values are approximations.</p>
<p>C++11 brought in the <code>auto</code> keyword for type inference, used as <code>auto whatIsThis = 5;</code></p>
<p><strong>Boolean expressions</strong> are expressions that return true or false. Boolean operations are relational operations like ==, &gt;, &lt;=.</p>
<p>Boolean operators in C++ are the unary ! (not or negation) and equivalency checks.</p>
<table><thead><tr><th>Symbol</th><th>Meaning</th></tr></thead><tbody>
<tr><td>!</td><td>Negation, like !ateCheese</td></tr>
<tr><td>==</td><td>Equality check</td></tr>
<tr><td>&amp;&amp;</td><td>boolean AND, conjunction</td></tr>
<tr><td>||</td><td>boolean OR, disjunction</td></tr>
<tr><td>&gt;</td><td>Greater than</td></tr>
<tr><td>&gt;=</td><td>Greater than or equals</td></tr>
<tr><td>&lt;</td><td>Less than</td></tr>
<tr><td>&lt;=</td><td>Less than or equals</td></tr>
</tbody></table>
<p>Boolean logic in C++ follow laws of propositions, eg De Morgan's law says that <code>!(x &amp;&amp; y) == !x || !y</code></p>
<p>Precedence rules apply, where unary operators have highest precedence, boolean operators the lowest, everything else falls in the middle.</p>
<p>C++ boolean operation can "short circuit evaluate" - that is, for <code>x || y</code> if x is true then y doesn't evaluate, and the whole expression returns true. For <code>x &amp;&amp; y</code> if x is false, the expression returns false before y evaluates.</p>
<p>C++ will coerce <code>int</code>s to work as <code>bool</code>s where any non-zero value is true.</p>
<p><strong>Enum</strong> types are enumerations over constants. If not specified values, they are monotonically increasings ints starting from zero. For example <code>enum Direction { NORTH, SOUTH, WEST, EAST = 500}</code> . There is a stronger version called a <strong>strong enum</strong> or <strong>enum class</strong> defined as <code>enum class Days { Mon, Tues, Wed}</code> that does not coerce to ints.</p>
<h2>Discrete Math Section 1.11</h2>
<p>We create a set of <strong>hypotheses</strong> we assume are true. An <strong>argument</strong> is a series of propositions, each of which are <strong>hypotheses</strong>, followed by a final proposition called the <strong>conclusion</strong>. An argument is <strong>valid</strong> if the conclusion is true when all hypotheses are true, else it is <strong>invalid</strong>. Denotation is:</p>
<p><img src="./hypotheses.png" alt="hypotheses denotation" />
<em>source: Discrete Math, Rosen</em></p>
<p>p1...pn are the hypotheses, and c is the conclusion. The symbol ∴ is read as "therefore". When p1...pn are all true, then the argument is valid. That is if <code>(p1 ^ p2 ^ (p3 ^ ...pn) --&gt; c</code> is a tautology then the argument is valid. Order doesn't matter for hypotheses due to commutative law of conjunction.</p>
<p>The way we prove validity is with truth tables. We look at each in which ALL hypotheses are true. If the conclusion is true in each of those rows, the argument is valid. If there is a row in which all hypotheses are true but the conclusion is false, then the argument is invalid. Consider</p>
<p>(p --&gt; q) ^ (p v q) ∴ q</p>
<table><thead><tr><th>p</th><th>q</th><th>p --&gt; q</th><th>p v q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td><td>T</td></tr>
<tr><td>F</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>T</td><td>F</td></tr>
</tbody></table>
<p>The only rows in which both hypotheses are true are rows 1 and 3. For both of those rows, q is true, so all the hypotheses are true and the conclusion is true so the argument is valid.</p>
<p>What if we went with the below?</p>
<p>¬p ^ (p --&gt; q) ∴ ¬q</p>
<table><thead><tr><th>p</th><th>q</th><th>¬p</th><th>p --&gt; q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>F</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>T</td><td>F</td></tr>
</tbody></table>
<p>The only row in which both hypotheses are true is 3, but the conclusion ¬q yields false so the argument is invalid.</p>
<p>The hypotheses and conclusion can be expressed in English as well, for something like "It is raining today AND if it is not raining then I will not ride my bike, therefore I will ride my bike".</p>
<p>Note that, in English, we might use propositions with known truth values, eg, 7 is an odd number, but the hypothesis MUST be expressed as a proposition over a domain and must be valid for all combinations of hypotheses.</p>
<h2>Discrete Math Section 1.12</h2>
<p>There are <strong>rules of inference</strong> that exist that we can use for hypotheses that we know to be true. Treat the SLASH operator (/) as a newline in a normally formatted argument (newline + conjunction)</p>
<table><thead><tr><th>Rule of Inference</th><th>Name</th></tr></thead><tbody>
<tr><td>p / (p --&gt; q) ∴ q</td><td>Modus ponens</td></tr>
<tr><td>¬q / (p --&gt; q) ∴ ¬p</td><td>Modus tollens</td></tr>
<tr><td>p ∴ p v q</td><td>Addition</td></tr>
<tr><td>p ^ q ∴ p</td><td>Simplification</td></tr>
<tr><td>p / q ∴ p ^ q</td><td>Conjunction</td></tr>
<tr><td>p --&gt; q / q --&gt; r ∴p --&gt; r</td><td>Hypothetical Syllogism</td></tr>
<tr><td>p v q / ¬p ∴ q</td><td>Disjunctive Syllogism</td></tr>
<tr><td>p v q / ¬p v r ∴ q v r</td><td>Resolution</td></tr>
</tbody></table>
<p>The process of applying the ruless of inference and laws of propositional logic is called a <strong>logical proof</strong>. A logical proof consistents of steps of pairing propositions with <strong>justifications</strong>. If the proposition in a step is a hypothesis, the justification is "Hypothesis" else it must follow from a previous step by applying one law of logic or rule of inference.</p>
<p>The structure of a proof is a list of numbered steps, where the left column is either the hypothesis and the right side Hypothesis, or the left side is a substitution and the right a rule of inference and line number representing input.</p>
<p>From the Rosen example</p>
<p>w: It is windy
r: It is raining
c: The game will be canceled</p>
<p>Then solve for</p>
<pre><code>If it is raining or windy or both, the game will be cancelled.
The game is not canceled
________
It is not windy
</code></pre>
<p>or</p>
<pre><code>(r v w)  --&gt; c
¬c
_______
¬w
</code></pre>
<p>The table then looks like</p>
<table><thead><tr><th>#</th><th>Proposition</th><th>Rule</th></tr></thead><tbody>
<tr><td>1</td><td>(r ∨ w) → c</td><td>Hypothesis</td></tr>
<tr><td>2</td><td>¬c</td><td>Hypothesis</td></tr>
<tr><td>3</td><td>¬(r ∨ w)</td><td>Modus Tollens 1, 2</td></tr>
<tr><td>4</td><td>¬r ^ ¬w</td><td>De Morgan's Law 3</td></tr>
<tr><td>5</td><td>¬w ^ ¬r</td><td>Commutative 4</td></tr>
<tr><td>6</td><td>¬w</td><td>Simplification 5</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.13</h2>
<p>We can apply the rules of inference to quantified statements, but we need to do so by substituting in one <strong>element</strong> from the domain. Eg "every employee who works hard got a bonus, Linda got a bonus, therefore some employee works hard". When an element has no special distinguishing characteristics from other elements in the domain we call it <strong>arbitrary</strong>. If it can be distinguished in some way, we call it <strong>particular</strong> (eg 3 is odd, so that is a particular element).</p>
<p>If the element is defined in a hypothesis, it is always a particular element and the definition of that element in the proof is labeled "Hypothesis". If an element is introduced for the first time in the proof, the definition is labeled "Element definition" and must specify whether the element is arbitrary or particular.</p>
<p>There are rules called <strong>existential instantiation</strong> and <strong>universal instantiation</strong> to replace a qualified variable with an element of the domain. To replace an element of the domain with a qualified variable we use <strong>existential generalization</strong> and <strong>universal generalization</strong>. This only works for non-nested quantifiers.</p>
<h4>Universal instantiation</h4>
<p>c is an element (arbitrary or particular)
∀x P(x)
∴ P(c)</p>
<p>"Sam is a student in the class. Every student passed the class. Sam is a student. Therefore Sam passed the class".</p>
<h4>Universal Generalization</h4>
<p>C is an arbitrary element
P(c)
∴ ∀x P(x)</p>
<p>"Let c be an arbitrary integer. c &lt;= c^2. Therefore all integers are less than or equal to their square"</p>
<h4>Existential Instantiation</h4>
<p>∃x P(x)
∴ (c is a particular element) ∧ P(c)</p>
<p>"There is an integer that is equal to it's square. Therefore, for some C, c == c^2"</p>
<p>*Note: each use of Existential instantiation must define a new element with its own name (e.g., "c" or "d").</p>
<h4>Existential Generalization</h4>
<p>c is an element (arbitrary or particular)
P(c)
∴ ∃x P(x)</p>
<p>"Sam is a particular student in the class. Sam completed the assignment. Therefore there exists a student in the class that completed the assignment."</p>
<p><strong>IMPORTANT</strong> - for every use of existential instantiation, we MUST use a different existential variable letter in order to avoid invalid proofs. EG if we say "c is a particular element" then later if we need another element, we must not use c again.</p>
<p>We can show an argument with quantified statements to be invalid by defining the domain and predicates which makes all hypotheses true but the conclusion false.</p>
<h2>Discrete Math Section 2.1</h2>
<p>We want to prove things in mathemtics. A <strong>theorem</strong> is a statement that can be proven to be true. A <strong>proof</strong> consists of a series of steps each of which follows logically from assumptions or previously proven statements, and the final step should be the result of the theorem proving true.</p>
<p>We might make use of <strong>axioms</strong> or statements we take to be true. A theorem might be something like "Every positive integer is less than or equal to its square."</p>
<p>How do we know where to start when writing proofs? We can apply known patterns to help break the problem down. Often we start by playing with different elements in the domain.</p>
<p>Rewriting the statement into precise mathematical language can help. Most theorems make assertions about all elements in a domain and are therefore universal statements, although the theorem may not explicitly state it as such. The first step is to name a <strong>generic object</strong> and prove the statement for that object.</p>
<p>For a universal statement, checking every element is known as a <strong>proof by exhaustion</strong>. This is doable for small domains, eg { -1, 0, 1 }. For larger domains, it is easier to invalidate by finding a <strong>counterexample</strong></p>
<h2>Discrete Math Section 2.2</h2>
<p>Many theorems take the form of a conditional where the conclusion follows a set of hypotheses. These can be expressed as p --&gt; c, where p is the conjunction of all hypotheses and c is the conclusion. In a <strong>direct proof</strong> we assume p to be true and the conclusion c is proven as a direct result of the hypotheses.</p>
<p>These hypotheses can also be universally qualified, like "for every integer x, if x is odd then x^2 is even"</p>
<h2>Discrete Math Section 2.3</h2>
<p>A <strong>proof by contrapositive</strong> proves a conditional theorem p --&gt; c by showing that ¬c --&gt; ¬p: that is, ¬c is assumed to be true and ¬p is proven as a result of ¬c. An example. Imagine the theorem "For every integer n, if n^2 is odd then n is odd." This can be described with the universal quantifer for the domain of all integers as ∀n (D(n^2) → D(n)) - in other words, assume negative conclusion and show negative hypothesis.</p>
<p>The contrapositive proof starts with an arbitrary n, assumes D(n) is false and proves that D(n^2) is false. Or, ∀n (¬D(n) → ¬D(n^2)). Imagine this example: 3n + 7 is odd therefore n is even. If we prove by contrapositive, we test the case where n is odd. We can describe even numbers as <code>2k</code> and odd numbers as <code>2k + 1</code>. So if we replace n with the above, we get <code>3(2k + 1) + 7</code> which yields <code>6k + 3 + 7</code> or <code>6k + 10</code> or <code>2(3k + 5)</code>, and we said that <code>2k</code> is an even number, so if <code>k = (3k + 5)</code> then 2k must be even. Therefore we can assert that <code>3n + 7</code> is even.</p>
<p>Another example:</p>
<p>Theorem: For every real number x, if x^3 + 2x + 1 ≤ 0, then x ≤ 0.</p>
<ol>
<li>Negate the conclusion and assume x &gt; 0</li>
<li>Since x &gt; 0, 2x &gt; 0 and x^3 &gt; 0</li>
<li>Since x^3 &gt; 0 and 2x &gt;0 and 1 &gt; 0, the sum is &gt; 0</li>
<li>x^3 + 2x + 1 &gt; 0 therefore the theorem is true</li>
</ol>
<h2>Discrete Math Section 2.4</h2>
<p>A <strong>proof by contradiction</strong> assumes the theorem is false and then tries to find some inconsistency that would lead this to be incorrect.  If t is the statement of the theorem, the proof begins with the assumption ¬t and leads to a conclusion r ∧ ¬r, for some proposition r. If the theorem being proven has the form p → q, then the beginning assumption is p ∧ ¬q which is logically equivalent to ¬(p → q). A proof by contradiction is sometimes called an <strong>indirect proof</strong></p>
<p>The proof by contrapositive method is a special case of proof by contradiction.</p>
<p>Example. Proof by contradiction that sqrt(2) is irrational</p>
<ol>
<li>assume the negation, or sqrt(2) is rational</li>
<li>Express sqrt(2) as n / d, since it's rational (d != 0 and no integer &gt; 1 can divide into n and d)</li>
<li>since <code>sqrt(2) = n / d</code> we can square both sides of the equation <code>2 = n^2 / (d  ^ 2)</code> and then multiply both sides by <code>d^2</code> to get <code>2d^2 = n^2</code></li>
<li>Since n^2 can be represented as 2 times some number, we can claim n^2 is even and thus n is even</li>
<li>If n is even, then <code>n = 2k</code> and if <code>2k = 2(d^2)</code> then d^2 must be a multiple of 2 and therefore even</li>
<li>if d^2 is even then d is even</li>
<li>If n and d are even, there exists an integer n that divides into both n and d, so therefore <code>n / d</code> cannot be rational so it must be irrational</li>
</ol>
<h2>Discrete Math Section 2.5</h2>
<p>A <strong>proof by cases</strong> takes a univerally quantified statement, breaks it down into classes, and proves an example of each case to be true. Every value in the domain must belong to at least one of the classes. For example, for the theorem "for every integer x, x^2 - x is even" can be proven in two cases: x is odd or x is even.</p>
]]></content:encoded></item>
<item><title>NYU Bridge to Tandon - Week 1</title><link>https://nthomas.org/2020-07-06-NYU-Tandon-Bridge-Week-1/</link><guid>https://nthomas.org/2020-07-06-NYU-Tandon-Bridge-Week-1/</guid><pubDate>Monday, 06 July 2020 11:24:07 +0000</pubDate><description>Logic, Fundamentals of System Hardware, Positional Number Systems</description><content:encoded><![CDATA[<h2>Discrete Math Section 1.1</h2>
<p>Logic is the study of formal reasonabing. A <em>proposition</em> is the base element. A proposition is a declarative sentence that is either true or false. eg The sky is blue, the house is warm, there are infinite prime numbers. It cannot be a command, eg close the door, say goodbye. It cannot be a question, eg have you had breakfast?</p>
<p>A proposition has a truth value.</p>
<table><thead><tr><th>Proposition</th><th>Truth Value</th></tr></thead><tbody>
<tr><td>The sky is blue</td><td>True</td></tr>
<tr><td>2 + 2 = 5</td><td>False</td></tr>
</tbody></table>
<p>We can assign propositions to <em>propositional variables</em>.</p>
<p><code>p: January has 31 days</code></p>
<p><code>q: February has 33 days</code></p>
<p>A <em>compound proposition</em> connects propositions with logical operators.</p>
<p><strong>Conjunction</strong> (the <em>and</em> operation) is signified with ^ - for example p is true, q is false, p ^ q is false. A conjunction is true if and only if all elements are true. In other words, false ^ false = false.</p>
<table><thead><tr><th>p</th><th>q</th><th>p ^ q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>F</td></tr>
<tr><td>F</td><td>F</td><td>F</td></tr>
</tbody></table>
<p><strong>Disjunction</strong> (the <em>or</em> operation) is denoted by v - that is, p v q = true because p is true. A disjunction is true if ANY of the propositions are true. That is, a disjunction is false only if both propositions are false. Disjunction is technically the "inclusive or"</p>
<table><thead><tr><th>p</th><th>q</th><th>p v q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>T</td></tr>
<tr><td>F</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>F</td></tr>
</tbody></table>
<p>There is also the <em>exclusive or</em> (XOR) denoted by ⊕. p ⊕ q is true if ONLY one of the two propositions are true. That is, both cannot be true at the same time (eg I am married ⊕ I am single, I am at the movies ⊕ I am in bed at home)</p>
<table><thead><tr><th>p</th><th>q</th><th>p ⊕ q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>F</td></tr>
<tr><td>T</td><td>F</td><td>T</td></tr>
<tr><td>F</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>F</td></tr>
</tbody></table>
<p><strong>Negation</strong> (the <em>not</em> operation) takes a boolean and returns the opposite. Denoted by ¬</p>
<table><thead><tr><th>p</th><th>¬q</th></tr></thead><tbody>
<tr><td>T</td><td>F</td></tr>
<tr><td>F</td><td>T</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.2</h2>
<p>Compound propositions can be made up of more than one operation. eg p ^ ¬q</p>
<p>There is a precedence list that determines order of operations if no parentheses are applies. In priority:</p>
<ul>
<li>¬ (not)</li>
<li>^ (and)</li>
<li>v (or)</li>
</ul>
<p>Examples:</p>
<ul>
<li>p ^ q v r =&gt; (p ^ q) v r</li>
<li>p v q ^ ¬r =&gt; p v (q ^ (¬r))</li>
</ul>
<p>The reduction of <code>p ^ ¬(q v r)</code> when p is T, q is F, r is T is in steps</p>
<ul>
<li>p ^ ¬(q v r)</li>
<li>T ^ ¬(F v T)</li>
<li>T ^ ¬(T)</li>
<li>T ^ F</li>
<li>F</li>
</ul>
<p>Truth tables will have 2^n number of rows where n is the number of unique variables. eg for p, q, r there are 8 rows total for every unique combination. One strategy when filling out truth tables is intermediate columns. eg if you have p, q, r and solving for <code>¬q ^ (p v r)</code></p>
<table><thead><tr><th>q</th><th>p</th><th>v</th><th>¬q ^ (p v r)</th></tr></thead><tbody>
</tbody></table>
<p>Then start by adding columns in the middle</p>
<table><thead><tr><th>q</th><th>p</th><th>v</th><th>¬q</th><th>(p v r)</th><th>¬q ^ (p v r)</th></tr></thead><tbody>
</tbody></table>
<h4>Exercise 1.2.4a</h4>
<table><thead><tr><th>p</th><th>q</th><th>¬p ⊕ q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>F</td></tr>
<tr><td>T</td><td>T</td><td>T</td></tr>
</tbody></table>
<h4>Exercise 1.2.4d</h4>
<table><thead><tr><th>p</th><th>q</th><th>r</th><th>r v p</th><th>(¬r ∨ ¬q)</th><th>(r ∨ p) ∧ (¬r ∨ ¬q)</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td></tr>
<tr><td>T</td><td>T</td><td>F</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>T</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>F</td><td>F</td><td>T</td><td>F</td></tr>
<tr><td>F</td><td>F</td><td>T</td><td>T</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>F</td><td>F</td><td>T</td><td>F</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.3</h2>
<p><strong>Conditional operation</strong> is denoted by --&gt; and expressed as p --&gt; q "if p then q". If P is true and Q is false, then p --&gt; q is false. Else, p --&gt; q is true. eg if there is a traffic jam then I will be late to work. if P (there is a traffic jam) and q (I am not late to work)  then p --&gt; q is false, because my predicate did not yield my expectation. if P is false (no traffic jam) and Q (i am late to work), p--&gt; q still holds. In a conditional, we call p the <strong>hypothesis</strong> and q the <strong>conclusion</strong>. If the hypothesis is false, the conditional is true. Truth Table below.</p>
<table><thead><tr><th>p</th><th>q</th><th>p --&gt; q</th></tr></thead><tbody>
<tr><td>T</td><td>T</td><td>T</td></tr>
<tr><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>T</td></tr>
<tr><td>F</td><td>F</td><td>T</td></tr>
</tbody></table>
<p>Think of it as a contract. The only way for the contract to be broken is if predicate P is true and consequence Q doesn't happen. eg if I sleep 8 hours I will feel rested. That contract can only be proven false if I sleep 8 hours and wake up feeling tired.</p>
<p>Terms to know:</p>
<p>The <strong>converse</strong> of p --&gt; q is q --&gt; p (converse means flip arguments). The <strong>contrapositive</strong> of p --&gt; q is ¬q --&gt; ¬p (contrapositive means negate and flip). The <strong>inverse</strong> of p --&gt; q is ¬p --&gt; ¬q (inverse means negate).</p>
<p>From book:</p>
<table><thead><tr><th>Proposition:</th><th>p → q</th><th>Ex: If it is raining today, the game will be cancelled.</th></tr></thead><tbody>
<tr><td>Converse:</td><td>q → p</td><td>If the game is cancelled, it is raining today.</td></tr>
<tr><td>Contrapositive:</td><td>¬q → ¬p</td><td>If the game is not cancelled, then it is not raining today.</td></tr>
<tr><td>Inverse:</td><td>¬p → ¬q</td><td>If it is not raining today, the game will not be cancelled.</td></tr>
</tbody></table>
<p>The <strong>biconditional</strong> operation is expressed by p &lt;--&gt; q and means "p if and only if q". That is, if p == q then p &lt;--&gt; q is true, else false. In other words, p &lt;--&gt; q means p == q.</p>
<p>For compound propositions, --&gt; and &lt;--&gt; take lower precedence than negation, conjuction, or disjunction when no parentheses are applied. This means that the proposition <code>p --&gt; q ^ r</code> is the same as <code>p --&gt; (q ^ r)</code>.</p>
<h2>Discrete Math Section 1.4</h2>
<p>A <strong>tautology</strong> is a compound proposition that always evaluates to true. That is, for all inputs, the result is true. Consider <code>p ∨ ¬p</code></p>
<table><thead><tr><th>p</th><th>¬p</th><th>p ∨ ¬p</th></tr></thead><tbody>
<tr><td>T</td><td>F</td><td>T</td></tr>
<tr><td>F</td><td>T</td><td>T</td></tr>
</tbody></table>
<p>A <strong>contradiction</strong> is a compound proposition that always evaluates to false. That is, for all inputs, the result is false. Consider <code>p ^ ¬p</code></p>
<table><thead><tr><th>p</th><th>¬p</th><th>p ^ ¬p</th></tr></thead><tbody>
<tr><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>F</td></tr>
</tbody></table>
<p>To prove something is not a tautology, you need to find the set of inputs that returns false (that is, anything false means the entirety cannot be a tautology). To prove something is not a contradiction, you need to find the set of inputs that returns true (that is, anything true means the entirety cannot be a contradiction).</p>
<p><strong>Logical equivalence</strong> - two compound propositions are said to be logically equivalent if they have the same truth value for all individual proposition values. We denote logical equivalence with =, for example for p and ¬¬p, for all values of p, p = ¬¬p. Propositions s and r are logically equivalent if and only if s &lt;--&gt; r is a tautology (that is, for all s and r, s == r). We can compare with a truth table.</p>
<table><thead><tr><th>p</th><th>¬p</th><th>p -&gt; ¬p</th></tr></thead><tbody>
<tr><td>T</td><td>F</td><td>F</td></tr>
<tr><td>F</td><td>T</td><td>T</td></tr>
</tbody></table>
<p><strong>De Morgan's Laws</strong> are logical equivalences to show how to distribute negation inside a parenthesized expression.</p>
<p>The first law is</p>
<p><code>¬(p ∨ q)   ≡   (¬p ∧ ¬q)</code></p>
<p>That is, you can distribute the negation to the inner elements of a paren and convert disjunction to conjunction. In code <code>!(true || true) == !true &amp;&amp; !true</code>. Imagine in English.</p>
<p>p: "I am hungry"
q: "I have a headache".</p>
<p>De Morgan's law says that "It is not true that I am hungry or I have a headache" is the same as "it is true that I am not hungry and I do not have a headache". More explicitly, if it's not true that EITHER are true, it must be true that BOTH are false.</p>
<p>The second law is (the same as the first, but swapping conjunction and disjunction)</p>
<p><code>¬(p ∧ q)   ≡   (¬p ∨ ¬q)</code></p>
<p>Imagine in English with the same p and q as above. De Morgan's second law states that "It is not true that I am hungry and I have a headache" is the same as " I am not hungry or I do not have a headache". More explicitly, if it's not true that BOTH are true, it must be true that at least one is false.</p>
<h2>Discrete Math Section 1.5</h2>
<p>If two propositions are logically equivalent, then one can be substituted for another within a more complex proposition. The compound proposition before the substitution is logically equivalent to the compound proposition after the substitution. For example</p>
<p>p → q ≡ ¬p ∨ q</p>
<p>therefore</p>
<p>(p ∨ r) ∧ (¬p ∨ q)  ≡  (p ∨ r) ∧ (p → q)</p>
<p>This is the substitution principle that we know from basic algebra.</p>
<p>There are a set of laws of propositional logic to show equivalence. Copied from text:</p>
<table><thead><tr><th>Law Name</th><th>Law 1</th><th>Law 2</th></tr></thead><tbody>
<tr><td>Idempotent laws:</td><td>p ∨ p ≡ p</td><td>p ∧ p ≡ p</td></tr>
<tr><td>Associative laws:</td><td>( p ∨ q ) ∨ r ≡ p ∨ ( q ∨ r )</td><td>( p ∧ q ) ∧ r ≡ p ∧ ( q ∧ r )</td></tr>
<tr><td>Commutative laws:</td><td>p ∨ q ≡ q ∨ p</td><td>p ∧ q ≡ q ∧ p</td></tr>
<tr><td>Distributive laws:</td><td>p ∨ ( q ∧ r ) ≡ ( p ∨ q ) ∧ ( p ∨ r )</td><td>p ∧ ( q ∨ r ) ≡ ( p ∧ q ) ∨ ( p ∧ r )</td></tr>
<tr><td>Identity laws:</td><td>p ∨ F ≡ p</td><td>p ∧ T ≡ p</td></tr>
<tr><td>Domination laws:</td><td>p ∧ F ≡ F</td><td>p ∨ T ≡ T</td></tr>
<tr><td>Double negation law:</td><td>¬¬p ≡ p</td><td></td></tr>
<tr><td>Complement laws:</td><td>p ∧ ¬p ≡ F and ¬T ≡ F</td><td>p ∨ ¬p ≡ T and ¬F ≡ T</td></tr>
<tr><td>De Morgan's laws:</td><td>¬( p ∨ q ) ≡ ¬p ∧ ¬q</td><td>¬( p ∧ q ) ≡ ¬p ∨ ¬q</td></tr>
<tr><td>Absorption laws:</td><td>p ∨ (p ∧ q) ≡ p</td><td>p ∧ (p ∨ q) ≡ p</td></tr>
<tr><td>Conditional identities:</td><td>p → q ≡ ¬p ∨ q</td><td>p ↔ q ≡ ( p → q ) ∧ ( q → p )</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.6</h2>
<p>Many math statements contain variables. "x is an odd number" cannot be a proposition because the truth value depends on the value of X. However, "5 is an odd number" IS a proposition. The truth value of a statement can be expressed a function P over a variable X. A <strong>predicate</strong> is a logical statement where the truth value is a function of one or more variables. If P is "x is an odd number", then P(5) is a proposition.</p>
<p>Predicates can have more than one variable, such as <code>R(x, y, z) : x + y = z</code>. The proposition R(2, 4, 6) is true.</p>
<p>The <strong>domain</strong> of a variable is the set of all possible values for the variable. The natural domain of the predicate "x is an odd number" is the set of all integers. Domains, if not clear from context, should be provided.</p>
<p>A non-math predicate could be "the dog ate dinner" - this depends on the dog, so P(dog). "The city has over 100,000 people" - this depends on the city, P(city).</p>
<p>It's possible that for all values of the domain, a statement is true. However, if the statement contains a variable, it is a predicate and not a proposition. That is - "the dog is cute" is a predicate, where the variable is the dog, but it's true for all values of the dog. Or if P(x) is <code>x + 1 &gt; 1</code> for all values in the domain of positive integers. For any valid x it's true, but the statement contains a variable X and is thus a predicate.</p>
<p>It is possible that, for all values of a domain, a predicate is true. So if you specify every value in your predicate, you can come up with a set of propositions with defined truth values. Another way to turn predicates into propositions is with a <strong>quantifier</strong>. The logical statement ∀x P(x) means "for all values of x, P(x)". The symbol is called the <strong>universal quantifier</strong> and ∀x P(x) is called a <strong>universally quantified statement</strong>. This means that ∀x P(x) is a proposition because it is either true or false. For example, "for all values of x in the domain of positive integers, x+1 &gt; 1" can be defined as ∀x(x + 1 &gt; 1).</p>
<p>This can be intuited as for the finite set {a1, a2, a3,... ax} then ∀x = P(a1) ^ P(a2) ^ P(a3) ... ^P(ax)</p>
<p>Some universally qualified statements can be proven to be true by showing the predicate holds for some <strong>arbitrary value</strong> in the domain. An arbitrary value assumes nothing other than it belongs in the domain. That is, ∀x(x + 1 &gt; 1) over the set of positive integers holds true and can be proved by selected any value.</p>
<p>A <strong>counterexample</strong> for a universally quantified statement is an element in the domain for which the predicate is false. If P(x) is "the fast food chain makes great burgers" and the domain is the set of fast food chains, Burger King as a value for X makes the predicate false. A more math-y example, ∀x(x^2 &gt; x) over the domain of positive integers is proven false where x = 1.</p>
<p>The logical statement ∃x P(x) is read as "there exists an x such that P(x)".  ∃x P(x) asserts that at least ONE value of X in the domain makes the predicate true. ∃ is called the <strong>existential quantifier</strong> and ∃x P(x) is called an <strong>existentially qualified statement</strong>.</p>
<p>This can be intuited as for the finite set {a1, a2, a3,... ax} then ∃x = P(a1) v P(a2) v P(a3) ... v P(ax).</p>
<p>An example in human words, ∃x (the fast-food chain x makes good burgers) is true for Five Guys. In math-y ideas, ∃x( x + 1 &gt; 1) for the domain of positive integers is true for at least one value, like 1.</p>
<p>Some existentially quantified statements can be proven false with an arbitrary value.  ∃x( x + 1 &lt; x) for the domain of positive integers is false and can be proven with x = 1. Or subtract x from both sides, you get 1 &lt; 0 which is false.</p>
<h2>Discrete Math Section 1.7</h2>
<p>It is possible to construct existential or universal quantifiers from logical operations (negation, conjunction, disjunction). Imagine P(x) : x is prime and O(x) : x is odd.</p>
<p>There exists some x that satisfies P(x) ^ O(x). This means <code>∃x(P(x) ^ O(x))</code> and can be proven true for x = 7. Or consider the statement "for all X, if x is prime then x is odd". This can be stated as <code>∀x(P(x) -&gt; O(x))</code>  and can be proven false with the counterexample x = 2 (x is prime, x is not odd). The universal and existential quantifiers have higher precedence than logical operations. That means that</p>
<p>∀x P(x) ∧ Q(x) is the same as</p>
<p>(∀x P(x)) ^ Q(x)</p>
<p>and NOT</p>
<p>∀x(P(x) ∧ Q(x))</p>
<p>A variable x in the predicate P(x) is referred to as a <strong>free variable</strong> because it's <em>free</em> to take on any value in the domain (not the same as a free variable in lambda calculus). The variable x in the quantified statement ∀x(P(x) is called a <strong>bound variable</strong> because it's bound to a quantifier. If a statement has no free variables, it is a proposition because we can determine its truth value.</p>
<p>In the statement (∀x P(x)) ∧ Q(x), the variable x in P(x) is bound by the universal quantifier, but the variable x in Q(x) is not bound by the universal quantifier. Therefore the statement (∀x P(x)) ∧ Q(x) is not a proposition. However, in the statement ∀x(P(x) ∧ Q(x)), the statement binds both uses of x so this is a proposition.</p>
<p>We can also have logical equivalence with quantified statements. For example, the statement "Every new employee hit their deadline" can be expressed as ∀x(N(x) -&gt; D(x)) or "for all X, if employee X is new, then employee X hit the deadline".</p>
<h4>Exercise 1.7.3d</h4>
<p>∃x(T(x) ^ ¬B(x))</p>
<h4>Exercise 1.7.3e</h4>
<p>∀x (T(x) --&gt; B(x))</p>
<h4>Exercise 1.7.4c</h4>
<p>∀x (S(x) --&gt; ¬W(x))</p>
<h4>Exercise 1.7.4h</h4>
<p>∀x (¬W(x) --&gt; S(x) v V(x))</p>
<h2>Discrete Math Section 1.8</h2>
<p>De Morgan's laws can be applied on quantified statements. If you take the English sentence "not every bird can fly", the logical equivalent is "there is a bird that cannot fly." That is, ¬∀x F(x) == ∃x ¬F(x)</p>
<p>Similarly, the English sentence "it is not true that there is a child in class that is absent today" is the same as "every child is not absent". For the predicate A(x), we can say ¬∃x F(x) == ∀x ¬F(x)</p>
<table><thead><tr><th>Original</th><th>Equivalent</th></tr></thead><tbody>
<tr><td>¬∀x F(x)</td><td>∃x ¬F(x)</td></tr>
<tr><td>¬∃x F(x)</td><td>∀x ¬F(x)</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.9</h2>
<p>If a predicate has more than one variable, ex P(x,y), then each variable must be bound by a separate quantifier (even if the same quantifier). A logical expression with multiple quantifiers than bnid different variables in the same predicate is said to have <strong>nested quantifiers</strong>.</p>
<table><thead><tr><th>Statement</th><th>Explanation</th></tr></thead><tbody>
<tr><td>∀x∃yP(x,y)</td><td>x and y are both bound</td></tr>
<tr><td>∀xP(x,y)</td><td>x is bound, y is free</td></tr>
<tr><td>∃y∃zT(x,y,z)</td><td>y and z are bound, x is free</td></tr>
</tbody></table>
<p>Consider predicate "M(x, y) : x sent an email to y" and proposition: ∀x ∀y M(x, y) which can be expressed as "Everyone sent an email to everyone" or, for every x, they sent an email to every y.</p>
<p>∀x ∀y M(x, y) is true if and only if for every pair x and y, M(x,y) is true. This includes x=y (sending email to self). If ANY pair of (x,y) yields false, the proposition is false.</p>
<p>Consider the proposition ∃x ∃y M(x, y), which is English can be stated as "there exists someone that sent an email to someone." This is true if, for ANY pair (x,y), M(x,y) is true. False if none.</p>
<p>We can alternate types of nested quantifiers, and include both, for example ∃x ∀y M(x, y) which we can say in English "there is someone to sent an email to everyone". If we switched quantifiers to ∀x ∃y M(x, y) we would say "Every person sent an email to someone." Quantifiers are applied left to right. Think about a <em>two player game</em>, one player is <em>existential player</em> and the other <em>universal player</em>. Existential player tries to make expression true since you only need one true for proof. Universal player tries to make expression false since you only need one false for proof.</p>
<p>In this case, pretend leftmost player can select a value and tries to win. Player to the right tries to win, that is, for their win condition select the value that achieves their goal.</p>
<p>Imagine the domain of all integers and ∀x ∃y (x + y = 0). No matter what x picks, y wants to win by proving at least one true. So if X picks 3, y picks -3 and wins the game, yielding true.</p>
<p>Now imagine the opposite ∃x ∀y (x + y = 0). Now Y wants to win by getting to false. So, as soon as X picks a number Y picks anything that invalidates. If X picks 3, Y can pick 0, 3 + 0 != 0, Y wins the game and invalidates returning false.</p>
<p>De Morgan's law applies to statements with nested quantifiers by toggling quantifier type when moving from outside to inside.</p>
<table><thead><tr><th>Before</th><th>After</th></tr></thead><tbody>
<tr><td>¬∀x ∀y P(x, y)</td><td>∃x ∃y ¬P(x, y)</td></tr>
<tr><td>¬∀x ∃y P(x, y)</td><td>∃x ∀y ¬P(x, y)</td></tr>
<tr><td>¬∃x ∀y P(x, y)</td><td>∀x ∃y ¬P(x, y)</td></tr>
<tr><td>¬∃x ∃y P(x, y)</td><td>∀x ∀y ¬P(x, y)</td></tr>
</tbody></table>
<h2>Discrete Math Section 1.10</h2>
<p>We need to sometimes express "everyone else", that is "everyone sent an email to everyone" means I even emailed myself but "everyone sent an email to everyone else" is correct. There is the conditional operator and inequality operator that we can use, which is expressed as ∀x ∀y((x =/= y) --&gt; M(x, y)) or, "for every person, they sent an email to everyone but themselves."</p>
<p>To express uniqueness, we can use an existential quantifier (which is true for at least one) and then use inequality to reflect all others. That is, saying "exactly one person was late to work" is the same as saying "there is an X that is late to work, and everyone else is not late to work." For the predicate L(x), this can be stated as ∃x(L(x) ^ ∀y((y =/=x) --&gt; ¬L(y)))</p>
<p>We are allowed to move quantifiers to the left IF the variable isn't involved in the left predicate. That is, for M(x, y) is "x is married to y" and A(x) is "x is an adult", we can say "Every adult is married to someone at the party" or "for every person x, if x is an adult then there is a person y that x is married to".</p>
<p>This can be written as ∀x(A(x) --&gt; ∃y M(x, y))</p>
<p>We can move the inner ∃y out to the left as ∀x∃y(A(x) --&gt; M(x, y))</p>
<h2>Fundamentals of System Hardware</h2>
<p>Module covers definition of a computer, types of computer, macro view of what's inside, what components do, commonalities, communication between components, how CPU works, memory hierarchy, hard disks, networking</p>
<p>What is a computer? Basic definition: an electromechanical device that takes input, does processing, returns output. Types of computers? There used to be mainframes, a central point of computing for a location (eg one mainframe per university campus). Today we have servers, something in a room or data center well controlled and chilled. Lots of computational hardware (RAM, CPU). We also have desktops, laptops, tablets, even smartphones.</p>
<h3>Components - What's inside?</h3>
<p>What's inside a computer? Power supplies, motherboards (most main components attach), CPUs, memory, secondary storage (eg SSD, HDD), tertiary storage (eg CD ROM), video controller cards. All of these components communicate via the motherboard / main board along the <strong>system bus</strong>. PSUs provide conversion from line voltage to 12V or 5V for parts internally. Motherboard is referred to as "circulatory system" metaphorically due to system bus and peripheral buses. CPU is the brain - all calculations done here. Registers, CU and ALU, L1 and L2 cache, etc. Main memory is our RAM, where code and data are stored - wiped when computer shuts down. Video card / GPU - does complex calculations in parallel programmming, stores info to display on screen.</p>
<p>What's common? All computers have at least one CPU, main memory (temp storage), secondary (permanent) storage. Most computers have a GPU for image rendering, a network interface (for net communication), peripheral interface (USB, Thunderbolt, etc). Each part typically does one or two of input, processing, output. eg SSD is IO, so input and output. CPU is processing.</p>
<p>Communication via parts or internal to machine is done via a <strong>bus</strong>. It's a physical, bidirectional pathway between two or more devices. The system bus is the main pathway between CPU and main memory, but also carries data to/from IO devices. eg moving code from RAM to CPU for computation, then back to RAM happens via the bus.</p>
<h3>Components -CPU</h3>
<p>CPU is a single piece of silicon  in the form of a chip, and is the only location where code can be <em>executed</em>. CPU runs on machine language, and operates in a <strong>fetch-decode-execute</strong> cycle. Each type of CPU has its own instruction set (x64 vs RISC-V). Each CPU has a small amount of local memory, called <strong>registers</strong>, used to perform operations and store information and results. A CPU may also have <strong>cache</strong> (L1, L2), to improve fetch time (faster than main memory).</p>
<p>CPU instructions are very granular and a small set, eg Move, Add, Subtract, Compare, Jump, etc etc. Mostly basic math operations. The CPU designer adds the capability to perform operations (end user programmer cannot change the instruction set). Instead, we use higher level languages that convert into instructions.</p>
<p>Instructions map to OpCodes (operation codes) mapped as numeric values. Instruction set is typically small (less than 100). When CPU receives an instruction, it knows what to do. eg ADD might be 0x00, AND = 0x20, JMP = 0xE9, etc etc.</p>
<p>Fetch-Execute (also known as Fetch-Decode): CPU finds an instruction, moves from main memory into CPU instruction register. Then CPU decodes instruction into actual logic (eg 0x00 means ADD), then potentially gets more variables from main memory (eg need to know what numbers to add). Then CPU executes (actually performs instruction on data). Then stores result somewhere. This cycle repeats and takes as little as 10 nanoseconds, meaning millions of instructions per second.</p>
<p>The instructions need to come from somewhere, and the CPU needs to store code in registers to execute. Why can't we only use registers then? Expensive - registers can only hold bytes of memory, and costs a lot of money to add. Instead we have a memory hierarchy, where each layer is more space, cheaper, but slower (eg registers &gt; cache &gt; RAM &gt; SSD &gt; HDD)</p>
<p><em>Sourced from https://software.intel.com/sites/default/files/managed/5f/b0/KitchenMemoryFigureScale.png</em></p>
<p><img src="../../assets/memory-hierarchy.png" alt="" /></p>
<p>In terms of scale, registers have nanosecond access time but can only fit bytes of data, needed for CPU instruction execution. L1 and L2 cache has nanosecond access time, measured in MB, CPU takes advantage of this automatically. RAM has ~10+ nanosecond access time, but GB of memory, volatile and cleared when system shuts down. Secondary storage has ~10 millisecond access (1 million times slower than RAM) but in terabytes, and permanent.</p>
<h3>Components - Memory</h3>
<p>RAM is Random Access Memory, known because its constant time to access any location (byte) in memory. Can be accessed by byte, or in units like word, int (4 bytes), etc. Volatile, so temporary and cleared when power is shut down. When a program is run, all machine language instructions are brought into RAM and pulled one by one into CPU for fetch-decode-execute.</p>
<p>Secondary storage can be broken down into two types.</p>
<ol>
<li>hard disk drives (spinning disks, measured in RPM). There's a head that determines height and a radius (from center to edge). Different read heads for different radii. Multiple spinning magnetic discs rotate together. Moving head takes time, so moving from inner most radius, reading, then outermost radius is SLOWER than just moving to adjacent radius. Each radius is called a track and holds some data and tracks are adjacent. Moving from track to adjacent track is RELATIVELY fast, compared to big jumps. Can take in order of milliseconds for big jumps.</li>
<li>Solid state drives have no moving parts. Just hold a number of chips like a USB flash drive does. All data stored in chips for the purpose of persistent storage. Data can accessed in random time (aka constant time no matter where byte is). Cost and design makes them smaller (by space) and more expensive than HDDs. Lack of moving parts also means less energy consumption.</li>
</ol>
<h3>Networking</h3>
<p>Networking - data can come from anywhere in the world now via networks that are connected. Networks are interconnected via the internet, so effectively all devices are connected.</p>
<p>Computers can be connected <em>physically</em> via wiring. Copper is standard for ethernet, usually 8 wires in 4 pairs, in an Unshielded Twisted Pair. Fiber is newer, transmit information via light over a piece of glass (a glass cable) with less attenuation. Faster, goes further, more expensive. We also have wireless connections, usually in form of WiFi but there's also microwave, etc.</p>
<p>We use protocols to determine, for example, when to start sending data, from whom, to whom - a shared language between computers for data transfer. Ethernet is both a protocol and a type of connection. Wifi is defined by 802.11. We also have ATM (asynchronous transfer mode) used for high capacity links.</p>
<p>We don't really wire devices any more, like old telephones. Rather we send <strong>packets</strong> of data (~1000 bytes) using multiple protocols. Protocols can be specific to types of applications, types of logical networks, types of physical networks. Packets encapsulate information of type of protocol as a <strong>header</strong> as well as data itself, and a network layer might add some more information into header. This allows receiver to read header and determine, for example, which application is the recipient. For example, if you have two browsers open, the network response should make it obvious which broweser gets the new data.</p>
<p>Common layers include Application (HTTP, SMTP - simple mail transfer protocol, IMAP - internet mail access protocol). Logical aka Network, usually two layers</p>
<ul>
<li>connection oriented vs connectionless - ordering and guarantee of delivery like UDP vs TCP</li>
<li>global delivery of packets - Internet Protocol aka IP, adds header so info can be routed back to your machine</li>
</ul>
<p>Another layer is physical - adds header AND footer, usually for local addressing. Think Ethernet or 802.11 needs to know which device on network.</p>
<p>So we add, eg, an HTTP header, a TCP header, AND an 802.11 Header just to get my phone to request a website.</p>
<h2>Positional Number Systems</h2>
<p>Data in memory can be represented as 0s and 1s, only two states. What kinds of data can we represent? Numbers - binary representation. Text, images, video, audio, all needs to eventually be 0s and 1s. Text can be mapped as a code to a char, eg 32 -&gt; space, 77 -&gt; M, 126 -&gt; ~. Images can be saved as sequence of colors (pixels), where each color is its own number value (like RGB from 0 to 255 for each of red, green, blue). Video then can be sequence of images. Audio - if we sample the voltage very frequently, we can encode audio.</p>
<p>We're familiar with <strong>decimal</strong>, or base 10. Once we hit 10, we can call this "one group of ten and 0 groups of ones" - same intuition in other bases. Imagine base 5? 0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 20, etc etc. Other systems are Octal (base 8), binary (base 2), hexadecimal (base 16)/</p>
<p>Binary - 0, 1, 10, 11, 100 (100 = 1 group of 4, 0 groups of 2s, 0 groups of 1).</p>
<p>Hexadecimal - 0, 1, 2...9, a, b, c, d, e, f, 10...1f, 20, 21...2f, 30</p>
<p>We need an idea of equivalent representations. We use subscripts, such as (13) 10 -&gt; 13 in decimal. What is 13 in decimal to octal? (15) 8. What is 13 in base 5? (23) 5. Binary? (1101) 2. Hexadecimal? (D) 16.</p>
<h3>Conversions</h3>
<p>Let's learn how to handle base conversions.</p>
<ul>
<li>Look at (375) 10,. We say that we have 3 hundreds, 7 tens, 5 ones. Also same as 3 * 10^2 + 7 * 10 ^ 1 + 5 * 10 ^ 0.</li>
<li>What about (125) 8? 1 * 8 ^ 2 + 2 * 8 ^ 1 + 5 * 8 ^ 0.</li>
<li>What about 1011 in binary? 1 * 2 ^ 3 + 0 * 2  ^ 2 + 1 * 10 ^ 1 + 1 * 10 ^ 0.</li>
<li>3b2 in hexadecimal? 3 * 16 ^ 2 + 11 * 16 * 1 + 2 * 16 ^ 0</li>
</ul>
<p>What about decimal to base B? Let's take 75 in decimal to binary. Well, we can come up with positional mapping. 2 ^ 8 is 256, so that position must be zero. So take the 6 digit ( 2 ^ 6 = 64) and calculate leftover, 75 - 64 = 11. Take 3 place, 2 ^ 3 =8, 11 - 8 = 3. Take  1 place, minus 2. Take 0 place, 1 - 1 = 0</p>
<p>75 = 00100000 + 00001000 + 00000010 + 00000001 = 001001011</p>
<p>Note - 1 + 2 + 4 + 8 + ...2 ^ k = 2 ^ (k+1) - 1</p>
<p>Let's try hexadecimal to binary. 3b9 == ? Need to map hex digit to 4 bit binary, eg 0 == 0000, 9 == 1001, a == 1010, f == 1111. So we go digit by digit. 9 -&gt; 1001, b -&gt; 1011, 3 -&gt; 0011. Add them in order and you get 0011 1011 1001.</p>
<p>To do it backwards, we do the same. Split binary into groups of 4 (start from right to left), left pad zeros then convert via mapping. 011011010011 becomes 0011 * 2 ^ 0 + 1101 * 2 ^ 4 + 0110 * 2 ^ 8. However, that's just the same as 6d3.</p>
<h3>Addition</h3>
<p>We know addition in decimals. Ones place, carry, tens digit, carry, etc etc. We can do same for anything else. Try octal:  365 +  243 -&gt; 5 +3 = 8, so 0 and carry one. 1 + 4 + 6 = 11, or 3 and carry one. 3 + 2 + 1 = 6. 600 + 40 + 0 = 630</p>
<h3>Subtraction</h3>
<p>We know decimals - start with ones, then tens, borrowing from place to the left as needed. Try again in octal. 536 - 351 -&gt; 6 - 1 = 5, then 3 - 5 doesnt work so borrow, borrow 8 from the left, 8 + 3 - 5 = 6, then 5 - 1 - 3 = 1, 165.</p>
<h3>Signed Numbers</h3>
<p>How do we represent -26 in binary? We don't have negative sign, only 0s and 1s. We can use <strong>sign and magnitude</strong> or hold a bit for sign, and the rest of the bits are the magnitude, or the number value. 1 in the sign bit is negative. This is why signed integers go up to 2 ^ 31 in 32 bit architecture. We more commonly use <strong>two's complement</strong> in computing. In a k-bit two's complement representation:</p>
<ul>
<li>a positive integer is represented by its k-1 bit unsigned, padded with a zero to the left (k = 3,  01 == 1)</li>
<li>The sum of a number and it's additive inverse is 2 ^ k</li>
</ul>
<p>Example: 26 in decimal in two's complement using 8 bits. 26 must be represented in binary in 7 bits, or 001101 and padded bit is 0. How about -26? Well, we know how to represent 2 ^ 8 in binary (10000000), so -26 must be the value that, when added to 26 in binary, is 2 ^ 8.  So 10000000 - 00011010.</p>
<p>Example: 00101101 as an 8 bit two's complement. What is the decimal? Starts with a zero, so must be positive. Next 7 bits are it's value. 45. How about 11101010? starts with a 1, so negative value. Then the sum of number and additive inverse = 2 ^ 8. x + 11101010 = 100000000, what is x? 00010110.</p>
<p>From <a href="https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html">Cornell</a> - to find the two's complement negative notation of a number: convert to binary, invert digits, add 1. So -26? First, 26 as binary in 8 bits is 00011010. Then inverted = 11100101. Then add one = 11100110. This also works converting from two's complement. Leftmost digit, if 1, means number is negative. So let's take 11100110 (-26) again. We know it's a negative number because first bit, so we invert, then add one. 11100110 inverted = 00011001 then add one = 00011010, or 26!</p>
]]></content:encoded></item>
<item><title>CS-537 Introduction to Operating Systems - Lecture 1</title><link>https://nthomas.org/2020-06-29-cs-537-lecture-1/</link><guid>https://nthomas.org/2020-06-29-cs-537-lecture-1/</guid><pubDate>Monday, 29 June 2020 11:24:07 +0000</pubDate><description>Learning about CPU Virtualization</description><content:encoded><![CDATA[<p>Link: http://pages.cs.wisc.edu/~remzi/Classes/537/Spring2018/Discussion/videos.html</p>
<p>What is virtualization? Mapping some small number of physical resources into many virtual resources - ex 4 core CPU, multiple virtual CPUs, memory address ranges map 0 to N for every process. Virtualization creates an illusion - each running program thinks it has its own CPU, its own private memory, etc</p>
<p>Key aspects of virtualization? Must be efficient, should not be much slower than physical machine (low overhead). Must be secure, should restrict programs to finite operations and restricted access. For example, not all programs should have access to entire HDD, or network, or entire memory range. Historical footnote, Intel processors exposed arbitrary reading of all memory.</p>
<p>Strategies for CPU virtualization - <strong>time-sharing</strong> vs <strong>space-sharing</strong>. Time-sharing - when running multiple processes, let CPU switch from program A to B to A back and forth, giving a little time to each. Space sharing - allocating different blocks of memory. CPUs use time-sharing (sometimes called multiprogramming).</p>
<p>Abstraction: <strong>Process</strong>.  A process is a running program. What are its components?</p>
<p><em>Memory</em> - private internally, no other process should access the memory directly. This process thinks this is all the memory in the world available - called a "(virtual) address space". What goes inside memory? Code, stack, heap</p>
<p><em>Registers</em> - just like normal registers: program counter (PC) (RIP in 64 bit CPU). Changes all the time so CPU knows where instruction is; Stack Pointer (SP) -where in stack are we; General Purpose registers - EAX, EDI, etc - places to put data while we work. Note: Registers are faster than memory/ When we virtualize CPU, registers are primary concern</p>
<p><em>IO state</em> - open files, for ex</p>
<p>"at the low level there are mechanisms" - how things work. eg you need ways to switch between running programs. On top of mechanisms are <em>policies</em> - rules for determining utilizing resources. Higher level decisions on top of low level mechanisms. eg when do we switch between processes? Focus of class is on mechanisms.</p>
<p>Core mechanism - Limited Direct Execution</p>
<p>What is Direct Execution? Pertains to efficiency. CPUs are very fast (bns of instructions per second). If you run a virtualized CPU, most of what you can do for efficiency is run directly on hardware (hardware is FAST).</p>
<p>If we're not careful and let programs just Directly Execute on hardware, they can do what they want. That's where limited comes in. Security, protection. Create environment where OS can prevent process from doing WHATEVER it wants. Sandbox the environment so program can get efficiency without freedom.</p>
<p>Start with a protocol that's Direct Exec (not limited). Let's say we have an OS, something simple. First program that runs on machine. OS boots up and runs. Lets say we want to run a user program (eg VSCode). Program A is some lifeless program on a disk. OS starts by loading program off disk into a process. Create an address space in memory for code, heap, stack (stack is initialized with argc and argv). CPU stack pointer points to somewhere in process stack. Program counter in PC points to first instruction in code of process. Now OS is no longer running, it jumps to Program A. Eventually Program A returns to OS when it's done.</p>
<p>Problems? What if Program A (a user process) wants to do something restricted? eg access disk, allocate more memory, create new process. What if OS wants to stop Program A and start Program B? We need some way to return to OS and toggle to B. We need OS to regain control of CPU What if A does something slow? eg disk IO, network IO. We need a way to switch away from slow process to unblock behavior.</p>
<p>Restricted problem - we need hardware machinery as well as OS behavior. When a program wants file IO, OS needs to make sure user can access that file. We start with a <em>mode</em> - a per-CPU bit. The mode tells us what kind of program is on the CPU. It's either the OS (running in "kernel mode") or a user-program (running in "user mode"). In kernel mode, OS can do anything (unrestricted access to hardware). Advantage of open-source OS, can scrutinize code that runs on your hardware. User programs can only do limited number of things - the hardware will prevent the program from doing restricted behavior.</p>
<p>How do we get into one of these modes? How do we transition modes?</p>
<p>First, at boot time - boot into kernel mode. We assume the first thing that runs is OS and we trust the OS. If somehow code is injected into OS, it'll be running in kernel mode and no hardware restrictions. When we want to run user program, we need a special instruction that transitions into user mode and also jumps to some location in user program.</p>
<p>When the user program wants to do something restricted (eg disk IO) we need to transition back into kernel mode. We also need to jump into kernel. We need some instruction to handle this. But we need to be careful. If we let a user program jump into kernel, we can't let the user program say where in kernel it wants to jump to. Gives too much freedom to user program (eg, what if user program jumps past user-permission check in kernel). So OS needs to provide mechanism to control where user program can jump to. So this jump for user program -&gt; kernel needs to be a RESTRICTED jump.</p>
<p>Provide two instructions - <em>trap</em> and <em>return from trap</em>.</p>
<p>Trap - hardware-provided instruction to jump into kernel at restricted location, and then elevate privilege level from user-mode to kernel-mode. Also will save enough register state to know how to return back to previous state.</p>
<p>Return from Trap - basically undo, restores state of process, de-escalate privileges, go back to plce in program right before execution of trap.</p>
<p>A (user mode) --&gt; Trap into OS --&gt; OS does stuff (kernel mode) --&gt; Return from Trap --&gt; Back to process A (user mode)</p>
<p>Services provided by OS in kernel mode are often called system calls - asking OS for permission to do things.</p>
<p>How does OS restrict WHERE user program can jump to? That happens at boot time. OS starts up in kernel mode. OS tells hardware where to jump to when a particular trap is issued. Usually you have a trap number (eg file read = trap 80). When trap number is called, OS has a set of <em>trap handlers</em> (created by a special instruction that tells hardware where trap handlers are in OS memory).</p>
<p>We need to save and restore state of a process (in this case, register state). When A traps into OS, there's behavior in registers and OS will use those registers. So before trap, registered need to be saved into maybe a kernel stack (often hardware will save). Similarly, when we return from trap after, we need to restore register stack (often done with hardware assistance).</p>
<p>How does OS regain control and stop Process A and move to Process B? When OS runs process A, OS isn't running any more. If A runs forever (<code>while (true) {}</code>) we're stuck. Coopertive approach - hope that process A just doesn't do anything bad. Kind of a security problem. Prefer the <strong>non-cooperative approach</strong> (a preemptive approach) - based on hardware support, use a <em>timer interrupt</em> to stop processes.</p>
<p>Timer interupt - at boot mode, OS runs in kernel mode. Installs trap handlers. It also starts an <em>interrupt timer</em> - a piece of hardware that, after period of time (eg in milliseconds) will interrupt CPU between instructions and runs OS timer interrupt handler to move back to OS from Process.</p>
<p>OS --&gt; Set up --&gt; Switch to process A --&gt; Timer interrupt triggers --&gt; Hardware switches to OS --&gt; OS runs timer interrupt handler --&gt; OS decided what to do next</p>
]]></content:encoded></item>
<item><title>Critical Connections In A Network - Tarjan&apos;s Algorithm</title><link>https://nthomas.org/2020-05-16-critical-connections-leetcode/</link><guid>https://nthomas.org/2020-05-16-critical-connections-leetcode/</guid><pubDate>Saturday, 16 May 2020 11:24:07 +0000</pubDate><description>Demystifying Leetcode #1192</description><content:encoded><![CDATA[<h2>Intro</h2>
<p>After reading source code for bundlers, linters, compilers, and other projects I've been convinced that everything is a graph. Well, not everything but enough. So I began digging into practice algorithm questions on Leetcode, AlgoExpert and other websites. I'm pretty comfortable at coding up BFS and DFS when it's obviously a matter of "search for this element" but knowing when to model something as a graph when it's a little more vague is a challenge.</p>
<p>One of my personal goals is being able to code up most Leetcode hard questions in under 20 minutes. My hope is that practicing repeatedly will give me enough exposure to graph and tree traversal algorithms, array sorting and searching, etc so when I do write new projects at work or contribute to open source, patterrn recognition will kick in. Most Leetcode hard questions are fairly well written, and if not at least have a well authored solution. However, no matter what, <a href="https://leetcode.com/problems/critical-connections-in-a-network/">Leetcode 1192</a> stumped me. It's flagged as hard, rated fairly highly on frequency of interviews seen, yet there's no canonical solution and many of the user answers aren't too clear.</p>
<p>I spent some time doing my own reading outside of Leetcode. I didn't find many of the Youtube videos to be helpful. However, in a bit of luck I stumbled onto these <a href="https://cims.nyu.edu/~brettb/dsSum2016/Lecture19.pdf">lecture notes</a> from an NYU course on data structures. It happens to be the final lecture for the entire course, which is somewhat satisfying to know that at least my graph theory knowledge only has holes at the later stages of education.</p>
<h2>Description</h2>
<p>First off, I highly recommend reading the <a href="https://leetcode.com/problems/critical-connections-in-a-network/">problem</a> once or twice. Then, take a best effort at solving it!</p>
<p>Now let's dig in.</p>
<blockquote>
<p>There are n servers numbered from 0 to n-1 connected by undirected server-to-server connections forming a network where connections[i] = [a, b] represents a connection between servers a and b. Any server can reach any other server directly or indirectly through the network.</p>
</blockquote>
<blockquote>
<p>A critical connection is a connection that, if removed, will make some server unable to reach some other server.</p>
</blockquote>
<blockquote>
<p>Return all critical connections in the network in any order.</p>
</blockquote>
<p>Our input parameters are <code>n</code>, the number of nodes in our graph, and an <a href="https://www.khanacademy.org/computing/computer-science/algorithms/graph-representation/a/representing-graphs">edge list</a>. We know the graph is undirected (that is, node A points to node B and node B points to node A).</p>
<p>We are looking for critical connections, or the edges that will effectively break our graph into two smaller graphs. That is</p>
<p><em>before</em></p>
<pre><code>    1 &lt;----&gt; 2 &lt;----&gt; 3
    ^        ^
    |        |
    v        v
    4 &lt;----&gt; 5

</code></pre>
<p>If we broke the connection between 2 and 3, and  then our graph breaks into two parts and nodes 1, 2, 4 and 5 are unreachable from 3.</p>
<p><em>after</em></p>
<pre><code>    1 &lt;----&gt; 2      3
    ^        ^
    |        |
    v        v
    4 &lt;----&gt; 5
</code></pre>
<p>The key insight here is that we're looking for the bridge between <a href="https://en.wikipedia.org/wiki/Strongly_connected_component">strongly connected components</a>. Luckily this is a solved problem, and <a href="https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm">Tarjan's algorithm</a> will come to the rescue.</p>
<p>Let's come up with a quick definition of a strongly connected component that works for us. A strongly connected component can be thought of as a graph in which no one edge is the weak link in the graph. Or, all edges have at least one backup to keep the graph tied together if it disappears.</p>
<p>If we take a set of nodes and number them in the order we visit them (like a timestamp, or just a monotonically increasing id), we can end up building an array of IDs. Let's call that the <code>dfsNumber</code> array, is for any node <code>i</code> the order we visited it in a depth first search.</p>
<p>Additionally, we want to know how far "backwards" into a graph a node can reach. If we know nothing about a node, the "oldest" seen node that node N can see is itself. Once we start to look at neighbor nodes, we can assert that the oldest node a neighbor can reach is the oldest node any of its neighbors can reach (ignoring the direct parent we came from). We can hold <code>oldestReachable</code> in an array, where <code>oldestReachable[i]</code> indicates the oldest timestamp reachable from node <code>i</code></p>
<p>Let's visualize this. I'll use letters for node names so <code>dfsNumbers</code> are clear.</p>
<pre><code>    A  &lt;---&gt;  B
    ^         ^
    |         |
    v         v
    D  &lt;---&gt;  C
</code></pre>
<p>If we start at node named A, we label it with a <code>dfsNumber</code> of 1, and we also assert that right now, the <code>oldestReachable</code> for A is also 1. Now we look at its neighbors and travel ONLY if they have no <code>dfsNumber</code>. Once we label it's neighbors, we look to see how far back thhe neighbor can reach.</p>
<p>So we'll pick B, give it a <code>dfsNumber</code> and <code>oldestReachable</code> of two. Then we dfs into C, labeled with 3, then D as 4. Remember this is DFS, so we have a call stack. So</p>
<ol>
<li>Visit and label A</li>
<li>visit and label B</li>
<li>
<pre><code>visit and label D
</code></pre>
</li>
<li>
<pre><code> visit and label D. Then we visit A.
</code></pre>
</li>
<li>
<pre><code>   A has a label so we do nothing. What's the oldest D can reach? Currently, it's 4. But we look. Can a neighbor reach older? Yes! A can reach older (itself)! So we declare that, `oldestReachable for D` is equal to `oldestReachable for A`, or `oldestReachable[D] = 1`
</code></pre>
</li>
</ol>
<p>now we unwind the call stack.</p>
<p>What's the oldest C can reach? Well, since we said D can reach to A and C can reach D, C can reach A or <code>oldestReachable[C] = 1</code></p>
<p>As we unwind the stack, we keep assigning all oldestReachable values to 1.</p>
<p>Now when would this be false? What's an example of a graph where some components do not have neighbors that can reach back into the graph? Another visualization and example.</p>
<pre><code>    A  &lt;---&gt;  B
    ^      /  ^
    |    /    |
    v  /      v
    D         C
</code></pre>
<p>A points to B and D. B points to A, D and C. C points to B. D points to B and D. Let's again begin our traversal, starting at A</p>
<ol>
<li>visit and label A. <code>dfsNumber</code> of 1, <code>oldestReachable</code> is itself of 1. Pick a neighbor - randomly select D</li>
<li>Visit and label D. <code>dfsNumber</code> of 2, <code>oldestReachable</code> is itself of 2. Pick a neighbor. We just came from A, so we have to go to B.</li>
<li>Visit and label B. <code>dfsNumber</code> of 3, <code>oldestReachable</code> is itself of 3. Pick a neighbor. We just came from D, so we have to go to C.</li>
<li>Visit and label C. <code>dfsNumber</code> of 4, <code>oldestReachable</code> is itself of 4. Pick a neighbor. We just came from B, so we have nowhere to go. Unwind the call stack and go back to B</li>
<li>B's <code>oldestReachable</code> is the lowest of its own <code>oldestReachable</code> and the <code>oldestReachable</code> of the neighbor we just procesesed (D). D = 4, B = 3. So D cannot reach further back into the graph on its own. Therefore we know that, if we remove the edge between B and D, D becomes part of a new graph.</li>
</ol>
<pre><code>    A  &lt;---&gt;  B
    ^      /
    |    /
    v  /
    D         C  (ALONE)
</code></pre>
<h2>Pseudocode</h2>
<p>Let's try and write this in pseudocode</p>
<pre><code>  numberOfNodes = n
  dfsNumber = new Array, numberOfNodes in length
  oldestReachable = new Array, numberOfNodes in length
  timestamp = 0

  dfs(node, parent, graph)
    timestamp++
    dfsNumber[node] = timestamp
    oldestReachable[node] = timestamp

    for neighbor of graph[node]
      if neighbor is parent, continue
      if !dfsNumber(neighbor) dfs(neighbor, node, graph) // unlabeled neighbor

      // check if neighbor can reach deeper. If yes, reset our oldestReachable

      oldestReachable[node] = min of oldestReachable[node], oldestReachable[neighbor]

      // check if this neighbor's oldest reachable isn't as old as where we are now

      if oldestReachable[neighbor] &gt; dfsNumber[node]
        collect [node, neighbor]


  dfs(idx, null, graph)
</code></pre>
<h2>Solution</h2>
<p>Now, Leetcode gives us an edge list but Tarjan's algorithm requires an adjacency list. So the first thing we do is process our data. Otherwise we follow our steps.</p>
<pre><code class="language-javascript">var criticalConnections = function(n, connections) {
  const adjacency = {}
  for (let i = 0; i &lt; n; i++) {
    adjacency[i] = []
  }

  for (let [node1, node2] of connections) {
    adjacency[node1].push(node2)
    adjacency[node2].push(node1)
  }

  const dfsNumber = new Array(n).fill(0)
  // this is what we called oldestReachable
  // the lowest number reachable by dfs from a node
  const dfsLow = new Array(n).fill(0)
  const criticalEdges = []
  let timestamp = 0

  function tarjan(node, parent, adjacency, edges) {
    timestamp++
    dfsNumber[node] = timestamp
    dfsLow[node] = timestamp

    for (let neighbor of adjacency[node]) {
      if (neighbor === parent) continue
      if (!dfsNumber[neighbor]) tarjan(neighbor, node, adjacency, edges)

      // resetting oldestReachable if neighbor can cycle back
      dfsLow[node] = Math.min(dfsLow[node], dfsLow[neighbor])

      // if neighbor cannot reach back to me or oldest, we have a critical edge
      if (dfsLow[neighbor] &gt; dfsNumber[node]) edges.push([node, neighbor])
    }
  }

  tarjan(0, null, adjacency, criticalEdges)

  return criticalEdges
}
</code></pre>
<p>And lo and behold, our solution works.</p>
<p>Let me know what you thought. If these kinds of articles are useful, and I should write more of them I'll happily do so. I'm most easily reached on Twitter (@nikhilthomas90).</p>
]]></content:encoded></item>
<item><title>What Functional Programming Taught Me About Object Oriented Programming</title><link>https://nthomas.org/2019-12-23-what-fp-taught-about-oop/</link><guid>https://nthomas.org/2019-12-23-what-fp-taught-about-oop/</guid><pubDate>Tuesday, 24 December 2019 06:08:07 +0000</pubDate><description>It&apos;s about clarity, not dogma</description><content:encoded><![CDATA[<p>#Introduction</p>
<h3>My background</h3>
<p>My introduction to programming professionally came in the era of everybody-choosing-Rails. After a brief stint at a coding bootcamp, I was able to write some code but my understanding of "object oriented" was simply <code>rails g model model_name</code>. I lacked an appreciation for how much I could delegate to various object classes and assumed that I had to be constantly modifying state with imperative workflows. Code looked very much like</p>
<pre><code class="language-ruby">user = User.find(id)
user.school = School.find(school_id)
user.is_enrolled? = true
user.classes.each { |class| class.cancel }
user.classes = []
user.save
</code></pre>
<p>As I began to explore more interactive web applications, that pseudo-OO style of highly imperative code with primitives became harder to maintain.</p>
<pre><code class="language-javascript">for(let event of eventList) {
  for (let org of event.organizations) {
    if (currentUser.organizations.includes(org.id) {
      // some DOM operation
    } else if (some other clause){
      if (this other thing is true) {
        // some other DOM operation
      }
    }
  }
}
</code></pre>
<p>A sufficiently large code base with lots of people working, with different features developed at the same time can get unwieldy. A coworker at the time was an enthusiast of Clojure(script) and we began using RamdaJS as a functional utility library and ImmutableJS for our data structures. Immediately I felt more in control and thought to myself "Wow, object orientation is terrible! This makes way more sense to just operate on data!"</p>
<p>Of course, as time went on I began to get too clever and focused on functional-at-all-costs.</p>
<pre><code class="language-javascript">compose(
  length,
  curry(someNativeFnForLists),
  map(someDataTransformation),
  filter(somePredicate),
)
</code></pre>
<p>This might be straightforward in a blurb (kinda...) but again, imagine doing a code review in Github after hours of feature development under tight deadlines. Does this convey intent to the reader? I was so concerned with using <code>reduce</code> and <code>map</code> because I thought Lisp-inspired FP was what mattered that I threw away the real goal, immutability and clarity of code.</p>
<p>I've been bouncing back and forth between the styles, slowly narrowing down on some shared set of values that allow me to write useful, easily-changed, quick-to-understand code. It turns out that there exists a family of languages that provided me the tools and framework needed to think about problem-solving.</p>
<h3>Inspirations</h3>
<p>The ML-family of languages ("meta-language", not "machine learning") is a functional school of thought with expressive types. Languages like OCaml, Haskell and Elm (and to some extent - Rust and Swift) are built with the expectations of fast compilers with many customized types, moduluar code with separation of boundaries, and immutability of data.</p>
<p>The first place this made sense was watching Yaron Minksy's talk <a href="https://blog.janestreet.com/effective-ml-revisited/">Effective ML</a>. Watching him take a blob of data representing a connection, seeing some "option" data in his types and quickly tearing them out into concrete types with known data made a ton of sense.</p>
<p>The second place I began to see this, but from an more object-oriented perspective, was Gary Bernhardt's <a href="https://www.destroyallsoftware.com/talks/boundaries">Boundaries</a> talk. In it, he describes pushing his mutation to the boundaries of his code and having as much of a functional core with an imperative shell to communicate with outside systems.</p>
<p>All of this, combined with coding in various languages and making many mistakes led me to start thinking about how to write code in a way that both OO and FP styles are trying to solve. But to get to that, I need to know what I care about.</p>
<h3>Coding Values</h3>
<p>TDD or test after? Static types or dynamic? Compiled or interpreted languages? Mutable or immutable? These are binary questions people ask (and are asked in interviews) where I think the answer fails to get at the crux of the matter. TDD isn't about tests. It's about confidence that your code solves the problem you want. Static types don't exist because some programmers hate the flexibility of duck-typing. Rather, it's about a formalization to the compiler of your assumptions and states of the world.</p>
<p>Since I write mostly enterprise software, I do care about performance and metrics than can be quantified in time. But my biggest wins seem to come from more qualitative metrics. Was a feature easy to implement? Are two functions very tightly coupled - that is, does changing function A break the tests of function B? Am I confident that adding a new function call doesn't break a call site elsewhere? I know I'm working in a garbage-collected environment, and there's a pretty good chance that the biggest performance hit to the user comes from the amount of work the <a href="https://en.wikipedia.org/wiki/No_Silver_Bullet">essential complexity</a> that is enforced by my problem domain.</p>
<p>My values are <em>confidence in my code</em>, <em>clarity of intent to another programmer</em>, <em>comfort in changing the code for future features</em> and much more. Knowing this, I've been able to take what I need from FP to write better OO code when needed.</p>
<h1>What I've taken from FP</h1>
<h3>Types are cheap</h3>
<p>This is heavily inspired from writing OCaml code. In OCaml, it would be very straightforward to write some code like</p>
<pre><code class="language-ocaml">module Math = struct
  type shape =
	| Circle of float
        | Rect of float * float
        | Triangle of float * float

  let get_area x : shape = match x with
	| Circle radius -&gt; radius ** 2.0 *. Math.pi
	| Rect (w, h) -&gt; w *. h
        | Triangle (base, h) -&gt; 0.5 *. base *. h
end

</code></pre>
<p>and let the compiler help make sure that every time I have a <code>shape</code> type, I'm handling the correct variants. In the old Lisp-inspired JS days, I might have written entire functions and workflows so that my <code>triangle</code> state never ended up entangled with my <code>rectangle</code>. But the ability to create types inside a module to logically separate my cases.</p>
<p>This cheapness of classes should be embraced. I'm probably not persisting Triangles in my database, but it's a perfectly useful way to hold onto my data inside the <code>Math</code> module so I should just have these classes where I need them. I wish Ruby had private classes scoped to modules, but the essential idea remains the same.</p>
<pre><code>module Math

  def calculate_area(shape)
    shape.get_area
  end

  class Triangle
    def get_area
      base * height * 0.5
    end
  end

  class Rectangle
    def get_area
      width * height
    end
  end

  class Circle
    def get_area
      radius ** 2 * 3.14
    end
  end
end
</code></pre>
<h3>More Types Yields More Refactorings</h3>
<p>Specifically, the <a href="https://refactoring.com/catalog/replaceConditionalWithPolymorphism.html">Replace Conditional With Polymorphism</a> and <a href="https://refactoring.com/catalog/replacePrimitiveWithObject.html">Replace Primitive With Object / Primitive Obsession</a> refactorings from Martin Fowler are very much an object-oriented translation of the ML type tagging. Perhaps you're writing an OCaml function that takes a name and an email address (both strings) to validate a user's information.</p>
<pre><code class="language-ocaml">val validate: string -&gt; string -&gt; bool
let validate email name = (* somethingHappens *)
</code></pre>
<p>You could very easily accidentally swap the others around in a call site by accident and the compiler wouldn't be able to help you with that - after all, strings are strings. But what if you had small types to tag the data?</p>
<pre><code class="language-ocaml">type email = Email of string
type name = Name of string

val validate : email -&gt; name -&gt; bool
let validate (Email emailAddress) (Name userName) = (* something happens *)
</code></pre>
<p>Similarly, you can use this idea to just make small structs with tests in object-oriented code. Yes, you lack the compiler guarantees in a dynamic language but you can convey intent better to your co-authors of code who are then less likely to make that mistake of swapping parameters.</p>
<h3>Create new instances as much as you want</h3>
<p>Just because a class instance can modify its internal state doesn't mean it has to. It's just as easy to calculate the new state and create a new instance of your object if you create classes as structs over data.</p>
<pre><code class="language-ruby">User = Struct.new(:name, :email) do
  def update_email(new_email)
    User.new(name, new_email)
  end
end

user = User.new("test", "test@email.com")
puts user
puts user.update_email("updated@email.com")
</code></pre>
<p>Encapsulate your data with a set of methods that describes behavior, create new instances to your heart's content, and push mutation as far away from core business logic so you know exactly where it happens. You might have a <code>UserUpdater</code> that takes a user and persists in the database, or a <code>PageRenderer</code> that handles drawing pixels (effectively React's concept of declarative programming), but your core objects can just take messages and pass around new objects.</p>
<h1>Takeaways</h1>
<p>I think my biggest takeaway from thinking about all this and everything I learned is that object oriented code and functional programming styled-code aren't these huge dichotomies. You don't need to pick a side. There are no winners or losers here. Everyone is trying to express intent and wrangle complexity over software as best as they can. Find the amount of expressiveness that lets people easily update their code. Classes can serve as ML-style types for the reader - humans aren't compilers, but if they know all shapes must have an area, that's a lot better than passing around primitives with no context. Immutability can help make sure that you know exactly what your data is representing at a given line. Ultimately, don't optimize for "the best OO style" or "the best FP style" because you read something on Twitter or Hacker News. Optimize for the person who's going to update your code after you're gone.</p>
]]></content:encoded></item>
<item><title>You Can Invent Javascript Scopes</title><link>https://nthomas.org/2019-11-11-you-can-invent-js-scope/</link><guid>https://nthomas.org/2019-11-11-you-can-invent-js-scope/</guid><pubDate>Monday, 11 November 2019 12:15:07 +0000</pubDate><description>A greatly simplified way to think about Javascript environments</description><content:encoded><![CDATA[<hr />
<p>This small post is a heavily simplified way to think about Javascript scopes. Since we're not writing a compiler or interpreter, we don't have a particularly nice AST to work with. However, for learning purpose I think this helps. For further education, read <a href="https://craftinginterpreters.com/">Crafting Interpreters</a>, <a href="compilerbook.com">Writing a Compiler in Go</a>, or the excellent posts by <a href="http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/">Dmitry Soshnikov</a></p>
<p>Repl.it found <a href="https://repl.it/@nt591/IdealSecondFlashdrives">here</a>
Github found <a href="https://github.com/nt591/inventing-js-scopes/commit/0241ac7ea6ece571428944fa64b6e3d1a08f4ab0">here</a></p>
<hr />
<p>Javascript closures were tremendously tricky for me to grasp. I had a coworker who tried to explain them as "a function that CLOSES OVER a value" which wasn't particularly illuminating, but I eventually began to get a feel for how scope worked. However, it wasn't until I wrote a compiler in Go that I began to better understand what was happening under the hood.</p>
<p>The common question of "write a function that only lets a function run once" is a great example.</p>
<pre><code class="language-javascript">const once = fn =&gt; {
  let canRun = true

  return (...args) =&gt; {
    if (canRun) {
      canRun = false
      return fn.apply(null, ...args)
    }
  }
}

let fn = once(() =&gt; console.log('hello'));
fn()
fn()
</code></pre>
<p>This just works. Why? I'll let you read Dmitry's post on scope chain for a very detailed review of Javascript scope, but let's perhaps make it simple for ourselves.</p>
<p>A program has a global environment. An environment is simple a map of keys to values, where keys are variables and values are their assignments. An environment can also have an outer environment, that is, a function has an internal environment that allows it to have a locally scoped variable assignment. Environments are first-class objects that can have an infinitely long outer-chain.</p>
<p>In code, that might look something like</p>
<pre><code class="language-javascript">class Environment {
  constructor(environment, store) {
    this.outer = environment;
    this.store = {};
  }

  get(name) {`
    if (this.store[name]) return this.store[name];
    if (this.outer) return this.outer.get(name);
    return null;
  }

  set(name, value) {
    return this.store[name] = value;
  }
}
</code></pre>
<p>A store is the environment's inner mapping of keys to values. The <code>outer</code> value refers to it's parent, for when we enter a function scope. Let's create a couple of helpers along the way.</p>
<pre><code class="language-javascript">const GLOBAL_ENV = new Environment();

function assign(env, name, value) {
  env.set(name, value);
}

function read(env, name) {
  env.get(name);
}

</code></pre>
<p>This will just allow us to test out our code in a nice, declarative fashion. Note that <code>Environment.prototype.get</code> looks up the outer chain by calling itself on the next environment up. As long as there are parents, we can keep looking until we get nothing. We could model this as a stack (pushing and popping as we enter and leave scope) but this is a little easier.</p>
<p>Let's model our functions as a class. We want to be able to capture the function we plan on executing, the environment at the time of it's creation, and for ease let's also capture an array of strings representing the parameters the function needs. We'll later use that list to look up our parameter values at runtime. It'll look something like</p>
<pre><code class="language-javascript">
class FunctionObject {
  constructor(env, fn, parameters = []) {
    this.fn = fn;
    this.parameters = parameters;
    // capture state of environment at creation time
    this.env = env;
  }

  eval(env, paramValues) {
    // assign an outer environment
    // make sure to keep captured store values
    let newEnv = new Environment(env, this.env.store);
    for (let i = 0; i &lt; paramValues.length; i++) {
      // for every parameter we pass in to eval, assign the value to environment store
      let param = this.parameters[i];
      let val = paramValues[i];
      newEnv.set(param, val);
    }

    // apply args from params to fn
    // all our functions need an environment to read from, so we push that to the front of the list
    // you can imagine some compiler step dynamically reading from environment
    let allParamValues = this.parameters.map(param =&gt; newEnv.get(param));
    let argsToCall = [newEnv].concat(allParamValues);
    return this.fn.apply(newEnv, argsToCall);
  }
}
</code></pre>
<p>Our <code>FunctionObject</code> can be created and later evaluated with parameter values. When we <code>eval</code> a function, we capture the environment at time of execution, then, make sure the closest lookup is the function's environment captured at creation time. You can then call something like</p>
<pre><code class="language-javascript">const env = GLOBAL_ENV
assign(env, 'x', 5);
const logX = (env) =&gt; console.log(env.get('x'));
const fnObj = new FunctionObject(env, logX, ['x']);
fnObj.eval(env, []); // logs 5

assign(env, 'x', 10)
fnObj.eval(env, []); // logs 10
</code></pre>
<p>You'll see that <code>logX</code> takes an environment and reads from it. That's actually the role of the interpreter or compiler, to replace all variable reads with the lookup. Since we're not writing an interpreter, you can imagine that function is actually <code>console.log(x)</code> that's later compiled into what we're writing.</p>
<p>What's really shown here is that we're able to NOT send an explicit value of <code>x</code> in the parameter list to a function and it can be read from an environment.</p>
<p>Our program then is a series of scope entries, where we can effectively open and close scopes by creating functions to capture and throw away environments.</p>
<pre><code class="language-javascript">const GLOBAL_ENV = new Environment();

const functionScope = (env) =&gt; {
  // create a local env, where it's outer scope is inherited
  let localEnv = new Environment(env)
  assign(localEnv, 'x', 1);
  assign(localEnv, 'y', 2);
  assign(localEnv, 'z', 100);
  let sumFn = (env, x, y) =&gt; console.log('OUTER ', env.get('x') + env.get('y') + env.get('z'));
  let sum = new FunctionObject(localEnv, sumFn, ['x', 'y', 'z']);

  sum.eval(localEnv, [5, 6]) // expect 5 + 6 + 100 = 111
  assign(localEnv, 'z', 200);
  sum.eval(localEnv, [5, 6]) // expect 5 + 6 + 200 = 211

  innerScope(localEnv);
  // note that in innerScope, we overrode the value of 'z' but that's thrown away when we get back here
  sum.eval(localEnv, [100, 100]) // expect 100 + 100 + 200 = 400
}

const innerScope = env =&gt; {
  let localEnv = new Environment(env);
  assign(localEnv, 'z', 300); //
  let sumFn = (env, x, y) =&gt; console.log('INNER ', env.get('x') + env.get('y') + env.get('z'));
  let sum = new FunctionObject(localEnv, sumFn, ['x', 'y', 'z']);
  sum.eval(localEnv, [5, 6]) // expect 5 + 6 + 300 = 311
}

functionScope(GLOBAL_ENV)
</code></pre>
<p>This is a quick and dirty way to think about function scope! When we enter a new scope, we capture all the outside environment values and use those, and when we leave that scope we no longer consider that our environment.</p>
<p>This was a fun learning exercise, and certainly only touches a VERY high level of how the Javascript engine translates a variable lookup, but hopefully the model of "scopes are just environments that are thrown away when a function ends, and environments are just maps of variables to values" helps further understand why</p>
<pre><code class="language-javascript">const once = fn =&gt; {
  let canRun = true

  return (...args) =&gt; {
    if (canRun) {
      canRun = false
      return fn.apply(null, ...args)
    }
  }
}

let fn = once(() =&gt; console.log('hello'));
fn()
fn()

</code></pre>
<p>actually works. Because the <code>once</code> function captures a scope that the inner function has access to and can modify.</p>
]]></content:encoded></item>
<item><title>Writing an Interpreter in OCaml - Lexical Analysis</title><link>https://nthomas.org/2019-08-19-writing-an-interpreter-in-go-ocaml/</link><guid>https://nthomas.org/2019-08-19-writing-an-interpreter-in-go-ocaml/</guid><pubDate>Monday, 19 August 2019 12:15:07 +0000</pubDate><description>Porting Thorsten Ball&apos;s Go interpreter</description><content:encoded><![CDATA[<hr />
<p>Code is on my <a href="https://github.com/nt591/monkey-ocaml/tree/46882b03ca7d911b5a12b0e47738778fde5ee7fc">Github</a></p>
<hr />
<h2>Background</h2>
<p>I've decided to take another stab at this interpreter. I'll be using Thorsten Ball's book <a href="https://interpreterbook.com/">Writing an Interpreter in Go</a> as my primary source, with extra reading material from Bob Nystrom's <a href="https://craftinginterpreters.com">Crafting Interpreters</a>. I find that Thorsten's book gets me productive faster, while Bob's material is deeper and helps clarify and illuminate what I don't understand.</p>
<p>Thorsten's book defines a language called Monkey, that's similar in syntax to Javascript. Curly braces, integer math, not whitespace sensitive, recursion, the whole nine yards.</p>
<p>For this project I'll be using the OCaml language. OCaml sits at an intersection of academia and large production-scale software. It's used at Jane Street, Docker, Bloomberg, Facebook and other companies. I plan on redoing this project in Haskell eventually - I missed some syntax and conventions from Haskell in writing this chapter. I find inline function composition and application is more readable with Haskell's dot notation and <code>$</code> application than pipelines.</p>
<pre><code class="language-haskell">-- Haskell
next_char . read_char $ lexer
</code></pre>
<pre><code class="language-ocaml">(* OCaml *)
lexer |&gt; read_char |&gt; next_char
</code></pre>
<h2>Requirements</h2>
<ul>
<li>Dune 1.11.x</li>
<li>opam 2.0.3</li>
<li>OCaml 4.07.1</li>
<li>Alcotest 0.8.5 (testing framework)</li>
<li>Fmt 0.8.8 (pretty printer for testing)</li>
</ul>
<p>Use <code>opam</code> to set up OCaml and install those dependencies. I chose a new version of Dune, and I went with Alcotest as it seemed relatively popular on the OCaml discord. It's also quite readable and pleasant to configure.</p>
<h2>Project structure</h2>
<p>Take a look at this <a href="https://github.com/nt591/monkey-ocaml/commit/7dba667d0c20c69aaddaa4640479f26f5673ee20">commit</a> for some files you can copy</p>
<pre><code>ROOT
  - dune-project
  - monkey.intall
  - monkey.opam
  - src/
    - dune
  - test/
    - dune
</code></pre>
<p>The dune files will let you link and build your code and run the tests. We'll update these as we move along, but for now this will get you started.</p>
<h2>Defining our first tokens</h2>
<p>The first step for Thorsten's interpreter is defining a subset of our tokens. In lexical analysis, a token is a simple data structure that represents a small piece of syntax in our programming language. A token could be a SEMICOLON, a PLUS_SIGN, an IDENTIFIER("x"), an INTEGER(5), or some other representation.</p>
<p>We're going to define our tokens in <code>src/token.ml</code>. Create that file and let's add some code. We're going to open a module and add basic tokens.</p>
<pre><code class="language-ocaml">module Token = struct
  type token_type =
    | ILLEGAL
    | EOF
    (* Identifiers and literals *)
    | IDENT of string
    | INT of int
    (* Operators *)
    | ASSIGN
    | PLUS

    (* -- Delimiters *)
    | COMMA
    | SEMICOLON
    | LPAREN
    | RPAREN
    | LBRACE
    | RBRACE
    (* -- Keywords *)
    | FUNCTION
    | LET
end
</code></pre>
<p>We're also going to add a <code>token_to_string</code> function so we can print out some tokens for our tests, as well as keep our compiler happy when it inevitably barks at us for unused code.</p>
<pre><code class="language-ocaml">let token_to_string = function
  | ILLEGAL -&gt; "ILLEGAL"
  | EOF -&gt; "EOF"
  | IDENT a -&gt; "IDENT " ^ a
  | INT a -&gt; "INT " ^ string_of_int a
  | ASSIGN -&gt; "ASSIGN"
  | PLUS -&gt; "PLUS"
  | COMMA -&gt; "COMMA"
  | SEMICOLON -&gt; "SEMICOLON"
  | LPAREN -&gt; "LPAREN"
  | RPAREN -&gt; "RPAREN"
  | LBRACE -&gt; "LBRACE"
  | RBRACE -&gt; "RBRACE"
  | FUNCTION -&gt; "FUNCTION"
  | LET -&gt; "LET"

</code></pre>
<p>Unlike Thorsten's Go implementation, we don't need to keep a list of constants. Rather, we can use OCaml's algebraic data types to represent tokens. In fact, OCaml is commonly used in compilers and similar dev tools for exactly this reason.</p>
<p>Thorsten then moves on to creating some tests, in order to build his <code>NextChar</code> function. We're going to similar add tests.</p>
<p>We'll need to set up a small module export. Create <code>src/monkey.ml</code> and add the following code.</p>
<pre><code class="language-ocaml">module Token = Token
module Lexer = Lexer

</code></pre>
<p>Create a file <code>test/test.ml</code> and let's add a bit of boilerplate.</p>
<pre><code class="language-ocaml">
open Monkey
include Lexer
include Token

let token_testable = Alcotest.testable Token.pretty_print (=)

let test_lexer_delimiters () =
  Alcotest.(check (list token_testable))
    "same token types" [
        Token.ASSIGN
      ; Token.PLUS
      ; Token.LPAREN
      ; Token.RPAREN
      ; Token.LBRACE
      ; Token.RBRACE
      ; Token.COMMA
      ; Token.SEMICOLON
      ; Token.EOF
      ]
      (Lexer.generate_tokens "=+(){},;")

let () =
  Alcotest.run "Lexer"
    [
      ( "list-delimiters",
        [ Alcotest.test_case "first case" `Slow test_lexer_delimiters ] );
    ]
</code></pre>
<p>See that <code>Token.pretty_print</code> function? We'll need to define a way to return a formatting string representing our Token. Let's go back into <code>src/token.ml</code></p>
<pre><code class="language-ocaml">let pretty_print ppf tok = Fmt.pf ppf "Token %s" (token_to_string tok)
</code></pre>
<p>We can look at Alcotest's <a href="https://github.com/mirage/alcotest/blob/24a77e6f8b025d8fd79a6bfcf5f77a26ba8b8a19/src/alcotest.ml#L773-L781">source code</a> to dig into what this code does. Effectively, for any inferred type <code>a</code> it takes a function that formats a type <code>a</code> and an equality checking function that takes two <code>a</code> and returns a boolean. You can look at the implementations of <code>int32</code> or <code>string</code> to get a hint of how that works. Our <code>token_testable</code> is a module that works on <code>Token.token_type</code>, our defined set of token tags. If we were to run <code>dune runtest</code> we'd get an error because we haven't yet implemented our lexer. So let's do that.</p>
<h2>Setting up the Lexer</h2>
<p>Let's create <code>src/lexer.ml</code>.</p>
<pre><code class="language-ocaml">(* lexer *)

module Lexer = struct
  include Token

  type lexer = {
    input : string;
    position : int;
    read_position : int;
    ch : char;
  }

  let null_byte = '\x00'

  let new_lexer input_string =
    {
      input = input_string;
      position = 0;
      read_position = 0;
      ch = null_byte;
    }
end

</code></pre>
<p>I chose to use a null_byte instead of an OCaml <a href="http://ocaml-lib.sourceforge.net/doc/Option.html">option type</a> because in my opinion, an Option represents a potential gap whereas we know that we won't have gaps in our source code. We'll simply be reading the string until we run out of string. The end of our source code isn't undefined, but a known value.</p>
<p>We need to define our <code>read_char</code> function. In a lexer, reading a character is simply incrementing our pointer in our lexer and looking at the next character in the input string.</p>
<pre><code class="language-ocaml">let read_char lexer =
  let read_to_end = lexer.read_position &gt;= String.length(lexer.input) in
  let new_ch = if read_to_end then null_byte else String.get lexer.input lexer.read_position
  in {lexer with position = lexer.read_position; read_position = lexer.read_position + 1; ch = new_ch}
</code></pre>
<p>OCaml is immutable by default, so we can't just adjust our lexer. Or rather, we can, but why do that when we can avoid that mutable state. We look to see if we're at the end of the string. If we are, the character is a null, else we get the next character in the string and update our lexer. In order to initialize our lexer, we can use our <code>read_char</code>. We'll update <code>new_lexer</code> to look like</p>
<pre><code class="language-ocaml">
let new_lexer input_string =
  let lexer = {
    input = input_string;
    position = 0;
    read_position = 0;
    ch = null_byte;
  } in
  read_char lexer
</code></pre>
<p>Awesome! Our test code is still calling <code>Lexer.generate_tokens</code> so we'll need to continue working on this.</p>
<pre><code class="language-ocaml">let next_char lexer = match lexer.ch with
  | '=' -&gt; (read_char lexer, Token.ASSIGN)
  | ';' -&gt; (read_char lexer, Token.SEMICOLON)
  | '(' -&gt; (read_char lexer, Token.LPAREN)
  | ')' -&gt; (read_char lexer, Token.RPAREN)
  | ',' -&gt; (read_char lexer, Token.COMMA)
  | '+' -&gt; (read_char lexer, Token.PLUS)
  | '{' -&gt; (read_char lexer, Token.LBRACE)
  | '}' -&gt; (read_char lexer, Token.RBRACE)
  | '\x00' -&gt; (lexer, Token.EOF)
  | _ -&gt; failwith "unmatched character"

let generate_tokens input_string =
  let lexer = new_lexer input_string in
  let rec gen lxr tokens =
    match next_char lxr with
    | (_, Token.EOF) -&gt; List.rev_append tokens [Token.EOF]
    | (l, tok) -&gt; gen l (tok :: tokens)
  in gen lexer []

</code></pre>
<p>This code matches on our known inputs and returns a tuple of an updated lexer and the last read token. This tuple allows us to collect tokens and continue to call <code>next_char</code> until it ends in our <code>generate_tokens</code> function.</p>
<p><code>generate_tokens</code> is defining a recursive function that calls <code>next_char</code> on a lexer. For any token, it'll add the token to a list of seen tokens and call <code>gen</code> again on the new lexer. Once it finds an <code>EOF</code> token, it will <code>rev_append</code> the collected tokens to the <code>EOF</code>.</p>
<p>Side note: <code>rev_append</code> reverses the first list and concatenates it to the second. Because we're using the OCaml <code>cons</code> operator in the non-EOF case, we're actually constructing a list in reverse order so we need to flip it at the end. The reason for this is that <code>cons</code> is constant time where <code>append</code> is linear time proportional to the first list (the operation has to traverse the entire list before adding the elements of the second list). So it's easier to reverse at the end (linear time once) instead of appending (linear time for every token).</p>
<p>If we run <code>dune runtest</code> we get passing tests!</p>
<h2>Up next</h2>
<p>The next chapter will take care of adding tokens for real source code.</p>
]]></content:encoded></item>
<item><title>Tictactoe in Haskell</title><link>https://nthomas.org/2019-08-12-tictactoe-in-haskell/</link><guid>https://nthomas.org/2019-08-12-tictactoe-in-haskell/</guid><pubDate>Monday, 12 August 2019 12:15:07 +0000</pubDate><description>A little CLI game for fun</description><content:encoded><![CDATA[<hr />
<p>Code can be found <a href="https://github.com/nt591/haskell-playground/blob/master/random/tictactoe.hs">here</a></p>
<hr />
<p>While I struggle with some misunderstandings of the OCaml module system, I decided to put my interpreter on temporary hold to try some Haskell. It's a language that's been on my radar and been a goal to learn. A combination of Haskell's terse notation ( it turns out <code>sum . take 10 $ (*) &lt;$&gt; [2,4,6] &lt;*&gt; [1,2,3,4,5]</code> is a completely legitimate, if daunting line of code ) combined with the new terminology (cue monad tutorial) caused some delay. But after deciding to commit, and OCaml's slightly more friendly entry to FP, I felt capable of getting my hands dirty. I felt particularly inspired after seeing code written for Github's <a href="https://github.com/github/semantic">Semantic</a> project. At some point, I'd like to work on some meaty Haskell productive code and there's no place to start like starting.</p>
<p>After reading a lot of <a href="http://learnyouahaskell.com">Learn You A Haskell For Great Good</a> and watching some Youtube videos, I felt reasonably comfortable in being able to write a small Tictactoe CLI game. I went with <a href="https://docs.haskellstack.org/en/stable/README/">Stack</a> as my build tool of choice.</p>
<p>Like OCaml, Haskell's type system encourages domain modeling early. I decided to make my board a list of 9 elements, where each element could be either an X, an O, or empty. Since Haskell doesn't really have a null type, I decided to create both a <code>Move</code> as well as a <code>Cell</code></p>
<pre><code class="language-haskell">import System.Environment
import Data.List

data Move = X | O
data Cell = Occupied Move | Empty
</code></pre>
<p>We're taking <code>System.Environment</code> because we'll need some IO behavior, and <code>Data.List</code> for some future functions.</p>
<p>I could have made <code>Cell</code> a Maybe type, but chose a more descriptive way to express a cell. This way, I can keep track of the move to play as well as see what was in the cell. I also needed a way to render this board out. Since I'm using custom types, I needed to create instances of the <a href="https://www.haskell.org/tutorial/stdclasses.html">Show typeclass</a>.</p>
<pre><code class="language-haskell">instance Show Move where
  show X = "X"
  show O = "O"

instance Show Cell where
  show (Occupied X)     = "X"
  show (Occupied O)    = "O"
  show Empty            = " "
</code></pre>
<p>I'm semi-positive I could have used <code>deriving (Show)</code> on my <code>Move</code> type but that'll be for a later refactor. Today's primary goal was just writing code. My next plan was to get some board-rendering code up. I needed a function that simply took my board, my <code>[Cell]</code> and output something pretty.</p>
<pre><code class="language-haskell">renderRow :: [Cell] -&gt; String
renderRow row = intercalate " | " $ fmap show row

dividingLine :: String
dividingLine = "----------"

renderBoard :: [Cell] -&gt; IO ()
renderBoard board = do
  putStrLn $ renderRow firstRow
  putStrLn dividingLine
  putStrLn $ renderRow secondRow
  putStrLn dividingLine
  putStrLn $ renderRow thirdRow
  where firstRow  = take 3 board
        secondRow = drop 3 . take 6 $ board
        thirdRow  = drop 6 board
</code></pre>
<p><code>renderRow</code> takes a list of cells and returns the readable version joined by pipes. <code>renderBoard</code> just some some list-slicing to render no more than 3 rows of 3 elements. Since I'm writing to console, I'll need to return an <code>IO ()</code>, the <a href="http://learnyouahaskell.com/input-and-output">IO monad</a>. Without getting too in the weeds, I/O is considered to be a side-effect and therefore Haskell forces you to wrap it in a monad.</p>
<p>If I were to call <code>renderBoard</code> with a list of empty elements <code>[Empty, Empty, Empty, Empty, Empty, Empty, Empty, Empty, Empty]</code> I would get a very pretty</p>
<pre><code>  |   |
----------
  |   |
----------
  |   |
</code></pre>
<p>My next goal was some idea of assignment. I needed to be able to take a <code>Move</code> and a <code>[Cell]</code> and return an updated board. There are a couple of rules to this</p>
<ol>
<li>The selected cell must be within bounds.</li>
<li>The selected cell must be free.</li>
</ol>
<p>Given this, I decided to simply create a map of input strings to List indices. Is it pretty? Nope. But it works fine for this case.</p>
<pre><code class="language-haskell">getBoardIndex :: String -&gt; Maybe Int
getBoardIndex "A1" = Just 0
getBoardIndex "A2" = Just 1
getBoardIndex "A3" = Just 2
getBoardIndex "B1" = Just 3
getBoardIndex "B2" = Just 4
getBoardIndex "B3" = Just 5
getBoardIndex "C1" = Just 6
getBoardIndex "C2" = Just 7
getBoardIndex "C3" = Just 8
getBoardIndex _    = Nothing
</code></pre>
<p>Pattern matching in Haskell is a little more terse than OCaml, in that I don't need a match statement. I simply create functions for every possiblity, similar to Elixir's matching. You'll see also I'm returning a <code>Maybe Int</code> - I chose this because not just do I care if a board index is real, but also if it's free. Two if statements, so I can use monadic binding, or the <code>&gt;&gt;=</code> operator. For reference:</p>
<pre><code class="language-haskell">  (&gt;&gt;=)            :: m a -&gt; (a -&gt; m b) -&gt; m b
</code></pre>
<p>What this says is "Give me a monad of some <code>a</code>, and give me a function that turns some <code>a</code> into a monad of <code>b</code> and I'll return a monad of <code>b</code>. If I have a <code>Maybe Int</code> from <code>getBoardIndex</code> and my function for "is that cell free to assign" takes an <code>Int</code> and returns a <code>Maybe</code> then I can use this binding.</p>
<pre><code class="language-haskell">data CellTransform = Success [Cell] | Fail String [Cell]


verifyIsFree ::  [Cell] -&gt; Int -&gt; Maybe Int
verifyIsFree board ix = if board !! ix == Empty then Just ix else Nothing

assignCell :: String -&gt; Move -&gt; [Cell] -&gt; CellTransform
assignCell location move board =
  case getBoardIndex location &gt;&gt;= verifyIsFree board of
    Nothing -&gt; Fail "Invalid move" board
    Just i -&gt; Success ((take i board) ++ [Occupied move] ++ (drop (i+1) board))
</code></pre>
<p>You'll see this new <code>CellTransform</code> type. I added a new type just to carry along error messages and an unmodified board if the board is taken. So my <code>verifyIsFree</code> takes a board and index, and if the board is free at that element returns the index in a Maybe, else Nothing. Since I'm doing some equality checks of a custom data type, I'll need to make sure that <code>Cell</code> is also an instance of the <a href="http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Eq.html">Eq typeclass</a></p>
<pre><code class="language-haskell">instance Eq Cell where
  Occupied X == Occupied X = True
  Occupied O == Occupied O = True
  Empty == Empty           = True
  _ == _                   = False
</code></pre>
<p>This just sets my equality operator for all possible states of a <code>Cell</code>.</p>
<p>Lastly, my actual game. I need my game to</p>
<ol>
<li>Ask for input</li>
<li>Try to assign the cell
3a) If the cell is invalid, tell the user and let them pick again
3b) If the cell is valid, check for a winner
4a) If there's a winner, alert them and end the game
4b) If there's no winner, hand over to the next player</li>
</ol>
<p>Let's get this coded out</p>
<pre><code class="language-haskell">playRound :: Move  -&gt; [Cell] -&gt; IO ()
playRound move board = do
  putStrLn $ (show move) ++ " 's turn."
  putStrLn $ "Pick a cell from A1 to C3."
  renderBoard board
  putStr "\nInput: "
  cell &lt;- getLine
  case assignCell cell move board of
    Fail err board -&gt; do
      putStrLn err
      playRound move board
    Success newBoard -&gt; do
      if isThereAWinner move newBoard then do
        putStrLn $ ("Winner! " ++ (show move) ++ " has won!")
        renderBoard newBoard
        return ()
      else playRound (nextMove move) newBoard
</code></pre>
<p>Since we're using I/O again, we need to return an IO Monad. That also gives us the <code>do notation</code> benefits of some slightly more imperative-reading code.</p>
<p>You'll see some fake functions - <code>isThereAWinner</code> and <code>nextMove move</code>. We can code these out.</p>
<pre><code class="language-haskell">nextMove :: Move -&gt; Move
nextMove X = O
nextMove O = X

isThereAWinner :: Move -&gt; [Cell] -&gt; Bool
isThereAWinner move board =
  or [
    -- check top row
    board !! 0 == (Occupied move) &amp;&amp; board !! 1 == (Occupied move) &amp;&amp; board !! 2 == (Occupied move),
    -- check middle row
    board !! 3 == (Occupied move) &amp;&amp; board !! 4 == (Occupied move) &amp;&amp; board !! 5 == (Occupied move),
    -- check bottom row
    board !! 6 == (Occupied move) &amp;&amp; board !! 7 == (Occupied move) &amp;&amp; board !! 8 == (Occupied move),
    -- check left column
    board !! 0 == (Occupied move) &amp;&amp; board !! 3 == (Occupied move) &amp;&amp; board !! 6 == (Occupied move),
    -- check middle column
    board !! 1 == (Occupied move) &amp;&amp; board !! 4 == (Occupied move) &amp;&amp; board !! 7 == (Occupied move),
    -- check right column
    board !! 2 == (Occupied move) &amp;&amp; board !! 5 == (Occupied move) &amp;&amp; board !! 8 == (Occupied move),
    -- check top left -&gt; bottom right
    board !! 0 == (Occupied move) &amp;&amp; board !! 4 == (Occupied move) &amp;&amp; board !! 8 == (Occupied move),
    -- check bottom left -&gt; top right
    board !! 6 == (Occupied move) &amp;&amp; board !! 4 == (Occupied move) &amp;&amp; board !! 2 == (Occupied move)
  ]

</code></pre>
<p>This is my least-favorite function here. It's readable with comments, but definitely isn't pleasant. I can't imagine changing this into a 5x5 tic-tac-toe board or something. But once we have this, we can create a <code>main</code> function.</p>
<pre><code class="language-haskell">main :: IO ()
main = do
  putStrLn $ "The game is beginning."
  let newBoard = replicate 9 Empty
  playRound X newBoard
</code></pre>
<p>And we can build our <code>tictactoe.hs</code> with <code>stack ghc tictactoe.hs</code> and run <code>./tictactoe</code> to play!</p>
<p>This was a fun experiment. I tried to avoid having to dig too into monadic operators, the State monad, or advanced Haskell techniques. My primary focus was to just type code and try to get comfortable with the syntax. The compiler is pretty helpful, but not as explicit as Elm's compiler. Since my career goals are to get a job writing backend ML-family code (Scala, OCaml, Haskell, etc) I'll keep on practicing. I'd love any project ideas. I might try to write a Lisp interpreter for a bigger, meatier project.</p>
]]></content:encoded></item>
<item><title>Types in ML-Inspired Languages</title><link>https://nthomas.org/2019-07-31-types-in-ml-inspired-languages/</link><guid>https://nthomas.org/2019-07-31-types-in-ml-inspired-languages/</guid><pubDate>Wednesday, 31 July 2019 12:15:07 +0000</pubDate><description>A tour of types in OCaml</description><content:encoded><![CDATA[<p>I gave a talk at work today. Slides can be found <a href="https://speakerdeck.com/nt591/types-in-ml-inspired-languages">here</a> and the repository <a href="https://github.com/nt591/types-talk-2019">here</a></p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Starting the Parser</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-7/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-7/</guid><pubDate>Wednesday, 05 June 2019 12:15:07 +0000</pubDate><description>Building a recursive descent parser</description><content:encoded><![CDATA[<p>We'll now be working on building our parser. Bob's book walks through a <a href="https://en.wikipedia.org/wiki/Recursive_descent_parser">recursive descent parser</a> - a top-down parser than builds an abstract syntax tree from the outermost grammer rule all the way into nested subexpressions until we reach the end. We've already defined our expressions, and our Scanner has our token definitions so we'll borrow that code to start the parser.</p>
<p>Taking a look at Bob's parser class, we can see some stateful values. <code>tokens</code> - a list of tokens, and <code>current</code> - some integer. We can lean on our previous pattern of passing around a context object in our module to make this work. Let's create a file called <code>parser.ml</code></p>
<pre><code class="language-ocaml">open Scanner

module Parser = struct

  type parser_context = {
    current: int;
    tokens: Scanner.token list
  }

end
</code></pre>
<p>Pretty straightforward - we simply hold all the tokens in a list, and point to the index of the one we're currently parsing.</p>
<p>Looking ahead, we're going to need to write some code for a few functions. We have <code>equality</code>, <code>match</code>, <code>check</code>, <code>advance</code>, <code>isAtEnd</code>, <code>peek</code>, <code>previous</code> and <code>comparison</code>. We want to make sure that every one of these functions in our implementation will take a context somewhere and return a context (alongside any other values in a tuple).</p>
<p>To flesh this out, let's create an implementation file.</p>
<pre><code class="language-ocaml">module Parser : sig
  type parser_context
  type expr
  type token_type
  type token

  val equality: expr -&gt; parser_context -&gt; expr * parser_context
  val match_types: token_type list -&gt; parser_context -&gt; bool * parser_context
  val check: token_type -&gt; parser_context -&gt; bool
  val advance: parser_context -&gt; token * parser_context
  val is_at_end: parser_context -&gt; bool
  val peek: parser_context -&gt; token
  val previous: parser_context -&gt; token
end
</code></pre>
<p>We'll define some abstract types in the file and later import the ones we want in our actual module.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Building our Context-Free Grammar</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-6/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-6/</guid><pubDate>Tuesday, 28 May 2019 12:15:07 +0000</pubDate><description>Setting up our expressions</description><content:encoded><![CDATA[<p>I HIGHLY recommend reading the chapter in entirety before moving into this post.</p>
<p>One of the advantages of using OCaml (or really, a functional language with a strong type system) is that we can dismiss a lot of the boilerplate and OO design pattern work that Bob needs to configure his <code>Expr.java</code>. Let's create a file called <code>expr.ml</code>.</p>
<p>The key bit of information in Bob's code is here:</p>
<pre><code class="language-java"> defineAst(outputDir, "Expr", Arrays.asList(
    "Binary   : Expr left, Token operator, Expr right",
    "Grouping : Expr expression",
    "Literal  : Object value",
    "Unary    : Token operator, Expr right"
  ));
</code></pre>
<p>What this does is doing is creating 4 classes, and for each class is setting properties. We can do the same with a variant type!</p>
<pre><code class="language-ocaml">open Scanner

type literal =
  | LiteralInt of int
  | LiteralString of string
  | LiteralBool of bool

type expr =
  | Binary of expr * Scanner.token * expr
  | Grouping of expr
  | Literal of literal option
  | Unary of Scanner.token * expr
</code></pre>
<p>Our tree contains nodes of many types. A binary node has a left and right expressions, and a token value. Our grouping is just an expression that we'll handle by adding parentheses. A literal is either a None (representing the word "nil") or Some int or string or boolean. Our unary is a token and an integer, where the token would be either <code>!</code> or <code>-</code>.</p>
<p>With these few pieces in mind, we can move on to building our parser.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Finishing the Lexer</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-5/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-5/</guid><pubDate>Wednesday, 22 May 2019 12:15:07 +0000</pubDate><description>Adding the rest of our operators</description><content:encoded><![CDATA[<hr />
<p>To follow along the source material, check out the <a href="https://craftinginterpreters.com/scanning.html">Scanning chapter</a> of Crafting Interpreters</p>
<hr />
<p>Moving on to section 4.5.2 of the book, we have multi-character operators. Cool! Specifically, we have the <code>!=</code>, <code>==</code>, <code>&lt;=</code> and <code>&gt;=</code> operators. Bob defines a <code>match</code> method that takes some expected character and checks to see if the current character is some character. If we're at the end, it's not. If the current character isn't the expected character, it's false. Else, we advance and return true. Okay that seems straightforward.</p>
<p>Let's define a <code>match</code> function. Remember that we don't have any global variables, so we're going to have to pass in context as well as the character. Since we want to conditionally mutate <code>current</code>, it makes sense that our function takes and returns a <code>scanner_context</code> object. We also want to maintain that boolean return, so let's return a <a href="https://ocaml.org/learn/tutorials/data_types_and_matching.html#Structures">tuple</a>.</p>
<p>We also need our match function to look at the character in the next position, so we'll need a helper function.</p>
<p>In our <code>scanner.mli</code> file we can define the following:</p>
<pre><code class="language-ocaml">val next_char: scanner_context -&gt; char option

val match_character: char -&gt; scanner_context -&gt; bool * scanner_context
</code></pre>
<p>This defines a function that takes a character and a context and returns a 2-tuple of boolean and context types.</p>
<p>Now we can write some code</p>
<pre><code class="language-ocaml">let next_char context =
  try Some (String.get context.source (context.current)) with Invalid_argument _ -&gt; None

let match_character expected_char context = if (is_at_end context) then (false, context) else
  match next_char context with
    | Some c when c = expected_char -&gt; (true, advance context)
    | _ -&gt; (false, context)
</code></pre>
<p>You may note that <code>next_char</code> and <code>current_char</code> look really similar. I don't really have a good solution for that but that's okay for now.</p>
<p>Now we can add some new cases to our match statement. We're going to have to write a lot of cases that look something like</p>
<pre><code class="language-ocaml">let x = match match_character '=' advanced_context with
  | (true, ctx) -&gt; add_non_literal_token BANG_EQUAL ctx
  | (false, ctx) -&gt; add_non_literal_token BANG ctx
</code></pre>
<p>But for all four above two-character operators. Since I'm finally being lazy, let's write a helper. Our new function needs to take an expected character to match, a token for the true case, a token for the false case, and a context. It will return a new context with the token added. That signature will look like</p>
<pre><code class="language-ocaml">val add_conditional_non_literal_token: char -&gt; token_type -&gt; token_type -&gt; scanner_context -&gt; scanner_context
</code></pre>
<p>Neat. Now we can write that function</p>
<pre><code class="language-ocaml">let add_conditional_non_literal_token expected_char token_if_true token_if_false context =
    match (match_character expected_char context) with
    | (true, new_context) -&gt; add_non_literal_token token_if_true new_context
    | (false, new_context) -&gt; add_non_literal_token token_if_false new_context
</code></pre>
<p>And our <code>scan_token</code> function now has the following new clauses:</p>
<pre><code class="language-ocaml">let scan_token context =
  let advanced_context = advance context in
  match (current_char advanced_context) with
  | Some '(' -&gt; add_non_literal_token LEFT_PAREN advanced_context
  | Some ')' -&gt; add_non_literal_token RIGHT_PAREN advanced_context
  | Some '{' -&gt; add_non_literal_token LEFT_BRACE advanced_context
  | Some '}' -&gt; add_non_literal_token RIGHT_BRACE advanced_context
  | Some ',' -&gt; add_non_literal_token COMMA advanced_context
  | Some '.' -&gt; add_non_literal_token DOT advanced_context
  | Some '-' -&gt; add_non_literal_token MINUS advanced_context
  | Some '+' -&gt; add_non_literal_token PLUS advanced_context
  | Some ';' -&gt; add_non_literal_token SEMICOLON advanced_context
  | Some '*' -&gt; add_non_literal_token STAR advanced_context
  | Some '!' -&gt; add_conditional_non_literal_token '=' BANG_EQUAL BANG advanced_context
  | Some '=' -&gt; add_conditional_non_literal_token '=' EQUAL_EQUAL EQUAL advanced_context
  | Some '&gt;' -&gt; add_conditional_non_literal_token '=' GREATER_EQUAL GREATER advanced_context
  | Some '&lt;' -&gt; add_conditional_non_literal_token '=' LESS_EQUAL LESS advanced_context
  | _ -&gt; failwith "Disallowed character"
</code></pre>
<p>So far we're doing great! Section 4.6 adds some more lexeme handling. Specifically, comments. Let's describe in words what Bob's code does.</p>
<p>If we see a slash, check to see if the next character is also a slash. If it is, just more forward until we either see a newline or reach the end of the file. If the next character ISN'T a slash, then just add a slash token. In our world we don't have multiline comments (whew).</p>
<p>So if we look a little ahead, we also have this <code>peek</code> function that, if we're at the end, returns a null character, else the character at the current position. We can use another <code>char option</code> here since we don't need nulls in this magical ML world.</p>
<p>It turns out we already wrote this function! <code>next_char</code> does the exact same thing. So we'll just use that. If you want, you can alias it as <code>let peek = next_char</code> but I won't.</p>
<p>We can use our <code>match_character</code> helper here to create another helper function. We want a function that either adds a slash token, or just moves on. Let's call that function <code>add_conditional_slash</code> for now. It'll take context and return context.</p>
<pre><code class="language-ocaml">val add_conditional_slash: scanner_context -&gt; scanner_context
</code></pre>
<pre><code class="language-ocaml">let add_conditional_slash context =
    let rec consume_line context =
      match (not (next_char context = Some '\n')), not (is_at_end context)) with
      | (true, true) -&gt; consume_line (advance context)
      | _ -&gt; context
    in
    if match_character '/' context then consume_line context else add_non_literal_token SLASH context
</code></pre>
<p>Let's explain this code:</p>
<p>We nest a function to consume the entire line that says "if the next character isn't a newline AND we're not at the end of the file, then move forward a character and check again. Once either we see a newline or end of file, return the context object.</p>
<p>Then we simply say if the next character is a slash, consume the line. Else, add a slash token. Maybe later we'll pull out this nested function but for now I think it's okay, we have no other use cases for it.</p>
<p>Right - so we have some default cases that Bob lists. Let's translate those by either returning context, or in the case of a newline, increment the line in our context.</p>
<pre><code class="language-ocaml">let scan_token context =
    let advanced_context = advance context in
    match (current_char advanced_context) with
    | Some '(' -&gt; add_non_literal_token LEFT_PAREN advanced_context
    | Some ')' -&gt; add_non_literal_token RIGHT_PAREN advanced_context
    | Some '{' -&gt; add_non_literal_token LEFT_BRACE advanced_context
    | Some '}' -&gt; add_non_literal_token RIGHT_BRACE advanced_context
    | Some ',' -&gt; add_non_literal_token COMMA advanced_context
    | Some '.' -&gt; add_non_literal_token DOT advanced_context
    | Some '-' -&gt; add_non_literal_token MINUS advanced_context
    | Some '+' -&gt; add_non_literal_token PLUS advanced_context
    | Some ';' -&gt; add_non_literal_token SEMICOLON advanced_context
    | Some '*' -&gt; add_non_literal_token STAR advanced_context
    | Some '!' -&gt; add_conditional_non_literal_token '=' BANG_EQUAL BANG advanced_context
    | Some '=' -&gt; add_conditional_non_literal_token '=' EQUAL_EQUAL EQUAL advanced_context
    | Some '&gt;' -&gt; add_conditional_non_literal_token '=' GREATER_EQUAL GREATER advanced_context
    | Some '&lt;' -&gt; add_conditional_non_literal_token '=' LESS_EQUAL LESS advanced_context
    | Some ' ' -&gt; advanced_context
    | Some '\r' -&gt; advanced_context
    | Some '\t' -&gt; advanced_context
    | Some '\n' -&gt; {advanced_context with line = advanced_context.line + 1}
    | _ -&gt; failwith "Disallowed character"
</code></pre>
<p>Not so bad! This is looking more and more readable. How rad is this? Let's handle literals and then write some tests.</p>
<p>Bob defers all string handling to a function called <code>string</code>. In words, it does the following.</p>
<p>As long as the next character isn't a quote, and as long as we're not at the end just consume. If we see a newline, increment line and keep going. If we see the end of the file along the way, throw an error. Once we see the closing quote, advance one more, then grab the string and add a String token. Okay! Let's make our own function. We'll call it <code>add_string_literal</code> which will take and output a scanner_context.</p>
<p>There's some conditonal line incrementing logic that I'm going to make it's own function just for ease as well.</p>
<pre><code class="language-ocaml">val increment_line_if_newline: scanner_context -&gt; scanner_context

val add_string_literal: scanner_context -&gt; scanner_context
</code></pre>
<pre><code class="language-ocaml"> let increment_line_if_newline context =
    match next_char context with
    | Some '\n' -&gt; {context with line = context.line+1}
    | _ -&gt; context

  let rec add_string_literal context =
     if( not ( next_char context = Some('"')) &amp;&amp; (not (is_at_end context)) ) then
      context |&gt; increment_line_if_newline |&gt; advance |&gt; add_string_literal
    else if (is_at_end context) then failwith "Unterminated string" else
    let str = (String.sub context.source (context.start + 1) (context.current - 1)) in
    add_literal_token STRING (Some (STRING_LITERAL str)) context
</code></pre>
<p>It's a bit gnarly and imperative. I'd like to find a way to pattern match out. BUT now we can add the following to our <code>scan_tokens</code></p>
<pre><code class="language-ocaml">| Some '"' -&gt; add_string_literal advanced_context
</code></pre>
<p>Starting to get exciting now. Let's also handle number literals in section 4.6.2. The rules of numbers are simple - if there is a <code>.</code> character, it must have 1 or more digits on both sides. So let's try and implement this.</p>
<p>We need a function <code>is_digit</code> that takes a <code>char option</code> and returns true or false, and then a <code>add_number_literal</code> function that takes and returns a <code>scanner_context</code>. We'll also create a <code>peek_next</code> along the way that takes a <code>scanner_context</code> and returns <code>char option</code>.</p>
<p>To make things easy, we should write a function that simply captures digits. Let's call it <code>capture_digits</code>. It'll just abstract out the Java code of <code>while (isDigit(peek())) advance();</code>. We can also write a function to defer to above with <code>capture_decimal</code></p>
<p>In our interface:</p>
<pre><code class="language-ocaml">val is_digit: char option -&gt; bool

val capture_digits: scanner_context -&gt; scanner_context

val capture_decimal: scanner_context -&gt; scanner_context

val peek_next: scanner_context -&gt; char option

val add_number_literal: scanner_context -&gt; scanner_context
</code></pre>
<p>And back in <code>scanner.ml</code></p>
<pre><code class="language-ocaml">let peek_next context =
    try Some (String.get context.source (context.current + 1)) with Invalid_argument _ -&gt; None

  let is_digit character = match character with
    | Some c when c &gt;= '0' &amp;&amp; c &lt;= '9' -&gt; true
    | _ -&gt; false

  let rec capture_digits context =
    if is_digit (next_char context) then context |&gt; advance |&gt; capture_digits else context

  let capture_decimal context = match (next_char context) with
    | Some '.' when (is_digit peek_next context) -&gt; context |&gt; advance |&gt; capture_digits
    | _ -&gt; context

  let add_number_literal context =
    let new_context = context |&gt; capture_digits |&gt; capture_decimal in
    let stringified_number = (String.sub new_context.source new_context.start (new_context.current - new_context.start)
    let parsed_number = int_of_string stringified_number in
    add_literal_token NUMBER NUMBER_LITERAL(stringified_number) new_context
</code></pre>
<p>Then just add in <code>scan_tokens</code> the following clause</p>
<pre><code class="language-ocaml">| digit when (is_digit digit) -&gt; add_number_literal advanced_context
</code></pre>
<p>Spicy. If you go ahead and <code>dune runtest</code> you should get some compiler errors about unused characters but no syntax or interface errors. Woohoo!</p>
<p>Let's finish up identifiers real quick. Once again, skimming ahead in Bob's chapter we see the claim <code>if isAlpha</code> or in English, "if the next lexeme begins with a letter or underscore, capture everything that's alphanumeric after and assume it's an identifier.</p>
<p>Our version of <code>is_alpha</code> will take a <code>char option</code> and return boolean. So will <code>is_alpha_numeric</code>. Our <code>add_identifier_literal</code> will need to take a scanner context and return one. Let's create a new <code>literal_type</code></p>
<pre><code class="language-ocaml">type literal_type = STRING_LITERAL of string | NUMBER_LITERAL of float | IDENTIFIER_LITERAL of string
</code></pre>
<p>This new type will just wrap our identifiers that users create. If a user defines a top levek function, it's an IDENTIFIER_LITERAL. If it's a usual keyword like AND, it's just an IDENTIFIER.</p>
<p>To our interface to define implementations.</p>
<pre><code class="language-ocaml"> val is_alpha: char option -&gt; bool

val is_alpha_numeric: char option -&gt; bool

val add_identifier_literal: scanner_context -&gt; scanner_context
</code></pre>
<p>And our implementation</p>
<pre><code class="language-ocaml">let is_alpha character = match character with
  | Some c when c &gt;= 'a' &amp;&amp; c &lt;='z' -&gt; true
  | Some c when c &gt;= 'A' &amp;&amp; c &lt;='Z' -&gt; true
  | Some c when c = '_' -&gt; true
  | _ -&gt; false

  let is_alpha_numeric character = (is_alpha character) || (is_digit character)

  let rec add_identifier_literal context =
    if (is_alpha_numeric (next_char context)) then context |&gt; advance |&gt; add_identifier_literal
    else
    let substring = (String.sub context.source context.start (context.start - context.current)) in
    match substring with
    | "and" -&gt; add_non_literal_token AND context
    | "class" -&gt; add_non_literal_token CLASS context
    | "else" -&gt; add_non_literal_token ELSE context
    | "false" -&gt; add_non_literal_token FALSE context
    | "for" -&gt; add_non_literal_token FOR context
    | "fun" -&gt; add_non_literal_token FUN context
    | "if" -&gt; add_non_literal_token IF context
    | "nil" -&gt; add_non_literal_token NIL context
    | "or" -&gt; add_non_literal_token OR context
    | "print" -&gt; add_non_literal_token PRINT context
    | "return" -&gt; add_non_literal_token RETURN context
    | "super" -&gt; add_non_literal_token SUPER context
    | "this" -&gt; add_non_literal_token THIS context
    | "true" -&gt; add_non_literal_token TRUE context
    | "var" -&gt; add_non_literal_token VAR context
    | "while" -&gt; add_non_literal_token WHILE context
    | _ -&gt; add_literal_token IDENTIFIER (Some (IDENTIFIER_LITERAL substring)) context

</code></pre>
<p>and then we can add in our <code>scan_tokens</code> clause the last piece.</p>
<pre><code class="language-ocaml">| alpha when (is_alpha alpha) -&gt; add_identifier_literal advanced_context
</code></pre>
<p>And now if you <code>dune runtest</code> one more time the only issue you should get is the call to <code>Scanner.scan_tokens</code> in <code>olox.ml</code>. Just go ahead and <code>open Scanner</code> at the top to fix that.</p>
<p>We now have a working scanner. In the next chapter move onto the code representation step.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Building a Lexer</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-4/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-4/</guid><pubDate>Thursday, 16 May 2019 12:15:07 +0000</pubDate><description>Working through a scanner for Lox</description><content:encoded><![CDATA[<hr />
<p>*To follow along the source material, check out the <a href="https://craftinginterpreters.com/scanning.html">Scanning chapter</a> of Crafting Interpreters</p>
<hr />
<p>For ease, I'll recommend you clone down this <a href="https://github.com/nt591/olox-starter-template">starter repo</a>. We'll be building a scanner for a Lox implementation in OCaml, which we'll call Olox. Let's create a file called <code>olox.ml</code>. This is going to be our entry point for the application, similar to <code>Lox.java</code> in the source material.</p>
<p>Bob's Java code handles the following three cases:</p>
<ol>
<li>If more than one argument is passed in, output a line telling the user the proper usage and exit.</li>
<li>If one argument is passed in, run a file with that name.</li>
<li>Else, just open a prompt.</li>
</ol>
<p>Okay, let's start by writing out the following code in our <code>olox.ml</code></p>
<pre><code class="language-ocaml">  let main = fun _ -&gt; match Array.length Sys.argv with
    | 0 -&gt; run_prompt ()
    | 1 -&gt; run_file (Array.get Sys.argv 0)
    | _ -&gt; print_endline "Usage: olox [script]"; exit 64;
</code></pre>
<p>We're pattern matching on the length of the arguments vector passed in. <a href="https://caml.inria.fr/pub/docs/manual-ocaml/libref/Sys.html">Sys.argv</a> takes an array of strings that we can use. If no arguments are passed in, we run a prompt. If one is passed in, we run a file with that first element. Else, we'll print an error and exit. We can't really run this code, since we have two undefined functions in <code>run_prompt</code> and <code>run_file</code> but we're just following along.</p>
<p>So now need to define those two functions. Bob's implementation defers <code>run_file</code> to a function called <code>run</code> by reading the bytes out of a file. <code>run_prompt</code> also just takes a line from input and calls <code>run</code>. So we now need to define those three functions.</p>
<p>Let's add the following in our module above our main function</p>
<pre><code class="language-ocaml">  let run source =
    let tokens = Scanner.scan_tokens in
    List.iter (fun token -&gt; print_endline token) tokens

  let run_prompt = fun _ -&gt;
    while true do
      print_string "&gt; ";
      let input = read_line ()
      in run input;
    done

  let run_file filename =
    let channel = open_in filename in
    try
      (* read entire file *)
      let line = really_input_string channel (in_channel_length channel) in
      run line;
      flush stdout;
      close_in channel
    with e -&gt;
      close_in_noerr channel;
      raise e
</code></pre>
<p>Like Bob's Java code, we have a <code>run</code> function that just gets tokens from a scanner - in his case, an instance of a Scanner class. We'll just use a to-be-defined module. Then we'll just iterate over the returned tokens and print them out.</p>
<p>Our <code>run_prompt</code> function will print a string as a prompt, then read in the next line and pass it to our run function.</p>
<p>Our <code>run_file</code> function takes some filename and creates an input channel, positioned at the beginning of the file. It then gets the entirety of the file by reading <code>in_channel_length channel</code> number of characters, then passes into <code>run</code>. For clarity, see the following type signatures.</p>
<p><code>val really_input_string : in_channel -&gt; int -&gt; string</code>
<code>val in_channel_length : in_channel -&gt; int</code></p>
<p><code>really_input_string</code> takes a channel and an int, and returns a string. <code>in_channel_length</code> returns the length of a channel.</p>
<p>So far so good! If you were to copy and paste this into utop, you'd error on the <code>Scanner</code> module. That's fine - feel free to replace <code>run</code> with something like <code>let run = print_endline</code> and test all those functions. In fact, let's try it right now. Create a file called <code>test.md</code> and add the following code.</p>
<pre><code>Hello, world!
This is the second line!
Now on line 3 with spec!@l characters
</code></pre>
<p>In your terminal, run <code>utop</code> and copy and paste the following code which replaces <code>run</code> with a simple print.</p>
<pre><code class="language-ocaml">let run = print_endline

let run_prompt = fun _ -&gt;
  while true do
    print_string "&gt; ";
    let input = read_line ()
    in run input;
  done

let run_file filename =
  let channel = open_in filename in
  try
    (* read entire file *)
    let line = really_input_string channel (in_channel_length channel) in
    run line;
    flush stdout;
    close_in channel
  with e -&gt;
    close_in_noerr channel;
    raise e
</code></pre>
<p>Try running <code>run_file "test.md";;</code> and then <code>run_prompt ();;</code> Anything in your prompt should be returned back. Neat! Now <code>ctrl+D</code> to exit.</p>
<p>Next up is error handling! Bob makes the following point and I think it's very valid</p>
<blockquote>
<p>The other reason I pulled the error reporting out here instead of stuffing it into the scanner and other phases where the error occurs is to remind you that it’s a good engineering practice to separate the code that generates the errors from the code that reports them.</p>
</blockquote>
<p>However, in the interest of not having a global mutable variable, and keeping some of my logic contained in the scanner (for now), I'm going to go ahead and handle all error management in the scanner. This way we can maintain all our scanner logic (line number, character positions) safely isolated and still report on it.</p>
<p>Let's jump right into it. Create a file called <code>scanner.ml</code>. Bob's examples creates classes for Token and TokenType. In our FP world, those can just be types that our scanner knows about so we'll wrap it all up inside one file. If we need to refactor later we can.</p>
<p>Looking at <code>Token.java</code> in the source, we see a giant enum. When I see enum, I think <code>variant types</code> - read up <a href="https://dev.realworldocaml.org/variants.html">here</a> if you need. Let's take the enum and create a variant type in our scanner.</p>
<pre><code class="language-ocaml">module Scanner = struct
  type token_type =
    (* single-character tokens *)
    | LEFT_PAREN
    | RIGHT_PAREN
    | LEFT_BRACE
    | RIGHT_BRACE
    | COMMA
    | DOT
    | MINUS
    | PLUS
    | SEMICOLON
    | SLASH
    | STAR

    (* One or two character tokens *)
    | BANG
    | BANG_EQUAL
    | EQUAL
    | EQUAL_EQUAL
    | GREATER
    | GREATER_EQUAL
    | LESS
    | LESS_EQUAL

    (* Literals *)
    | IDENTIFIER
    | STRING
    | NUMBER
    (* Keywords *)
    | AND
    | CLASS
    | ELSE
    | FALSE
    | FUN
    | FOR
    | IF
    | NIL
    | OR
    | PRINT
    | RETURN
    | SUPER
    | THIS
    | TRUE
    | VAR
    | WHILE
    | EOF
end
</code></pre>
<p>Just to quote section 4.2.2,</p>
<blockquote>
<p>There are lexemes for literal values—numbers and strings and the like. Since the scanner has to walk each character in the literal to correctly identify it, it can also convert it to the real runtime value that will be used by the interpreter later.</p>
</blockquote>
<p>Before we move on, we'll need to have some way of wrapping literals. Our token is going to want to have a reference of token type AND optional literals - e.g. a token could be of type TRUE with no literal, or of type NUMBER with a literal <code>2</code>. So, let's start by just making two types for literals - string and number</p>
<pre><code class="language-ocaml">  type literal_type = STRING_LITERAL of string | NUMBER_LITERAL of float
</code></pre>
<p>This doesn't solve all our problems, but at least we now have some idea of what a literal can be. Moving on to make our token, we should define a type that represents a token. Bob's Token class has a type of TokenType, a lexeme of String, a literal of some Object, and a line of Int. We can do something similar with</p>
<pre><code class="language-ocaml">type token = {
  lexeme: string;
  literal: literal_type option;
  line: int;
  token_type: token_type;
}
</code></pre>
<p><em>Nikhil why is that literal an option?</em> Well, as we had mentioned before - a token does not need to contain a literal. There is no literal for the token_type <code>VAR</code>, so we may not need a literal in our token. Options represent that. Real World OCaml's <a href="https://dev.realworldocaml.org/guided-tour.html#options">chapter on options</a> is a great primer for more info.</p>
<p>Lets take a moment to look at the initial Scanner code in section 4.4. The file defined a class <code>Scanner</code> that has a field <code>source</code>, a string that represents the code we're scanning. <code>scanTokens</code> takes a list of tokens, checks to see if we're at the end of the source and if not, scans tokens. Once at the end, we add a token representing the end of the file and return the list of tokens. We also have the use of a helper function that determines if we're at the end of the source code.</p>
<p>We see that the original Java code has some fields for use. Let's embrace our FP-world and rather than create a class, we're going to create a type that represents some record of fields. We can then ensure all our functions take and emit that type.</p>
<pre><code class="language-ocaml">type scanner_context = {
  source: string;
  start: int;
  current: int;
  line: int;
}
</code></pre>
<p>Now that we have a type that contains all the fields we know about, let's define our methods. To start, let's create an interface file. Create a file called <code>scanner.mli</code> with the following</p>
<pre><code class="language-ocaml">module Scanner : sig
  type token_type

  type literal_type

  type token

  type scanner_context

  val scan_tokens: string -&gt; token list

  val is_at_end: scanner_context -&gt; bool
end
</code></pre>
<p>In our interface, I'll be using abstract types. <code>type token</code> and <code>type scanner_context</code> now hide the implementation of the type from any module that implements this signature. You can read more about them on the <a href="https://ocaml.org/learn/tutorials/modules.html#Abstract-types">OCaml website</a> and this Cornell class's <a href="http://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/modules/abstract_types.html">notes</a>.</p>
<p>Our signatures implement what Bob's Java code does - <code>is_at_end</code> returns true or false for the state of the application. <code>scan_tokens</code> will go through the source code and return all the tokens.</p>
<p>Let's prove that our interface actually does something! Open <code>dune</code> and replace with the following code`</p>
<pre><code class="language-lisp">(executable
  (name olox)
  (libraries oUnit)
)

(alias
  (name    runtest)
  (deps    (:x olox.exe))
  (action  (run %{x})))
</code></pre>
<p>Now just run <code>dune build</code> and <code>dune runtest</code>. You should see the following errors.</p>
<pre><code>File "scanner.ml", line 1:
Error: The implementation scanner.ml
       does not match the interface .olox.eobjs/byte/scanner.cmi:
       ...
       In module Scanner:
       The value `is_at_end' is required but not provided
       File "scanner.mli", line 56, characters 2-40: Expected declaration
       In module Scanner:
       The value `scan_tokens' is required but not provided
       File "scanner.mli", line 54, characters 2-48: Expected declaration
</code></pre>
<p>There's a bit more above it but let's come back to that later. Our interface works! We stated that the module must have two specific functions but did not implement them. Now to do that.</p>
<p>We'll first write <code>is_at_end</code>. The function takes our wrapping context and returns a boolean. I wrote</p>
<pre><code class="language-ocaml">let is_at_end ctx = ctx.current &gt;= (String.length ctx.source)
</code></pre>
<p>Then we'll have our <code>scan_tokens</code> function. This function takes just the source code string as an input, and will return a list of tokens. Somewhere in the middle we'll need to wrap up the rest of our context. OCaml has local <code>let</code> bindings we can use for this. We also now realize we need to maintain a running list of tokens that we're adding. Let's add tokens to our <code>scanner_context</code>. In the definition of <code>type scanner_context</code> just add <code>tokens: token list;</code> inside.</p>
<p>Looking at Bob's implementation, we have a few more pieces of information. We have a <code>scanToken</code> function that does...something. We can at least assume it changes the value of <code>current</code> globally due to the reset inside the loop. We also have some addition of an EOF token. Let's sketch out what a <code>scan_token</code> function might look like. Given that it'll change two values, current and our token list, let's have it receive and output a <code>scanner_context</code>. In our <code>.mli</code> file add</p>
<pre><code class="language-ocaml">  val scan_token: scanner_context -&gt; scanner_context
</code></pre>
<p>Bob's token addition basically just instantiates a token object and adds it to the global list. Let's do the same, by writing a function <code>add_token</code> that takes all the fields of a token as well as context and returns the new context.</p>
<pre><code class="language-ocaml">  val add_token: token_type -&gt; string -&gt; literal_type option -&gt; int -&gt; scanner_context -&gt; scanner_context
</code></pre>
<p>Okay! We have some loosely defined code interfaces. Let's implement. In <code>scanner.ml</code> we'll do the eaier of the two first.</p>
<pre><code class="language-ocaml">let add_token token_type lexeme literal line ctx =
  {ctx with tokens = ctx.tokens @ [{
      token_type;
      lexeme;
      literal;
      line
    }]
  }
</code></pre>
<p>There's a performance issue here where OCaml's list appending operator (either <code>@</code> or <code>List.append</code>) runs in <code>O(n)</code> time by walking through the length of the first list. It's not ideal but that's what we get for using a linked list. For now, we'll ignore this and come back to it when it's a performance issue.</p>
<p>Our next function will be our scanning. Look at Bob's implementation. In words - he gets the next character, checks to see what it is, then adds a token. I'm going to break this into three functions - one function to just increment the <code>current</code> counter, one to get the next character, and our add token above.</p>
<p>We can add the following two interfaces</p>
<pre><code class="language-ocaml">  val advance: scanner_context -&gt; scanner_context

  val current_char: scanner_context -&gt; char option
</code></pre>
<p>And then define our functions</p>
<pre><code class="language-ocaml">  let advance context = {context with current = context.current + 1}

  let current_char context =
    try Some (String.get context.source (context.current -1)) with Invalid_argument _ -&gt; None
</code></pre>
<p>I'm using an <code>option</code> for the <code>current_char</code> because it's possible to get an out of bounds exception on string access, and it's better to just be safe than sorry.</p>
<p>We also have sone unary function <code>addToken</code> that Bob defines, that just defers to a binary function. I'm going to define these two functions as either adding a literal, or a nonliteral. That means we'll need a function <code>add_literal_token</code> and <code>add_non_literal_token</code> - the former takes both a <code>token_type</code> and <code>Some literal</code> and the latter can defer to the former with a <code>None</code>. We're going to again have <code>context</code> passed along as the last argument in order to make pipelining our functions easy.</p>
<pre><code class="language-ocaml">val add_literal_token: token_type -&gt; literal_type option -&gt; scanner_context -&gt; scanner_context

val add_non_literal_token: token_type -&gt; scanner_context -&gt; scanner_context
</code></pre>
<p>Great. Now to just write those little helpers...</p>
<pre><code class="language-ocaml">let add_literal_token token_type literal_type context =
  let text = (String.sub context.source context.start (context.current - context.start)) in
  add_token token_type text literal_type context.line context

let add_non_literal_token token_type context = add_literal_token token_type None context
</code></pre>
<p>We create a substring from the start position and capture the difference between start and current, then add the token. <code>String.sub</code> in OCaml takes a start and length, rather than a start and end so we need to do a little math.</p>
<p>Now that we have these, we can write out our <code>scan_token</code> function and finally write <code>scan_tokens</code>. Again, since we see that <code>scan_token</code> in Bob's example cares about two fields - the current character, and the token list, let's pass in our context and get out context.</p>
<pre><code class="language-ocaml">val scan_token: scanner_context -&gt; scanner_context
</code></pre>
<pre><code class="language-ocaml">let scan_token context =
  let advanced_context = advance context in
  match (current_char advanced_context) with
  | Some '(' -&gt; add_non_literal_token LEFT_PAREN advanced_context
  | Some ')' -&gt; add_non_literal_token RIGHT_PAREN advanced_context
  | Some '{' -&gt; add_non_literal_token LEFT_BRACE advanced_context
  | Some '}' -&gt; add_non_literal_token RIGHT_BRACE advanced_context
  | Some ',' -&gt; add_non_literal_token COMMA advanced_context
  | Some '.' -&gt; add_non_literal_token DOT advanced_context
  | Some '-' -&gt; add_non_literal_token MINUS advanced_context
  | Some '+' -&gt; add_non_literal_token PLUS advanced_context
  | Some ';' -&gt; add_non_literal_token SEMICOLON advanced_context
  | Some '*' -&gt; add_non_literal_token STAR advanced_context
  | _ -&gt; failwith "Disallowed character"
</code></pre>
<p>WHEW. That's a whole lot of work but we did it. We now have a way to scan tokens from a string. Now for the fun part.</p>
<p>In our next post let's try to write more matching cases. And if we're lucky, some tests.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Lexing</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-3/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-3/</guid><pubDate>Wednesday, 15 May 2019 20:15:07 +0000</pubDate><description>Understanding lexical analysis</description><content:encoded><![CDATA[<p>An interpreter is a program that takes some input code and immediately executes it.</p>
<p>There are multiple steps to an interpreter that Bob Nystrom covers in detail on <a href="https://craftinginterpreters.com/a-map-of-the-territory.html">his website</a>. For the first pass, we'll be building a <a href="https://imantung.github.io/tree-walk-interpreter/">tree-walking interpreter</a>. This shrinks the problem domain a bit. In a nutshell, we'll have to solve the following problems.</p>
<p>Step one is lexical analysis - taking in some stream of text (the code input) and breaking it into chunks or <strong>lexemes</strong>. For example, if I were to type</p>
<pre><code class="language-javascript">const x = 2;
</code></pre>
<p>we have 5 lexemes - the keyword <code>const</code>, the variable <code>x</code>, the equals sign, the integer <code>2</code>, and the semicolon. However, in order to maintain the context or type of lexeme, we're going to have to tag them with a type. You can think of each chunk broken into a <strong>token</strong>, which might look like the following</p>
<pre><code class="language-javascript">  const token1 = {
    lexeme: "=",
    type: "EQUALS"
  }

  const token2 = {
    lexeme: "2",
    type: "NUMBER"
  }
</code></pre>
<p>in OCaml we'd use the built in type system and end up with something more like</p>
<pre><code class="language-ocaml">type token_type = EQUALS | NUMBER
type token = {
  lexeme: string;
  type: token_type;
}

let token1 = {
  lexeme = "=";
  type: EQUALS
}

let token1 = {
  lexeme = "2";
  type: NUMBER
}
</code></pre>
<p>Step two is parsing - taking our list of tokens and building an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract syntax tree</a>. This tree representation lets us build out a data structure that can throw errors when leaf nodes are invalid, recurse and find subexpressions, etc.</p>
<p>If you were to imagine the following</p>
<pre><code class="language-javascript">  (1 + 2) / 3
</code></pre>
<p>This might be represented as nodes of</p>
<pre><code class="language-ocaml">  type fn = Add | Subtract | Multiply | Divide
  let tree =
    | Literal of int
    | Node of fn * tree * tree

  Node(
    Divide,
    Node(Add, 1, 2)
    3
  )
</code></pre>
<p>The following post will focus on building out step one.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Setting up your environment</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-2/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-2/</guid><pubDate>Wednesday, 15 May 2019 17:15:07 +0000</pubDate><description>Creating a workspace with all your requirements</description><content:encoded><![CDATA[<hr />
<p><em>If you want to just clone a repo and move on, check out this <a href="https://github.com/nt591/olox-starter-template">sample</a></em></p>
<hr />
<p>The easiest way to get an environment set up is to just follow the Real World OCaml <a href="http://dev.realworldocaml.org/install.html">guide</a>. I'm going to enumerate the steps here in less detail. Since I'm running on macOS, this guide will be tailored to that. Check out the RWO guide for more options.</p>
<ol>
<li>
<p>Get OPAM installed. Opam is the OCaml package manager and is available on Homebrew. If you have Homebrew installed, it's as easy as <code>brew install opam</code></p>
</li>
<li>
<p>This should have installed OCaml for you. To check, run <code>ocaml -version</code> and make sure you have a response.</p>
</li>
<li>
<p>Next up, we need to configure OPAM. Run <code>opam init</code> and walk through the questions. I answered yes to everything.</p>
</li>
<li>
<p>Now we need some packages. We're going to start by installing just <a href="https://opensource.janestreet.com/core/"><code>Core</code></a> and <a href="https://opam.ocaml.org/blog/about-utop/"><code>utop</code></a>. <code>Core</code> is a replacement to the OCaml standard library. It has some different APIs for labeled arguments, more functionality in every module, and has a consistent interface for <code>Container</code> structures. To be honest, we could just install <code>Base</code> instead of <code>Core</code>, where <code>Base</code> supplies those features, but RWO uses <code>Core</code> so let's stick with it. <code>utop</code> is a toplevel (like the Ruby REPL <code>irb</code>) that is really easy to work with for testing code on the fly. For the purpose of this tutorial, I'm actually not going to use <code>Base</code> or <code>Core</code> so you can omit it.</p>
</li>
<li>
<p>Let's also install <code>dune</code> for our build tooling, with <code>opam install dune</code>.</p>
</li>
<li>
<p>open up <code>~/.ocamlinit</code> and add the following to get all of <code>Core</code> set up in the toplevel</p>
</li>
</ol>
<pre><code class="language-ocaml">#use "topfind";;
#thread;;
</code></pre>
<ol start="7">
<li>Configure your editor. I use Visual Studio Code and really like the ReasonML tool <a href="https://github.com/jaredly/reason-language-server"><code>Reason Language Server</code></a></li>
</ol>
<p>And that's a minimal install! If you check out the repo I linked above, you can run tests with <code>dune runtest</code> and play with some code.</p>
]]></content:encoded></item>
<item><title>Crafting Interpreters in OCaml - Preface</title><link>https://nthomas.org/crafting-interpreters-in-ocaml-1/</link><guid>https://nthomas.org/crafting-interpreters-in-ocaml-1/</guid><pubDate>Wednesday, 15 May 2019 14:15:07 +0000</pubDate><description>Resources and rules for the project</description><content:encoded><![CDATA[<p>I'll be writing a series of posts documenting my goals of learning <a href="http://ocaml.org/">OCaml</a> as well as understanding how interpreters and compilers work. The motivation here is twofold.</p>
<ol>
<li>I want to challenge myself and step out of the comfort of usual web development and all the ease of the JS ecosystem</li>
<li>I want to better understand how programming languages actually <em>work</em>.</li>
</ol>
<p>To learn OCaml, I've worked through a Cornell class's online notes <sup class="footnote-reference"><a href="#1">1</a></sup> as well as read much of version 1 of <a href="https://realworldocaml.org">Real World OCaml</a>. When version 2 comes out, I'll try to update these notes. I've also been devouring <a href="https://www.youtube.com/channel/UCDsVC_ewpcEW_AQcO-H-RDQ">YouTube videos</a> from Jane Street as well as spending a lot of time in the <a href="https://discordapp.com/invite/reasonml">ReasonML / OCaml Discord</a></p>
<p>To understand more about interpreters, I'll be using two primary resources.</p>
<ul>
<li><strong>Crafting Interpreters</strong><sup class="footnote-reference"><a href="#2">2</a></sup> by Bob Nystrom</li>
<li><strong>Writing an Interpreter in Go</strong><sup class="footnote-reference"><a href="#3">3</a></sup> by Thorsten Ball</li>
</ul>
<p>By the time this series is done, I hope to have written an interpreter for the Lox language used in Crafting Interpreters using OCaml. I'll be establishing a few rules in order to throw myself into the deep end.</p>
<ol>
<li>Where possible, I will use the OCaml data type system to help ensure exhaustive checking of various states. <a href="https://ocaml.org/learn/tutorials/data_types_and_matching.html#Variants-qualified-unions-and-enums">Variant types</a> are a powerful tool available in OCaml (and Haskell, F# and many other ML-inspired langauges) that can serve as a way of flagging different cases. For example, a chunk of code can be a string, a number, or some keyword. Using these types, I could state that a code chunk is</li>
</ol>
<pre><code class="language-ocaml">type Chunk =
| Keyword
| String of string
| Number of int
</code></pre>
<ol start="2">
<li>
<p>Where possible, I'll avoid mutation, classes and global state. In chapter 1, Bob uses a Scanner class with fields of <code>start</code> and <code>current</code>, both ints, to track lexeme parsing (aka just parsing out phrases). Rather than this, I'll be writing a Scanner module and passing around a context object between functions.</p>
</li>
<li>
<p>Where possible, I'll use recursion over looping. In the book, there are times Bob uses <code>while</code> loops to pick out phrases. For example, continue parsing out a chunk of code while the next character is not a space, then stop. That code might look something like (in JS).</p>
</li>
</ol>
<pre><code class="language-javascript">  let start = 0;
  let current = 0;
  let phrase = "";
  const testString = "Hello world!";

  while (testString[current] != " ") {
    phrase = phrase + testString[current];
    current++;
  }

  // phrase = "Hello"
</code></pre>
<p>Rather than a while loop, I'll be using pattern matching and recursion. In OCaml, that might look something like the following.</p>
<pre><code class="language-ocaml">  type context = {
    start: int;
    current: int;
    phrase: string;
    source: string;
  }

  let ctx = {
    start = 0;
    current = 0;
    phrase = "";
    source = "Hello world!";
  }

  let rec capture_until_space context =
    let next_char = (String.get context.source context.current) in
    match next_char with
    | ' ' -&gt; context
    | _ -&gt; capture_until_space {
        context with current = context.current + 1;
                     phrase = (context.phrase ^ Char.escaped next_char);
      }

  (*
    # capture_until_space ctx;;
    - : context = {start = 0; current = 5; phrase = "Hello"; source = "Hello world!"}
  *)
</code></pre>
<p>If I succeed in doing this, I can maintain purity.</p>
<p>I'll be also attempting to write unit tests along the way using <a href="http://ounit.forge.ocamlcore.org/api-ounit/OUnit2.html">OUnit2</a>. I'll also attempt to implement a setup with <a href="https://github.com/ocaml/dune">Dune</a> to make it easier to build and follow along. I know the Reason community has tools like <a href="http://esy.sh">Esy</a> as well. If I'm ambitious, I'll use that as well to get everything set up.</p>
<p>Wish me luck!</p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
<p>CS3110 just ran in the Spring of 2019 and notes are <a href="http://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/">here</a></p>
</div>
<div class="footnote-definition" id="2"><sup class="footnote-definition-label">2</sup>
<p>Bob's book is a work in progress and can be found <a href="https://craftinginterpreters.com">here</a></p>
</div>
<div class="footnote-definition" id="3"><sup class="footnote-definition-label">3</sup>
<p>Thorsten's book will be used to supplement my understanding, and can be found <a href="https://interpreterbook.com/">here</a></p>
</div>
]]></content:encoded></item></channel></rss>