Luna McNulty's BlogProgramming and Maybe Other Thingshttps://lmcnulty.me/words/atom.xml2022-03-08T13:24:18.755134ZLuna McNultyHow to Pronounce “th” in EnglishLuna McNultyhttps://lmcnulty.me/words/english-th2020-05-19T00:00:00Z
<p>In my article on <a href="../french-vowels">French vowels</a>, I explained how different languages have different phonemes, giving the example of how native French speakers have trouble with the “th” sounds while learning English. In fact, these sounds are quite unusual cross-linguistically, appearing in only about 4% of the world’s languages —so they give trouble to a huge number of English second-language learners, not just the French. I’m a native English speaker, so I don’t remember learning to pronounce these sounds as a toddler —but I thought it would be an interesting phonetics exercise to try and describe their pronunciation. I refrained from publishing this until I heard back from one of my French friends that this was helpful to them, so hopefully it will be to others as well.</p>
<hr />
<p>The first thing to understand is that there are actually <strong>two</strong> ‘th’ sounds in English. Although most English speakers aren’t aware of the difference, they can tell between the words “thigh” (part of the leg) and “thy” (an old-fashioned way of saying “you”).</p>
<p>The ‘th’ in “<a href="https://forvo.com/word/thigh/#en">thigh</a>” is /θ/ in IPA.<br />
The ‘th’ in “<a href="https://forvo.com/word/thy/#en">thy</a>” is /ð/ in IPA.</p>
<p>I’ll explain the difference later, but let’s start with /θ/</p>
<p>When non-native speakers don’t know how to make this sound, they usually produce a /f/ or a /s/ instead: eg, they’ll sometimes say “fink” or “sink” instead of “think.” This isn’t arbitrary: they choose these sounds because they are both <strong>fricatives</strong>, just like /θ/. A fricative is a sound that’s made by making a closure between two parts of the mouth and letting out a small stream of air.</p>
<p>When you make an /s/, you touch the tip of your tongue to the alveolar ridge (the little bump on the top of your mouth behind the teeth). Hold the /s/ in “yessssssss—”. You will feel how you are releasing a little bit of air between your tongue and the roof of your mouth.</p>
<p>Now try the same thing with /f/. You make an /f/ by touching your front teeth to your bottom lip. Now if you hold the /f/ in « stufffffff— », you’ll feel how you are letting the air pass between your lip and your teeth. Notice that a little bit of air also goes around the sides of your teeth.</p>
<p>/θ/ is the same, only instead of making the closure with your teeth and your lips, you make it with your teeth and your tongue. To make the /θ/, touch the tip of your tongue to your two front teeth. Then blow a little air between your teeth and your tongue, just as you do to make an /f/, just substituting the lips for the tongue. The opening of your mouth should not be too wide, or else all the air will go around and not between the tongue and the teeth. However, you should have a little opening at the sides of your mouth, or else the sound will not be loud enough. Once you’re doing this, you should be able to transition to saying “—thhhhhhhhigh.”</p>
<p>/ð/ is exactly the same, only you also vibrate your vocal folds. To understand what this means, put your hand on your chest. Then hold a /v/ sound as in “slavvvvvvvvvvvv—” you will feel a vibration in your chest. Now do the same thing but holding /f/, like in « sauffffff ». You’ll notice that you don’t feel the same vibration in your chest when you make the /f/ sound.</p>
<p>Linguistics call this property <strong>voice</strong>. Languages differentiate between sounds depending on whether they are voiced or not. /v/ is the voiced version of /f/, /z/ is the voiced version of /s/, /d/ is the voiced version of /t/, etc.</p>
<p>To learn how to control the voicing, try to transition between a /s/ sound and a /z/ sound. They are exactly the same except for the voicing. Keep your hand on your chest and go “ssssssss zzzzzzzzs ssssssss zzzzzzzz.” As you do this you can become conscious of how your vocal folds are vibrating.</p>
<p>To make a /ð/, try and hold a /θ/. A good word that ends with this is “bathhhhhh—”. Now, as you hold the /θ/ sound, start vibrating your vocal folds, the same way you did as you transitioned from /s/ to /z/. If you do it, you’ll be making the /ð/ sound and you can transition to “—thhhhhy”</p>
<p>Some words that use /θ/ (voiceless): think, thanks, thunder, thirsty, thumb, throw, theory, throat, bath.</p>
<p>Some words that use /ð/ (voiced): this, they, father, weather, bother, bathe.</p>
<p>As is often the case in English orthography, there is to my knowledge no way to determine the sound from the spelling of the word. However, now that you know the IPA for each sound, you’ll at least be able to tell from the pronunciation guides in dictionaries.</p>
Meal Plan ExplainedLuna McNultyhttps://lmcnulty.me/words/meal-plan2020-02-13T00:00:00Z
<p>When I first arrived at Brown, I found the meal plans confusing. However, during my four years, I’ve finally figured out all the details. For anyone still confused, it’s actually quite simple:</p>
<p>Meals at campus eateries can be paid for in Bear Bucks, Meal Points, Meal Credits, cash, credit, and traveler’s checks. You may combine a maximum of three payment methods, which may not include both meal points and Bear bucks, except at the Ivy Room.</p>
<p>One credit can be exchanged for between $7.60 and $8.20 (or less) worth of food from a campus eatery. However you cannot exchange a credit for points at the Blue Room on Saturdays or on Wednesdays after 12am and before 4pm. Furthermore, you cannot exchange credits for points at any campus eatery except the V-Dub on days that fall within the first or last two weeks of a semester or that are designated as federal holidays, but not both. When a point is exchanged for credits, those credits must be used immediately, and any remaining credits are discarded, unless you use two or more credits in one swipe. Note that a “swipe” is distinct from a “transaction.”</p>
<p>Students on a meal plan (excluding “flex” meal plans) receive the corresponding number of credits and points every Thursday. Unlike points, credits do not carry over between weeks, but they carry over between days. However, you may not use more than three credits within a 24-hour period. Therefore, if you are on a 20 meals/week plan, you can only use carried-over points on Sundays. Note that the 24-hour period is extended to 36 hours during reading period. This is the only time during which the Sharpe Refectory serves breakfast on Sundays, however, meal plan-holders are not given an additional credit for the extra meal.</p>
<p>Students may choose either a weekly or a “flex” meal plan. The twenty meals/week is available to all students throughout their four years at Brown, but not to fifth-year seniors. Of the remaining weekly meal plans, the 14 meals/week plan is offered to all students except freshmen, and the 7 and 10 meals/week plans to juniors and seniors, respectively. Of the flex plans, the flex 460 plan is available to freshmen, Flex 330 to sophomores, and Flex 240 to juniors and seniors, not respectively. Graduate students are allowed purchase a meal plan, but it is considered embarrassing. To determine the number of credits, points, and guest swipes provided in a given meal plan, consult the Dining services website at <a href="http://brown.cafebonappetit.com/">brown.cafebonappetit.com</a> or <a href="https://dining.brown.edu/">dining.brown.edu</a>. Note: the provided figures on these websites are not guaranteed to be accurate or to match one another.</p>
<p>Meal plans include between 2 and 5 guest swipes per semester per year (inclusive). Unlike ordinary credits, guest swipes may be used for a person other than the student on the meal plan. Married couples count as the same person for this purpose, but they require two guest swipes to be admitted to the Sharpe Refectory or the V-Dub. Brown University recognizes marriages performed in every country except the Czech Republic.</p>
<p>All campus eateries except the Blue Room are closed beginning at the first day of Spring break and ending on the day two-days before classes resume (not inclusive). Meal plans are not credited between the first Thursday of Spring break and the day before classes resume (inclusive). On odd-numbered years,<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> campus eateries are open one additional day before classes resume.</p>
<p>The Blue Room is open from 9am to 4pm on Mondays, Wednesdays and Thursdays and from 9am to 1pm on Fridays. On Tuesdays and Sundays, it’s open from 10:30am to 9pm, and it is open all day on Sunday. Additionally, the Blue Room closes every 300 days not including Federal Holidays except Christmas, unless it falls on a Sunday or Monday, starting from July 2, 1776.</p>
<p>The meal plans of graduating seniors are terminated at 2pm on the day of their graduation. However, in special circumstances, students may receive a dean’s note to extend their meal plan at most four days, but no fewer than two.</p>
<p>Muslim students in good academic standing may elect to cancel their meal plan for the month of Ramadan. They will be refunded by an amount posted in the University Chaplain’s office up to four days before the start of Ramadan. Note that the amount may be, but is not necessarily, dependent on whether a student is receiving financial aid. This offer is extended to students who have not recorded themselves as Muslim in Banner, however they will be refunded by the same amount as students not receiving financial aid, regardless of their own status. Note also that Sikhs and Yazidis are considered Muslim for this purpose.</p>
<p>Every fortnight, the President of the College and zero or more representatives from Johnson & Wales University will meet at an agreed-upon location for secret coin-flip. If the result is “heads,” then the cost of fruits and snack-foods will be synchronized between Josiah’s and Andrew’s. Otherwise, one of the two will have prices 20 cents higher than the other on an alternating basis.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Reminder: students should use the Hebrew calendar to determine the number of the current year, excepting Francophone Studies concentrators and students of the Eastern Orthodox faith, the former of whom may optionally use the French Republican calendar and the latter of whom are <em>required</em> to use the Julian Calendar.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
One Brain, Two MindsLuna McNultyhttps://lmcnulty.me/words/one-brain-two-minds2022-01-15T00:00:00Z
<div class="preface">
<p>This is a post I made in the forum of my Spring 2021 class on <em>Language Processing in Humans and Machines</em>. It was be written late at night while I felt enlightened after finishing an assigned reading – as a result, it should be taken with a grain of salt.</p>
<p>The paper in question was <a href="http://ruccs.rutgers.edu/images/personal-zenon-pylyshyn/proseminars/Proseminar13/ConnectionistArchitecture.pdf">Connectionism and cognitive architecture: A critical analysis</a>, a 1988 critique of connectionism by Jeremy Fodor and Zenon Pylyshyn. Connectionism is a once-popular theory which viewed cognition in humans as fundamentally similar to computations done in neural networks. Fodor and Pylyshyn argued that some of the brain’s capabilities could only be explained if it was doing symbol processing in the manner of classical Turing machines.</p>
</div>
<hr />
<p>I just finished the Fodor and Pylyshyn reading, and throughout I was pre-occupied with one idea that was only addressed briefly at the end: the possibility that a connectionist and classical models of cognitive processing could co-exist in the same brain. F&P mention throughout the paper that connectionist models could serve as an implementation for a classical architecture / Turing machine. Only at the very end, they suggest that</p>
<blockquote>
<p>A good bet might be that networks sustain <em>some</em> cognitive processes as can be analyzed as the drawing of statistical inferences; as far as we can tell, what network models really are is just analog machines for computing such inferences. Since we doubt that much of cognitive processing does consist of analyzing statistical relations, this would be a quite a modest estimate of the prospects for network theory compared to what the connectionists themselves have been offering.</p>
</blockquote>
<p>This possibility seems much more significant today, where we can see that neural nets can do all sorts of tasks, and the question is whether they can do <em>everything</em> the brain can do. Supposing that they can’t, it still seems plausible that brains use a network-like architecture for many tasks in addition to a neurally-implemented Turing machine that handles what networks can’t. The answer to the question of “how do you combine them?” mentioned in Monday’s lecture then might simply be “you don’t.” The network system doesn’t have access to symbols, and the symbolic system doesn’t have access to nodes. There would be, essentially, two largely-independent minds in one brain.</p>
<p>Indeed, a lot of people have the intuition that the brain has two systems, one of which is precise and methodical and the other of which is more fuzzy and intuitive. In pop psych these are called the left and right sides of the brain. We know that these systems aren’t actually divided in the brain this way, but it’s conceivable the mythology reflects some real aspect of how the brain works. In <em>Thinking Fast and Slow</em>, Daniel Kahneman talks in similar terms about how human behavior can be understood in terms of a fast, automatic “system one” and a slow, logical “system two.” The existence of two systems explains why almost everyone has intuitions like “if 2 chickens can lay 2 eggs in 2 days, 4 chickens can lay 4 eggs in 4 days” even though we can pretty easily work out that it’s wrong. The fast, associationist system gives the wrong answer before the slow, methodical system gives the right answer. That would make perfect sense if system one were a massively-parallel neural network and system two was a serial Turing machine (at some level of representation).</p>
<p>This would also seem to explain certain observations about language processing if we suppose that both systems are involved in <em>overlapping</em> but <em>disconnected</em> language-related tasks. For instance, Rumelhart and McClelland gave the example of “The man the boy the girl hit kissed moved” to show that we have trouble understanding the deep recursive structures that the classical model says are so important. However, even though most people would fail to comprehend this sentence on the first pass, we <em>can</em> understand it if we think about it a little longer. Again, this makes sense if we suppose that the “first pass” is carried out by a fast, connectionist-like neural architecture and the second is carried out by a slow neural Turing machine. This would also explain why the different levels of language processing seem to happen in parallel even though we have trouble formulating rules that work on a partial input: first-pass processing is done with a parallel architecture, while the rules are carried out on a serial one.</p>
<p>The presence in the brain of a slow, rule-based system for processing language might also enable the network-based system to do more with less data. The systems could be independent of each other in terms of their representations, but could potentially take as input the output of the other system. This would enable the rule-based system to generate valid sentences which could be fed as input to the network system, allowing it to get really good at quickly performing most, but not all, language-related tasks. (I’m not sure if a two-system model of cognition could explain the data problem on other tasks, though.)</p>
<p>This also seems consistent with biological evolution. A connectionist network can do some useful tasks with just a few neurons, so it’s easy to imagine a mutation creating a proto-brain of just a few neurons that grew over many generations, to the point where it was large enough to implement a Turing machine (in addition to its other functions). By contrast, if the brain were <em>just</em> a Turing machine, it’s hard to imagine how it would evolve from more rudimentary structures, especially without maintaining any residual functions.</p>
<p>If the human brain in particular evolved to implement a Turing machine, that would also explain the leap between what human brains can do and what other animals can do. A chimp’s brain doesn’t seem all <em>that</em> different from a human brain, but no matter how hard we’ve tried, we can’t teach a chimp to use recursive syntactic structure. That would make sense if human brains have, and chimp brains lack, a system that fundamentally works by processing symbols according to recursive constituent relations.</p>
<p>Likewise, my understanding is that humans started to do all the things we associate with our cognitive sophistication over animals—like language, religion, art, technological development—at around the same time on an evolutionary scale. That too would make sense if all of those things are a result of the ability to process symbols, which appeared all at once when the human brain evolved to implement a Turing machine.</p>
<p>Fodor and Pylyshyn claimed that it’s implausible that human cognition is systematic and the cognition of other animals is not. However, I wasn’t convinced by their argument. They state:</p>
<blockquote>
<p>It is not, however, plausible that only the minds of verbal organisms are systematic. Think what it would mean for this to be the case. It would have to be quite usual to find, for example, animals capable of representing the state of affairs a<strong>R</strong>b but incapable of representing the state of affairs b<strong>R</strong>a.</p>
</blockquote>
<p>I don’t find it at all obvious that this isn’t usual among non-human animals, at least if I’m understanding what’s being said correctly. I’ve heard that Galapagos tortoises don’t fear larger animals (like humans) because they don’t have any natural predators. It seems reasonable to suppose, then, that they can’t conceive of something eating a tortoise. However, presumably they can conceive of a tortoise eating something else, seeing as they haven’t all starved by now.</p>
<hr />
<p>I’ve since learned that the idea I proposed here is known as “Dual Process Theory.” A TA referred me to <a href="https://royalsocietypublishing.org/doi/pdf/10.1098/rstb.2012.0211">this paper</a> which makes similar arguments more formally, and with reference to cognition in general more than just language.</p>
Project Euler 151Luna McNultyhttps://lmcnulty.me/words/project-euler-1512019-08-16T00:00:00Z
<p>This article is inspired by the idea of “literate programming,” a technique pioneered by an ancient sorcerer named Donald Knuth. In this paradigm, a program is written as a document that explains the problem being solved. I accomplished this in a fairly naïve way by using a script that walks through the code in-order and generates a markdown file from the comments. Usually, literate programming is done with more sophisticated tools that assemble the code out-of-order so that it can be presented in a manner optimized for human comprehension. I didn’t feel the need to do this, because the program here is short and already pretty coherent as-is.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> Thus, you could copy the code here directly into a python session and have a working result.</p>
<p>The program in question is the solution to a problem from <a href="https://projecteuler.net/">Project Euler</a>, a website that offers over 600 math problems designed to be solved through programming. I thought long and hard before publishing this article, because when you solve a Project Euler problem, you’re given the message:</p>
<blockquote>
<p>We hope that you enjoyed solving this problem. Please do not deprive others of going through the same process by publishing your solution outside of Project Euler</p>
</blockquote>
<p>However, this problem has been out for a long time now, and many others have posted their solutions. Since it is already very easy to cheat, I don’t think that posting one additional solution is likely to deprive any additional people of the chance to solve the problem. And if someone is going to cheat, they might as well do it with a solution that carefully explains the problem in detail.</p>
<p>Nevertheless, if you have not solved the problem yourself, I <strong>strongly</strong> encourage you to try and do it yourself first. Even if you get stuck, I would recommend coming back to it another day before reading my answer.</p>
<p>And just to keep my conscience clear, I have included a mistake in this program. It will give the wrong answer unless you make a small change.</p>
<p>Now, without further ado:</p>
<h2 id="problem-151">Problem 151</h2>
<blockquote>
<p>A printing shop runs 16 batches (jobs) every week and each batch requires a sheet of special colour-proofing paper of size A5.</p>
<p>Every Monday morning, the foreman opens a new envelope, containing a large sheet of the special paper with size A1.</p>
<p>He proceeds to cut it in half, thus getting two sheets of size A2. Then he cuts one of them in half to get two sheets of size A3 and so on until he obtains the A5-size sheet needed for the first batch of the week.</p>
<p>All the unused sheets are placed back in the envelope.</p>
<p>At the beginning of each subsequent batch, he takes from the envelope one sheet of paper at random. If it is of size A5, he uses it. If it is larger, he repeats the ‘cut-in-half’ procedure until he has what he needs and any remaining sheets are always placed back in the envelope.</p>
<p>Excluding the first and last batch of the week, find the expected number of times (during each week) that the foreman finds a single sheet of paper in the envelope.</p>
<p>Give your answer rounded to six decimal places using the format x.xxxxxx.</p>
</blockquote>
<h2 id="solution">Solution</h2>
<p>To solve this problem, we’ll need some way to represent a bag. We could write a <code>Bag</code> class, but that would probably just over-complicate the situation. The bag is really just 5 integers: one for each paper size. Python is already pretty great at working with integers, so we would just be creating more work for ourselves by writing a class.</p>
<p>(Since this is just a math/programming exercise, there’s no need to worry about maintainability, sharability, and such.)</p>
<p>We could instead use an array of integers. For instance, bag[0] would be the count of a1 sheets, bag[1] would be the count of a2 sheets, etc. A bag that contained 1 A2 sheet, 2 A4s and 2 A5s would be written thus:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>array_bag <span class="op">=</span> [<span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">2</span>, <span class="dv">2</span>]</span></code></pre></div>
<p>But there’s actually an even simpler way, which is to just use a single integer. Each digit will contain the number of sheets of paper of the corresponding size. The above bag would be:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a>int_bag <span class="op">=</span> <span class="dv">1022</span></span></code></pre></div>
<pre><code>(0) - 10000's place - A1
1 - 1000's place - A2
0 - 100's place - A3
2 - 10's place - A4
2 - 1's place - A5</code></pre>
<p>We’ll need a few utility functions to work with numbers in this format.</p>
<p>First, we’ll want to be able to convert a paper size to the digit place in which it is counted, as in the table above. To get the place of the nth digit in a number, you just take 10^n. Since the size numbering is “backwards,” we need to take 10^(5 - size)</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="kw">def</span> place(size): <span class="co"># Python uses the ** operator for exponentiation,</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a> <span class="cf">return</span> <span class="dv">10</span><span class="op">**</span>(<span class="dv">5</span> <span class="op">-</span> size) <span class="co"># because ^ is used for bitwise XOR, as in C.</span></span></code></pre></div>
<p>To get the number of sheets of a particular size, we need to strip away the other digits. For instance, <code>sheets_of_size(3, 1211)</code> should return 2.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="kw">def</span> sheets_of_size(n, bag):</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a> r <span class="op">=</span> bag <span class="co"># Start with the number of the bag. eg. 1211</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a> p <span class="op">=</span> place(n) <span class="co"># Get the place of the number 100</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a> r <span class="op">=</span> r <span class="op">/</span> p <span class="co"># Divide the bag number by the place number 12.11</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> r <span class="op">=</span> trunc(r) <span class="co"># Truncate the digits after the decimal point 12</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a> <span class="cf">return</span> r <span class="op">%</span> <span class="dv">10</span> <span class="co"># Remove the preceding digits by taking % 10 2</span></span></code></pre></div>
<p>One of the main advantages of representing bags as integers is that it’s easy to add and subtract sheets: we just need to add an integer. To add an a3, and a4, and an a5, instead of writing:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a>array_bag[<span class="dv">2</span>] <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a>array_bag[<span class="dv">3</span>] <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a>array_bag[<span class="dv">4</span>] <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
<p>we can just write:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a>int_bag <span class="op">+=</span> <span class="dv">111</span></span></code></pre></div>
<p>Indeed, we’ll have to do exactly this to represent the foreman cutting a piece of paper from the bag. Cutting a paper always yields one of each smaller paper size, including A5, since the foreman uses up one of them.</p>
<p>Let’s write a function to do this. It will take a bag and the paper size to be cut, and return the resulting bag.</p>
<pre><code>eg. cut(1, 10000) → 1111
cut(2, 1011) → 122
cut(3, 1210) → 1121
cut(5, 1101) → 1100</code></pre>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="kw">def</span> cut(size, bag):</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a> p <span class="op">=</span> place(size) </span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a> <span class="cf">return</span> <span class="bu">int</span>(</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a> bag <span class="co"># Take the original bag eg. 1210</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a> <span class="op">-</span> p <span class="co"># Remove the sheet - 100</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a> <span class="op">+</span> ((p <span class="op">-</span> <span class="dv">1</span>) <span class="op">/</span> <span class="dv">9</span>) <span class="co"># Add one of each smaller sheet + (99 / 9)</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a> ) </span></code></pre></div>
<p>To solve the problem, we’ll need to test whether a bag is has only one sheet remaining. We could calculate this mathematically fairly easily, but it would be slower and require more code then just listing each bag.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="kw">def</span> only_one(bag):</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a> <span class="cf">return</span> (bag <span class="op">==</span> <span class="dv">1</span> <span class="kw">or</span> bag <span class="op">==</span> <span class="dv">10</span> <span class="kw">or</span> </span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a> bag <span class="op">==</span> <span class="dv">100</span> <span class="kw">or</span> bag <span class="op">==</span> <span class="dv">1000</span> <span class="kw">or</span> bag <span class="op">==</span> <span class="dv">10000</span>)</span></code></pre></div>
<p>We’ll also want to be able to get the total number of sheets in the bag, for reasons that will soon become clear.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="kw">def</span> total_sheets(bag):</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a> <span class="cf">return</span> (</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a> sheets_of_size(<span class="dv">1</span>, bag)</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a> <span class="op">+</span> sheets_of_size(<span class="dv">2</span>, bag)</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a> <span class="op">+</span> sheets_of_size(<span class="dv">3</span>, bag)</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a> <span class="op">+</span> sheets_of_size(<span class="dv">4</span>, bag)</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a> <span class="op">+</span> sheets_of_size(<span class="dv">5</span>, bag)</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a> )</span></code></pre></div>
<p>Now that we’ve laid the groundwork, let’s get into exactly how we’ll solve the problem. The obvious solution is to check every possible outcome of each batch, count the number where the bag has only one sheet in it, and find the average.</p>
<p>This is the right path, but we also have to remember that not every possibility is equally likely. For instance, if the foreman starts the batch with the bag 0120 (1 A3, 2 A2s), he’s twice as likely to cut an A2 as an A3. Consequently, we need to get every possibility paired with its chance of occurring. However, the chance of getting a bag depends on the chance of getting the bag that came before it. Consequently, we need to descend a tree of possibilities. Four batches down, it will look like this:</p>
<pre><code>10000 (100%)
1111 (100%)
222 (25%)
133 (8.33%)
213 (8.33%)
221 (8.33%)
1022 (25%)
233 (5%)
1013 (10%)
1021 (10%)
1101 (25%)
212 (8.33%)
1012 (8.33%)
1100 (8.33%)
1110 (25%)
221 (8.33%)
1021 (8.33%)
1101 (8.33%)</code></pre>
<p>At each batch, there are more and more possibilities, and the probability of one of them occurring falls each time. On each level, the probabilities add up to 100%, because some bag is guaranteed to result from each batch the foreman runs.</p>
<p>To get all the probabilities, we’ll need write a function that will take a bag and its probability of occurring, and return a list of the possible bags after the foreman runs the batch, and their probabilities of occurring.</p>
<pre><code>eg. possibilities([1022, .25]) → [[ 233, .05],
[1013, .1 ],
[1021, .1 ]]</code></pre>
<p>We’ll use proportions instead of percents to represent probabilities. A proportion is the same as a percent chance divided by 100. Basically, instead of “per 100” it’s “per 1.” If this is confusing, one way to think of it is that if something happens one half of the time, it happens .5 of the time.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="kw">def</span> possibilities(poss):</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a> bag <span class="op">=</span> poss[<span class="dv">0</span>] <span class="co"># We're using an array instead of two arguments so that</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a> prob <span class="op">=</span> poss[<span class="dv">1</span>] <span class="co"># we can pass items in the list returned by this function</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a> <span class="co"># as arguments to the function.</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a> </span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a> p <span class="op">=</span> [] <span class="co"># The possibilities will be stored in a list.</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a> <span class="cf">for</span> size <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">1</span>, <span class="dv">6</span>): <span class="co"># For each paper size</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a> num_of_size <span class="op">=</span> sheets_of_size(size, bag)</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a> <span class="cf">if</span> num_of_size <span class="op">></span> <span class="dv">0</span>:</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true"></a> p.append([</span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a> <span class="co"># The `cut` function that we wrote earlier returns the</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true"></a> <span class="co"># resulting bag if we cut a sheet of `size`.</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true"></a> cut(size, bag),</span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true"></a> </span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true"></a> <span class="co"># Multiplying the probabilities of two events gives the</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true"></a> <span class="co"># probability that both will occur. Since a result can only</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true"></a> <span class="co"># occur if the result that produced it also occurred, we need </span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true"></a> <span class="co"># to multiply the probability of both events together.</span></span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true"></a> <span class="co"># its probability </span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true"></a> (num_of_size <span class="op">/</span> total_sheets(bag)) <span class="op">*</span> prob</span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true"></a> ])</span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true"></a></span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true"></a> <span class="cf">return</span> p</span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true"></a></span></code></pre></div>
<p>Now, using the above function, we need to recursively traverse the probability tree and add up the probabilities of the cases where there is only one sheet in the bag.</p>
<p>We’ll use a function that will take two parameters. One, <code>poss</code> will be a possibility, in the same format as above: [bag, probability]. The next, <code>n</code>, will be the number of recursions that should be used (or the depth that the possibility tree should be descended.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="kw">def</span> expected(poss, n):</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a> bag <span class="op">=</span> poss[<span class="dv">0</span>]</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a> prob <span class="op">=</span> poss[<span class="dv">1</span>]</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a> <span class="co"># If this result contains only one sheet, augment the expected value </span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a> <span class="co"># by the probability of getting this result.</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a> p_sum <span class="op">=</span> <span class="bu">int</span>(only_one(bag)) <span class="op">*</span> prob</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a> <span class="co"># If we've reached the maximum number of recursions, don't explore</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a> <span class="co"># the possibilities which result from this one.</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a> <span class="cf">if</span> n <span class="op">==</span> <span class="dv">0</span>: <span class="cf">return</span> p_sum</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a> </span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a> <span class="co"># For each resulting possibility, augment the expected value by </span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a> <span class="co"># the output of this function applies to it, reducing `n` to </span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a> <span class="co"># prevent infinite recursion.</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a> <span class="cf">for</span> e <span class="kw">in</span> possibilities(poss):</span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a> p_sum <span class="op">+=</span> expected(e, n <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true"></a></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true"></a> <span class="cf">return</span> p_sum</span></code></pre></div>
<p>Now we’ve done everything we need to solve the problem! We just need to be sure to pass the right parameters to the functions we wrote.</p>
<p>Because the foreman starts every week with an A5 sheet (10000), the probability is 100% (1).</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a>start <span class="op">=</span> [<span class="dv">10000</span>, <span class="dv">1</span>]</span></code></pre></div>
<p>The answer has to exclude the first and last of the 16 batches, so we will go 14 iterations deep rather than 15.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a>solution <span class="op">=</span> expected(start, <span class="dv">14</span>)</span></code></pre></div>
<p>And finally, we output our answer rounded to 6 decimal places!</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="bu">print</span>(<span class="bu">round</span>(solution, <span class="dv">6</span>))</span></code></pre></div>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Pedants will point out that this technique <a href="https://en.wikipedia.org/wiki/Literate_programming#Contrast_with_documentation_generation">not technically literate programming</a>. To them, I’ll point out that I specifically used the words “inspired by the idea of” rather than “is.”<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
Dreaming with GPT-2Luna McNultyhttps://lmcnulty.me/words/dreaming-with-gpt-22019-11-08T00:00:00Z
<p>This story is an <a href="https://en.wikipedia.org/wiki/Exquisite_corpse">exquisite corpse</a> written in collaboration with OpenAI’s GPT-2 language model, accessed using Fabrice Bellard’s <a href="http://textsynth.org/">textsynth.org</a>.</p>
<hr />
<p>I had a dream last night. In the dream I saw a man come down a long hallway and stand at the very end. The man was my father. The man was my father. He stood there for a long time, not talking to anyone. The hallway opened into an auditorium with a crowd of dozens, maybe even hundreds, of people, but he just stood there and stared. I could make out a few words in his mouth. He was silently breathing out an expression I heard him say many times growing up.</p>
<p>“I will never be forgotten.”</p>
<p>His lips kept moving to these words, again and again. I remember how I felt in my dreams. Somehow, I knew that he was feeling the same way: that the moment he saw me, he would be gone. So he just stared into the crowd, his affirmation of “I will never be forgotten” becoming more and more rapid. The crowd became a haze of colors, some more saturated and clear than others. A moment later, my father turned into a pillar of pure-black void. I could make out only his lips moving to his new message. As the rest of his body dissolved, his lips announced</p>
<p>“I have been forgotten.”</p>
<p>My father was gone. I was still standing at the other end of the hallway, which now lead straight into the void, with the colored haze illuminating the perimeter. A moment later, the void became a vast, black sky. The hallway was gone, and I was on the roof of my childhood home. As I stood there, the black sky turned into a bright, glowing, purple sunset. The sunset gave way to a blue afternoon sky, which in turn became a golden dawn. I looked at all the colors in the sky: blue, orange, yellow, black, gold. Somehow, I saw my father in all of them. I felt his smile, his laugh, his smile with his laugh. I remembered how he took me onto the roof, making me promise not to tell my mother. I remembered how, for several weeks, I had been crying with tears of joy on my face. My mother was concerned, and kept asking me to explain what was going on, but I just said “I know” and turned away from her.</p>
<p>Then, the roof disappeared, along with the sky. I saw a rainbow, or rather, a multitude of them coming together and vanishing in a flash. I was temporarily blinded, but as my eyes adjusted to the brightness, I realized that I was in some kind of laboratory, with bright fluorescent lights everywhere. There was nobody else there, just the fluorescent lights, my father standing still, looking straight ahead, and the various rainbow colors flying all around. The rainbow colors were strange and otherworldly, but my eyes were fixed on the back of my father’s head.</p>
<p>“I’m coming with you” he said. He slowly turned to face me. He was smiling. I rushed towards him with my arms open, and he received me with a tight embrace. He was now a giant. And I: a giantess. The ground was now a vast desert littered with sandstone and limestone. But we crossed the desert in a few gigantic strides, and jumped over a mountain range, and finally reached the ocean. I had never seen my father smile so much as he did that day. We watched the light glisten on the water as the sun approached the horizon.</p>
<p>“It’s a long trip, but it’s worth it.” I said.</p>
<p>“Yes, I’m glad I was able to come with you this far,” he said. Then he turned to face me. “But I cannot return with you.”</p>
<p>“What? Are you crazy?” I exclaimed.</p>
<p>“I’m sorry, but it is the way of this world,” he said. “I cannot go back.”</p>
<p>His took one step into the water. There was no wind. There was no sound but the waves. And there I was. Watching my dad wade into the ocean.</p>
<p>“It’s not like this when we are together,” I said.</p>
<p>“No,” he said, “this is how it is now, and how it has always been.” My heart leapt, and my stomach knotted. I ran towards my father, but he pushed me away. “I must go now,” he said.</p>
<p>I was standing up to my ankles in water, but only my cheeks felt wet. I stared at his back, and my gaze landed directly on the back of my father.</p>
<p>“You will always be remembered!” I shouted. “You will always be loved.”</p>
<p>Rainbow lights swirled about the periphery of my vision as I watched my father proceed deeper into the ocean, then begin to swim. My stomach tightened. He became a point on the horizon, and finally, he was gone. I could only watch as he disappeared.</p>
No One is “Non-Technical”Luna McNultyhttps://lmcnulty.me/words/non-technical2022-01-15T00:00:00Z
<p>As I’ve been searching for jobs, I’ve noticed that many postings list as a requirement the ability to communicate with “non-technical” people. While it’s important for those with certain technical expertise to be able to communicate their ideas to those without it, this goal is not helped by characterizing some people as “non-technical.”</p>
<p>First off, many supposedly “non-technical” people in fact have advanced technical skills – just ones that don’t concern computers. Someone who can repair cars is still very technically capable even if they don’t know how to send an email. There’s nothing special about computers that makes people who understand them uniquely capable, important, or “technical”.</p>
<p>Even if we take “technical” as referring narrowly to computer skills, almost everyone in a modern workplace is technical at some level. If you know what a file is, that’s a technical skill – one that <a href="https://www.theverge.com/22684730/students-file-folder-directory-structure-education-gen-z">many people lack</a>. To be sure, some people have more computer skills than others – but it’s a difference of degree, not of kind. When you’re trying to communicate technical ideas, not everyone who you might consider “non-technical” is at the same level. An octogenarian just learning to send a text message is different from an Art History PhD who recently got into Digital Humanities is different from that kid in <a href="https://youtube.com/watch?v=3S5BLs51yDQ">the “what’s a computer” commercial</a>. Communicating with these three “non-technical” people is likely to require different strategies.</p>
<p>Furthermore, calling people “non-technical” underrates their ability to learn. We’re all at a certain level of technical proficiency, and we all have the potential to reach a higher one. Some people are discouraged from doing so because they think of that they’re not “technical” – that they’re not “computer people.” But in fact, there are no “computer people.” There are only regular people who accumulated bits of computer knowledge little by little – sometimes from being taught in school, but more often from facing their own computer problems with the confidence and will to learn. Far too many people lack that confidence <em>because</em> they learn to think of themselves as “non-technical.” It’s true that not everyone has time or energy to devote specifically to learning about computers – and they shouldn’t have to. However, anyone who uses computers at all will at times encounter organic opportunities to learn: we should avoid encouraging people to squander these in a fit of learned helplessness.</p>
<p>When we explain a technical concept to someone who lacks some of the requisite background, we should do so in a way that facilitates their own technical growth. This requires us to recognize that everyone is a technical person – some of us are just at different places in our journey than others.</p>
Advice for College Students (Part 1)Luna McNultyhttps://lmcnulty.me/words/college-advice-12020-01-31T00:00:00Z
<style>
.pull-away {
font-size: 120%;
text-align: center;
float: right;
width: 20ch;
display: block;
position: relative;
padding-left: 3ch;
padding-right: 3ch;
margin: 5px;
margin-bottom: 0px;
background: none;
border: none;
}
.pull-away:before, .pull-away:after {
font-size: 300%;
line-height: .2em;
vertical-align: bottom;
position: absolute;
bottom: 0px;
line-height: 100%;
}
.pull-away:after {
content: "”";
right: 0px;
}
.pull-away:before {
content: "“";
left: 0px;
}
</style>
<p>Last semester, I spent time with some first-years who, upon learning that I was a senior, asked me if I had any accumulated “wisdom” to share. At first I found this funny, but I’ve nevertheless reflected on the question and decided to record my thoughts.</p>
<blockquote class="pull-away">
college courses are not croissants
</blockquote>
<p>The items are arranged roughly in order of importance. Thus, this article has more high-level, philosophical sort of advice, some of which might seem obvious. You might want to jump right to <a href="../college-advice-2">part 2</a> if you’re interested in more specifics.</p>
<h2 id="prioritize-learning">Prioritize Learning Before Grades</h2>
<p>Unless you study <a href="../college-advice-2/#study-abroad-every-semester">outside the U.S.</a>, college is an enormous expense. It might not be worth it if all you’re getting out of it is a diploma. Yes, there are some schools and fields where degrees carry a lot of economic value. However, if you’re studying something like literature at a mid-tier university or if you want to go into a field where a degree isn’t required, you should primarily be in college because you love learning and want to grow as a person. If you succeed in this, college will be worth it.</p>
<p>I experienced High School mostly as an ordeal I had to get through so that I could go to a good college. Yes, a few classes, particularly in Art and English, impacted me in a deeper way, but many contributed to my personal and intellectual development about as much as the SATs. I checked my grades at least once a day, and on a few occasions I convinced my parents to let me stay home from school to have more time to study for tests. This approach is the exact opposite of what you should do in college. Unless you’re planning to go to Law/Med School, college is not a means to end. It is a rare opportunity in life to spend several years working with the end-goal of improving yourself: take advantage of it. The letters on your transcript are of secondary importance.</p>
<p>Understand that the optimal strategies for maximizing grades are not necessarily the most effective for learning. For instance, when I was a TA in 2D Game Engines, a lot of students would turn in projects that were not very polished or engaging as games<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> but which technically met all the requirements for an A. They were acting completely rationally from the perspective of someone trying to optimize grades: any extra time spent on sprite animation or level generation could have been used studying for midterms. However, the students who “irrationally” chose to put in the extra work came out of the class knowing how to make great games that they could proudly include in a portfolio.</p>
<p>Some people will try to take easy classes because they’re protective of their GPA. My school tries to deter this by giving students the option to take any class pass/fail, which I think is a great idea. But even if your school makes you take every class for a grade, you should still not take classes just because they will be easy. This is not only a bad way to learn, but it might not even work: sometimes the hard classes aren’t what you would expect. For instance, I took Moral Philosophy because the subject interested me, but I didn’t imagine that it would be very hard. However, I didn’t do as well as I expected, partly because I still had a little of <a href="http://existentialcomics.com/comic/191">this guy</a> in me.</p>
<p>If you take classes below your level and forget everything after tests, you might be able to leave college with a 4.0 GPA while having learned very little, but you would only be <a href="https://www.smbc-comics.com/comic/2011-05-15">cheating yourself</a>.</p>
<h2 id="go-to-class">Go to class</h2>
<p>This should be obvious, but it’s important enough to be worth emphasizing.</p>
<p>Yes, there might be a few courses that you can pass without attending lectures. Many of my classmates in Software Engineering skipped class because the content wasn’t needed to complete assignments, which constituted almost the entire course grade. However, the students who attended got more out of the class.</p>
<p>Besides the direct value of course content, the routine of going to class is incredibly valuable: it motivates you to do the work you need, and it gives you feedback on how well you’re understanding the material. If you skip classes, you will end up sleeping during the day and just generally being dysfunctional.</p>
<p>Some classes might record lectures. Do not use this as an excuse to skip class: anytime you tell yourself that you’ll watch the lecture capture, there’s a high chance that you won’t.</p>
<h2 id="read-and-annotate-the-assigned-texts">Read <em>and annotate</em> the assigned texts</h2>
<p>Readings are the main homework in humanities classes. Skipping a reading is like deciding not to hand in a problem-set or a coding project. Also, understand that “reading” a text is not the same as “looking at it.” Even if your eyes passed over some words, your brain may not have learned anything. This will become clear on the occasions when you have to read something like this:</p>
<blockquote>
<p>When we come to the concomitant question of the consciousness of the subaltern, the notion of what the work cannot say becomes important. In the semioses of the social text, elaborations of insurgency stand in the place of ‘the utterance’. The sender – ‘the peasant’ – is marked only as a pointer to an irretrievable consciousness. As for the receiver, we must ask who is ‘the real receiver’ of an ‘insurgency’? The historian, transforming ‘insurgency’ into ‘text for knowledge’, is only one ‘receiver’ of any collectively intended social act. <a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
</blockquote>
<p>The best way to ensure that you’re actually reading is to highlight and annotate the text. I resisted doing this for far too long, because I didn’t like defacing books. However, many or most of the time your readings will not be given in the format of print books. More often I as given low-quality PDF scans from books. These are not well-suited to on-screen reading, So you should either print them out or annotate them on a tablet. I recommend <a href="http://xournal.sourceforge.net/">Xournal</a> if you’re on GNU/Linux. If you don’t interact with the text, especially when it’s on a screen, the temptation to skim is too strong.</p>
<h2 id="sit-in-the-front-row">Sit in the front row</h2>
<p>If you’re too intimidated to sit in the front row, sit in the second row. Professors will remember and judge you better if you sit closer to them. I had one class where I sat in the seat directly in front of the professor, and as a consequence, he would individually greet me when I entered the classroom of 40+ people.</p>
<p>Besides making a good impression, sitting in the front will make it easier for you to ask questions, both because you’ll have an easier time making yourself heard, and you’ll start to find the professor less intimidating.</p>
<p>In the front, you’ll also be less tempted to look at your phone or stare into space. You will also have an extra incentive to <a href="#go-to-class-and-do-the-readings">go to class</a>, because your absence will be noticed.</p>
<h2 id="speak-in-class">Speak in class</h2>
<p>Speaking in class can be intimidating. Nevertheless, learning to speak elegantly in front of a group of smart people is an important skill, and college is one of the best places to learn it. This is one of the reasons why you should do the readings: you’ll won’t feel confident speaking in class if you’re behind.</p>
<p>If you identify a class where you have trouble contributing, be sure to take advantage of times when the class shifts to a topic you know more about. For instance, I had a lot of trouble following the math-heavy lectures in my Computer Graphics class, so I made sure to answer the professor’s questions when we talked about art history.</p>
<h2 id="put-away-the-laptop">Put away the laptop</h2>
<p>I like computers. I spend most of my time in front of one. I’m even part of the <a href="https://www.pewresearch.org/fact-tank/2019/09/25/one-in-five-americans-now-listen-to-audiobooks/">small minority</a> that reads ebooks more than paper books. Nevertheless, I recommend putting away your laptop in class.</p>
<p>Some professors will say that they don’t mind laptops, but virtually none <em>prefer</em> that you use one. Taking notes by hand will give a better impression, if for no other reason than that it’s certain that you’re not using Facebook in class. (If you do use Facebook in class, spend some time thinking about <a href="#prioritize-learning">why</a> you’re in that class—or in college generally.) If you take notes by hand, you’ll be less distracted and you’ll feel closer to your professor due to not having a barrier in front of you.</p>
<p>A lot of people like to use laptops because they think they can take more detailed notes. This is probably less important than you think. The main benefit of note-taking is that it helps you pay attention and remember what you learned: you’re more likely to remember something that you wrote down by hand, even if you never review your notes. There’s probably something better to review than your own notes anyway: a textbook, lecture slides, notes you get from a classmate, etc.</p>
<p>Even if you’re the type of person who gets a lot out of typed-up notes and doesn’t get distracted by a laptop, screens have a captivating effect that can distract other people in class. <a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a></p>
<p>And one last minor note: your college notes can be an opportunity to improve your handwriting. I started college with awful handwriting that I hated: now I regularly get compliments on it.</p>
<h2 id="continue-working-on-fun-stuff">Continue working on fun stuff</h2>
<p>When you’re in college, you’ll almost always have schoolwork that you “should” be doing. (If you don’t, you should probably be taking more advanced classes.) This can sometimes make you feel as if you can’t justify putting effort into things besides schoolwork. After all, how can you read a book for pleasure when you have three other books to read for your classes? How can you spend time learning to make a website when you have a project due this week?</p>
<p>Resist this type of thinking. As much as it might feel like it, you almost certainly don’t spend every free minute on your schoolwork, and any time you “save” by not reading a novel is more likely to be wasted scrolling through internet feeds.</p>
<p>Formal education is kind of a weird way to learn. It’s super useful, but the way that people naturally learn is by exploring, playing around, and trying new things. You should continue doing this even as you’re in a formal learning environment: you will learn twice as much. I learned JavaScript while I “should” have been working on Syntax homework, I learned LaTeX while I “should” have been writing papers, and I wrote about half of my blog posts while I was procrastinating on another task.</p>
<p>Incidentally, this also goes for social relationships. When I’ve turned down invitations because I “have work to do,” I’ve almost never accomplished whatever it is I wanted to do in the time I gained. In fact, I’ve probably lost more time wallowing in loneliness than I’ve ever saved from not being around other people.</p>
<p>In fact, even if you really did have to make academic sacrifices for your social life, that’s usually going to be worth it. No one on their deathbed wishes that they had spent more time studying and less time with friends and family.</p>
<p>I realize that there are some people who need to hear the opposite advice: the type who see college primarily as a way to meet attractive people their own age. However, I somehow doubt that many of those people read blogs hosted on <code>gitlab.io</code>.</p>
<h2 id="take-artisan-classes">Take “artisan” classes</h2>
<p>Artisan classes are those that are designed and taught by a single professor. These tend to be more niche subjects in which the professor is a passionate expert. It’s a very good sign if the course contains information that the professor personally discovered, invented, or theorized: there’s likely no better way you could learn that specific material. Sometimes, it will be slanted towards the professor’s particular views on a subject. For instance, my syntax professor spent a lot of time on <a href="https://en.wikipedia.org/wiki/Categorial_grammar">Categorial Grammar</a>, which is not the most mainstream theory. However, it’s better to get great coverage of one viewpoint than poor coverage of many.</p>
<p>Avoid “mass-produced” classes. These are ones that have many sections, are offered every semester, and have large class sizes. They are less likely to have you do meaningful work, as the grading burden will be huge. They are often taught by adjuncts or grad students who would rather be doing something else. The less your teacher cares, the less value you get from taking a class rather than just reading about the subject on your own.</p>
<p><small>(To be clear: Grad students can be great teachers — my first two French classes were both taught by grad students, and they were both excellent. But those were small, highly intimidate classes, not the type I’m talking about here.)</small></p>
<p>You might need to take this sort of class as a requirement for your major or a pre-requisite for other courses you’d like to take: that’s why so many people need to take them. I recommend that you see if you can have the requirement waived or replaced by a different class. You might be surprised, and there’s no harm in asking.</p>
<p>A “real” croissant rolled by hand will cost more than one molded from frozen dough in a factory. But college courses are not croissants: you generally pay the same for a lovingly-crafted seminar as for a detached, uninspired lecture.</p>
<p><a href="../college-advice-2">Continue to Part 2</a></p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>To be clear, a lackluster project didn’t necessarily mean that a student wasn’t learning. Some students had less experience programming and had to put in a lot of effort to meet the minimum requirements: they also learned a lot, just different things from the more experienced programmers.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>Gayatri Chakravorty Spivak, <em>Can the Subaltern Speak</em><a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>Faria Sana, Tina Weston, and Nicholas J. Cepeda, “<a href="https://www.sciencedirect.com/science/article/pii/S0360131512002254">Laptop multitasking hinders classroom learning for both users and nearby peers</a>,” <em>Computers & Education</em> 62 (2013) p. 24-31<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
A Better Solution for Married SurnamesLuna McNultyhttps://lmcnulty.me/words/surnames2020-06-19T00:00:00Z
<p>In my country, tradition dictates that when a couple gets married, the woman takes her husband’s last name, which is also passed on to any children they might have. This has two benefits:</p>
<ol type="1">
<li>You can usually refer to a household by name, as in: “let’s invite the Tooks and the Baggins’ for dinner.”</li>
<li>You can sort of use it to trace your patrilineal ancestry.</li>
</ol>
<p>However, it has a bunch of problems:</p>
<ol type="1">
<li>It’s associated with the now widely-rejected idea that the man is supposed to be the head of the household.</li>
<li>Not every marriage <em>has</em> a man and a woman anymore.</li>
<li>Changing your last name may be detrimental in certain careers.</li>
<li>Over time, it <a href="https://en.wikipedia.org/wiki/Galton%E2%80%93Watson_process">results in fewer and fewer last names</a>, because a surname goes extinct if there are no male children to carry it. This has had a particularly strong impact on China, where people have had names like “Li” or “Zhang” since at least 2,500 years ago, which was before the West even <em>had</em> surnames, let alone settling whether they would be Latin or Greek. Thus, Chinese names have had more time to go extinct, which is why I ended up calling both of my Chinese teachers in school Li lăoshi<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</li>
</ol>
<p>Some people have taken to hyphenating their last name with that of their spouse. This is probably more egalitarian and mitigates the career damage that comes with a name-change. However, it has the serious issue that it gets unreasonable over multiple generations. For instance, if Alice Abrams and Bob Bell get married and name their daughter “Carol Abrams-Bell,” that works fine. But what happens when Carol marries Frank Delaney-Edwards? They could proceed recursively and adopt the name Abrams-Bell-Delaney-Edwards, but that’s monstrous. Alternately they could follow the Iberian tradition and use the first name from each pair, yielding Abrams-Delaney. But that has the same problem as the traditional system: half the names get lost.</p>
<p>But this problem is inevitable, isn’t it? Everyone has 4 grandparents, 8 great-grandparents, and 2<sup>n+2</sup> great<sub>1</sub>-great<sub>2</sub>…great<sub>n</sub>-grandparents. You can’t share a name with all of them. So at some point one parent’s name has to predominate, right?</p>
<p>Wrong. There’s a way for everyone to share a name with both of their parents, their children, and their spouse. Just follow these rules:</p>
<ol type="1">
<li><p>When a couple gets married, they <strong>invent a new name</strong>.</p>
<div class="example">
<p>Romeo Montague and Juliet Capulet get married. When he’s giving the vows, Friar Lawrence asks them “What will be the name of this union?” Together they answer “Starcross.”</p>
</div></li>
<li><p>This name gets added to their current last names with a hyphen.</p>
<div class="example">
<p>“Romeo Montague-Starcross” and “Juliet Capulet-Starcross”</p>
</div></li>
<li><p>When a couple has children, they inherit <strong>only the new name</strong>.</p>
<div class="example">
<p>This is an alternate reality where Romeo and Juliet act much more sensibly, so they survive and have a child, whom they decide to name after the Friar who married them. The child is thus named “Lawrence Starcross”</p>
</div></li>
</ol>
<p>This means you won’t share a name with your grandparents, but you already don’t share a name with half of your grandparents, so surnames were never a very meaningful reflection of your family beyond one generation anyway. Furthermore, since each child’s name carries a “reference” to their parent, you get a “linked list” tracing your ancestry matrilineally <em>and</em> patrilineally.</p>
<figure>
<img src="tree.png">
<figcaption>
Here, suppose that Alice and Bob decided to make their household name a combination of their original names (another good solution, with which this is “backwards-compatible”). However, Frank was so turned off by this that he decided not to marry Carol, who was then so upset that she teleported through time and space to 16th-century Verona and married Lawrence Starcross. Realizing that they both had incredibly un-Italian names, they then decided that their household name would reflect their home, and their child would be named “Antonio.”
</figcaption>
</figure>
<p>This solution combines the benefits of both the traditional and hyphenated systems. Like the traditional system:</p>
<ul>
<li>It preserves household names (even distinguishing between the households of male siblings!)</li>
<li>It allows tracing ancestry, in a less direct but more complete way.</li>
</ul>
<p>Like the hyphenated system:</p>
<ul>
<li>It’s egalitarian.</li>
<li>It keeps a reasonable number of different names in usage.</li>
<li>It doesn’t require anyone to abandon their own family name.</li>
<li>It signals that you’re the type of person to break with tradition. (Which may or may not be desirable)</li>
</ul>
<p>Finally, it has several unique benefits:</p>
<ul>
<li>It gives people more choice over their own name</li>
<li>It’s a gender-neutral way of indicating marriage status. Eg. Miss vs. Mrs. indicates whether a woman is married, but there’s no equivalent for men. If this system were widely-adopted, a hyphen would mean married, and no hyphen would mean unmarried.</li>
</ul>
<p>Assuming that you’re willing to accept that you won’t share a name with one of your 32 great-great-great-great-grandfathers, I can think of three disadvantages:</p>
<ol type="1">
<li><p>Hyphenated names are slightly more cumbersome and could be mistaken for a middle name plus a last name when pronounced.</p></li>
<li><p>You’d end up with “fad” family names. For instance, you can imagine that many couples who married in the year 2000 might have decided to choose the name “Millennium.” This isn’t necessarily bad, but I don’t personally like the sound of it, and I hope that people would try to choose unique names.</p></li>
<li><p>Both partners would “have to” go through the bureaucracy of adding to their name. This would preferably be mitigated by reducing the amount of bureaucracy required, but I wouldn’t hold my breath on that.</p></li>
</ol>
<p>I am not married and do not intend to marry anytime soon. However, if I ever do, I hope to convince my partner to go along with this idea. In the unlikely event that this article convinces you and you go along with it, please let me know! My preferred email address can always be found on <a href="https://lmcnulty.gitlab.io/resume/">my résumé</a>.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>In Chinese, you address your teacher by their family name + lăoshī (老师), which means “teacher”.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
Advice for College Students (Part 2)Luna McNultyhttps://lmcnulty.me/words/college-advice-22020-01-31T00:00:00Z
<p>The <a href="../college-advice-1">last part</a> focused more on the general, philosophical advice I had: I feel that most of it was more important, if not obvious. This article has some more diverse, shorter, and specific suggestions, some of which I’m less confident about.</p>
<h2 id="take-project-based-classes-but-not-too-many">Take project-based classes, but not too many</h2>
<p>Classes can generally be divided between those that grade on projects or papers and those that grade on homework and tests. This has a lot to do with subject matter, but all things being equal, I think projects are the better way to learn. You’ll learn more from a class that has you implement malloc than one that asks some test questions about how it’s used. Likewise, I have firm memories of every paper I’ve written in college, whereas I barely remember any of the tests I’ve taken.</p>
<p>That said, projects take a lot of time, and you have to be careful not to take too many project-heavy classes at once. I once tried to take an intensive programming class, a studio art class, and a creative writing class at the same time. This did not go well.</p>
<p>Ironically, High School trained me to be very good at taking tests, which got me into college. Once I was there, I took very few of them. I might have gotten better grades if I were just being evaluated on tests, but <a href="../college-advice-1/#prioritize-learning">that’s beside the point</a>.</p>
<h2 id="take-intro-linguistics">Take Intro Linguistics</h2>
<p>For some reason, most people know very little about linguistics, which is strange since it’s relevant in so many contexts. Eg. learning IPA lets you read pronunciations in dictionaries. I think linguistics helped me understand <a href="../french-vowels">French pronunciation</a> better than any of my actual French classes. Not all schools offer linguistics, but if yours does, I would recommend that you take at least the intro course, regardless of what field you’re in.</p>
<h2 id="audit-some-classes">Audit some classes</h2>
<p>This is highly dependent on your school’s policies, but mine allowed students to take almost any class as an audit—meaning you would be registered for the class and have access to all the course materials, but you would not receive a grade. This meant there were no drawbacks to taking a fifth class as an audit every semester. If you missed assignments or class time, it’s wouldn’t be a big deal, because you weren’t going to get credit anyway.</p>
<h2 id="declare-all-potential-majors">Declare all potential majors</h2>
<p>This might depend on your school’s policies, but at my school, there was no real drawback to having multiple declared majors: if you didn’t meet the requirements, you just wouldn’t get the double major.</p>
<p>As it happens I’ve almost taken enough classes to get a degree in Francophone Studies. If I had declared a French major before my senior year, I might have been able to finish the degree if I had wanted to. However, per school policies, it’s too late for me to add a second major.</p>
<p>A related note: don’t let your major become your identity. Once I declared myself as a Computer Science major, I started subconsciously coercing myself into what I imagined a CS major should be like. It’s better to be authentic and think of yourself a student who happens to be studying <code>$MAJOR</code>, rather than a <code>$MAJOR</code> student. Hyper-specialization is for grad students and <a href="#study-abroad-every-semester">Europeans</a>.</p>
<h2 id="dont-stack-classes-unless-you-need-to">Don’t stack classes unless you need to</h2>
<p>Some people try to take classes only on Tuesday and Thursday. This is a good idea if you work on the other days of the week, but it’s a bad idea otherwise. You will likely develop a bad sleep schedule and be unproductive on your days off. You also probably won’t get as much out of your classes if you don’t have any time to rest between them.</p>
<h2 id="do-schoolwork-in-dedicated-locations">Do schoolwork in dedicated locations</h2>
<p>Our brains are very good at separating behaviors into different contexts. If the place where you typically study is different from the place where you sleep or goof off, then you’ll be less tempted to do those things when you’re trying to study.</p>
<p>University libraries are amazing places. Being in one helps you focus, but it also makes you feel smart and productive, as you’ll be surrounded by books and other people studying.</p>
<h2 id="only-do-extra-curriculars-that-you-really-like">Only do extra-curriculars that you really like</h2>
<p>This is a change from High School, where you try to do as much as possible, since it all ends up going on your college application. Grad schools and employers don’t care nearly as much about what you do outside your academic field. So there’s no need to try and cram as many activities as possible intro your schedule: do the things you enjoy most, and leave it at that.</p>
<h2 id="think-about-letters-of-recommendation">Think about letters of recommendation</h2>
<p>If you’re thinking of going to grad school, letters of recommendation are extremely important: more important than your grades. You should plan in advance to make strong connections with professors that you can later ask for letters of recommendation. I’m far from an expert on this, but Matt Might has <a href="http://matt.might.net/articles/how-to-apply-and-get-in-to-graduate-school-in-science-mathematics-engineering-or-computer-science/">a good article</a> on it.</p>
<h2 id="use-your-schools-mental-health-resources">Use your school’s mental-health resources</h2>
<p>Many schools offer counseling or psychological services, paid for partially or entirely by your tuition. Take advantage of this: I spent several years thinking I didn’t need to see a therapist, but when I started seeing one it helped a lot.</p>
<h2 id="study-abroad-one-semester">Study abroad one semester</h2>
<p>I spent a semester studying Art History in Paris. I probably would have learned more Art History if I had taken those classes somewhere English-speaking, but I learned <em>so much more</em> about everything else: the benefit is too profound to be effectively stated here.</p>
<p>Don’t assume that you can’t afford to study abroad. I actually spent less money on living expenses in Paris (of all places) than on room and board in a semester at my home university. This won’t necessarily be the case for you, but you should at least look at the numbers before you write off the option.</p>
<h2 id="study-abroad-every-semester">Study abroad <em>every</em> semester</h2>
<p>If you haven’t decided where you want to go to school, consider doing your entire degree in another country. Why? Well, if you live in the United States, then it will be cheaper for you to study in almost any other country in the world. Although it’s by no means necessary, this suggestion applies double if you have EU citizenship, or if you can obtain it.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>To be crystal clear: I did not do this. I’m putting this suggestion here because it never occurred to me that I <em>could</em> have done it until I spent a semester in Europe, and the economics of doing a degree abroad seems at least worth considering.</p>
<p>In continental Europe, you can often attend a public university for almost no tuition. It helps a lot if you speak a foreign language, since undergraduate programs are especially likely to be taught in a local language, but English-language programs aren’t completely unheard of.</p>
<p>While I was doing my study-abroad in France, people were protesting because the government had raised fees for non-EU students. The change was from ~300€ / year to ~3000€ / year. That’s a big increase, but it’s still less than one third of in-state tuition at your average American public university and an order of magnitude less than a private university. Nevertheless, people were so outraged by the change that <a href="https://studely.com/wp-content/uploads/2019/03/Liste-des-universit%C3%A9s-qui-nappliqueront-pas-la-hausse-des-frais-.pdf">many French universities</a> simply refused to implement the higher prices: that’s how vast the difference is between Europe and the U.S.</p>
<p>If you can’t imagine doing your degree in a foreign language, there’s still a pretty good chance that you can save money going to school in <a href="https://www.businessinsider.com/college-tuition-costs-usa-canada-england-2017-3">Canada or England</a>.</p>
<p>Note that the purpose and form of undergraduate education is different in other countries, and <a href="#take-intro-linguistics">some</a> of the suggestions here <a href="#use-your-schools-mental-health-resources">won’t apply</a>. In Europe, undergraduate education is much more focused and specialized. Universities there are more strictly focused on academics rather than student life. European students generally don’t take courses outside their field of study, which they select when they apply to university. This is partially because a bachelor’s degree in Europe lasts just three years, and also because there’s more expectation that European students will have gotten their “general education” in High School. Overall, a European Bachelor’s is a sort of pre-master’s degree, while an American one is more of a post-high school degree.</p>
<p>I think I’m glad that I got more of a liberal-arts, American-style education. However, I’ve met some Computer Science students who have complained about my school’s incredibly lax requirement of taking two classes total that involve writing. Those people might well have been better-off studying in Europe.</p>
<h2 id="learn-to-use-headings">Learn to use headings</h2>
<p>Before college, you don’t usually have to write anything very long. However, once you start getting assignments that have two-digit numbers of pages, the techniques you used before start to break down. Long texts become much easier when you use sections and headings — it’s easier to write ten one-page essays than one ten-page essay. That’s how I managed to write this much in this article.</p>
<p>If you’re in a creative writing class, you can omit headings and split sections with horizontal rules, like this:</p>
<hr />
<p>Or some people will use centered asterisks:</p>
<div style="text-align: center">
***
</div>
<p>Of course, unicode means that you can be creative:</p>
<div style="text-align: center; font-size: 300%; line-height: 0px;">
<p>⅏</p>
</div>
<p>But don’t get carried away:</p>
<div style="text-align: center; font-size: 150%; line-height: 0px;">
<p>🤩 🤪 🥳</p>
</div>
<p>Really. Don’t.</p>
<h2 id="use-pandoc">Use Pandoc</h2>
<p>If you’re not a computer geek, skip this section.</p>
<p>If you <em>are</em> a computer geek, then you probably hate Microsoft Word files. You need to use a big lumbering program to view or edit them, and you always have to do a lot of manual fiddling with menus and dialogues to get everything to look exactly how you want. LaTeX exists (and professors like it), but there’s a steep learning curve and lots of things that can go wrong. Eg. <code>pdflatex</code> has trouble handling unicode. Also, LaTeX just isn’t that pleasant to use for simple text (math is another matter).</p>
<p>Wouldn’t it be nice to just write your papers with Markdown in <code>(vim|emacs)</code>?</p>
<p>I originally did this by compiling Markdown to HTML with <code>markdown.pl</code>, styling with custom CSS, then printing to a PDF from the browser (and later, <a href="https://weasyprint.org/">WeasyPrint</a>). This sort of worked, but there was no good way to do footnotes, since no one has <a href="https://www.w3.org/TR/css-gcpm-3/#turning-elements-into-footnotes">implemented</a> <code>float: footnote</code> yet. I always ended up going back to LibreOffice for any substantial work.</p>
<p>However, eventually I discovered <a href="https://pandoc.org/">Pandoc</a>, which can convert between tons of different document formats. As it happens, it has out-of-the-box support for many Markdown extensions, including footnotes, and it can convert to beautiful LaTeX-formatted PDFs with highly sane defaults (eg. code highlighting, inline LaTeX math).</p>
<p>If you don’t like Markdown, <a href="http://asciidoc.org/">AsciiDoc</a> or <a href="https://docutils.sourceforge.io/docs/user/rst/quickstart.html">reStructuredText</a> will work pretty well, although the syntax can be pretty ridiculous. Textile is good if you often work with poetry, since it preserves line-breaks. These are all supported in Pandoc, although I find that it handles Markdown a little better.</p>
<h2 id="turn-off-spell-check-until-youre-done-writing">Turn off spell-check until you’re done writing</h2>
<p>There are two reasons to do this.</p>
<p>First: You’ll improve your spelling. I used to have a lot of words that I would always spell wrong and then immediately get corrected by the computer. This meant that I never bothered trying to get into the habit of spelling correctly, which is a problem when you’re not using a computer.</p>
<p>Second: Spell-check is distracting. When writing, you should be thinking about the words you’re saying, not the representation on the page. There’s also no real point in correcting spelling if you might delete those words later—which is likely if you’re revising effectively.</p>
<h2 id="take-the-time-to-clean-up-your-code">Take the time to clean up your code</h2>
<p>When you’ve been working on a coding project for a few dozen hours and you finally solve the problem, it can be really tempting to hand in your code right away. Don’t do this: you can save a lot of software engineering points if you take just a little bit more time to refactor and clean up magic numbers, bad memory management etc.</p>
<h2 id="dont-be-limited-by-what-your-school-teaches">Don’t be limited by what your school teaches</h2>
<p>My intro CS class taught Java, so for I a while I thought I needed to use Java for any programming I did, since I hadn’t been formally taught any other languages.</p>
<p>Novices and non-programmers tend to over-estimate the difficulty of learning programming languages. Firstly, the term “programming <em>language</em>” calls to mind Latin or Mandarin Chinese more than middle school Algebra, even though it’s much more like the latter. Further, learning one’s first programming language is hard, so people assume that learning subsequent languages will be too. In fact, once you’ve learned your first language, you’ll understand almost all of the concepts necessary to learn another one very quickly.</p>
<p>For some reason, there’s also a lot of people who brag about knowing however-many programming languages, which might lead people to infer that it’s hard to do. In fact, there are so many people who know 10/15/20 programming languages because they are not that hard to learn. It’s also not especially useful to know a huge number of languages. It’s worthwhile to know C and Python, because they are good for different tasks. However, there’s not much point in learning Ruby after you already know Python, because their niches are very similar.</p>
<p>As it happens, Java wasn’t a great language for most of the things I wanted to do. The summer after my freshman year, I wrote a game in LibGDX—an engine I chose just because it was based on Java. I now realize that it wouldn’t have been much more effort to learn to make games in JavaScript, which would have let me develop my game for a more appropriate platform. But I was scared to use a different language because no one gave me permission. That was silly.</p>
<h2 id="youll-probably-pass-your-classes">You’ll probably pass your classes</h2>
<p>If you’ve passed all of your classes so far, you’ll probably pass most of your classes this semester too. Stay calm, you’ve got this.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Getting EU citizenship could be more likely than you think. Some EU countries allow citizenship-by-descent. Eg. If your grandparent was born in Ireland, you can <a href="https://en.wikipedia.org/wiki/Irish_nationality_law#By_descent">become an Irish citizen</a>. If you can prove that you have <em>any</em> Hungarian ancestry, you can <a href="https://en.wikipedia.org/wiki/Hungarian_nationality_law#Citizenship_by_ancestry">become a Hungarian citizen</a> if you’re willing to learn the language. Several countries, including Spain and Germany, have also given citizenship to Jews whose families were historically victimized by anti-Semitic policies.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
Writing BMP Images from ScratchLuna McNultyhttps://lmcnulty.me/words/bmp-output2020-03-28T00:00:00Z
<p>I work with image files all the time. Many of my programming projects revolve around <a href="https://lmcnulty.gitlab.io/wallpaper-generator/">making pretty pictures</a> or turning them into websites. Recently, however, it occurred to me that I’ve never worked with a raster graphics file directly. At the lowest-level, I’ve always just manipulated an RGB array in memory, then called out to some library to handle saving my work.</p>
<p>This thought made me a little uncomfortable. The way I used computers was completely overturned once I came to understand the underlying text representation that makes up web pages, config files, source code, etc. What if understanding binary data representations is just as important?</p>
<p>Thus, I decided it was time to write a program that could produce images from scratch. This meant using a language without a runtime, that’s good for manipulating bytes directly, and which I already needed to brush up on anyway: C. I went with BMP as an output format. There are a few even simpler formats, like <a href="https://en.wikipedia.org/wiki/Truevision_TGA">TGA</a>, but since I’m writing a blog post, I thought I might as well go with something viewable in a browser.</p>
<p><a href="https://gitlab.com/lmcnulty/bmp-encoder">For the impatient, here’s the end product</a>. For everyone else, let’s walk through the problem together.</p>
<h2 id="dissecting-a-bmp">Dissecting a BMP</h2>
<p>Before we begin coding, let’s start by looking inside an existing BMP file. Here’s the one we’ll be starting with: <img src="1x1.bmp"/></p>
<p>Ok, maybe that’s too hard to see. Let’s scale it by 50x: <img src="1x1.bmp" style="
vertical-align: middle;
width: 50px;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<p>Yes, this is just a 1x1 pixel image with RGB color (239, 65, 53). I made it in GIMP, making sure to check “Do not write color space information” in the export dialogue.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> Now, let’s look inside the file and see how it’s made. The tool I usually use for this sort of thing is <code>cat</code>. Let’s got ahead and try it:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a>$ <span class="fu">cat</span> 1x1.bmp</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="ex">BM</span>:6(#.#.5A�</span></code></pre></div>
<p>Oh yeah. This is a binary file. So to see inside it, we’ll need a different tool. For this, let’s use the command <code>xxd</code>. This will show us the byte data of a file in hexadecimal format. We’ll use the <code>-g 1</code> flag to group the data in 1-byte groups:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a>$ <span class="ex">xxd</span> -g 1 1x1.bmp </span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="ex">00000000</span>: 42 4d 3a 00 00 00 00 00 00 00 36 00 00 00 28 00 BM:.......6...(.</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a><span class="ex">00000010</span>: 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00 ................</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a><span class="ex">00000020</span>: 00 00 04 00 00 00 23 2e 00 00 23 2e 00 00 00 00 ......#...#.....</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a><span class="ex">00000030</span>: 00 00 00 00 00 00 35 41 ef 00 ......5A..</span></code></pre></div>
<p>This is better, but how do we read it?</p>
<p>The most import part is in the center, starting with <code>42</code> <code>4d</code>… and ending with …<code>ef</code> <code>00</code>. This is the byte-data of the file in hexadecimal format. As a reminder, hexadecimal is a base-sixteen representation of a number using a-f for the digits after 9. Some people can easily convert between hexadecimal and decimal in their heads, but for the rest of us, there’s <code>printf</code>. Let’s convert the <code>42</code> and <code>4d</code> to decimal:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a>$ <span class="bu">printf</span> <span class="st">"%d\n"</span> 0x42</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="ex">66</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a>$ <span class="bu">printf</span> <span class="st">"%d\n"</span> 0x4d</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="ex">77</span></span></code></pre></div>
<p>These numbers are the <a href="https://www.ascii-code.com/">ascii codes</a> for ‘B’ and ‘M’, which appear at the start of every BMP file to indicate its format. You’ll notice that these letters also appear on the right-hand side of the <code>xxd</code> output: it’s just a representation of the same data in ascii. We won’t need to worry about it too much, since most of our data is not in ascii format. Finally, on the left-hand side, we have <code>000000xx:</code>. These are just the indices in hexadecimal. There are 16 columns in each row of bytes, so the numbering increases 0x10 in each row.</p>
<p>Now that we know how to read the <code>xxd</code> output, let’s try and interpret the byte data. In principle we should be looking at <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4813e7fd-52d0-4f42-965f-228c8b7488d2?redirectedfrom=MSDN">the specification</a>, but that will be boring… so lets just go to the <a href="https://en.wikipedia.org/wiki/BMP_file_format#File_structure">Wikipedia article</a>, which tells us that every BMP file starts with a 14-byte header. Let’s mark that in blue:</p>
<pre>
<span style="color: blue">42 4d 3a 00 00 00 00 00 00 00 36 00 00 00</span> 28 00
00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
00 00 04 00 00 00 23 2e 00 00 23 2e 00 00 00 00
00 00 00 00 00 00 35 41 ef 00
</pre>
<p>The middle of the file is a little complicated, so for now let’s skip to the last part: the pixel array. Each pixel is three bytes, one for each color. Since BMP is a Microsoft format and Microsoft likes doing things backwards, the order of the bytes is normally Blue, Green, Red, starting at the bottom-left pixel of the image. Each row of pixels gets zero-padded to a multiple of four bytes. We have one row of one pixel, so it will be three bytes padded to four. Thus, our pixel array will by the last four bytes of the file. Let’s just confirm that <code>35 41 ef</code> matches the colors we chose at the beginning: which were (239, 65, 53)</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a>$ <span class="bu">printf</span> <span class="st">"%d %d %d\n"</span> 0x35 0x41 0xef</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="ex">53</span> 65 239</span></code></pre></div>
<p>Great, it matches. Not coincidentally, <span style="color: #ef4135">this is the color we get if we add</span> <code>color: #ef4135</code> in CSS. Let’s use it to mark the pixel array in our <code>xxd</code> output:</p>
<pre>
<span style="color: blue">42 4d 3a 00 00 00 00 00 00 00 36 00 00 00</span> 28 00
00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
00 00 04 00 00 00 23 2e 00 00 23 2e 00 00 00 00
00 00 00 00 00 00 <span style="color: #ef4135">35 41 ef 00</span>
</pre>
<p>In-between the header and the pixel data, we have 40 bytes. This matches the size of a DIB header in <code>BITMAPINFOHEADER</code> format, so it’s probably safe to assume that this is what we have. Let’s mark it in green:</p>
<pre>
<span style="color: blue">42 4d 3a 00 00 00 00 00 00 00 36 00 00 00</span> <span style="color: green">28 00
00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
00 00 04 00 00 00 23 2e 00 00 23 2e 00 00 00 00
00 00 00 00 00 00 </span><span style="color: red">35 41 ef 00</span>
</pre>
<p>Great. Now that we’ve got a basic understanding of the format, let’s lay the groundwork for our C code.</p>
<h2 id="starting-with-c-the-bmp-header">Starting with C — The BMP Header</h2>
<p>Let’s try and write a program that will output an identical 1×1 BMP file. Here’s what we’ll start with:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdio.h></span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdlib.h></span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a> <span class="co">// Make an array containing the B and M characters that</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a> <span class="co">// identify the file as a BMP.</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(<span class="st">"test.bmp"</span>, <span class="st">"w+"</span>); <span class="co">// Open a file for writing</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp); <span class="co">// Write the tag to the file</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a> fclose(fp); <span class="co">// Close the file</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>}</span></code></pre></div>
<p>If we run this now, it will output the first two bytes of the BMP header. Let’s add the rest. According to Wikipedia, the rest of the header consists of:</p>
<ul>
<li><strong>4 bytes</strong> The size of the BMP file in bytes</li>
<li><strong>4 bytes</strong> Reserved; actual value depends on the application that creates the image</li>
<li><strong>4 bytes</strong> The offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found.</li>
</ul>
<p>We’ll figure out how to calculate the size and offset later. But for now, let’s just copy what we got from the <code>xxd</code> output.</p>
<pre>
00000000: <span style="color: blue">42 4d <strong>3a 00 00 00</strong> <em>00 00 00 00</em> <strong>36 00 00 00</strong></span> 28 00 BM:.......6...(.
</pre>
<ul>
<li><strong>4 bytes</strong> The size of the BMP file in bytes: <code>3a 00 00 00</code></li>
<li><strong>4 bytes</strong> Reserved; actual value depends on the application that creates the image: <code>00 00 00 00</code></li>
<li><strong>4 bytes</strong> The offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found: <code>36 00 00 00</code></li>
</ul>
<p>These are all integers, so we’ll add a new <code>int</code> array to our C code and write it to the file. To represent a hexadecimal number in C, you put <code>0x</code> before it. Note that we write <code>0x3a</code> rather than <code>0x0000003a</code> because the file data is stored in little-endian format, where the least-significant bit comes first, whereas in C, we write the hex numbers in big-endian format. If this is confusing, think about how dates are written differently in various parts of the world. Europeans write dates in Day-Month-Year format, where the smallest unit comes first. The Chinese, by contrast, write dates in Year-Month-Day format, where the biggest unit comes first.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> The dates are the same, whether it’s 03-28-2020 or 2020-28-03; the former is just in big-endian and the latter in little-endian. The same is true with hex numbers: <code>0x3a000000</code> in little endian equals <code>0x0000003a</code> in big endian.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdio.h></span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdlib.h></span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a> <span class="co">// Create an integer array for the header </span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a> <span class="dt">int</span> header[] = {</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a> <span class="bn">0x3a</span>, <span class="co">// File Size</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a> <span class="bn">0x00</span>, <span class="co">// Unused</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a> <span class="bn">0x36</span> <span class="co">// Byte offset of pixel data</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a> };</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(<span class="st">"test.bmp"</span>, <span class="st">"w+"</span>);</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp);</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a> fwrite(&header, <span class="kw">sizeof</span>(header), <span class="dv">1</span>, fp); <span class="co">// Write the header to disk</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a> fclose(fp);</span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a>}</span></code></pre></div>
<h2 id="the-dib-header">The DIB Header</h2>
<p>Great. Now that we’ve made our way through the BMP header, we need to write the DIB header. This will specify more information needed to display the image. It can be in various formats, but we saw above that we’re using the 40-byte <code>BITMAPINFOHEADER</code>. So again, let’s check each field against our <code>xxd</code> output:</p>
<pre>
<span style="color: blue">42 4d 3a 00 00 00 00 00 00 00 36 00 00 00</span> <span style="color: green"><strong>28 00
00 00</strong> <em>01 00 00 00</em> <strong>01 00 00 00</strong> <em>01 00</em> <strong>18 00</strong> <em>00 00
00 00</em> <strong>04 00 00 00</strong> <em>23 2e 00 00</em> <strong>23 2e 00 00</strong> <em>00 00
00 00</em> <strong>00 00 00 00</strong> </span><span style="color: red">35 41 ef 00</span>
</pre>
<ul>
<li><strong>4 bytes</strong> - The size of this header, in bytes: <code>28 00 00 00</code>.
<ul>
<li>This is 40 bytes in hexadecimal. Our header will remain the same length.</li>
</ul></li>
<li><strong>4 bytes</strong> - The bitmap width in pixels: <code>01 00 00 00</code></li>
<li><strong>4 bytes</strong> - The bitmap height in pixels: <code>01 00 00 00</code>.
<ul>
<li>Instead of copying directly, we should output <code>width</code> and <code>height</code> variables.</li>
</ul></li>
<li><strong>2 bytes</strong> - The number of color planes (must be 1): <code>01 00</code></li>
<li><strong>2 bytes</strong> - the number of bits per pixel: <code>18 00</code>
<ul>
<li>We will combine these into a single integer so that we can store it in the same array.</li>
</ul></li>
<li><strong>4 bytes</strong> - The compression method being used: <code>00 00 00 00</code>
<ul>
<li>0 means no compression</li>
</ul></li>
<li><strong>4 bytes</strong> - The image size (in bytes): <code>04 00 00 00</code>
<ul>
<li>This is actually ignored for images without compression, so we can just use 0.</li>
</ul></li>
<li><strong>4 bytes</strong> - The horizontal resolution of the image: <code>23 2e 00 00</code></li>
<li><strong>4 bytes</strong> - The vertical resolution of the image: <code>23 2e 00 00</code>
<ul>
<li>As far as I can tell, these only matter for printing. We’ll just use what Gimp gave us.</li>
</ul></li>
<li><strong>4 bytes</strong> - the number of colors in the color palette, or 0 to default to 2<sup>n</sup>: <code>00 00 00 00</code></li>
<li><strong>4 bytes</strong> - the number of important colors used, or 0 when every color is important; generally ignored: <code>00 00 00 00</code></li>
</ul>
<p>Adding all of these to our C code, we get:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdio.h></span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdlib.h></span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a> <span class="dt">int</span> width = <span class="dv">1</span>; <span class="co">// Keep these in variables, since</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a> <span class="dt">int</span> height = <span class="dv">1</span>; <span class="co">// they'll change later.</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a> <span class="dt">int</span> header[] = {</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a> <span class="bn">0x3a</span>, <span class="bn">0x00</span>, <span class="bn">0x36</span>,</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a> <span class="bn">0x28</span>, <span class="co">// Header Size</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a> width, height, <span class="co">// Image dimensions in pixels</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a> <span class="bn">0x180001</span>, <span class="co">// 24 bits/pixel, 1 color plane</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a> <span class="dv">0</span>, <span class="co">// BI_RGB no compression</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a> <span class="dv">0</span>, <span class="co">// Pixel data size in bytes</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a> <span class="bn">0x002e23</span>, <span class="bn">0x002e23</span>, <span class="co">// Print resolution</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="co">// No color palette</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a> };</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(<span class="st">"test.bmp"</span>, <span class="st">"w+"</span>);</span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp);</span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true"></a> fwrite(&header, <span class="kw">sizeof</span>(header), <span class="dv">1</span>, fp); </span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true"></a> fclose(fp);</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true"></a>}</span></code></pre></div>
<h2 id="the-pixel-data">The Pixel Data</h2>
<p>Finally, we need to output an array of pixels. Recall that each pixel is represented by its B, G, and R values, and each row is padded to a multiple of 4 bytes. Since we have one pixel, that means we’ll be outputting an array of 4 bytes.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdio.h></span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="pp">#include </span><span class="im"><stdlib.h></span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a> <span class="dt">int</span> width = <span class="dv">1</span>; </span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a> <span class="dt">int</span> height = <span class="dv">1</span>;</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a> <span class="dt">int</span> header[] = {</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a> <span class="bn">0x3a</span>, <span class="bn">0x00</span>, <span class="bn">0x36</span>, <span class="bn">0x28</span>, width, height, <span class="bn">0x180001</span>, </span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="bn">0x002e23</span>, <span class="bn">0x002e23</span>, <span class="dv">0</span>, <span class="dv">0</span> </span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a> };</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a> <span class="co">// Store as char array, because we want each value to take up</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a> <span class="co">// one byte.</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a> <span class="dt">char</span> bitmap[] = {</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a> <span class="bn">0x35</span>, <span class="co">// Blue</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a> <span class="bn">0x41</span>, <span class="co">// Green</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true"></a> <span class="bn">0xef</span>, <span class="co">// Red</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true"></a> <span class="bn">0x00</span> <span class="co">// Padding</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true"></a> };</span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true"></a></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(<span class="st">"test.bmp"</span>, <span class="st">"w+"</span>);</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp);</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true"></a> fwrite(&header, <span class="kw">sizeof</span>(header), <span class="dv">1</span>, fp); </span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true"></a> fwrite(&bitmap, <span class="kw">sizeof</span>(bitmap), <span class="dv">1</span>, fp);</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true"></a> fclose(fp);</span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true"></a>}</span></code></pre></div>
<p>Alright, now let’s run the program and see what we get:</p>
<p><img src="test.bmp" width="50" height="50" style="
width: 50px;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<p>Yay! Let’s try and change the color:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="dt">char</span> bitmap[] = {</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a> <span class="bn">0xa4</span>, <span class="co">// Blue</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a> <span class="bn">0x55</span>, <span class="co">// Green</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a> <span class="bn">0x00</span>, <span class="co">// Red</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a> <span class="bn">0x00</span> <span class="co">// Padding</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a>};</span></code></pre></div>
<p><img src="test2.bmp" width="50" height="50" style="
width: 50px;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<p>Hurrah!</p>
<h2 id="generalizing">Generalizing</h2>
<p>Ok, so now we know how to output a valid BMP file. But sometime we might want to output a different image, maybe even one with more than one pixel. So let’s refactor our <code>main</code> into a function that outputs a given RGB array to a file.</p>
<p>The pixel data is stored in a 1D array —this might seem counter-intuitive for a 2D image, but it’s the normal way of handling pixel data in computer graphics, because it’s more efficient and avoids having to deal with pointers to arrays. Since the pixel data is 1D, in addition to passing its length as an argument (as C doesn’t automatically track array lengths), we also need the width of the image so we know where each new row starts.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="dt">void</span> write_bmp(<span class="dt">char</span> *filename, <span class="dt">char</span> rgb[], <span class="dt">int</span> length, <span class="dt">int</span> width) {</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a> <span class="co">// Calculate the image height from its width and the array length</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a> <span class="dt">int</span> height = (length / <span class="dv">3</span>) / width;</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a> <span class="co">// The size of the pixel data. For now, use width + 1 to handle </span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a> <span class="co">// row padding.</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a> <span class="dt">int</span> bitmap_size = <span class="dv">3</span> * height * (width + <span class="dv">1</span>);</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a> <span class="co">// The pixel data is now variable-length, so we need to use</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a> <span class="co">// malloc.</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a> <span class="dt">char</span> *bitmap = (<span class="dt">char</span> *) malloc(bitmap_size * <span class="kw">sizeof</span>(<span class="dt">char</span>));</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a> <span class="co">// Zeroing out the data will set all the pixels to black.</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i < bitmap_size; i++) bitmap[i] = <span class="dv">0</span>;</span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a> <span class="dt">int</span> header[] = {</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a> <span class="dv">0</span>, <span class="co">// File size... update at the end.</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a> <span class="dv">0</span>, <span class="bn">0x36</span>, <span class="bn">0x28</span>,</span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a> width, height, <span class="co">// Image dimensions in pixels</span></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a> <span class="bn">0x180001</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="bn">0x002e23</span>, <span class="bn">0x002e23</span>, <span class="dv">0</span>, <span class="dv">0</span>,</span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true"></a> };</span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true"></a> <span class="co">// Update file size: just the sum of the sizes of the arrays</span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true"></a> <span class="co">// we write to disk.</span></span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true"></a> header[<span class="dv">0</span>] = <span class="kw">sizeof</span>(tag) + <span class="kw">sizeof</span>(header) + bitmap_size;</span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true"></a></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(filename, <span class="st">"w+"</span>);</span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp);</span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true"></a> fwrite(&header, <span class="kw">sizeof</span>(header), <span class="dv">1</span>, fp);</span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true"></a></span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true"></a> <span class="co">// Malloc returns a pointer, so we no longer need to get the</span></span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true"></a> <span class="co">// adress of bitmap</span></span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true"></a> fwrite(bitmap, bitmap_size * <span class="kw">sizeof</span>(<span class="dt">char</span>), <span class="dv">1</span>, fp);</span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true"></a> fclose(fp);</span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true"></a> </span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true"></a> free(bitmap);</span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true"></a>}</span></code></pre></div>
<p>Now that we’ve got that, let’s try calling it on an RGB array. For no particular reason, let’s try and output this 6×6 pixel image:</p>
<p><img src="french_flag.bmp" width="150" style="
width: 150px;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a> <span class="dt">char</span> fr[] = {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">85</span>, <span class="dv">164</span>, <span class="co">// Bleu</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a> <span class="dv">255</span>, <span class="dv">255</span>, <span class="dv">255</span>, <span class="co">// Blanc</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">65</span>, <span class="dv">53</span>, <span class="co">// Rouge</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">85</span>, <span class="dv">164</span>,</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a> <span class="dv">255</span>, <span class="dv">255</span>, <span class="dv">255</span>,</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">65</span>, <span class="dv">53</span>,</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a> };</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a> write_bmp(<span class="st">"french_flag.bmp"</span>, fr, <span class="kw">sizeof</span>(fr) / <span class="kw">sizeof</span>(<span class="dt">char</span>), <span class="dv">3</span>);</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>}</span></code></pre></div>
<p>Run that program and we get:</p>
<p><img src="french_flag_black.bmp" width="150" style="
width: 150px;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<p>Ok, so we’ve got the dimensions right. Now all we need to do is copy the RGB data into the <code>bitmap</code> array in the correct order (BGR) and with the correct padding.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">// Function to round an int to a multiple of 4</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="dt">int</span> round4(<span class="dt">int</span> x) {</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a> <span class="cf">return</span> x % <span class="dv">4</span> == <span class="dv">0</span> ? x : x - x % <span class="dv">4</span> + <span class="dv">4</span>;</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a>}</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a><span class="dt">void</span> write_bmp(<span class="dt">char</span> *filename, <span class="dt">char</span> rgb[], <span class="dt">int</span> length, <span class="dt">int</span> width) {</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a> <span class="dt">int</span> height = (length / <span class="dv">3</span>) / width;</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a> <span class="co">// Pad the width of the destination to a multiple of 4</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true"></a> <span class="dt">int</span> padded_width = round4(width * <span class="dv">3</span>);</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true"></a> </span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true"></a> <span class="dt">int</span> bitmap_size = height * padded_width * <span class="dv">3</span>;</span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true"></a> <span class="dt">char</span> *bitmap = (<span class="dt">char</span> *) malloc(bitmap_size * <span class="kw">sizeof</span>(<span class="dt">char</span>));</span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i < bitmap_size; i++) bitmap[i] = <span class="dv">0</span>;</span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true"></a></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true"></a> <span class="co">// For each pixel in the RGB image...</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> row = <span class="dv">0</span>; row < height; row++) {</span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> col = <span class="dv">0</span>; col < width; col++) {</span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true"></a> </span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true"></a> <span class="co">// For R, G, and B...</span></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> color = <span class="dv">0</span>; color < <span class="dv">3</span>; color++) {</span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true"></a></span>
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true"></a> <span class="co">// Get the index of the destination image</span></span>
<span id="cb12-24"><a href="#cb12-24" aria-hidden="true"></a> <span class="dt">int</span> index = row * padded_width + col * <span class="dv">3</span> + color;</span>
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true"></a></span>
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true"></a> <span class="co">// Set the destination to the value of the src at row, col.</span></span>
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true"></a> bitmap[index] = rgb[<span class="dv">3</span>*(row * width + col) + (<span class="dv">2</span> - color)];</span>
<span id="cb12-28"><a href="#cb12-28" aria-hidden="true"></a> }</span>
<span id="cb12-29"><a href="#cb12-29" aria-hidden="true"></a> }</span>
<span id="cb12-30"><a href="#cb12-30" aria-hidden="true"></a> }</span>
<span id="cb12-31"><a href="#cb12-31" aria-hidden="true"></a></span>
<span id="cb12-32"><a href="#cb12-32" aria-hidden="true"></a> <span class="dt">char</span> tag[] = { <span class="ch">'B'</span>, <span class="ch">'M'</span> };</span>
<span id="cb12-33"><a href="#cb12-33" aria-hidden="true"></a> <span class="dt">int</span> header[] = {</span>
<span id="cb12-34"><a href="#cb12-34" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="bn">0x36</span>, <span class="bn">0x28</span>, width, height, <span class="bn">0x180001</span>, </span>
<span id="cb12-35"><a href="#cb12-35" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="bn">0x002e23</span>, <span class="bn">0x002e23</span>, <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb12-36"><a href="#cb12-36" aria-hidden="true"></a> };</span>
<span id="cb12-37"><a href="#cb12-37" aria-hidden="true"></a> header[<span class="dv">0</span>] = <span class="kw">sizeof</span>(tag) + <span class="kw">sizeof</span>(header) + bitmap_size;</span>
<span id="cb12-38"><a href="#cb12-38" aria-hidden="true"></a> <span class="dt">FILE</span> *fp = fopen(filename, <span class="st">"w+"</span>);</span>
<span id="cb12-39"><a href="#cb12-39" aria-hidden="true"></a> fwrite(&tag, <span class="kw">sizeof</span>(tag), <span class="dv">1</span>, fp);</span>
<span id="cb12-40"><a href="#cb12-40" aria-hidden="true"></a> fwrite(&header, <span class="kw">sizeof</span>(header), <span class="dv">1</span>, fp);</span>
<span id="cb12-41"><a href="#cb12-41" aria-hidden="true"></a> fwrite(bitmap, bitmap_size * <span class="kw">sizeof</span>(<span class="dt">char</span>), <span class="dv">1</span>, fp);</span>
<span id="cb12-42"><a href="#cb12-42" aria-hidden="true"></a> fclose(fp);</span>
<span id="cb12-43"><a href="#cb12-43" aria-hidden="true"></a> free(bitmap);</span>
<span id="cb12-44"><a href="#cb12-44" aria-hidden="true"></a>}</span></code></pre></div>
<p>Alright, that should do it. Now lets write out some flags (chosen for simplicity):</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="dt">int</span> main() {</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a> <span class="dt">char</span> fr[] = {</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">85</span>, <span class="dv">164</span>, <span class="co">// Bleu</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a> <span class="dv">255</span>, <span class="dv">255</span>, <span class="dv">255</span>, <span class="co">// Blanc</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">65</span>, <span class="dv">53</span>, <span class="co">// Rouge</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">85</span>, <span class="dv">164</span>,</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a> <span class="dv">255</span>, <span class="dv">255</span>, <span class="dv">255</span>,</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">65</span>, <span class="dv">53</span>,</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a> };</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a> write_bmp(<span class="st">"french_flag.bmp"</span>, fr, <span class="kw">sizeof</span>(fr) / <span class="kw">sizeof</span>(<span class="dt">char</span>), <span class="dv">3</span>);</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a> <span class="dt">char</span> bgm[] = {</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="co">// Black</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a> <span class="dv">253</span>, <span class="dv">218</span>, <span class="dv">36</span>, <span class="co">// Yellow</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">51</span>, <span class="dv">64</span>, <span class="co">// Red</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a> <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>,</span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a> <span class="dv">253</span>, <span class="dv">218</span>, <span class="dv">36</span>,</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a> <span class="dv">239</span>, <span class="dv">51</span>, <span class="dv">64</span>,</span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a> };</span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a> write_bmp(<span class="st">"belgian_flag.bmp"</span>, bgm, <span class="kw">sizeof</span>(bgm) / <span class="kw">sizeof</span>(<span class="dt">char</span>), <span class="dv">3</span>);</span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a> </span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a> <span class="co">// Flag of the United Arab Emirates</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a> <span class="dt">char</span> *uae = (<span class="dt">char</span>*) malloc(<span class="dv">12</span> * <span class="dv">6</span>* <span class="kw">sizeof</span>(<span class="dt">char</span>) * <span class="dv">3</span>);</span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> row = <span class="dv">0</span>; row < <span class="dv">6</span>; row++) {</span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> col = <span class="dv">0</span>; col < <span class="dv">12</span>; col++) {</span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true"></a> <span class="cf">if</span> (col < <span class="dv">3</span>) {</span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col)] = <span class="dv">255</span>;</span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">1</span>] = <span class="dv">0</span>;</span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">2</span>] = <span class="dv">0</span>;</span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true"></a> } <span class="cf">else</span> <span class="cf">if</span> (row < <span class="dv">2</span>) {</span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col)] = <span class="dv">0</span>;</span>
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">1</span>] = <span class="dv">116</span>;</span>
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">2</span>] = <span class="dv">33</span>;</span>
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true"></a> } <span class="cf">else</span> <span class="cf">if</span> (row < <span class="dv">4</span>) {</span>
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col)] = <span class="dv">255</span>;</span>
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">1</span>] = <span class="dv">255</span>;</span>
<span id="cb13-38"><a href="#cb13-38" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">2</span>] = <span class="dv">255</span>;</span>
<span id="cb13-39"><a href="#cb13-39" aria-hidden="true"></a> } <span class="cf">else</span> {</span>
<span id="cb13-40"><a href="#cb13-40" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col)] = <span class="dv">0</span>;</span>
<span id="cb13-41"><a href="#cb13-41" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">1</span>] = <span class="dv">0</span>;</span>
<span id="cb13-42"><a href="#cb13-42" aria-hidden="true"></a> uae[<span class="dv">3</span> * (row * <span class="dv">12</span> + col) + <span class="dv">2</span>] = <span class="dv">0</span>;</span>
<span id="cb13-43"><a href="#cb13-43" aria-hidden="true"></a> }</span>
<span id="cb13-44"><a href="#cb13-44" aria-hidden="true"></a> }</span>
<span id="cb13-45"><a href="#cb13-45" aria-hidden="true"></a> }</span>
<span id="cb13-46"><a href="#cb13-46" aria-hidden="true"></a> write_bmp(<span class="st">"uae_flag.bmp"</span>, uae, <span class="dv">3</span>*<span class="dv">6</span>*<span class="dv">12</span>, <span class="dv">12</span>);</span>
<span id="cb13-47"><a href="#cb13-47" aria-hidden="true"></a></span>
<span id="cb13-48"><a href="#cb13-48" aria-hidden="true"></a> <span class="co">// Transgender Pride Flag</span></span>
<span id="cb13-49"><a href="#cb13-49" aria-hidden="true"></a> <span class="dt">char</span> *trans = (<span class="dt">char</span>*) malloc(<span class="dv">8</span> * <span class="dv">5</span>* <span class="kw">sizeof</span>(<span class="dt">char</span>) * <span class="dv">3</span>);</span>
<span id="cb13-50"><a href="#cb13-50" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> row = <span class="dv">0</span>; row < <span class="dv">5</span>; row++) {</span>
<span id="cb13-51"><a href="#cb13-51" aria-hidden="true"></a> <span class="cf">for</span> (<span class="dt">int</span> col = <span class="dv">0</span>; col < <span class="dv">8</span>; col++) {</span>
<span id="cb13-52"><a href="#cb13-52" aria-hidden="true"></a> <span class="cf">if</span> (row == <span class="dv">0</span> || row == <span class="dv">4</span>) {</span>
<span id="cb13-53"><a href="#cb13-53" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col)] = <span class="dv">91</span>;</span>
<span id="cb13-54"><a href="#cb13-54" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">1</span>] = <span class="dv">207</span>;</span>
<span id="cb13-55"><a href="#cb13-55" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">2</span>] = <span class="dv">250</span>;</span>
<span id="cb13-56"><a href="#cb13-56" aria-hidden="true"></a> } <span class="cf">else</span> <span class="cf">if</span> (row == <span class="dv">1</span> || row == <span class="dv">3</span>) {</span>
<span id="cb13-57"><a href="#cb13-57" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col)] = <span class="dv">245</span>;</span>
<span id="cb13-58"><a href="#cb13-58" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">1</span>] = <span class="dv">171</span>;</span>
<span id="cb13-59"><a href="#cb13-59" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">2</span>] = <span class="dv">185</span>;</span>
<span id="cb13-60"><a href="#cb13-60" aria-hidden="true"></a> } <span class="cf">else</span> {</span>
<span id="cb13-61"><a href="#cb13-61" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col)] = <span class="dv">255</span>;</span>
<span id="cb13-62"><a href="#cb13-62" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">1</span>] = <span class="dv">255</span>;</span>
<span id="cb13-63"><a href="#cb13-63" aria-hidden="true"></a> trans[<span class="dv">3</span> * (row * <span class="dv">8</span> + col) + <span class="dv">2</span>] = <span class="dv">255</span>;</span>
<span id="cb13-64"><a href="#cb13-64" aria-hidden="true"></a> }</span>
<span id="cb13-65"><a href="#cb13-65" aria-hidden="true"></a> }</span>
<span id="cb13-66"><a href="#cb13-66" aria-hidden="true"></a> }</span>
<span id="cb13-67"><a href="#cb13-67" aria-hidden="true"></a> write_bmp(<span class="st">"trans_flag.bmp"</span>, trans, <span class="dv">3</span>*<span class="dv">5</span>*<span class="dv">8</span>, <span class="dv">8</span>);</span>
<span id="cb13-68"><a href="#cb13-68" aria-hidden="true"></a></span>
<span id="cb13-69"><a href="#cb13-69" aria-hidden="true"></a>}</span></code></pre></div>
<p>And there we have it:</p>
<p><img src="french_flag.bmp" height="100" style="
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
margin-right: 4px;"><img src="belgian_flag.bmp" height="100" style="
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
margin-right: 4px"><img src="uae_flag.bmp" height="100" style="
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
margin-right: 4px"><img src="trans_flag.bmp" height="100" style="
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;"></p>
<h2 id="limitations">Limitations</h2>
<p>This only produces uncompressed files. This is fine for the tiny images we’re making: in fact, it beats most other formats in terms of disk usage at this size because of how little metadata we use. However, as the image size increases, we start to get very large file sizes: at 512×512, the output of this program is more than 100x the size of the same image converted to a lossless PNG. Thus, you wouldn’t want to use this program as much more than a toy. Nevertheless, walking through a toy program can be a great way to learn.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Actually, I didn’t realize to do this until after I had gone through these steps. But it makes everything simpler, so lets pretend I did it right the first time.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>Incidentally, the Chinese date format is almost always the one you want to use in computing, because its the only one where the alphabetical sort gives the dates in-order.</p>
<p>The US date format doesn’t make much numeric sense, and thus doesn’t help understanding endian-ness, because it puts the <em>second</em>-smallest unit first.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
Most Programming is UILuna McNultyhttps://lmcnulty.me/words/most-programming-is-ui.html2019-06-06T00:00:00Z
<p>Most programming is just user interfaces.</p>
<p>By that, I don’t just mean that GUIs tend to be more code than any other part of a graphical application, although that’s probably true as well. Instead, I mean that most tasks involved in programming are forms of UI development.</p>
<p>We tend to think of the UI as a separate, and relatively unimportant, part of a program. There’s a core part, which solves a problem, and there’s a peripheral part, which exposes it to the user. We even use this language in systems when talking about the “kernel” vs the “shell.”</p>
<p>But there’s a flaw in this way of thinking: Most programs don’t solve any problems. They just make it easier to access solutions that already exist.</p>
<p>You probably already have a program on your computer that’s capable of solving almost any problem you care to name. Type <code>python3</code> in your terminal and you’ll be greeted by a prompt through which you could in principle accomplish anything that’s computable and which gives you most of the algorithmic solutions through <code>import</code> statements. However, no one wants to edit their documents by typing calls to <code>f.read()</code> and <code>f.write()</code>, nor their images with direct calls to PIL. They’d rather use a text editor or a painting program. But ultimately those programs will still be calling similar functions, just with a superior <em>interface</em>.</p>
<p>There’s <a href="https://news.ycombinator.com/item?id=9224">an infamous post on “Hacker News”</a> in which a commenter responds to the announcement of Dropbox by questioning its usefulness when compared with:</p>
<blockquote>
<p>“getting an FTP account, mounting it locally with curlftpfs, and then using SVN or CVS on the mounted filesystem”</p>
</blockquote>
<p>And this person wasn’t exactly wrong. Dropbox wasn’t successful because it solved the problem of accessing files from different devices; The value was in making it <em>easy</em>.</p>
<p>The vast majority of code written today is accomplishing something similar. Think of how many developer-hours have been spent on CRUD apps which are effectively interfaces over MySQL/Postgres/Oracle/etc. It doesn’t matter whether it’s the front-end or the back-end: both are concerned mainly with organizing and transporting information such that it can be interacted with in a way simpler than typing out SQL queries.</p>
<p>There are certainly types of programming that I would not consider to be UI work. For example, programming a media codec is not UI work. But for every 1 codec implementation, there are about 1,000 media apps that access it.</p>
<p>Now, think about your own programming. How often do you personally solve an unsolved problem? By contrast, how often do you put together a more convenient way to handle an already-solved problem? Unless you have a PhD, I’m guessing that the latter is a lot more common.</p>
<p>“Hold on,” you might say, “I’m a back-end engineer. Even if I’m not inventing anything new, I don’t go anywhere near the UI.” To you I say this: think a little more broadly about what an interface is and who your users are. For example, have you ever written an API endpoint? What does the last word in “API” stand for again?</p>
<p>If you’re not writing interfaces for end-users, you’re writing interfaces for other programmers. You can take this idea as far down as you like. For example, consider the act of writing a function. Occasionally, you write one because you have a problem that is best dealt with through recursion, or whatever. However, just as often, you write a function so that you don’t have repeat the same steps at multiple points in the code. Maybe it’s for software engineering best practices, or maybe it’s to save yourself from repetitive stress injury: the point is, when you write a function, you do it to make programming easier. You are writing a UI <em>for yourself</em>.</p>
<p>But if we use the words “user interface” so broadly, aren’t we basically making the term useless? In a sense, yes. It would be kind of silly if all programmers started calling themselves UI developers. However, the point here is that it’s important to think about <em>all</em> of your code as an interface, not just a black box behind a front-end. Take the above example: when you write a function, you should think about its user experience. For example here are four user interfaces for the same function:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="fu">stroke</span>(canvas<span class="op">,</span> <span class="st">"red"</span><span class="op">,</span> <span class="dv">10</span><span class="op">,</span> <span class="dv">10</span><span class="op">,</span> <span class="dv">45</span><span class="op">,</span> <span class="dv">45</span><span class="op">,</span> <span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="fu">stroke</span>(canvas<span class="op">,</span> <span class="dv">10</span><span class="op">,</span> <span class="dv">45</span><span class="op">,</span> <span class="dv">10</span><span class="op">,</span> <span class="dv">45</span><span class="op">,</span> <span class="st">"red"</span><span class="op">,</span> <span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a>canvas<span class="op">.</span><span class="fu">stroke</span>({<span class="dt">from</span><span class="op">:</span> {<span class="dt">x</span><span class="op">:</span> <span class="dv">10</span><span class="op">,</span> <span class="dt">y</span><span class="op">:</span> <span class="dv">10</span>}<span class="op">,</span> <span class="dt">to</span><span class="op">:</span> {<span class="dt">x</span><span class="op">:</span> <span class="dv">45</span><span class="op">,</span> <span class="dt">y</span><span class="op">:</span> <span class="dv">45</span>}<span class="op">,</span> <span class="dt">color</span><span class="op">:</span> <span class="st">"red"</span><span class="op">,</span> <span class="dt">width</span><span class="op">:</span> <span class="dv">2</span>})</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a>stroke<span class="op">.</span><span class="fu">from</span>(<span class="dv">10</span><span class="op">,</span> <span class="dv">10</span>)<span class="op">.</span><span class="fu">to</span>(<span class="dv">45</span><span class="op">,</span> <span class="dv">45</span>)<span class="op">.</span><span class="fu">colored</span>(<span class="st">"red"</span>)<span class="op">.</span><span class="fu">withWidth</span>(<span class="dv">2</span>)<span class="op">.</span><span class="fu">on</span>(canvas)<span class="op">;</span></span></code></pre></div>
<p>When you decide which of these you want to write, you should think about the question in terms of efficiency, predictability, discoverability, and the other principles of UI design. It doesn’t matter whether you’re working with pixels or sockets, toolbars or databases: as a programmer, you use your creativity to build something for other human beings. Don’t forget about them.</p>
Using Code to Intuit the Monty Hall ProblemLuna McNultyhttps://lmcnulty.me/words/monty-hall2020-06-23T00:00:00Z
<p>The Monty Hall problem is a famous puzzle that shows how statistical facts can be unintuitive. It’s based on a game show in which the contestant selects one of three doors, receiving whatever prize is behind it. One door has a desirable prize behind it, which is usually a car, and the others have a gag prize, usually a goat. My father told me about this problem when I was probably under 10, and at the time I neither understood the math, nor why anyone would want a car when they could have <a href="https://xkcd.com/1282/"><em>a goat</em></a>. So for the rest of this article, assume that the preferred prize is a goat, and the others are, say, bags of sand.</p>
<blockquote class="pull-away" style="width: 11em;">
writing the problem in code, the answer seemed <em>completely obvious</em>.
</blockquote>
<p>Anyway, after the contestant selects a door, the show host (originally named Monty Hall) opens another door, revealing a bag of sand. The contestant then has the option to switch to the remaining door. The question is: Is it better to switch or does it make no difference?</p>
<p>The answer, as you’ve probably heard before, is that it’s better to switch, because the probability of getting the goat when you initially pick a door is ⅓, which doesn’t change until you switch. This is confusing, however, because it seems like after the host shows you the bag of sand, there’s one door with a goat behind it, and one door with sand behind, so the probability of winning should be ½, regardless of which door you pick. There are a zillion articles explaining why this isn’t the case, which comes down to the fact that this is a <em>conditional</em> probability, not just a choice between two random options. However, depending on your background, you may have a better intuition for code than for math, in which case the easiest way to understand the problem is by seeing it as a program.</p>
<p>Take the following Python script.. If you’re new to Python, don’t be intimidated by the <a href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">list comprehensions</a>, which is where you have <code>for</code>s and <code>if</code>s inside brackets: they are not hard to understand and they will make your life much easier if you learn to use them.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="im">import</span> random, statistics</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="kw">def</span> simulate(switch):</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a> <span class="co">"""Return True if the contestant wins the goat, False otherwise."""</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a> <span class="co"># Shuffle the doors and pick one at random</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a> doors <span class="op">=</span> [<span class="st">'goat'</span>, <span class="st">'sand'</span>, <span class="st">'sand'</span>]</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a> random.shuffle(doors)</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a> choice <span class="op">=</span> random.randint(<span class="dv">0</span>, <span class="dv">2</span>)</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a> <span class="co"># Pick one of the doors with sand behind it to show </span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true"></a> remaining_sand <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">3</span>) <span class="cf">if</span> doors[i] <span class="op">==</span> <span class="st">'sand'</span> <span class="kw">and</span> i <span class="op">!=</span> choice]</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true"></a> shown <span class="op">=</span> random.choice(remaining_sand)</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true"></a> <span class="co"># If switch is selected, switch choice to the remaining door</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true"></a> <span class="cf">if</span> switch: choice <span class="op">=</span> [i <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">3</span>) <span class="cf">if</span> <span class="kw">not</span> i <span class="kw">in</span> (choice, shown)][<span class="dv">0</span>]</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true"></a> </span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true"></a> <span class="cf">return</span> doors[choice] <span class="op">==</span> <span class="st">'goat'</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true"></a></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true"></a>trials <span class="op">=</span> <span class="dv">100000</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true"></a><span class="bu">print</span>(<span class="st">"Switch: "</span>, statistics.mean([simulate(<span class="va">True</span>) <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(trials)]))</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true"></a><span class="bu">print</span>(<span class="st">"Stay: "</span>, statistics.mean([simulate(<span class="va">False</span>) <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(trials)]))</span></code></pre></div>
<p>Run the program, and we see that indeed, you win twice as often if you switch than if you stay:</p>
<pre><code>Switch: 0.66462
Stay: 0.33362</code></pre>
<p>But we already knew that. However, the key insight is on line 16:</p>
<pre><code>if switch: choice = [i for i in range(3) if not i in (choice, shown)][0]</code></pre>
<p>This is the line that changes the choice to the remaining door. If <code>switch</code> is <code>False</code>, it won’t run. Comment out line 16, and your IDE or your linter will tell you something like this:</p>
<pre><code>monty-hall.py:13:4: W0612: Unused variable 'shown' (unused-variable)</code></pre>
<p>When the contestant doesn’t switch, the information provided by opening the door isn’t used at all. In fact, the Python VM will probably skip over the lines computing <code>shown</code> because it’s not used. In other words, choosing not to switch is <em>exactly the same</em> as picking randomly from three options.</p>
<p>I decided to write this article because after writing the problem in code, the answer seemed <em>completely obvious</em>. Before, I was sort of able to understand the standard stats textbook explanation, but the answer still seemed sort of opaque. Hopefully, there will be a few other people out there to whom code is a more native language than math who will find this as clarifying as I did.</p>
French Pronunciation: VowelsLuna McNultyhttps://lmcnulty.me/words/french-vowels2020-01-11T00:00:00Z
<p>Learning and teaching pronunciation is difficult, because most people, regardless of the languages they know, don’t have the vocabulary to explain how sounds are made. They’ll give meaningless advice like “no, it’s a <em>fuller</em> sound.” To give a real explanation you need to know basic phonetic theory. I’ll introduce all the linguistics concepts necessary in this article, but you may need to look to other sources to really get it. If you don’t have the opportunity to <a href="../college-advice-2/#take-intro-linguistics">take an intro linguistics class</a>, I would recommend <a href="https://www.youtube.com/channel/UCByBOwwOU5tKddYpsrHiUUw">Xidnaf’s channel</a> for a highly approachable introduction to linguistics concepts.</p>
<p>This article assumes that you’re an English speaker with decent knowledge of French, but you’re looking to improve your pronunciation.</p>
<h2 id="spelling-and-pronunciation">Spelling and pronunciation</h2>
<p>Both English and French have rather strange spelling systems where the letters in a word only loosely correspond to its pronunciation.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> For instance “lie,” “Thai,” “my,” “buy,” and “pi” all end in the same sound, which is represented with different letters every time. Likewise, in French, « cent », « s’en », « sang », « sans », « sens », and « sent » are all pronounced the same way. Conversely, the same letter can make different sounds depending on the word: Compare the ‘c’ in « ciel » and « encore ».</p>
<p>While discussing about pronunciation, it’s important to be able to talk about sounds independent of spelling. Since no one wants to have to constantly say “‘c’ as in « ciel »,” linguists invented the International Phonetic Alphabet (IPA) where every sound (phoneme), in every language, corresponds to exactly one symbol.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> This is what you’ll typically see used for dictionary pronunciations. <a href="https://en.wiktionary.org/">Wiktionary</a> in particular has fantastic IPA coverage for many different languages.</p>
<p>In IPA, « cent » and « s’en » are both /sɑ̃/. Note that IPA symbols are typically surrounded by slashes.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> In many cases, the symbol represents the sound that you would expect: /p/ is the sound at the start of “pig” and « pantalon ». However, in other cases it’s not so obvious: /j/ represents the sound at the beginning of “yam” rather than “jam.” This article won’t teach you the whole IPA, but it will introduce symbols as needed. <a href="http://www.ipachart.com/">This website</a> allows you to click on any IPA symbol to hear the corresponding sound.</p>
<p>It’s important to understand that different languages use different sounds. For instance, English has the phoneme /ð/, which is written as ‘th.’ French doesn’t have this sound, which is why French people will sometimes say “zese sings” instead of “these things.” Likewise, English doesn’t have /ʁ/ (the French ‘r’), which is why you might have difficulty saying « ronronner ».</p>
<p>So how do you learn a sound that isn’t part of your language? Besides imitation and practice (which you’ll still definitely need), it helps to think about the features that allow us to differentiate between sounds.</p>
<h2 id="vowels-and-features">Vowels and features</h2>
<p>Vowels, in almost all languages, are distinguished by three properties: height, frontness, and roundness. Height and frontness both refer to the position of the tongue, while roundness refers to the shape of the lips.<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p>
<p>Probably the most noticeable feature of vowels is the <strong>height</strong> of the tongue in the mouth.<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a> Say each of the following words in succession, and observe as your tongue progressively falls lower: “beat” → “bait” → “bet” → “bat.”</p>
<p>While height is the vertical position tongue, <strong>frontness</strong> is the depth in the mouth. It’s slightly harder to detect, but if you go from saying “bet” to “but,” you should notice the difference. It’s more dramatic between /i/, the vowel in “beat,” and /u/, in “boot,” but note that there is also a difference in roundness.</p>
<p><strong>Roundness</strong> is just the shape of the lips. In the words “boot,” “boat,” and “bought,” your lips make a round, ‘o’ shape. By contrast, they will be in a more neutral position in “bet,” “but”, “beat,” and “bat.”</p>
<h2 id="the-vowel-chart">The vowel chart</h2>
<p>Phonetic properties can be very abstract. There are two important ways you can learn to recognize them: listening and seeing. To visualize vowels, we typically plot them on a chart where the y-axis represents height and the x-axis represents backness. The chart isn’t square, because as the tongue moves lower, it tends to move further back, due to the shape of the human mouth.</p>
<p>Round vowels are placed to the right of each vertical line, while unrounded vowels are placed on the left. Schwa is exactly in the center, because it’s somewhat in-between.</p>
<p>You can click the phonemes on the chart below to hear them and see examples of uses in English and French.</p>
<style>
.vowel { display: none; }
</style>
<section id="select-a-vowel" class="vowel" style="display: block">
<h4>Select a Vowel</h4>
<table>
<tr>
<td>
English:
</td>
<td>
<em>None</em>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<em>None</em>
</td>
</tr>
</table>
</section>
<section id="i---high-front-unrounded" class="vowel i">
<h4>/i/ - High Front Unrounded</h4>
<audio src="phonemes/i.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
B<strong>ea</strong>n, <strong>see</strong>, m<strong>e</strong>, l<strong>i</strong>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
Par<strong>i</strong>s, <strong>i</strong>l, l<strong>y</strong>re
</td>
</tr>
</table>
</section>
<section id="y---high-front-rounded" class="vowel y">
<h4>/y/ - High Front Rounded</h4>
<audio src="phonemes/y.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<em>None</em>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
t<strong>u</strong>, v<strong>u</strong>, m<strong>u</strong>rm<strong>u</strong>re, <strong>u</strong>nité
</td>
</tr>
</table>
</section>
<section id="e---mid-high-front-unrounded" class="vowel e">
<h4>/e/ - Mid-High Front Unrounded</h4>
<audio src="phonemes/e.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
h<strong>ay</strong>, r<strong>a</strong>di<strong>a</strong>tion, g<strong>ae</strong>lic
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>e</strong>t, s<strong>e</strong>s, l<strong>e</strong>s, pi<strong>e</strong>d
</td>
</tr>
</table>
</section>
<section id="ø---mid-high-front-rounded" class="vowel null">
<h4>/ø/ - Mid-High Front Rounded</h4>
<audio src="phonemes/null.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<em>None</em>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>eu</strong>ro, p<strong>eu</strong>
</td>
</tr>
</table>
</section>
<section id="ɛ---mid-low-front-unrounded" class="vowel epsilon">
<h4>/ɛ/ - Mid-Low Front Unrounded</h4>
<audio src="phonemes/epsilon.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
M<strong>eh</strong>, l<strong>e</strong>t
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>e</strong>lle, t<strong>e</strong>nis, fil<strong>e</strong>t
</td>
</tr>
</table>
</section>
<section id="œ---mid-low-front-rounded" class="vowel oe">
<h4>/œ/ - Mid-Low Front Rounded</h4>
<audio src="phonemes/oe.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<em>None</em>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>œu</strong>vre, h<strong>eu</strong>re, c<strong>œu</strong>r
</td>
</tr>
</table>
</section>
<section id="ɪ---high-front-tensed-unrounded" class="vowel II">
<h4>/ɪ/ - High Front Tensed Unrounded</h4>
<audio src="phonemes/I2.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<strong>i</strong>t, b<strong>i</strong>t
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<em>None</em>
</td>
</tr>
</table>
</section>
<section id="æ---low-mid-low-front-unrounded" class="vowel ae">
<h4>/æ/ - Low-Mid-Low Front Unrounded</h4>
<audio src="phonemes/ae.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<strong>a</strong>t, <strong>a</strong>bdicate
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<em>None</em>
</td>
</tr>
</table>
</section>
<section id="a---low-front-unrounded" class="vowel a">
<h4>/a/ - Low Front Unrounded</h4>
<audio src="phonemes/a.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<em>None</em>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
L<strong>à</strong>, P<strong>a</strong>ris
</td>
</tr>
</table>
</section>
<section id="ə---schwa" class="vowel schwa">
<h4>/ə/ - Schwa</h4>
<audio src="phonemes/schwa.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
<strong>a</strong>bout, mem<strong>o</strong>ry
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
j<strong>e</strong>, ch<strong>e</strong>val
</td>
</tr>
</table>
</section>
<section id="u---high-back-rounded" class="vowel u">
<h4>/u/ - High Back Rounded</h4>
<audio src="phonemes/u.mp3">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
tr<strong>ue</strong>, n<strong>ew</strong>
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>ou</strong>, v<strong>ou</strong>s
</td>
</tr>
</table>
</section>
<section id="ʊ---high-back-tensed-rounded" class="vowel upsilon">
<h4>/ʊ/ - High Back Tensed Rounded</h4>
<audio src="phonemes/upsilon.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
p<strong>u</strong>t, w<strong>oo</strong>d
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<em>None</em>
</td>
</tr>
</table>
</section>
<section id="o---mid-high-back-rounded" class="vowel o">
<h4>/o/ - Mid-High Back Rounded</h4>
<audio src="phonemes/o.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
wr<strong>o</strong>te, b<strong>oa</strong>t
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<strong>ô</strong>ter, m<strong>o</strong>t
</td>
</tr>
</table>
</section>
<section id="ʌ---mid-low-back-unrounded" class="vowel wedge">
<h4>/ʌ/ - Mid-Low Back Unrounded</h4>
<audio src="phonemes/wedge.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
n<strong>u</strong>t, str<strong>u</strong>t
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
<em>None</em>
</td>
</tr>
</table>
</section>
<section id="ɔ---mid-low-back-rounded" class="vowel reverse-c">
<h4>/ɔ/ - Mid-Low Back Rounded</h4>
<audio src="phonemes/reverse-c.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
c<strong>au</strong>ght, b<strong>ou</strong>ght
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
d<strong>o</strong>nner, f<strong>o</strong>rt
</td>
</tr>
</table>
</section>
<section id="ɑ---low-back-unrounded" class="vowel hatless-a">
<h4>/ɑ/ - Low Back Unrounded</h4>
<audio src="phonemes/hatless-a.ogg">
</audio>
<table>
<tr>
<td>
English:
</td>
<td>
h<strong>o</strong>t, n<strong>o</strong>t, b<strong>a</strong>th (Received Pronunciation)
</td>
</tr>
<tr>
<td>
French:
</td>
<td>
p<strong>a</strong>s, p<strong>â</strong>te
</td>
</tr>
</table>
</section>
<style> .middle, .round, .unround { transform: scale(1.4); cursor: pointer; } .vowel-chart { width: 30em; position: relative; height: 14em; overflow: hidden; } .vowel-chart .row { display: flex; width: 100%; justify-content: space-between; float: right; clear: both; box-sizing: border-box; height: 2em; position: relative; line-height: 2em; } .vowel-chart .row:nth-child(odd):after { content: " "; position: absolute; top: 1em; left: 0em; right: 0em; border: 1px solid #ddd; z-index: -1; } .vowel-chart .col { width: 6em; position: relative; } .vowel-chart .col span { box-sizing: border-box; display: inline-block; width: 3em; text-align: center; } .vowel-chart:after { content: " "; position: absolute; top: 0px; bottom: 0px; right: 3em; border: 1px solid #ddd; } .vowel-chart:before{ content: " "; position: absolute; top: 0px; height: 18em; left: 2em; border: 1px solid #ddd; transform-origin: 0% 0%; transform: rotate(-37deg); } .vowel-chart .mid-bar { content: " "; position: absolute; top: 0px; height: 18em; left: calc(50% - .5em); border: 1px solid #ddd; transform-origin: 0% 0%; transform: rotate(-21deg); z-index: -1; } .vowel-chart .col .middle { margin: auto; width: 100%; } .vowel-chart .row:nth-child(2) { width: 90%; padding-left: 15%; padding-right: 15%; } .vowel-chart .row:nth-child(3) { width: 90%; } .vowel-chart .row:nth-child(4) { width: 85%; } .vowel-chart .row:nth-child(5) { width: 80%; } .vowel-chart .row:nth-child(6) { width: 75%; } .vowel-chart .row:nth-child(7) { width: 70%; } .en { color: #00FF00; } .fr { color: #FF0000; } .end { color: #FF9955; } </style>
<div class="vowel-chart">
<div class="row">
<span class="col"> <span class="unround" onclick="revealVowel('i')">i</span><span class="round fr" onclick="revealVowel('y')">y</span> </span> <span class="col"> <span class="unround"></span><span class="round"></span> </span> <span class="col"> <span class="unround"></span><span class="round" onclick="revealVowel('u')">u</span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround en" onclick="revealVowel('II')">ɪ</span><span class="round"></span> </span> <span class="col"> <span class="unround"></span><span class="round"></span> </span> <span class="col"> <span class="unround"></span><span class="round en" onclick="revealVowel('upsilon')">ʊ</span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround" onclick="revealVowel('e')">e</span><span class="round fr" onclick="revealVowel('null')">ø</span> </span> <span class="col"> <span class="unround"></span><span class="round"></span> </span> <span class="col"> <span class="unround"></span><span class="round" onclick="revealVowel('o')">o</span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround"></span><span class="round"></span> </span> <span class="col"> <span class="middle" onclick="revealVowel('schwa')">ə</span> </span> <span class="col"> <span class="unround"></span> <span class="round"></span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround" onclick="revealVowel('epsilon')">ɛ</span><span class="round fr" onclick="revealVowel('oe')">œ</span> </span> <span class="col"> <span class="unround"></span><span class="round"></span> </span> <span class="col"> <span class="unround en" onclick="revealVowel('wedge')">ʌ</span><span class="round end" onclick="revealVowel('reverse-c')">ɔ</span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround en" onclick="revealVowel('ae')">æ</span><span class="round en fr"></span> </span> <span class="col"> <span class="unround en"></span><span class="round"></span> </span> <span class="col"> <span class="unround"></span><span class="round"></span> </span>
</div>
<div class="row">
<span class="col"> <span class="unround end" onclick="revealVowel('a')">a</span><span class="round en fr"></span> </span> <span class="col"> <span class="unround en"></span><span class="round"></span> </span> <span class="col"> <span class="unround" onclick="revealVowel('hatless-a')">ɑ</span><span class="round"></span> </span>
</div>
<div class="mid-bar">
</div>
</div>
<!--script>
function revealVowel(vowel) {
console.log(vowel);
for (e of document.querySelectorAll(".vowel")) {
e.style.display = "none";
}
for (e of document.querySelectorAll("." + vowel)) {
e.style.display = "block";
e.querySelector('audio').play();
}
}
</script-->
<table>
<tr>
<td>
Black
</td>
<td>
Exists in English and French
</td>
</tr>
<tr>
<td style="color: #00FF00">
Green
</td>
<td>
Exists only in English
</td>
</tr>
<tr>
<td style="color: #FF0000">
Red
</td>
<td>
Exists only in French
</td>
</tr>
<tr>
<td style="color: #FF9955">
Orange
</td>
<td>
Exists in French and some dialects of English
</td>
</tr>
</table>
<hr />
<p>English has a lot of vowels<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a>, so most of the ones used in French should already be familiar. There are, however, 3-5 that you have to learn (not including diphthongs or nasalizations): /y/, /ø/, /œ/, /a/, and /ɔ/.</p>
<h3 id="y-ø-and-œ">/y/, /ø/, and /œ/</h3>
<p>Fortunately, the first three new vowels are just rounded versions of vowels that exist in English.</p>
<p>/y/ is the vowel sound in French words like « t<strong>u</strong> », « v<strong>u</strong> », « <strong>u</strong>nité », and « b<strong>u</strong>reau ». Do not be confused by the notation: it does not make a sound like the letter ‘y’. Instead, it is the rounded equivalent of /i/, the sound in French words like « s<strong>i</strong> », « <strong>î</strong>le », and « f<strong>i</strong>nir » or English words like “m<strong>e</strong>,” “tr<strong>ee</strong>,” “s<strong>ea</strong>,” and “funn<strong>y</strong>.” Note that the spelling is totally inconsistent: it’s the sound we’re talking about, not the letter.</p>
<p>To make a /y/ sound, start by saying “me” and hold it. “Meeeee—”. Then, without changing anything, make your lips into an ‘o’ shape, as if you were going to start whistling. Then transition into saying « —uuuu<em>unité</em> ». Maintain the mouth position and say « unité » again. It should now sound similar to the same word <a href="https://forvo.com/search/unit%C3%A9/">said by a native speaker</a>.</p>
<p>You can follow the same process for /œ/ and /ø/.</p>
<p>/œ/ is the rounded version of /ɛ/, which is the sound in “l<strong>e</strong>t,” “<strong>e</strong>tymology,” and “m<strong>eh</strong>”. Hold “meeeeeeeeeh—”, then round your lips, and transition to « —oooo<em>oœuvre</em> ». This is a nice case where the IPA transcriptions lines up with the spelling, as in words like « cœur » and « mœurs ». However, it’s also the sound when ‘eu’ comes in the middle of a word, as in « heure » and « seul ».</p>
<p>/ø/ is between /œ/ and /y/. It’s the rounded version of /e/, which is the sound in “f<strong>a</strong>ce” and “m<strong>a</strong>y”. Hold “Maaaaaaaaay—” but try not to pronounce the “y” if it comes out in your dialect. Round your lips, then transition to « —eee<em>eux-mêmes</em> ».</p>
<h3 id="ɔ">/ɔ/</h3>
<p>You might already know this vowel. To test if it’s in your dialect, say “cot,” (as in: a little bed) followed by “caught.” If it sounds different, then you probably already know /ɔ/. It’s the vowel in “caught.” In French, it appears in words like « fort », « donner », and « sol ».</p>
<p>If you don’t have it in your dialect, just round your lips as you say the vowel in “cot,” just as in the examples above. Compare to a <a href="https://forvo.com/word/caught/#en_uk">UK English accent</a>.</p>
<h3 id="a">/a/</h3>
<p>In most dialects of English, /a/ (distinct from /ɑ/) appears only in diphthongs, where it blends with another vowel, as in “price” (/praɪs/) or “mouth” (/maʊθ/). In French, /a/ appears in words like « <strong>a</strong>mi », « P<strong>a</strong>ris », and « l<strong>à</strong> ».</p>
<p>To pronounce /a/, first note that it’s at the very bottom of the vowel chart. The lowest front vowel in English is /æ/ as in “c<strong>a</strong>t.” Start with that sound, then open your mouth even wider. It should sound sort of like the ‘a’ in “la” or “ma,” but with the tongue further towards the front of the mouth.</p>
<p>In some dialects of French, /a/ and /ɑ/ have merged into one vowel that’s in the middle between the front and back. You might think that this means you can get away with just using the English /ɑ/ everywhere, but I’ve been told by a Parisian that Americans are always pronouncing our ’a’s wrong, so you should be careful.</p>
<h3 id="nasalizations">Nasalizations</h3>
<p>If you’ve been studying French for a while, you’ve probably noticed that ‘m’s and ’n’s at the ends of words aren’t usually pronounced. The most obvious example is « faim » vs « femme ». If you pronounce the ’m’ in « faim », you may end up saying “I have woman” instead of “I’m hungry.”</p>
<p>However, the ’m’s and ’n’s aren’t totally silent either. So what’s going on? To understand, note that both sounds, /m/ and /n/ in IPA, are nasal. When you say “emmmmmmmm,” notice that your mouth is closed, and the air comes out of your nose.</p>
<p>When an ‘m’ or ‘n’ comes at the end of a French word, instead of being pronounced itself, it causes the vowel before it to nasalize, meaning that some of the air will come out of your nose instead of your mouth.</p>
<p>In IPA, a vowel will have a small tilde (˜) above it to indicate that it’s nasalized. Thus, « faim » is written /fɛ̃/, while « femme » is written /fɛm/.</p>
<h3 id="minimal-pairs">Minimal pairs</h3>
<p>The above « femme/faim » contrast in an example of a minimal pair: a pair of words that differ in only one sound. These are very useful for studying languages, an can help you practice you pronunciation. The tables below show commonly confused sounds which you can practice differentiating.</p>
<h4 id="y-vs.-u">/y/ vs. /u/</h4>
<p>English speakers usually mistake /y/ for /u/, the sound in English words like “l<strong>o</strong>se”, “t<strong>u</strong>be”. Remember: /y/, as in « vu » is with round lips.</p>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/y/</th>
<th style="text-align: left;">/u/</th>
<th style="text-align: left;">Contrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">v<strong>u</strong></td>
<td style="text-align: left;">v<strong>ou</strong>s</td>
<td style="text-align: left;">« Avez-vous vu ça ? »</td>
</tr>
<tr class="even">
<td style="text-align: left;">t<strong>u</strong></td>
<td style="text-align: left;">t<strong>ou</strong>s</td>
<td style="text-align: left;">« As-tu tous les papiers ? »</td>
</tr>
<tr class="odd">
<td style="text-align: left;">d<strong>u</strong></td>
<td style="text-align: left;">d<strong>ou</strong>x</td>
<td style="text-align: left;">« Château du Doux »<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a></td>
</tr>
<tr class="even">
<td style="text-align: left;">l<strong>u</strong></td>
<td style="text-align: left;">l<strong>ou</strong>p</td>
<td style="text-align: left;"></td>
</tr>
<tr class="odd">
<td style="text-align: left;">dess<strong>u</strong>s</td>
<td style="text-align: left;">dess<strong>ou</strong>s</td>
<td style="text-align: left;"></td>
</tr>
<tr class="even">
<td style="text-align: left;">b<strong>û</strong>che</td>
<td style="text-align: left;">b<strong>ou</strong>che</td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
</div>
<h4 id="e-vs.-ø">/e/ vs. /ø/</h4>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/e/</th>
<th style="text-align: left;">/ø/</th>
<th style="text-align: left;">Contrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>e</strong>t</td>
<td style="text-align: left;"><strong>eux</strong></td>
<td style="text-align: left;">« C’est entre nous et eux. »</td>
</tr>
<tr class="even">
<td style="text-align: left;">h<strong>é</strong>ros</td>
<td style="text-align: left;"><strong>eu</strong>ro</td>
<td style="text-align: left;">« Un héros européen »</td>
</tr>
<tr class="odd">
<td style="text-align: left;">dout<strong>er</strong></td>
<td style="text-align: left;">dout<strong>eux</strong></td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
</div>
<div class="pretty-table">
<h4 id="o-vs.-ø">/o/ vs. /ø/</h4>
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/o/</th>
<th style="text-align: left;">/ø/</th>
<th style="text-align: left;">Contrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">d<strong>os</strong></td>
<td style="text-align: left;">d<strong>eux</strong></td>
<td style="text-align: left;">« la bête à deux dos »</td>
</tr>
<tr class="even">
<td style="text-align: left;">p<strong>eau</strong></td>
<td style="text-align: left;">p<strong>eu</strong></td>
<td style="text-align: left;">« le symptôme : peau peu chaude »</td>
</tr>
<tr class="odd">
<td style="text-align: left;">chev<strong>aux</strong></td>
<td style="text-align: left;">chev<strong>eux</strong></td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
<h4 id="ɛ-vs.-œ">/ɛ/ vs. /œ/</h4>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/ɛ/</th>
<th style="text-align: left;">/œ/</th>
<th style="text-align: left;">Constrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">s<strong>e</strong>l</td>
<td style="text-align: left;">s<strong>eu</strong>l</td>
<td style="text-align: left;">« Le sel de Guérande, c’est le seul sel. »</td>
</tr>
<tr class="even">
<td style="text-align: left;">s<strong>e</strong>rt</td>
<td style="text-align: left;">s<strong>œu</strong>r</td>
<td style="text-align: left;">« La sœur sert le dîner. »</td>
</tr>
<tr class="odd">
<td style="text-align: left;">m<strong>e</strong>r</td>
<td style="text-align: left;">m<strong>œu</strong>rs</td>
<td style="text-align: left;"></td>
</tr>
<tr class="even">
<td style="text-align: left;">p<strong>ai</strong>r</td>
<td style="text-align: left;">p<strong>eu</strong>r</td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
</div>
<h4 id="ø-vs.-ɔ">/ø/ vs. /ɔ/</h4>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/ø/</th>
<th style="text-align: left;">/ɔ/</th>
<th style="text-align: left;">Contrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">n<strong>eu</strong>tre</td>
<td style="text-align: left;">n<strong>o</strong>tre</td>
<td style="text-align: left;">« notre Neutre se cherche par rapport au paradigme »<a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a></td>
</tr>
</tbody>
</table>
</div>
<h4 id="e-vs-ɛ">/e/ vs /ɛ/</h4>
<p>These both exist in English, but the contrast might not be obvious. With /e/, the tongue and pitch will be slightly higher.</p>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/ɛ/</th>
<th style="text-align: left;">/e/</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>ai</strong>t</td>
<td style="text-align: left;"><strong>e</strong>t</td>
</tr>
<tr class="even">
<td style="text-align: left;">all<strong>ait</strong></td>
<td style="text-align: left;">all<strong>er</strong></td>
</tr>
<tr class="odd">
<td style="text-align: left;">cot<strong>ait</strong></td>
<td style="text-align: left;">cot<strong>é</strong></td>
</tr>
<tr class="even">
<td style="text-align: left;">vol<strong>et</strong></td>
<td style="text-align: left;">vol<strong>er</strong></td>
</tr>
<tr class="odd">
<td style="text-align: left;">av<strong>ait</strong></td>
<td style="text-align: left;">av<strong>ez</strong></td>
</tr>
</tbody>
</table>
</div>
<h4 id="ɛ-vs.-ɛ">/ɛ/ vs. /ɛ̃/</h4>
<div class="pretty-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">/ɛ/</th>
<th style="text-align: left;">/ɛ̃/</th>
<th style="text-align: left;">Contrastive Phrase</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">s<strong>e</strong>c</td>
<td style="text-align: left;">c<strong>in</strong>q</td>
<td style="text-align: left;">« En cinq sec. »</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>ai</strong>de</td>
<td style="text-align: left;"><strong>In</strong>de</td>
<td style="text-align: left;">« Pour l’Inde, l’aide est apportée. »</td>
</tr>
<tr class="odd">
<td style="text-align: left;">p<strong>aix</strong></td>
<td style="text-align: left;">p<strong>ain</strong></td>
<td style="text-align: left;">« Paix, pain, liberté ! »</td>
</tr>
<tr class="even">
<td style="text-align: left;">s<strong>ais</strong></td>
<td style="text-align: left;">s<strong>ein</strong></td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
</div>
<h3 id="a-final-note">A Final Note</h3>
<p>You might have noticed some patterns in how spelling relates to pronunciation. As it turns out, although there are usually several ways to spell the same vowel sound, French is surprisingly consistent and <a href="https://www.ipasource.com/media/ipasource/cms/extra/diction/French%20Charts.pdf">regular</a> in vowel spelling. It is almost always possible to correctly infer the vowel in a word just based on its orthography. I was surprised to learn this, because until recently I was only dimly aware of the vowel sounds that exist in the language. This is why phonetics is so important: it makes you aware of the limitations in your understanding of a language in order to overcome them.</p>
<section class="footnotes" role="doc-endnotes">
<hr />
<ol>
<li id="fn1" role="doc-endnote"><p>Besides that fact that their orthographic systems were designed before linguistics even existed, English and French have weird spelling systems because in many cases spellings are based on the history of the word rather than its current realization. For instance, the “gh” in “through” and “plough” was once pronounced, but has fallen out of the language. Also, there’s the fact that the alphabet was inherited from Latin, which had a completely different set of sounds.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2" role="doc-endnote"><p>Really, the same sound can have multiple symbols due to broad transcription. However, IPA is much closer to a 1-to-1 mapping than any other alphabet.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3" role="doc-endnote"><p>Technically, slashes indicate that the transcription uses only symbols that are contrastive in the language. If a feature is transcribed but not contrastive in the language, such as aspiration in English, then the transcription should be put between brackets: […].<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4" role="doc-endnote"><p>English also contrasts tense and lax vowels: compare “eat” with “it.” However, this contrast does not exist in French, so it can be set aside.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn5" role="doc-endnote"><p>Sometimes, high vowels are called “close,” and low vowels are called “open.”<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6" role="doc-endnote"><p>French actually has more vowels than English, but most of them are just nasalized version of other vowels. English also has tons of diphthongs. Both languages have an unusually high number of vowels: Spanish and Greek, by contrast, <a href="https://www.eupedia.com/linguistics/number_of_phonemes_in_european_languages.shtml">have only five</a>.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7" role="doc-endnote"><p><a href="https://fr.wikipedia.org/wiki/Altillac#Lieux_et_monuments">A castle in the Altillac commune</a>.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8" role="doc-endnote"><p>Roland Barthes, <em>Le Neutre</em><a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>