Charged with responsibilities, bound by commitments, and consumed by
+troubles, all the while racing to meet the incessant march of time, we
+often lose sight of what life really is and what is important within it.
+
The following videos helped me see the life beyond that race. First, the
+four reminders taught me how to distinguish between the life and the
+race. Second, the six paramitas taught me how to develop the life
+while still staying in the race. Finally, the one universe put the
+life, the race, and the self into perspective on the grandest of scales.
+
Thus I invite you to watch them (they are short videos: not longer than
+ten minutes each) at your convenience if you wish to develop yourself
+further while keeping the life, the race, the self, and the universe all
+in perspective. :-)
+
+
Four reminders
+
Reminded by Ringu Tulku:
+
+
+
Life is precious
+
Impermanence
+
Samsara
+
Karma
+
+
+
+
+
+
If we are reminded of these four things, and if we really integrate these four
+understandings or change our attitudes along these four ways, that alone can
+transform our life: we can each become a more responsible/joyful/understanding
+person. Therefore, these four reminders are said to be the first thing we try
+to reflect on, if we want to transform ourselves.
+
+
Life is precious
+
How happy/satisfied we are depends on how much we appreciate what we have.
+It’s not about the problems that we have, but how much we concentrate on the
+problems that makes us unhappy, sad, and depressed. Therefore, one of the main
+reminders is that we need to appreciate what we have.
+
That is the first principle to transform our life. And if we really work on
+this, this alone can also transform our way of feeling.
+
+
Impermanence
+
Everything changes: there’s nothing that doesn’t change. Good times can
+change, but bad times can also change. So even when I go through the most
+difficult times, I need to remind myself that there’s nothing which doesn’t
+change.
+
Everything changes: that’s the nature of the life/phenomenon. The more we see
+this clearly, the more we know that nothing really exists… in the same
+way all the time, and the more we can let things be.
+
And we also understand that life is change: if there’s no change, there’s
+no life/development/living. Therefore, we need to let things happen:
+there’s no use holding on too much to either negative/bad/hurtful things
+that happened to us or good/positive things that might be with us sometimes.
+
If we really understand this deeply (the impermanence: that everything is
+changing) and really integrate it with our life, this could also transform our
+life. That’s the second reminder.
+
+
Samsara
+
Samsara means that all of us, human beings, have weaknesses/problems and we
+need to understand that nobody is perfect. There’s lots of
+pain/suffering/problems around us in the world. When we understand that
+deeply, we don’t need to expect everything to be perfect.
+
The more we understand that, the more we appreciate the little good things
+that people do for us: if we find someone being a little nicer/kinder, we can
+appreciate it more because we know that people can be mean, have problems,
+and carry lots of negative feelings. So the more we understand these
+weaknesses (the Samsaric state of mind: that there are the
+problems/pain/weaknesses in people), the more we can understand how to
+appreciate the positive things.
+
Also, if people do negative things, we can still be compassionate/forgiving
+to them because of the Samsaric way of being we all have (with all the
+negative emotions/ignorance/selfishness). We all have
+greed/aggression/ignorance and that allow us to feel more compassionate to
+people, even those who are doing not-so-positive things.
+
The more we understand Samsara, the more we feel at ease living with other
+people because we don’t expect too much from everybody and also because we
+don’t expect too much from ourselves. This is the third reminder.
+
+
Karma
+
Everything has causes/conditions and happens interdependently/relatively: what
+I am now is because of my past and what I will be in the future is because of
+my present so my future is, in a way, in my hands. Therefore, I have to
+take responsibility for myself and think/act in a proper/careful way because
+how I act with my body, speech, and mind is very important not only for my
+own future but also because it affects the world/people around me.
+
+
Six paramitas
+
Explained by Ringu Tulku:
+
+
+
Giving
+
Conduct
+
Patience
+
Diligence
+
Meditation
+
Wisdom
+
+
+
+
+
+
+
One universe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/4digit-base36-timestamp.html b/4digit-base36-timestamp.html
new file mode 100644
index 0000000..c5a5f89
--- /dev/null
+++ b/4digit-base36-timestamp.html
@@ -0,0 +1,1496 @@
+Encoding four base 36 digits as a timestamp - The Terminal Programmer
Convert a string containing 4 uppercase alphanumeric characters, whose
+values range from A through Z and 0 through 9, into a “HH:MM:SS”
+(24 hours, 60 minutes, 60 seconds) timestamp whose value ranges from
+“00:00:00” through “23:59:59”.
+
The string has 1679616 possible values (36 × 36 × 36 ×
+36) whereas the timestamp has 86400 possible values (24 × 60 ×
+60). Thus the challenge is to convey more information using less.
+
How would you solve this problem? Pause for a moment to think about it.
+In fact, go now and implement your solution before reading about my
+particular approach and solution below, as this is quite a fun puzzle! :)
+
+
Approach
+
Each character in the input string can be represented as a base 36 digit:
+
+
+
Characters 0 through 9 become integers 0 through 9 respectively.
+
Characters A through Z become integers 10 through 35 respectively.
+
+
Let’s examine the base 36 and binary representations of each character:
+
+
+
+
Character
+
Base 36
+
Bit 6
+
Bit 5
+
Bit 4
+
Bit 3
+
Bit 2
+
Bit 1
+
+
+
+
0
+
0
+
.
+
.
+
.
+
.
+
.
+
0
+
+
+
1
+
1
+
.
+
.
+
.
+
.
+
.
+
1
+
+
+
2
+
2
+
.
+
.
+
.
+
.
+
1
+
0
+
+
+
3
+
3
+
.
+
.
+
.
+
.
+
1
+
1
+
+
+
4
+
4
+
.
+
.
+
.
+
1
+
0
+
0
+
+
+
5
+
5
+
.
+
.
+
.
+
1
+
0
+
1
+
+
+
6
+
6
+
.
+
.
+
.
+
1
+
1
+
0
+
+
+
7
+
7
+
.
+
.
+
.
+
1
+
1
+
1
+
+
+
8
+
8
+
.
+
.
+
1
+
0
+
0
+
0
+
+
+
9
+
9
+
.
+
.
+
1
+
0
+
0
+
1
+
+
+
A
+
10
+
.
+
.
+
1
+
0
+
1
+
0
+
+
+
B
+
11
+
.
+
.
+
1
+
0
+
1
+
1
+
+
+
C
+
12
+
.
+
.
+
1
+
1
+
0
+
0
+
+
+
D
+
13
+
.
+
.
+
1
+
1
+
0
+
1
+
+
+
E
+
14
+
.
+
.
+
1
+
1
+
1
+
0
+
+
+
F
+
15
+
.
+
.
+
1
+
1
+
1
+
1
+
+
+
G
+
16
+
.
+
1
+
0
+
0
+
0
+
0
+
+
+
H
+
17
+
.
+
1
+
0
+
0
+
0
+
1
+
+
+
I
+
18
+
.
+
1
+
0
+
0
+
1
+
0
+
+
+
J
+
19
+
.
+
1
+
0
+
0
+
1
+
1
+
+
+
K
+
20
+
.
+
1
+
0
+
1
+
0
+
0
+
+
+
L
+
21
+
.
+
1
+
0
+
1
+
0
+
1
+
+
+
M
+
22
+
.
+
1
+
0
+
1
+
1
+
0
+
+
+
N
+
23
+
.
+
1
+
0
+
1
+
1
+
1
+
+
+
O
+
24
+
.
+
1
+
1
+
0
+
0
+
0
+
+
+
P
+
25
+
.
+
1
+
1
+
0
+
0
+
1
+
+
+
Q
+
26
+
.
+
1
+
1
+
0
+
1
+
0
+
+
+
R
+
27
+
.
+
1
+
1
+
0
+
1
+
1
+
+
+
S
+
28
+
.
+
1
+
1
+
1
+
0
+
0
+
+
+
T
+
29
+
.
+
1
+
1
+
1
+
0
+
1
+
+
+
U
+
30
+
.
+
1
+
1
+
1
+
1
+
0
+
+
+
V
+
31
+
.
+
1
+
1
+
1
+
1
+
1
+
+
+
W
+
32
+
1
+
0
+
0
+
0
+
0
+
0
+
+
+
X
+
33
+
1
+
0
+
0
+
0
+
0
+
1
+
+
+
Y
+
34
+
1
+
0
+
0
+
0
+
1
+
0
+
+
+
Z
+
35
+
1
+
0
+
0
+
0
+
1
+
1
+
+
+
Notice that bit 6 is present in only 4 characters, W through Z, and
+absent in all others. Thus, if we randomly picked a character from the
+set of 36 possible characters, the probability of finding bit 6 in our
+pick is 4 ÷ 36, or 0.1111111111111111, which approximately denotes a
+11% chance of success.
+
In contrast, notice that bit 1 is present in every character. Thus, if
+we randomly picked a character from the set of 36 possible characters, the
+probability of finding bit 1 in our pick is 36 ÷ 36, or 1, which
+denotes a 100% chance of success.
+
These facts are recorded in the table below.
+
+
+
+
Bit
+
Number of characters present in
+
Probability of presence
+
+
+
+
6
+
4
+
0.1111111111111111
+
+
+
5
+
20
+
0.5555555555555556
+
+
+
4
+
28
+
0.7777777777777778
+
+
+
3
+
32
+
0.8888888888888888
+
+
+
2
+
34
+
0.9444444444444444
+
+
+
1
+
36
+
1
+
+
+
The timestamp, with its 86400 possible values, cannot represent all
+1679616 values possible in the string unless we reuse some of them. That
+is, instead of mapping each unique string value to exactly one unique
+timestamp value, we map multiple unique string values to the same
+unique timestamp value, thereby reusing it.
+
This reuse happens automatically when we discard information. For example,
+if we discard the fourth character from two different strings “ABCD” and
+“ABCX”, then they would both become “ABC”. Next, if the string “ABC” were
+mapped to the timestamp “01:02:03”, then both strings “ABCD” and “ABCX”
+would map to the same “01:02:03” timestamp, thereby achieving reuse.
+
We can discard information strategically based on the probabilities
+listed in the table above. In particular, we can discard bits with lower
+presence probabilities with less risk of information loss because
+discarding nonexistent bits from a value does not change it. For example,
+bit 6 is present 11% of the time, so we only risk information loss 11% of
+the time by discarding it. In contrast, bit 1 is present 100% of the time,
+so we risk information loss 100% of the time by discarding it.
+
In binary, the string occupies 24 bits (4 characters × 6 bits per
+character) and the timestamp occupies at least 16 bits (216 < 86400
+possible values < 217). Therefore, we need to discard at least 8 bits
+(24 string bits - 16 timestamp bits) of information from the string to fit
+it within the timestamp’s 16 bits. This translates to 2 bits (8 bits
+discarded ÷ 4 characters) discarded per character.
+
Since lower bits are more likely to be present than higher ones, as the
+table above shows, we should keep as many lower bits as possible and
+discard as few higher bits as necessary. Thus, we shall discard the upper
+2 bits (bit 6 and 5) from each character in the string to fit the string
+within the timestamp.
+# Converts the given string into a "HH:MM:SS" timestamp.
+defsolution(string)
+ digits=digits36(string)
+ num_seconds=compress(*digits)
+ Time.at(num_seconds).utc.strftime('%H:%M:%S')
+end
+
+# Converts each character in the given string into a base 36 digit.
+defdigits36(string)
+ string.each_char.map{|char|digit36(char)}
+end
+
+# Converts the given character into a base 36 digit:
+# decimal digits (0..9) + uppercase letters (10..35)
+defdigit36(char)
+ casechar
+ when'0'..'9'thenchar.ord-'0'.ord
+ when'A'..'Z'thenchar.ord-'A'.ord+10
+ elseraiseArgumentError,"not a base36 digit: #{char.inspect}"
+ end
+end
+
+# Combines the given base 36 digits together into a 16-bit integer.
+SECONDS_PER_DAY=24*60*60# hours * minutes/hour * seconds/minute
+SECONDS_PER_DAY_BITS=Math.log2(SECONDS_PER_DAY).ceil# 2 ** exponent
+defcompress(*chars)
+ return0ifchars.empty?
+ bits=SECONDS_PER_DAY_BITS/chars.length
+ mask=(1<<bits)-1
+ chars.inject(0)do|acc,char|
+ # grab lower bits from each character and place them side by side
+ (acc<<bits)|(char&mask)
+ end%SECONDS_PER_DAY
+end
+
+
Save the above code to a solution.rb file to play with it:
To evaluate how well the algorithm performs, we feed it different sets of
+input strings and analyze its output: the more input strings it maps to
+unique timestamps, the smaller the reuse probability and the better it is.
In the best case, where all input characters fed into the algorithm lack
+the upper bits it discards, information is never lost. Therefore, the
+probability of timestamp reuse is zero.
+
In the worst case, where all input characters fed into the algorithm
+contain the upper bits it discards, some information is always lost.
+Therefore, the probability of timestamp reuse is greater than zero.
+
In the “no” case, where some input characters fed into the algorithm
+contain the upper bits it discards, information is sometimes lost.
+However, since this input set contains all 1679616 possible input
+strings, timestamps will always be reused: the timestamp, with its 86400
+possible values, cannot represent all 1679616 values possible in the
+string unless we reuse some of them.
+
This observation applies to any such algorithm.
+
+
Comparison
+
My solution to this problem is based on probabilities. However, there are
+many other solutions, some of which are known as hash functions.
+Let’s compare them, along with my solution, to see how well they perform.
+
To compare these algorithms fairly, we need to feed them the same set of
+strings. Unlike the singular evaluation of my solution above, here we
+don’t have the luxury of partitioning the input space into best and worst
+cases up front, because such cases vary wildly across these algorithms.
+
Here, I have chosen to feed all 4-subsets of the 36 possible uppercase
+alphanumeric characters, which produces 58905 unique strings containing
+4 unique uppercase alphanumeric characters each, into these algorithms.
+Although this choice poorly approximates reality, wherein strings may
+contain repeated characters, some such sacrifice is necessary to produce
+a non-exhaustive set of inputs with which to compare these algorithms.
+
The results are recorded in the table below, sorted by reuse probabilities
+in ascending order: from the best result first to the worst result last.
+
The key result to notice here is that my solution had the
+best performance among all of these algorithms. I was delighted to see
+this because I expected it fare far worse than real algorithms. :-)
+
One surprising result is that the ∅ solution, which simply discards
+the fourth character and doesn’t bother with any of this algorithmic
+nonsense, performs better than half of these algorithms. Although its
+performance isn’t praiseworthy, it nevertheless hints that perhaps,
+sometimes, ignoring a problem can be just as effective as solving it.
+
Augh! What am I saying? Those are not the words of a true engineer!
+Problems must always be solved and solved well, so long as it’s fun. ;-)
+Anyway, that’s all folks. I hope you enjoyed this algorithmic treatise.
+But wait! Don’t leave just yet because there are some fun behind the
+scenes goodies waiting for you below! :-)
$ ruby -r ./comparison.rb -r pp -e'pp Comparator.compare Input.combinations'> comparison.txt
+
+$ vim comparison.txt # do some post-processing to format the results into a nice data table
+
You can even define a new algorithm and measure its performance:
+
>>defCompressor.my_new_solution(a,b,c,d)
+>>a+b-c*d
+>>end
+:my_new_solution
+
+>>Comparator.measureInput.combinations,:my_new_solution
+{"Compression algorithm"=>:my_new_solution,
+ "Number of strings (domain)"=>58905,
+ "Number of timestamps (codomain)"=>1183,
+ "Number of unique timestamps"=>7,
+ "Number of timestamps reused"=>1176,
+ "Probability of timestamp reuse"=>0.9940828402366864,
+ "Number of strings mapped to reused timestamps"=>58898,
+ "Probability of string mapped to reused timestamp"=>0.9998811645870469}
+
You can also compare it to other algorithms, just as this article did:
+
>>Comparator.compareInput.combinations
+# ... too much output, so it's omitted ...
+
If that’s too much, selectively compare just the algorithms you want:
+
>>Comparator.compareInput.combinations,/my_new|char/
+[{"Compression algorithm"=>:merge_fourth_char_into_others,
+ "Number of strings (domain)"=>58905,
+ "Number of timestamps (codomain)"=>9936,
+ "Number of unique timestamps"=>1841,
+ "Number of timestamps reused"=>8095,
+ "Probability of timestamp reuse"=>0.8147141706924316,
+ "Number of strings mapped to reused timestamps"=>57064,
+ "Probability of string mapped to reused timestamp"=>0.9687462863933453},
+ {"Compression algorithm"=>:discard_fourth_char,
+ "Number of strings (domain)"=>58905,
+ "Number of timestamps (codomain)"=>6545,
+ "Number of unique timestamps"=>561,
+ "Number of timestamps reused"=>5984,
+ "Probability of timestamp reuse"=>0.9142857142857143,
+ "Number of strings mapped to reused timestamps"=>58344,
+ "Probability of string mapped to reused timestamp"=>0.9904761904761905},
+ {"Compression algorithm"=>:my_new_solution,
+ "Number of strings (domain)"=>58905,
+ "Number of timestamps (codomain)"=>1183,
+ "Number of unique timestamps"=>7,
+ "Number of timestamps reused"=>1176,
+ "Probability of timestamp reuse"=>0.9940828402366864,
+ "Number of strings mapped to reused timestamps"=>58898,
+ "Probability of string mapped to reused timestamp"=>0.9998811645870469}]
+
+# Combines the given base 36 digits together into a 16-bit integer.
+moduleCompressor
+ extendself
+
+ # _ _ _ _ _
+ # _ __ _ _| | | ___ ___ | |_ _| |_(_) ___ _ __
+ # | '_ \| | | | | | / __|/ _ \| | | | | __| |/ _ \| '_ \
+ # | | | | |_| | | | \__ \ (_) | | |_| | |_| | (_) | | | |
+ # |_| |_|\__,_|_|_| |___/\___/|_|\__,_|\__|_|\___/|_| |_|
+ #
+
+ # Why bother with all of this? Just discard the fourth character.
+ defdiscard_fourth_char(a,b,c,d)
+ (a*(36**2))+(b*36)+c
+ end
+
+ # _ _ _
+ # _ __ ___ _ _ ___ ___ | |_ _| |_(_) ___ _ __ ___
+ # | '_ ` _ \| | | | / __|/ _ \| | | | | __| |/ _ \| '_ \/ __|
+ # | | | | | | |_| | \__ \ (_) | | |_| | |_| | (_) | | | \__ \
+ # |_| |_| |_|\__, | |___/\___/|_|\__,_|\__|_|\___/|_| |_|___/
+ # |___/
+
+ defmerge_fourth_char_into_others(a,b,c,d)
+ c+=(d&0b11)
+ b+=(d&0b1100)>>2
+ a+=(d&0b110000)>>4
+ (a*(36**2))+(b*36)+c
+ end
+
+ # This is the solution described in this article. (((THE WINNER!)))
+ defpreserve_lower_bits(a,b,c,d)
+ upper_byte=((a&0xF)<<4)|(b&0xF)
+ lower_byte=((c&0xF)<<4)|(d&0xF)
+ (upper_byte<<8)|lower_byte
+ end
+
+ # Generate preserve_bits_WXYZ() functions that try all combinations.
+ preserve_bits_helper=lambdado|a,b,c,d,bits|
+ fetch_bits=lambdado|char|
+ bits.each_with_index.mapdo|bit,index|
+ char[bit]<<index
+ end.reduce(:|).to_i
+ end
+ upper_byte=(fetch_bits.(a)<<4)|fetch_bits.(b)
+ lower_byte=(fetch_bits.(c)<<4)|fetch_bits.(d)
+ ((upper_byte<<8)|lower_byte)
+ end
+ (0..5).to_a.combination(4).eachdo|bits|
+ define_method"preserve_bits_#{bits.sort.reverse.map(&:next).join}"do
+ |a,b,c,d|preserve_bits_helper.(a,b,c,d,bits)
+ end
+ end
+
+ defpreserve_lower_bits_then_add_upper_bits(a,b,c,d)
+ lower_bits=lambda{|char|(char&0b001111)}
+ upper_bits=lambda{|char|(char&0b110000)>>4}
+ upper_byte=(lower_bits.(a)<<4)|lower_bits.(c)
+ lower_byte=(lower_bits.(b)<<4)|lower_bits.(d)
+ extra_byte=(upper_bits.(a)<<6)|(upper_bits.(c)<<4)|
+ (upper_bits.(b)<<2)|(upper_bits.(d)<<0)
+ ((upper_byte<<8)|lower_byte)+extra_byte
+ end
+
+ defpreserve_lower_bits_then_xor_upper_bits(a,b,c,d)
+ lower_bits=lambda{|char|(char&0b001111)}
+ upper_bits=lambda{|char|(char&0b110000)>>4}
+ xor_helper=lambdado|char|
+ ((upper_bits.(char)^(lower_bits.(char)>>2))<<2)|
+ (lower_bits.(char)&0b11)
+ end
+ upper_byte=(xor_helper.(a)<<4)|xor_helper.(c)
+ lower_byte=(xor_helper.(b)<<4)|xor_helper.(d)
+ ((upper_byte<<8)|lower_byte)
+ end
+
+ # _ _ _ _
+ # _ __ ___ __ _| | ___ ___ | |_ _| |_(_) ___ _ __ ___
+ # | '__/ _ \/ _` | | / __|/ _ \| | | | | __| |/ _ \| '_ \/ __|
+ # | | | __/ (_| | | \__ \ (_) | | |_| | |_| | (_) | | | \__ \
+ # |_| \___|\__,_|_| |___/\___/|_|\__,_|\__|_|\___/|_| |_|___/
+ #
+
+ # https://en.wikipedia.org/wiki/Pearson_hashing
+ PEARSON_TABLE=(0...2**16).to_a.shuffle
+ defpearson_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ index=hash^char
+ hash=PEARSON_TABLE[index]
+ end
+ hash
+ end
+
+ # http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ defrotating_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash=(hash<<4)^(hash>>28)^char
+ end
+ hash
+ end
+
+ # http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ defbernstein_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash=33*hash+char
+ end
+ hash
+ end
+
+ # http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ defbernstein_xor_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash=33*hash^char
+ end
+ hash
+ end
+
+ # http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ defshift_add_xor_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash^=(hash<<5)+(hash>>2)+char
+ end
+ hash
+ end
+
+ # http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1
+ deffnv1_hash(a,b,c,d)
+ hash=2166136261
+ [a,b,c,d].eachdo|char|
+ hash=(hash*16777619)^char
+ end
+ hash
+ end
+
+ # http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1a
+ deffnv1a_hash(a,b,c,d)
+ hash=2166136261
+ [a,b,c,d].eachdo|char|
+ hash=(hash^char)*16777619
+ end
+ hash
+ end
+
+ # https://en.wikipedia.org/wiki/Jenkins_hash_function
+ defjenkins_one_at_a_time_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash+=char
+ hash+=(hash<<10)
+ hash^=(hash>>6)
+ end
+ hash+=(hash<<3)
+ hash^=(hash>>11)
+ hash+=(hash<<15)
+ hash
+ end
+
+ # Algorithm::Nhash - Exim nhash algorithm
+ # http://cpansearch.perl.org/src/MOOLI/Algorithm-Nhash-0.002/lib/Algorithm/Nhash.pm
+ NHASH_PRIMES=[3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,
+ 61,67,71,73,79,83,89,97,101,103,107,109,113]
+ defexim_nhash(a,b,c,d)
+ sum=0
+ i=0
+ [a,b,c,d].eachdo|char|
+ i+=28
+ i%=29
+ sum+=NHASH_PRIMES[i]*char
+ end
+ sum
+ end
+
+ # https://en.wikipedia.org/wiki/Fletcher%27s_checksum
+ deffletcher16_checksum(a,b,c,d)
+ sum1=0
+ sum2=0
+
+ [a,b,c,d].eachdo|char|
+ sum1=(sum1+char)%255
+ sum2=(sum2+sum1)%255
+ end
+
+ (sum2<<8)|sum1
+ end
+
+ # https://en.wikipedia.org/wiki/BSD_checksum
+ defbsd_checksum(a,b,c,d)
+ checksum=0# The checksum mod 2^16.
+ [a,b,c,d].eachdo|char|
+ checksum=(checksum>>1)+((checksum&1)<<15)
+ checksum+=char
+ checksum&=0xffff# Keep it within bounds.
+ end
+ checksum
+ end
+
+ # https://en.wikipedia.org/wiki/Longitudinal_redundancy_check
+ deflrc_checksum(a,b,c,d)
+ lrc=0
+ [a,b,c,d].eachdo|char|
+ lrc=(lrc+char)&0xFF
+ end
+ lrc=(((lrc^0xFF)+1)&0xFF)
+ end
+
+ # http://www.cse.yorku.ca/~oz/hash.html
+ defdjb2_hash(a,b,c,d)
+ hash=5381
+ [a,b,c,d].eachdo|char|
+ hash=((hash<<5)+hash)+char# hash * 33 + c
+ end
+ hash
+ end
+
+ # http://www.cse.yorku.ca/~oz/hash.html
+ defsdbm_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash=char+(hash<<6)+(hash<<16)-hash
+ end
+ hash
+ end
+
+ # http://www.cse.yorku.ca/~oz/hash.html
+ deflose_lose_hash(a,b,c,d)
+ hash=0
+ [a,b,c,d].eachdo|char|
+ hash+=char
+ end
+ hash
+ end
+
+end
+
+moduleSolver
+ extendself
+
+ # Converts the given 4-character string into a "HH:MM:SS" timestamp.
+ defsolution(string,compressor)
+ a,b,c,d=digits36(string)
+ num_seconds=Compressor.send(compressor,a,b,c,d)
+ Time.at(num_seconds).strftime('%H:%M:%S')
+ end
+
+ # Converts each character in the given string into a base 36 digits.
+ defdigits36(string)
+ string.each_char.map{|char|digit36(char)}
+ end
+
+ # Converts the given character into a base 36 digit:
+ # decimal digits (0..9) + uppercase letters (10..35)
+ defdigit36(char)
+ casechar
+ when'0'..'9'thenchar.ord-'0'.ord
+ when'A'..'Z'thenchar.ord-'A'.ord+10
+ else-1
+ end
+ end
+
+ # Converts the given base 36 digit into a character.
+ defundigit36(code)
+ casecode
+ when0..9then('0'.ord+code).chr
+ when10..35then('A'.ord+(code-10)).chr
+ else'?'
+ end
+ end
+
+end
+
+moduleInput
+ extendself
+
+ DOMAIN=('0'..'9').to_a+('A'..'Z').to_a
+
+ defcombinations
+ DOMAIN.combination(4)
+ end
+
+ defpermutations(domain=DOMAIN)
+ ifblock_given?
+ domain.eachdo|a|
+ domain.eachdo|b|
+ domain.eachdo|c|
+ domain.eachdo|d|
+ yield[a,b,c,d]
+ end
+ end
+ end
+ end
+ else
+ enum_for__method__,domain
+ end
+ end
+
+ defbest_case
+ permutationsDOMAIN[0..15]
+ end
+
+ defworst_case
+ permutationsDOMAIN[16..35]
+ end
+
+ aliasno_casepermutations
+
+end
+
+moduleComparator
+ extendself
+
+ # Compares the fitness of all compressors that match the given regexp.
+ defcompare(inputs,compressor_regexp=//)
+ Compressor.singleton_methods.grep(compressor_regexp).
+ map{|compressor|measure(inputs,compressor)}.
+ sort_by{|measurement|measurement['Probability of timestamp reuse']}
+ end
+
+ # Measures the fitness of the given compressor.
+ defmeasure(inputs,compressor)
+ reuse_by_timestamp=measure_reuse(inputs,compressor)
+
+ num_keys=reuse_by_timestamp.count
+ num_values=reuse_by_timestamp.values.reduce(:+).to_i
+
+ actual_reuse=reuse_by_timestamp.select{|k,v|v>1}
+ num_keys_reused=actual_reuse.count
+ num_values_reused=actual_reuse.values.reduce(:+).to_i
+
+ {
+ 'Compression algorithm'=>compressor,
+ 'Number of strings (domain)'=>num_values,
+ 'Number of timestamps (codomain)'=>num_keys,
+ 'Number of unique timestamps'=>num_keys-num_keys_reused,
+ 'Number of timestamps reused'=>num_keys_reused,
+ 'Probability of timestamp reuse'=>num_keys_reused/num_keys.to_f,
+ 'Number of strings mapped to reused timestamps'=>num_values_reused,
+ 'Probability of string mapped to reused timestamp'=>num_values_reused/num_values.to_f,
+ }
+ end
+
+ defmeasure_reuse(inputs,compressor)
+ reuse_by_timestamp=Hash.new(0)
+ inputs.eachdo|characters|
+ string=characters.join
+ timestamp=Solver.solution(string,compressor)
+ reuse_by_timestamp[timestamp]+=1
+ end
+ reuse_by_timestamp
+ end
+
+end
+
+
Updates
\ No newline at end of file
diff --git a/about.html b/about.html
new file mode 100644
index 0000000..14fc327
--- /dev/null
+++ b/about.html
@@ -0,0 +1,655 @@
+About - The Terminal Programmer
“Mastering others is strength. Mastering yourself is true power.” —
+Laozi
+
“A man ought to live in this world like a lotus leaf, which grows in
+water but is never moistened by water.” — Swami Vivekananda
+
“Courage is standing up for what you believe in, even when you cannot
+win.” — Goomoonryong
+
“If you think that you will be inferior in doing something, you will
+be on that road very soon.” — Hagakure
+
“Why curse the darkness when you can light a candle? For all the
+darkness in the world cannot extinguish the light of a single candle!”
+— St. Francis of Assisi
+
“There is something to be learned from a rainstorm. When meeting with
+a sudden shower, you try not to get wet and run quickly along the
+road. But doing such things as passing under the eaves of houses, you
+still get wet. When you are resolved from the beginning, you will not
+be perplexed, though you still receive the same soaking. This
+understanding extends to all things.” — Hagakure
+
“Iron is full of impurities that weaken it; through forging—by
+exposure to heat, cold, and hammering—the impurities are forced out
+and the iron is transformed into razor-sharp steel. Human beings
+develop in the same fashion.” — Morihei Ueshiba
+
“Falling down a flight of one thousand stairs begins with slipping on
+the first step.” — Confucius
+
“Holding onto anger is like grasping a hot coal with the intent of
+throwing it at someone else; you are the one who gets burned.” —
+Buddha
+
“One often meets his destiny on the road he takes to avoid it.” —
+Oogway
+
“I’m tired of trying to do something worthwhile for the human race,
+they simply don’t want to change!” — August Dvorak
+
“Everything you do in life will be insignificant, but it’s very
+important that you do it.” — Mahatma Gandhi
+
“Do good and happiness will follow; but don’t get caught up in your
+good deeds. Forget your past and don’t place any hope on the future.
+Don’t regret the past… because if you do, it will consume you and
+you’ll find no peace.” — Phra Khru Bah, speaking to orphaned children.
+
“You believe a man can change his destiny? — I believe a man does
+what he can, until his destiny is revealed.” — Katsumoto & Algren
+
“He spirals down cold barren space as his safety rope spins silently
+above him in the everlasting expanse of Mt. Everest” — ???
+
“In the Kamigata area, they have a sort of tiered lunchbox they use
+for a single day when flower viewing. Upon returning, they throw them
+away, trampling them underfoot. The end is important in all things.”
+— Hagakure
+
+
+
Notable works
+
+
+
“When you don’t create things, you become defined by your tastes rather
+than ability. Your tastes only narrow and exclude people. So create.”
+— Why the Lucky Stiff
+
+
+
Academic research
+
+
+
“A jug fills drop by drop.” — Buddha
+
+
+
+
Kurapati, S. N. (2007). Specification-driven functional verification
+with Verilog PLI & VPI and SystemVerilog DPI. Masters thesis, University
+of California, Santa Cruz.
+[PDF]
+[HTML]
+
Kurapati, S. N. (2005). A Brief Survey of High-Level Approaches to
+Implementing Distributed Applications. Unpublished manuscript,
+University of California, Santa Cruz.
+[PDF]
+[HTML]
+
Kurapati, S. N. (2004). Addiction to Massively Multi-player On-line
+Games: An Ethical Analysis. Unpublished manuscript, University of
+California, Santa Cruz.
+[PDF]
+[HTML]
inotifytest-elixir:
+Tests your Elixir source code files when they change.
+
inotifytest-ruby:
+Tests your Ruby source code files when they change.
+
inotifytest-racket:
+Tests your Racket source code files when they change.
+
inotifytest-sml:
+Tests your Standard ML source code files when they change.
+
+
+
+
+
Project management
+
+
+
+
floss-rebase:
+Reapplies the stacked branch architecture:
+master => other => patch => minor => major
+
floss-merge:
+Merges all branches up to and including current branch into master branch.
+
floss-changes:
+Prints release notes ranging from the current branch to the master branch.
+
floss-reflect:
+Updates all text files in the project that mention the current version
+and/or date to match the first version and date from the HISTORY_FILE.
+
floss-commit:
+Creates a new commit or updates an existing commit that is labelled with
+the first version and date from the HISTORY_FILE.
+
floss-publish:
+Publishes manuals to the GitHub pages website after a project release.
+
+
+
+
+
Format conversion
+
+
+
+
iterm2xrdb:
+Converts iTerm2 color schemes into xrdb(1) format.
+
xrdb2hterm:
+Converts xrdb(1) color schemes into Chrome Secure Shell preferences.
+
+
+
C programming language
+
+
+
knights-tour:
+Knight’s Tour algorithm parallelized via GPGPU.
+
simple-ftp:
+A rudimentary FTP server and command-line client.
+
+
+
Javascript programming language
+
+
+
jquery-horizoll:
+Horizontal screen scrolling with automatic realignment.
\ No newline at end of file
diff --git a/acer-c720-linux.html b/acer-c720-linux.html
new file mode 100644
index 0000000..db606fa
--- /dev/null
+++ b/acer-c720-linux.html
@@ -0,0 +1,275 @@
+Lubuntu 14.10 on the Acer C720 Chromebook - The Terminal Programmer
For six long months, I used a Crouton chroot (running Debian Sid)
+alongside my Acer C720 Chromebook’s native Chrome OS as my primary
+working environment: the Secure Shell extension provided direct, non-SSH
+terminal access to the chroot, where I did all of my real work, while
+Chrome browser provided a means to access my e-mail and the Internet.
+
However, I wasn’t able to reliably run graphical X11 applications in
+Chrome OS when the
+need arose occasionally (e.g. to display a plot in R or Octave) and, by
+the time a workaround for this problem was identified,
+my growing dissatisfaction with Chrome OS’ limitations and my concerns
+over personal data and activities being collected, stored, and processed
+in Google’s cloud prompted me to completely replace Chrome OS with a
+standard Linux installation.
+
I tried Elementary OS Freya, Xubuntu 14.10, and Lubuntu 14.10 and
+finally settled on the latter because it had the smallest operational
+overhead (in terms of power, CPU, and memory consumption) when my laptop
+was idle, according to Intel’s PowerTOP tool.
+
Now that this configuration has stabilized, having lasted an entire week
+of continuous use (no shutdown, only suspend and resume) without any
+problems, I decided to write this article and share it with the world:
+documented below is how I replaced Chrome OS with Lubuntu 14.10 on my
+Acer C720 Chromebook. May this knowledge liberate you from Google’s
+clutchescloud as well. :-)
Choose “Try Lubuntu without installing” at the USB stick’s boot menu.
+
Press Control+Alt+T to open a terminal and then type in the following:
+
xbacklight = 15 # temporarily reduce laptop's display brightness
+sudo swapoff -a # disable swap to avoid security error later on
+
+
Double-click the “Install Lubuntu 14.10” icon on the desktop to start
+the installation process, in which:
+
+
+
Choose “Erase disk and install Lubuntu”.
+
Choose “Encrypt the new Lubuntu installation for security”. Make
+sure to turn off swap by running sudo swapoff -a before entering
+your encryption passphrase to avoid a security error that aborts
+the installation.
+
Choose “Use LVM with the new Lubuntu installation”.
+
Choose “Continue Testing” instead of rebooting.
+
+
Press Control+Alt+T to open a terminal and then type in the following:
This will absorb the encrypted swap volume into your root volume,
+thereby giving you an extra 4 GiB (or 2 GiB depending on your
+Chromebook model) of disk space to work with.
+
Reboot the computer when you are ready to start using Lubuntu 14.10.
+
Disable encrypted swap, which the root volume absorbed earlier:
+
sudo sed -i 's,^/dev/mapper/lubuntu--vg-swap_1,#&,' /etc/fstab
+
+
Enable TRIM support on the root volume since it’s on a solid state disk:
+
sudo sed -i '/vg-root/s/errors=remount-ro/&,noatime,discard/' /etc/fstab
+
+
+
+
Fixes
+
After successful installation, apply the following fixes.
+
+
Touchpad
+
Enable touchpad by upgrading your Linux kernel to version 3.17 or newer,
+which has the touchpad driver built-in. To do this, download the upgrade
+packages
+(namely the linux-headers*amd64.deb and linux-generic*amd64.deb files)
+and install them using the sudo dpkg -i command.
+
+
Kernel
+
Prevent system freezes that randomly occur under Linux kernel 3.17+:
+
echo 'options i915 semaphores=0' | sudo tee -a /etc/modprobe.d/i915.conf
+
I arrived at the above solution by starting with John Lewis’ suggested
+workaround and
+experimentally removing redundant overrides (according to modinfo i915 |
+grep parm output) that re-specified default values.
+
+
Power
+
Prevent instant shutdown when power key is (accidentally) pressed:
+
echo 'HandlePowerKey=ignore' | sudo tee -a /etc/systemd/logind.conf
+
+
Wi-Fi
+
Enable Wi-Fi power savings and improve connection stability:
+
echo 'options ath9k ps_enable=1 use_chanctx=1 btcoex_enable=1 bt_ant_diversity=1' | sudo tee -a /etc/modprobe.d/ath9k.conf
+
+
Sound
+
Specify the sound card that alsamixer(1) should use by default:
+
echo 'options snd_hda_intel index=1' | sudo tee -a /etc/modprobe.d/alsa.conf
+
Apply the Chromebook keyboard layout from within an X11 session:
+
setxkbmap -model chromebook
+
+
Sleep
+
Make suspend and resume work correcly when lid is closed and opened:
+
+
/etc/rc.local
+
#!/bin/sh -e
+#
+# rc.local
+#
+# This script is executed at the end of each multiuser runlevel.
+# Make sure that the script will "exit 0" on success or any other
+# value on error.
+#
+# In order to enable or disable this script just change the execution
+# bits.
+#
+# By default this script does nothing.
+
+# prevent ehci-pci errors that occur after resume from suspend:
+# [ 386.093443] ehci-pci 0000:00:1d.0: port 2 resume error -19
+# [ 386.093699] ehci-pci 0000:00:1d.0: port 1 resume error -19
+echo-n 0000:00:1d.0 > /sys/bus/pci/drivers/ehci-pci/unbind
+
+/etc/pm/sleep.d/20_acer_c720_chromebook
+
+exit 0
+
+
/etc/pm/sleep.d/20_acer_c720_chromebook
+
#!/bin/sh
+# the following tweaks were suggested by powertop https://01.org/powertop
+
+# turn off bluetooth, which causes OS hangs
+rfkill block bluetooth
+
+# Wireless Power Saving for interface wlan0
+iwconfig wlan0 power on
+
+# NMI watchdog should be turned off
+echo 0 > /proc/sys/kernel/nmi_watchdog
+
+# VM writeback timeout
+echo 1500 > /proc/sys/vm/dirty_writeback_centisecs
+
+# Enable audio codec power management
+echo 1 > /sys/module/snd_hda_intel/parameters/power_save
+
+# Enable SATA link power Management for *
+for policy in /sys/class/scsi_host/*/link_power_management_policy
+do echo min_power >$policy
+done
+
+# Runtime PM for PCI Device *
+for control in /sys/bus/pci/devices/*/power/control
+do echo auto >$control
+done
+
+# Runtime PM for PCI Device Intel Corporation 8 Series USB xHCI HC
+echo auto > /sys/bus/pci/devices/0000:00:14.0/power/control
+
+# Runtime PM for PCI Device Intel Corporation 8 Series USB EHCI #1
+echo auto > /sys/bus/pci/devices/0000:00:1d.0/power/control
+
+
Extras
+
After successful installation, you can set up these extras.
+
+
+
Install skippy-xd to emulate Chrome OS’ window list (F5) key:
\ No newline at end of file
diff --git a/chess-diagrams-in-lyx-with-pdflatex.html b/chess-diagrams-in-lyx-with-pdflatex.html
new file mode 100644
index 0000000..6db00a0
--- /dev/null
+++ b/chess-diagrams-in-lyx-with-pdflatex.html
@@ -0,0 +1,20 @@
+Chess diagrams in LyX with PDFLaTeX - The Terminal Programmer
LyX 1.4.3 fails to generate a PDF with PDFLaTeX if you
+have chess diagrams in your document. The solution is to enable the
+chess package in your LyX document’s LaTeX preamble, as follows.
+
\usepackage{chess}
+
As obvious as this may seem, it took nearly three hours of fiddling
+around and scouring the web before I happened upon this gem of a
+solution.
+
Furthermore, I use the following in my document’s LaTeX preamble to enable
+chess diagrams for presentations with
+LaTeX-beamer (the “Beamer
+Presentation Class” document class in LyX).
+
% for chess diagrams
+\usepackage[ps,mover]{lyxskak}
+\newgame
+\usepackage{chess}% to make it work in pdflatex
+
\ No newline at end of file
diff --git a/compiling-jedit-in-ubuntu-linux.html b/compiling-jedit-in-ubuntu-linux.html
new file mode 100644
index 0000000..bd408cf
--- /dev/null
+++ b/compiling-jedit-in-ubuntu-linux.html
@@ -0,0 +1,32 @@
+Compiling jEdit under Ubuntu - The Terminal Programmer
Look inside the dist directory to enjoy the fruit of your labor! :)
+
+
\ No newline at end of file
diff --git a/compiling-workrave-in-fedora-core-4.html b/compiling-workrave-in-fedora-core-4.html
new file mode 100644
index 0000000..f9ba928
--- /dev/null
+++ b/compiling-workrave-in-fedora-core-4.html
@@ -0,0 +1,23 @@
+Compiling Workrave in Fedora Core 4 - The Terminal Programmer
Workrave is a fantastic break-reminder program which
+aids the responsible user in preventing RSI and eye-strain. It is far better
+than gnome-typing-monitor, which periodically invokes instinctual panic by
+flashing a red square whence less than one minute remains before a break!
+
Presently, there is no pre-built binary package for Workrave under Fedora Core
+4, so I’ve had to track down dependencies and compile it myself. The necessary
+dependencies can be installed by executing this command:
After doing this, the source package compiles smoothly, without dependency
+errors.
+
I should also mention that I am using, in addition to the default Fedora
+repositories, the DAG,
+Dries,
+FreshRPMS, and
+Livna repositories—whose yum repo
+files can be acquired here.
+
Place these repo files into /etc/yum.repos.d to make use of the
+aforementioned repositories.
+
\ No newline at end of file
diff --git a/contest-rails-testing-monkeypatch.html b/contest-rails-testing-monkeypatch.html
new file mode 100644
index 0000000..6b723cd
--- /dev/null
+++ b/contest-rails-testing-monkeypatch.html
@@ -0,0 +1,56 @@
+Using Contest's context() in Rails' controller tests - The Terminal Programmer
Using Contest's context() in Rails' controller tests
Suraj N. Kurapati
Contest is a Ruby library that brings Shoulda-style nestable
+contexts, in the form of context() methods, and an easier way to define
+test blocks, in the form of should() methods, to Ruby’s Test::Unit
+testing framework.
RuntimeError: @controller is nil: make sure you set it in your test's setup method.
+
For example, the following controller test triggers the error:
+
require'test_helper'
+require'contest'
+
+classExamplesControllerTest<ActionController::TestCase
+ context"index action"do
+ should"return a list of examples"do
+ get:index,:format=>:json
+ assert_response:success
+ end
+ end
+end
+
This happens because, internally, the context() method creates a new
+Test::Unit::TestCase-derived class whose name does not contain that of
+the parent controller test (which is ExamplesController in the above
+example). As a result, Rails complains when it is unable to automatically
+set the @controller instance variable inside the block passed to the
+context() method.
+
Robert Gleeson suggested a workaround where you manually assign the
+correct value to the @controller instance variable on behalf of Rails in
+the created context’s setup() block:
I automated the application of this workaround through the following
+monkeypatch, which you can add directly to your Rails’ test helper file:
+
classActionController::TestCase
+ defself.context*args,&block
+ super*argsdo
+ setupdo
+ # establish the rails controller being tested
+ # https://github.com/citrusbyte/contest/issues/5#issuecomment-677003
+ ancestors=self.class.ancestors
+ test_case=ancestors[ancestors.index(ActionController::TestCase)-1]
+ @controller=test_case.controller_class.new
+ end
+ class_eval&block
+ end
+ end
+end
+
And with that, you are now riding the Rails, Contest style! :-)
+
\ No newline at end of file
diff --git a/copyleft-mit-license.html b/copyleft-mit-license.html
new file mode 100644
index 0000000..ea8246e
--- /dev/null
+++ b/copyleft-mit-license.html
@@ -0,0 +1,147 @@
+Copyleft variation of the MIT license - The Terminal Programmer
I had been using the GNU General Public
+License for some years now without
+fully understanding its implications. Recently, I spent some time thinking
+about my ethical beliefs regarding free software and discovered that the GPL
+does not satisfy them. I investigated other open source licenses, but nothing
+fit. So, with the help of others, I developed a copyleft variation of the
+famous MIT license.
+
+
Beliefs
+
These are my ethical beliefs regarding open-source software:
+
+
+
Source code should always be available for software because it allows the user to learn from and improve upon the software. In line with the academic spirit, this ensures that everyone has access to knowledge (the source code) – not just an elite few.
+
The license should only govern my source code and derivatives of it – nothing more. In this manner, I avoid imposing my beliefs on software that simply uses my source code.
+
+
+
Candidates
+
The license that best embodies my ethical beliefs is the Creative Commons’
+by-sa (attribution + share-
+alike) license. In particular, I like by-sa because it ensures that my source
+code (and derivatives of it) will never be locked away or kept secret.
+
Unfortunately, CC does not recommend using their licenses for software. A
+friend suggested that I try the SleepyCat
+license, whose copyleft
+condition (the third condition) states:
+
+
+
Redistributions in any form must be accompanied by information on how to
+obtain complete source code for the DB software and any accompanying
+software that uses the DB software. The source code must either be included
+in the distribution or be available for no more than the cost of distribution
+plus a nominal fee, and must be freely redistributable under reasonable
+conditions. For an executable file, complete source code means the source
+code for all modules it contains. It does not include source code for modules
+or files that typically accompany the major components of the operating system
+on which the executable file runs.
+
+
I found this condition to be:
+
+
+
too aggressive: I only want the license to govern my source code and its derivatives. I do not wish to impose the copyleft condition on any accompanying software that uses my source code.
+
too brittle: In the phrase “must be freely redistributable under reasonable conditions”, what is considered to be reasonable?
+
+
The term reasonable is very subjective, and I can easily imagine it being
+abused by evildoers. Ultimately, a judge will have to decide the meaning of
+this word in court.
+
I looked at other licenses that fit the by-sa ideology (particularly MPL,
+CDDL, CPL, EPL) but found them all to be very lengthy and full of legalese. In
+contrast, I admire the short length and great comprehensibility of the MIT
+license and so, I decided to make a copyleft variation of it.
+
+
Approach
+
The MIT license has the following properties (from Ed Burnette’s
+survey of free software licenses):
+
+
+
+
Code is protected by copyright? Yes
+
Code can be used in closed source projects? Yes
+
Program that uses (incorporates) the software can be sold commercially? Yes
+
Source to bug fixes and modifications must be released? No
+
Provides explicit patent license? No
+
+
+
My goal was to modify the condition paragraph of the MIT license so that
+question 4 above is given a “Yes” answer. The resulting license is a weak
+copyleft variation of the MIT license.
+
+
Result
+
Copyright <year> <holder>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+* All copies and substantial portions of the Software (the "Derivatives")
+ and their corresponding machine-readable source code (the "Code") must
+ include the above copyright notice and this permission notice.
+
+* Upon distribution, the Derivatives must be accompanied by either the Code
+ or--provided that the Code is obtainable for no more than the cost of
+ distribution plus a nominal fee--information on how to obtain the Code.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
Many discouraged me from pursuing my own license because I was inadvertently
+contributing to the problem of license
+proliferation, but I proceeded nonetheless
+because no existing license served my particular ethical beliefs so concisely
+as this.
Now, let us examine the list of conditions one by one.
+
+
+
All copies and substantial portions of the Software (the “Derivatives”) and
+their corresponding machine-readable source code (the “Code”) must include the
+above copyright notice and this permission notice.
+
+
This condition ensures that the copyright and permission notice are never
+removed from the software. Does this sound familiar? Of course! This
+condition is simply a restatement of the original MIT license’s condition
+paragraph.
+
+
+
Upon distribution, the Derivatives must be accompanied by either the Code or
+–provided that the Code is obtainable for no more than the cost of
+distribution plus a nominal fee–information on how to obtain the Code.
+
+
This condition ensures access to the software’s source code because the code
+is either (1) distributed along with the software or (2) obtainable by some
+other means. This is the primary innovation that makes this license a
+copyleft variation of the MIT license.
+
Updates
\ No newline at end of file
diff --git a/dactyl-manuform-5x6-keyboard.html b/dactyl-manuform-5x6-keyboard.html
new file mode 100644
index 0000000..e0e80f4
--- /dev/null
+++ b/dactyl-manuform-5x6-keyboard.html
@@ -0,0 +1,403 @@
+Dactyl Manuform 5x6 programmable ergonomic keyboard - The Terminal Programmer
Video: Introduction to the Dactyl Manuform keyboard.
+
+
+
Prior to this, I used an ErgoDox EZ keyboard for 6 years and then a Kinesis
+Advantage keyboard for 11 years before that. All of these have a shared lineage:
+the Advantage has contoured keywells but isn’t split, the ErgoDox is split but
+isn’t contoured, and the Dactyl has the best of both: it’s split and contoured!
The keyboard boots up into the following default “base” layer when powered on.
+When held, the purple keys in the thumb
+clusters activate the subsequent layers according to the legendary Miryoku’s
+6-layer design with 3-key thumb activation.
+
+
+
Interactive: Hover your mouse over the purple keys to see each layer!
+
The up/down arrow keys on the right-hand home row diverge from Vim’s HJKL order
+because it feels more natural to follow the inward-rising curve of the keyboard’s
+contoured keywell, which elevates the thumb above the pinky finger and, similarly,
+the middle finger (up arrow) above the ring finger (down arrow).
+
This is a longstanding preference that I formed 17 years ago, in my early days of
+using the Kinesis Advantage with the Dvorak layout, whose lack of HJKL provided
+the freedom to reimagine the arrangement of arrow keys on the home row.
+
+
+
+
Select & edit
+
+
Editing and selection keys line the outer edges of the home block, respectively.
+This opposition enables inward rolls, where selections can be followed by edits.
+For example, to copy everything, I would first tap the “Select all” key with my
+pinky finger and then roll inward to tap the “Copy” key with my index finger.
+
The copy and paste keys are stacked vertically, in that order, to allow the index
+finger to rake down upon them in a natural curling motion toward the palm. This
+order is also logical, since pasting requires something to be copied first.
+
The versatile “Select word/line” key at the right edge of the home row is powered
+by Pascal Getreuer’s word selection QMK macro, which automates common
+selection tasks that require holding down Control and Shift with the arrow keys:
+
+
+
+
Tapping it selects the word under the cursor; shift-tapping it selects the line.
+Further taps extend the selection by another word (unshifted) or line (shifted).
+
+
Number layer
+
+
+
A 3x3 numeric keypad (using the standard 10-key layout) occupies the home block.
+The period key is poised to be tapped using the metacarpal joint of the thumb,
+allowing the thumb to rest on the zero key, ready for more numerical data entry.
+
+
+
+
Prefix signs
+
+
Signs that commonly prefix numbers are positioned above the 3x3 numeric keypad.
+
+
+
~ approximately
+
$ dollar amount
+
# literal number
+
@ at the rate of
+
+
+
+
+
Date & time
+
+
The slash and minus keys are positioned for MM/DD and YYYY-MM-DD date entry.
+Similarly, the colon key is positioned above them for HH:MM:SS time stamp entry.
+
+
+
: time stamp separator
+
- ISO-8601 date separator
+
/ American date separator
+
+
+
+
+
Inequalities
+
+
Comparison operators are positioned along the outer left edge of the home block.
+
+
+
~ approximately
+
< less than
+
> greater than
+
= equal to
+
+
The greater/equals keys are stacked vertically, in that order, to allow the
+index finger to rake down upon them in a natural curling motion toward the palm.
+
+
Function layer
+
+
+
The function keys are arranged in the same 10-key layout as the Number
+layer’s 3x3 numeric keypad so that you can develop common muscle
+memory for both layers. The remaining F10-F12 keys wrap around the home block
+because they’re found in shortcuts such as BIOS save/quit, fullscreen toggle,
+and devtools, respectively.
+
+
Symbol layer
+
This is the crown jewel of my keyboard’s configuration: an entire layer dedicated
+to the entry of symbols that are essential for computer programming.
+It’s the result of several hundreds of layout iterations over the last 8+ years.
@ is at the same relative position as the Tab key on standard keyboards.
+They pair well: the former denotes references and the latter expands them.
+
` is on the same key as ' on the base layer, for `' legacy curly quotes.
+
! is on the same key as Shift on the base layer; both invert things.
+
: is on the same key as Esc on the base layer; they’re duals in Vim.
+
+
+
+
+
Vim editor shortcuts
+
+
+
+
^ and $ are on the home row, for jumping to the beginning/end of line.
+
% is on the home row, for jumping to the matching delimiter at cursor.
+
# is on the home row, to search backwards for the word under cursor.
+
= is on the home row, to automatically indent the line or selection.
+
{ and } are on the home block, for jumping to previous/next paragraph.
+
< and > are on the home block, for decreasing/increasing indentation.
+
: is on the inner thumb key, for entering Vim’s command mode.
+
; is on the outer thumb key, for repeating a f/F/t/T character search.
+
* is on the thumb joint, to search forwards for the word under cursor.
+
+
+
+
+
Adjacent key bigrams
+
+
+
+
#! for shebang lines in UNIX scripts.
+
.* for regular expressions.
+
*. for filesystem globs.
+
() for parentheses.
+
+
+
+
+
Outer corner bigrams
+
+
These are easy to find because they’re on the outer corners of the keyboard.
+
+
+
!~ for regular expression “not matching” in Perl, Ruby, and Elixir.
+
/* and */ for multi-line comments in C, CSS, and JavaScript.
+
\/ for escaped regular expression delimiters in Vim.
+
~/ for home directory paths in UNIX.
+
?! for interrobang in English prose.
+
+
+
+
+
Inward rolling bigrams
+
+
+
+
<- for assignment in R and in Elixir’s with statements.
+
-> for thin arrows in C, C++, and Elixir.
+
=> for fat arrows in Perl, Ruby, and Elixir.
+
=~ for regular expression matching in Perl, Ruby, and Elixir.
+
!~ for regular expression “not matching” in Perl, Ruby, and Elixir.
+
!= for “not equal to” value comparison in many languages.
+
<= for “less than or equal to” comparison in many languages.
+
^= for bitwise XOR assignment in C and related languages.
+
|> for the pipe operator in Elixir.
+
!( for negating a group in Boolean expressions.
+
"$ for quoted variable substitution in Bourne shell.
+
!$ for last argument of previous command in Bourne shell.
+
$? for exit status of previous command in Bourne shell.
+
<% for directive tags in Ruby’s ERB and Elixir’s EEx templates.
+
#{ for string interpolation in Ruby and Elixir.
+
#[ and ![ for metadata attributes in Rust.
+
`' for legacy curly quotes.
+
/? for query parameters in URLs.
+
/* for starting comments in C, CSS, and JavaScript.
+
[] for square brackets.
+
<> for angle brackets.
+
{} for curly braces.
+
+
+
+
+
Outward rolling bigrams
+
+
+
+
~> for pessimistic version constraint in SemVer.
+
-= for negative accumulation in C and related languages.
+
+= for accumulation in C and many languages.
+
%= for modulo assignment in C and related languages.
+
>= for “greater than or equal to” value comparison.
+
>& and &< for file descriptor redirection in Bourne shell.
+
$_ for value of last argument of previous command in Bourne shell.
+
%> for directive tags in Ruby’s ERB and Elixir’s EEx templates.
+
${ for variable interpolation in Bourne shell.
+
%{ for maps (hash tables) in Elixir.
+
~/ for home directory paths in UNIX.
+
*/ for closing comments in C, CSS, and JavaScript.
+
+
+
Mouse layer
+
+
+
Movement keys are located centrally in the home block, resembling WASD keys, and
+mouse acceleration controls are poised for pinky finger access, so you can
+independently move the mouse pointer and also change its speed at the same time.
+
Mousewheel down/up keys are also placed on the home block, specifically on
+the same keys as J/K (down/up in Vim) on the base layer for muscle memory reuse.
+
+
Light layer
+
+
+
Keys for lowering/raising light settings line the central columns of home block,
+whereas keys for applying lighting modes line the outer edges of the home block.
+
+
Firmware
+
My keyboard’s entire QMK firmware configuration, as described in this article, is
+made available on GitHub in the sunaku_dm5x6 branch of my
+personal QMK fork.
You can upload the provided QMK Keymap JSON file named
+keymap_config.json into the QMK Configurator app to view or
+customize the keymap and all of its layers. When you’re finished, download the
+keymap back to the same file, overwriting it.
+
+
Building the firmware
+
Navigate into the directory shown in the Firmware section above and run make to:
+
+
+
Convert the keymap_config.json file into C source code.
+
Wrap the C source code with a custom header and footer.
+
Compile the wrapped up C source code using qmk compile.
+
Flash the compiled firmware (HEX file) to the keyboard.
+
+
All these steps are handled by the provided Makefile, shown below for reference:
+
# NOTE: Don't name the QMK Configurator JSON keymap file as "keymap.json"
+# because `qmk compile` directly translates it into C and compiles it too,
+# thereby completely bypassing this Makefile and our keymap header/footer!
+
+TOPLEVEL=`git rev-parse --show-toplevel`
+KEYBOARD=handwired/dactyl_manuform/5x6_5
+KEYMAP=sunaku
+
+all:flash
+
+flash:build
+ qmk flash -kb$(KEYBOARD)-km$(KEYMAP)
+
+build:keymap.c config.h rules.mk
+ test!-e keymap.json # see comment at the top of this Makefile
+ qmk compile -kb$(KEYBOARD)-km$(KEYMAP)
+
+keymap.c:keymap_config.json keymap_header.c keymap_footer.c config.h
+ qmk json2c -o$@$<
+ spot=$$(awk'/THIS FILE WAS GENERATED/ { print NR-1 }'$@)&&\
+ sed-e"$$spot r keymap_header.c"-e"$$ r keymap_footer.c"-i$@
+
+clean:
+ qmk clean
+
+clobber:clean
+ rm-fv keymap.c
+ rm-fv$(TOPLEVEL)/$$(echo$(KEYBOARD) | tr / _ )_$(KEYMAP).hex
+
+.PHONY:clean clobber build flash
+
\ No newline at end of file
diff --git a/darcs-2-enhances-amend-record.html b/darcs-2-enhances-amend-record.html
new file mode 100644
index 0000000..457874e
--- /dev/null
+++ b/darcs-2-enhances-amend-record.html
@@ -0,0 +1,36 @@
+Darcs 2 enhances amend-record - The Terminal Programmer
The main lesson regarding these new semantics is that patches depends on
+primitive patches, not on named patches. A named patch is really just a set
+of primitive patches.
+
+
To better understand this feature, consider this example:
+
+
+
Patch X modifies files A, B, and C.
+
Patch Y modifies files A and C.
+
You modify file B and want to amend patch X.
+
+
Darcs 2 allows this (hurray!) whereas Darcs 1 does not. As a result, you had
+to go through a fair bit of manual labor to achieve the same effect in Darcs
+1:
+
+
+
Create a temporary branch.
+
Unrecord patch Y.
+
Amend patch X.
+
Push the new patch X.
+
Recreate patch Y.
+
Finally, push patch Y.
+
+
\ No newline at end of file
diff --git a/darcs-migration-tailor-ascii-error.html b/darcs-migration-tailor-ascii-error.html
new file mode 100644
index 0000000..3530431
--- /dev/null
+++ b/darcs-migration-tailor-ascii-error.html
@@ -0,0 +1,15 @@
+ASCII/Unicode error in Tailor - The Terminal Programmer
When using Tailor to migrate from
+Subversion to the Darcs distributed version control
+system, I encountered an error saying:
+
'ascii' codec can't encode character
+
According to this Tailor bug
+report, the solution is to
+specify the --encoding=utf-8 option to Tailor. However, that did not work
+for me. Instead, I had to change Python’s default system encoding, in the
+/etc/python2.4/site.py file, from "ascii" to "utf-8" to solve the
+problem.
+
\ No newline at end of file
diff --git a/defining-methods-for-erb-templates.html b/defining-methods-for-erb-templates.html
new file mode 100644
index 0000000..932711c
--- /dev/null
+++ b/defining-methods-for-erb-templates.html
@@ -0,0 +1,26 @@
+Defining methods for ERB templates - The Terminal Programmer
While converting ruby-vpi documentation from DocBook-XML to RedCloth, I
+wanted an easy way to pass a bunch of text to my own methods, in a fashion
+similar to if-statements and iterators. This would allow me to make
+admonitions easily by writing code like the following in my ERB template.
+
<%cautiondo%>
+Nothing is as it seems!
+<%end%>
+
At first glance, it seems that the content between the do and end
+keywords is just passed as a string inside the block argument to the
+method. However, in truth, the block argument contains an expression that
+appends the content between do and end to the _erbout variable,
+like this:
+
_erbout<<"Nothing is as it seems!"
+
ERB allows us to specify an alternate name for _erbout (its evaluation
+buffer) in its constructor. We can use this ability to capture the content
+passed from an ERB template to a Ruby method as follows.
+
TODO: finish this article!
+
Updates
\ No newline at end of file
diff --git a/dwm-spawn-cwd-patch.html b/dwm-spawn-cwd-patch.html
new file mode 100644
index 0000000..391c8a8
--- /dev/null
+++ b/dwm-spawn-cwd-patch.html
@@ -0,0 +1,92 @@
+Spawn from current working directory in DWM - The Terminal Programmer
I switched to DWM recently
+after six productive years of WMII and one of the things I missed from my
+previous WMII configuration was the ability to open new programs in the
+currently focused client’s working directory.
+
Why? It’s all about spatial locality. For example, when I’m editing
+in Vim, I like to start some new terminals (or file managers) in the same
+working directory as the file I’m editing to do additional things. Or the
+scenario could be the opposite: when I’m browsing the filesystem in a file
+manager, I want to start Vim inside the directory I am currently browsing.
+
Without this feature, I have to manually navigate to that working
+directory in each helper application (terminal, file manager, or editor)
+that I launched. This becomes tiresome and inefficient as time goes on.
+
Thus, to correct this imbalance and regain my productivity, I dusted off
+my C knowledge and ported this feature to DWM using just 27
+statement-lines of code (SLOC), as you can see in the patch below.
\ No newline at end of file
diff --git a/ebola-virus.html b/ebola-virus.html
new file mode 100644
index 0000000..20b9741
--- /dev/null
+++ b/ebola-virus.html
@@ -0,0 +1,91 @@
+Investigating the Ebola HF virus - The Terminal Programmer
Ebola HF is a type of VHF (viral hemorrhagic fever) that is only found in
+Africa. This virus is zoonotic, which means it stays on an animal host until
+it is passed onto another host. The Ebola HF virus belongs to a family of
+viruses called Filoviridae which divides up into four known strains: Ebola
+Zaire, Ebola Sudan, Ebola Ivory Coast, and Ebola Reston [1]. All four strains
+have no vaccination, and still claim victims in isolated areas of Africa such
+as Gabon, Sudan, and Zaire (also known as The Democratic Republic of the
+Congo). When cynomolgous monkeys infected with Ebola Reston were shipped from
+the Philippines to the United States and Italy for research, an outbreak
+occurred and some researchers were exposed to the virus. Fortunately the Ebola
+Reston did not affect humans, but it did affect the cynomolgous monkeys and
+other primates.
+
Although the natural reservoir (the place Ebola HF originated from) of Ebola
+HF is unknown, the virus was first found in 1976 in the small Bandundu
+province near the Ebola River in Zaire (now known as The Democratic Republic
+of the Congo). In this first outbreak, the number of deaths among the 318
+infected people (Ebola Zaire) was 88% [2]. Later that year another smaller
+breakout occurred in Southern Sudan where 53% of the 284 infected people
+(Ebola Sudan) died [3].
+
The process of being infected by the Ebola HF virus from another person has
+yet to be understood, but the symptoms and ways of transmission of the disease
+are known. During the first few days of infection, most patients will have
+fevers, joint pains, and diarrhea. Some patients will have sore throat, skin
+rashes; bloody diarrhea, red and itchy eyes, and hiccups. During the last days
+of infection, most patients will have chest pain, and shock. Some patients
+become blind and bleed in their digestive tract in the last hours of their
+life.
+
Transmitting the virus between humans requires some person has to have touched
+an animal carrying the virus. Then that person would go and touch his family
+and friends, and so on. Some common ways of transferring the virus from person
+to person are: touching and handling used needles with bodily liquids still on
+them, touching any bodily liquids of the carrier, touching dead victims,
+touching infected people’s bleeding wounds or skin rashes, and even touching
+or wearing their clothes.
+
In the fight for a cure for Ebola HF, several procedures were created to
+identify the disease in patients. The lgG ELISA (Antigen-Capture-Enzyme-
+Linked-Immunosorbent Assay) laboratory procedure is used to test patients for
+virus infection [4]. This specific procedure is a part of a pathogen testing
+protocol called ELISA (Enzyme-Linked-Immunosorbent Serologic Assay) [4]. It
+detects specific substances such as viruses, pathogens, and other infectious
+disease. There are no developed vaccines for any strain of Ebola HF virus, but
+there are many people who have survived the disease. The disease can be
+prevented if people are careful around needles, and dead victims, and if they
+are infected, they should see the doctor right away for the lgG ELISA
+procedure. There are a few cases of success, for example, in Kikwit, Zairevii
+in 1995, seven out of eight infected patients lived when they were given blood
+from patients who had survived the disease. This data seems promising, but
+there were too few people to experiment with, and each person had his/her own
+qualities which could have greatly affected his/her outcome. For this reason,
+this data was considered useless by the CDC9.
+
According to the CDC [6], African health-care facilities are not the best
+places to get help for an Ebola HF infection. Patients are often cared for
+without protection such as masks and gloves, and they are exposed to the same
+clothes that physicians wear while treating those who are dying. Needles and
+syringes are not sterilized or thrown away; instead they are simply rinsed
+with water and reused again. For example, a blood sample would be taken from a
+dying person, and then the needle would be rinsed and used to inject into a
+vial of medicine into another person. I think it is naive that these
+physicians look past such simple things as sterilization. They are infecting
+more people rather than saving them [5].
+
The African health-care facilities need more attention from world
+organizations such as the Red Cross, CARE, and the CDCix. People need to be
+aware of the conditions in these facilities so we can help the facilities
+incorporate standard sterilization procedures. This will significantly reduce
+the number of deaths due to bio-hazardous medical equipment. The facilities
+and these organizations can also invoke public support, to raise money, and
+get food for those suffering from the disease and their families. Also, if
+more biotechnology and pharmaceutical companies invest in medical research
+they could greatly profit; this will give the researchers funding so they can
+start work on finding a cure for the disease.
+
I believe that the African health-care facilities need to incorporate standard
+sterilization techniques before caring for patients. The Ebola HF virus needs
+to be further researched so that a cure can be found. And people of the world
+need to be more aware of this disease. After all, those who are suffering are
+human, and if we don’t help them survive, who will be left to help us?
\ No newline at end of file
diff --git a/educ92a-essay.html b/educ92a-essay.html
new file mode 100644
index 0000000..71ebd80
--- /dev/null
+++ b/educ92a-essay.html
@@ -0,0 +1,156 @@
+Forced assimilation in American public education - The Terminal Programmer
American public education, both today and in contemporary times, has not
+functioned without some degree of forced assimilation; that is, it has catered
+specifically to the dominant culture in America while forcibly assimilating
+students who represent the ethnic or socio-economic minority.
+
These are the results of a crude implementation of some of the chief ideals of
+public education: to foster community [4] and “national unity” [5]. So to
+better implement these ideals, the public education system in America must
+focus on promoting “ethno-cultural unity” [5] as a way to achieve “national
+unity” [5] rather than forcing assimilation, which imparts disunity.
+
+
Priority
+
The concept of assimilation is primarily a concern for the minority, as it is
+they who undergo the process firsthand. That is, the minority student feels a
+sense of disconnection in the school environment due to differences in
+language, appearance, and social norms of demeanor. In contrast, assimilation
+does not affect the dominant culture as much, because schools are run by its
+members and schools serve to promote its values and ideas. Thus the dominant
+culture usually sees no problems in the education system and is apathetic
+towards the concerns of minorities [2]. This also fuels opposition to changes
+in the education system whereby the dominant culture feels that:
+
+
+
The system suits me just fine, because it represents my way of life; why
+then, shall I change it? The minority students should adopt these values
+because they are good, and having done so will assist them in being successful
+the workplace.
+
+
Obviously, such a viewpoint is wholly single-minded and crudely justifies the
+teaching methods utilized by the education system:
+
+
+
Which values are “good” and which are not?
+
What is “successful” behavior and what is not?
+
+
It is unjust for the dominant culture to simply dismiss the concerns of
+minorities, since we live in a democratic [4] society. Thus the answer can be
+obtained most fairly if all groups understand each-other’s needs and
+priorities, i.e. there must be diversity, which fuels an interpenetration of
+cultural understanding [4], in order to improve our education system.
+
+
Language
+
A lack of fluency in dominant culture’s language can be the biggest
+contributor in the concern of forced assimilation, as the majority of classes
+taught in schools, save foreign language classes, are in English. A student
+who lacks fluency in English may feel isolated from their peers and their
+teachers.
+
For example, Richard Rodriguez writes that as a result of him becoming fluent
+in English, he had lost confidence in speaking Spanish, which to him was
+associated with closeness with his family and relatives [3]. Thus he felt
+guilty for leaving behind his native language only to become “successful”
+within the dominant culture [3].
+
Furthermore, the American public education system often flags the difficulty
+in learning English as a “deficiency,” and industriously places minority
+students into lower-track classes [2]. This further segregates minority
+students from receiving high-track education [2] as they are made to think
+that they are unqualified to achieve the “good” or valued positions in the
+dominant society.
+
For example, when Malcolm X, a brilliant student, is asked to describe his
+future ambitions by his English teacher, he replies that he aspires to become
+a lawyer. In response his teacher suggests that his aspiration is not
+“realistic” [1] and he should aim to become a carpenter instead [1].
+
+
Appearance
+
The appearance of students is strictly controlled in public schools, more
+so in the past than today, but nevertheless in favor of the dominant
+culture. For example, Malcolm X “conks” (to straighten one’s hair by
+incinerating the scalp with lye) his natural hair in order to make it
+appear more like that of the dominant culture’s valued hair [1]. He later
+describes this act as his “first really big step towards self degradation”
+[1]. Through school dress- codes and images of popular media, the dominant
+culture imposes its valued image of a “beautiful” person upon the masses.
+
For example, before I emigrated to America, I had never seen an “black” nor
+“white” person, i.e. there was no institutionalized sense of “race.” However,
+after several years of residence, I noticed that I favored images of “white”
+people over those of “black” ones. More specifically, I favored the straight
+hair over “corn-rows” or “du-rags” that many people wear in recent times. I
+was shocked! How is it that I, who had no prior knowledge, bias, nor prejudice
+towards any of these people, became to favor one over the other? This is due
+to my subconscious being bombarded with ideas of what is “beautiful” and what
+is not: the images I saw on television, cinemas, and daily life in public
+schools.
+
Furthermore, if a school has a dress-code which enforces a certain kind of
+clothing to be worn, it may further alienate minority students. For example,
+if students were not allowed to wear any head decorations, i.e. hats, when
+they recited the national anthem so as to give respect to “the flag,” then
+minority students may not comply. If the school forces the removal of such
+head decorations, then a Sikh student, who wears a turban, would certainly
+feel humiliated and violated to have been forced to remove his turban. It is
+equivalent to violating a person’s universal human rights, such as denying
+them the right of clothing. In such a situation, the student who does not
+remove their head decorations becomes alienated from the rest, for the student
+appears to lack patriotism and may thus be interpreted as some form of a
+heretic. Also, if the school’s administrators are not understanding of the
+minority student’s situation, they may brashly expel them or move them into a
+lower track [2].
+
+
Demeanor
+
Alongside the troubles of assimilating with dominant language is that of the
+social norms of demeanor. One is, in many ways, subtly outcast from the
+dominant society if one does not exercise the mannerisms that the dominant
+culture expects.
+
For example, in my culture, one does not openly address nor speak with others
+with whom one is not acquainted with. However, now in America, every morning
+as I am walking to class, somebody will greet me with the words “good
+morning.” Upon hearing this I become very uncomfortable, almost
+claustrophobic, as I must respond to them for the sake of not insulting them.
+It is like being coerced to perform under the spotlight of a large opera hall.
+Sometimes I simply nod and carry on, but I feel guilty to have offended the
+other person. Of course, it may not be of such importance to those of the
+dominant culture, as it is their natural and customary way of greeting others.
+
I also find it difficult to forge meaningful relationships with others,
+especially when meeting someone new. For example, in America, one often says
+“it was nice to meet you,” when one departs with someone. However, in my mind
+these words are extremely superficial, for I feel that the other person is
+simply uttering this dialogue for the sake of their own demeanor and has
+embodied no true feelings in their message. In this manner, I feel as though
+disjoint from my peers for I cannot accurately gauge the depth of their
+dialogue or gestures. I feel as though it is inevitable for one to assimilate
+with the dominant culture in order to survive; this message weighs heavily, in
+my opinion, in the minds of minority students as they step out of their homes
+and into the world of dominant culture, outside.
+
+
Conclusion
+
Considering the clashes between the dominant culture and those of minorities,
+I feel that the education system is trying to promote “national unity” [5] by
+forcing it upon students and ignoring “ethno-cultural unity” [5], whereas it
+would be more effective to gain “national unity” [5] through the promotion of
+“ethno-cultural unity” [5].
+
In this manner, students can be better connected to and gain a deeper
+understanding of each-other, which facilitates the formation of relationships.
+These relationships form from an interpenetration of needs and wants which is
+found in a community. Having developed such a community, we observe the
+process of “socioendosmosis” [4], which in turn promotes “national unity” [5].
+
Thus the key to “national unity” [5] is not the propagation of the dominant
+mono-culture, but rather in diversification of our public schools and
+institutions.
+
+
References
+
(cited in IEEE format)
+
+
+
M. X, “Mascot,” in The Evolution of Education, D. Swanger, Eds. Ohio: Thomson Learning Custom Publishing, 2002. pp. 260-274.
+
J. Oakes, “The Distribution of Knowledge,” in The Evolution of Education, D. Swanger, Eds. Ohio: Thomson Learning Custom Publishing,
+
+
+
pp. 305-336.
+
+
R. Rodriguez, “Aria,” in The Evolution of Education, D. Swanger, Eds. Ohio: Thomson Learning Custom Publishing, 2002. pp. 378-386.
+
J. Dewey, “Labor and Leisure,” in The Evolution of Education, D. Swanger, Eds. Ohio: Thomson Learning Custom Publishing, 2002. pp. 51-60.
+
D. Swanger, “The Evolution of Education.” Ohio: Thomson Learning Custom Publishing, 2002.
+
+
\ No newline at end of file
diff --git a/elixir-fileio-speedup.html b/elixir-fileio-speedup.html
new file mode 100644
index 0000000..65d44b7
--- /dev/null
+++ b/elixir-fileio-speedup.html
@@ -0,0 +1,112 @@
+Speeding up I/O on very large files in Elixir - The Terminal Programmer
At work today, a colleague needed to process a very large (1.5 GiB) log file,
+line by line, using Elixir 1.4.2 and Erlang 19.2 on Linux 3.16. A simple iex
+experiment yielded abysmal performance, taking 8 hours and 7 minutes to finish!
Inconceivably, it hung again! So I now suspected that something might be wrong
+with that log file itself. Paging through it, I discovered an enormous sequence
+of NUL bytes embalmed several thousand lines deep. That was unexpected, but why
+should NUL bytes only affect Elixir and not Python? Were C programs immune too?
+
$ grep-c'regex_to_grep' path/to/very/large/file
+grep: line too long
+
Aha! GNU Grep actually had a safeguard built-in to prevent memory exploitation
+by aborting on very long lines, likely elongated by that enormous NUL sequence.
+
Perhaps excessive line lengths also paralyzed Elixir? To verify, I wrote my
+own file reader that would lazily read a single 128 KiB (32 filesystem blocks,
+commonly sized to be 4 KiB) sized chunk at a time and then split it into lines:
+
file="path/to/very/large/file"
+lines=file|>File.stream!([],32*4096)|>
+ Stream.map(&String.split(&1,~r/(?<=\n)/))|>
+ Stream.transform("",fnlines,acc->
+ ifEnum.empty?(lines)do
+ ifacc==""do
+ {:halt,acc}
+ else
+ {[acc],""}
+ end
+ else
+ # prepend previous partial line to this chunk
+ [first_line|rest_lines]=lines
+ lines=[acc<>first_line|rest_lines]
+ acc=""
+
+ # carry any partial line at end of this chunk
+ unlessList.last(lines)|>String.ends_with?("\n")do
+ {lines,[acc]}=Enum.split(lines,-1)
+ end
+
+ {lines,acc}
+ end
+ end)
+num_lines=lines|>Enum.count
+
This workaround successfully prevented the enormous NUL byte sequence from
+hanging Elixir (hurray!) but the overall operation took 24 minutes to complete.
+
How do I optimize this further? Well, I would imagine that Elixir already uses
+a similar (in spirit) line splitting algorithm that is already heavily optimized,
+so eliminating my own would be ideal (deleted code is debugged code). In
+addition, I also have control over the sizing of chunks I read from the file.
+
+
Solution
+
Following this intuition, I searched the web and unearthed two disparate hints:
+
+
+
Use IO.binstream/2 to read the file as a raw byte sequence (not as UTF-8,
+which is the case with IO.stream/2 and thus with File.stream!/2 as well).
+http://stackoverflow.com/a/31397000
+
Ask for large chunks while reading the file to counteract the overhead of
+:file.read/2 and use additional buffering on top of it for performance.
+http://stackoverflow.com/a/9771547
+
+
Applying these hints yielded 16x speedup, cutting execution time to 90
+seconds:
That’s actually good performance, since wc took 74 seconds to count characters:
+
$ time wc path/to/very/large/file
+ 92474 498826 1603770580 path/to/very/large/file
+wc path/to/very/large/file 73.57s user 0.84s system 100% cpu 1:14.41 total
+
Although this performance still pales in comparison to Python’s 4 seconds, it’s
+a reasonable trade-off considering the concurrency and fault-tolerance merits of
+Elixir over Python. Furthermore, this subpar performance might be a remnant of
+the underlying algorithms being used to fill buffers and split lines inside Elixir,
+given the similarity in execution time with wc when counting characters.
+
+
Conclusion
+
Read large files as raw binary streams (not as UTF-8) along with enough caching:
\ No newline at end of file
diff --git a/elixir-parallel-grep.html b/elixir-parallel-grep.html
new file mode 100644
index 0000000..b46582a
--- /dev/null
+++ b/elixir-parallel-grep.html
@@ -0,0 +1,98 @@
+A parallel grep(1) prototype in Elixir - The Terminal Programmer
Note: This all happened a year ago. I’m just late in writing about it.
+
+
A colleague needed a way to quickly grep(1) through several terabytes of
+plain text reports at work today, so I wrote a basic prototype of parallel
+grep(1) in Elixir during my lunch break. Its performance, to my surprise,
+was astonishing!
+
$catsearch.exs
+defmoduleSearchdo
+ deflaunch(regex,files)do
+ files|>Stream.reject(&File.dir?/1)|>Enum.map(fnfile->
+ spawn(__MODULE__,:search,[regex,file,self])
+ end)
+ end
+
+ defsearch(regex,file,receiver)do
+ file|>File.stream!|>Enum.each(fnline->
+ caseRegex.run(regex,line)do
+ nil->;# line didn't match so ignore it
+ matches->sendreceiver,{file,matches}
+ end
+ end)
+ end
+
+ defresults(count\\0)do
+ receivedo
+ _result->results(count+1)# consume the message and loop again
+ after1000->IO.puts"got #{count} results; no more in last 1s"
+ end
+ end
+end
+
+[pattern|files]=System.argv
+regex=Regex.compile!(pattern)
+Search.launch(regex,files)
+Search.results
+
Extracting lines, along with capture groups, that matched a Perl-compatible
+regular expression from a small sample of 17GB of plain text reports took
+19 seconds for the prototype (note that it waits for an extra second
+after finding the last search result), compared to 1 minute and 50
+seconds for GNU grep 2.5.1:
+
$ regex='^\s*(\S+)(?:\s+([\d\.e+-]+)){4,}'
+
+$ du-csh /some/*/location/ | tail-1
+17G total
+
+$ time elixir -r search.exs --"$regex" /some/*/location/*
+165984 results; nothing more in last 1s
+elixir -r search.exs -- 220.98s user 6.38s system 1147% cpu 19.816 total
+
+$ time grep-Pq"$regex" /some/*/location/*
+grep-Pq 31.92s user 6.51s system 34% cpu 1:50.30 total
+
This meant that the Elixir prototype was 5.7 times faster than grep(1), but why?
+
The answer is parallelism: grep(1) is a single OS process that handles a
+single file at a time. Whereas in the Elixir prototype, each file is
+handled by its own dedicated Erlang “process”, which is an actor
+(see also Command Pattern) that is significantly lighter than any OS
+process or thread. This difference is readily seen in the CPU utilization
+statistics reported by the time(1) command:
+
+
+
+
+
User time
+
System time
+
CPU utilization
+
Elapsed time
+
+
+
+
Elixir
+
220.98s user
+
6.38s system
+
1147% cpu
+
19.816 total
+
+
+
grep(1)
+
31.92s user
+
6.51s system
+
34% cpu
+
1:50.30 total
+
+
+
Here we see that the Elixir prototype utilized 11.47 CPU cores worth of
+computational resources, whereas grep(1) utilized only 34% of only one
+CPU core. Since the machine that ran this search had 24 CPU cores,
+several of which were already under heavy use by ~800 other users on it,
+this was a remarkable result.
+
In short, parallelism won. And so did Elixir and the Erlang VM, for this
+was my first time, in 18 years of programming, seeing my code exhaust
+a machine’s entire CPU capacity with no extra effort on my part! The
+revolution has arrived.
+
\ No newline at end of file
diff --git a/engram-keyboard-layout.html b/engram-keyboard-layout.html
new file mode 100644
index 0000000..8943687
--- /dev/null
+++ b/engram-keyboard-layout.html
@@ -0,0 +1,445 @@
+Switching to Arno's Engram 2.0 keyboard layout - The Terminal Programmer
It happened again: for the third time in my life, I switched to a better
+keyboard layout, embarking on a months-long journey of reprogramming my brain,
+rewiring decades worth of accumulated muscle memory through deliberate practice.
Arno’s Engram layout v2.0 is an optimized key layout for touch typing in
+English based on ergonomic considerations, with a protocol and software
+for creating new, optimized key layouts in other languages.
+
[{ 1| 2= 3~ 4+ 5< 6> 7^ 8& 9% 0* ]} /\
+ bB yY oO uU '( ") lL dD wW vV zZ #$ @`
+ cC iI eE aA ,; .: hH tT sS nN qQ
+ gG xX jJ kK -_ ?! rR mM fF pP
+
+
+
+
+
+
+
+
+
+
+
+
+
Review
+
Engram’s novel placement of punctuation in the middle columns of the keyboard
+eliminates lateral movement of the index fingers while simultaneously providing
+quick access to frequently used punctuation marks (i.e. comma and period feel
+heavenly). I haven’t seen any other layouts do this before; especially spilling
+over the remaining letters to the right hand pinky cluster — it’s pure genius!
+
The layout is very comfortable and it also lends itself well to programming.
+Notably, I’m a die-hard Vim user and it passes my test for ergonomic Vim usage:
+
+
+
J and K are adjacent (identical to Dvorak, actually) — ideal 💯
+
H and L are adjacent (albeit on the same finger) — good 👍
+
W is on right-hand strong ring finger — good 👍
+
B is on left-hand weak pinky finger — ouch 🤕 (but the left-hand strong
+ring finger can easily hop over to take the load off the pinky — okay 👍)
+
+
+
+
Update: I wrote that remark about the B key back when I was using the
+ flat/2D ErgoDox EZ keyboard, and it’s no longer a concern for me beyond the
+ learning stage – especially on my current curved/3D keyboard. 🥣
+
+
+
+
Punctuation is in the middle so either hand can type it — ideal 💯
+
Shifted symbols on number keys are intuitive or mnemonic — great 👍
+
+
In addition, Engram also arranges Vim’s operator+motion sequences as rolls (mostly
+inward rolls) and alternations, plus some keys have Vim-esque affinity:
+
+
+
ea (inroll) go to end of word and append
+
bi (inroll) go to start of word and insert
+
ciw (inroll) change inside word
+
caw (inroll) change around word
+
yiw (inroll) yank inside word
+
yaw (inroll) yank around word
+
dw (roll) delete to end of word
+
diw (alt+roll) delete inside word
+
daw (alt+roll) delete around word
+
t (jump upto char) comes before f (jump onto char) in left-to-right order
+
n and p are clustered for next/previous menu navigation and completion
+
y (yank) and p (paste) are on opposite hands to since they’re so different
+
b (beginning of word) and w (end of word) are split apart and left-to-right
+
and so on…
+
+
I want to thank Arno Klein for
+sharing his genius with the world. 🙇 This layout is truly a masterpiece;
+a game changer! 🤩 Moreover, its systematic and methodical invention (and
+generous explanation thereof) allows interested people to understand how
+this layout has come to be (by simply rendering the Python notebook on
+their own system) and to perform further research and improvement.
+
+
One week later
+
It has been a week since I switched to Engram and here is my experience so far:
Curiously, the BEAKL-15 layout that I switched over from has the same
+left-hand vowel cluster as Engram, and this commonality significantly reduced
+my transition time. In contrast, it took me a month to become comfortable with
+that vowel cluster when I previously switched from Dvorak to BEAKL-15 six
+months ago.
After another week of real-world use (but this time without as much
+dedicated typing practice), I’m happy to say that I’m now comfortably
+typing at ~60 WPM as the new Engram muscle memory is finally taking
+precedence in my brain (thank goodness for neuroplasticity!). 🤓
+
I made a few tweaks to the ortholinear version of Engram on my Ergodox
+EZ keymap
+for programming convenience and general comfort:
+
+
+
I relocated the @ # / keys to the unused left-hand pinky keys (the
+slash is especially handy for URLs, file paths, and Vim searching)
+
I put redundant square & angle brackets on either single-hand
+(otherwise the ortholinear split disconnects them, very far apart)
+
I use a dedicated symbol layer
+that further minimizes reaching by clustering frequent sigils &
+delimiters along the home row/block.
+
+
In addition, I would like to emphasize that Engram feels very
+comfortable to type in. With BEAKL-15, I often felt like I’m “climbing
+a wall” when typing (for example, the word “spring” has me reaching up
+and down a lot). Whereas with Engram, I feel like I’m “spreading my
+wings” and gracefully taking flight. It’s a significant qualitative
+improvement. 😌
+
+
One month later
+
It’s been a month since I’ve switched to Engram 2.0 and I’m happy to report
+that I’m comfortably typing at 70+ WPM. 🤩
+
In particular, I’ve noticed that my inter-key typing speed is the fastest I’ve
+ever observed in my 23 years of typing! 🏃💨 Portions of words (especially
+those in rolls) form rapidly on my screen as I type, though entire words take
+relatively longer due to momentary pauses as my muscle memory context switches
+between different finger/hand placements. This is no doubt the result of those
+interkey speed and finger strength matrices applied from Arno Klein’s extensive
+research. 👌
+
Overall, I feel that I’m now at 95% confidence while typing: subconsciously
+finding the right keys to press and positioning my fingers in anticipation of
+the next characters coming down the pipeline. Productivity wise, I feel 50%
+more efficient.
+
Surprisingly, this newfound speed & efficiency was achieved while maintaining
+typing comfort, not at its expense. For example, the word “highlight” is
+delightful to type in Engram whereas I had to mentally prepare myself and
+concentrate to type it quickly in BEAKL-15.
+
However, I do notice minor slowdowns on upward-traveling same-finger bigrams
+such as “je” in the word “object”, but this is more influenced by the style of
+keyboard being used than Engram itself. For instance, I notice this more on my
+Ergodox, which has larger & distantly placed keys than those on my laptop. In
+general, Engram scores well with low 1.24% Same Finger Bigrams (SFBs). 👌
+
In conclusion, Engram feels very comfortable due to its generous allotment of
+inward rolls (that “you” and “th” feel amazing), heavier emphasis on home row
+and bottom rows, balanced distribution between vertical climbs & horizontal
+spreads. 💯
+
+
Two years later
+
I’ve reached a comfortable speed of 100+ WPM with Engram 2.0 after properly
+configuring my programmable keyboard with custom firmware that disambiguates
+home row mods and lets me type more naturally, without artificial slowdowns. 🥲
Learning a new layout is primarily a matter of time and effort: it just takes
+practice, hang in there! :sunglasses::thumbsup: And continue training with
+layout agnostic tutors like KeyBr.com and N-gram Type.
+
It may be helpful to write down the layout on a piece of paper: each row has only
+4 letters anyway (except for the outliers Z and Q), so it chunks very well into
+human memory’s working capacity of about 5-7 items at a time (according to
+something I read in general science news).
+
Writing (and presenting or teaching – per Feynman) brings together different
+memory subsystems and solidifies knowledge in a holistic way. If you’re a visual
+thinker like I am, this may help you immensely. ️🌤️ Draw the
+layout, make interconnections between related letters, and you’ll be set.
+
For example, @jlangstrom on Discord came up with a novel mnemonic system
+🧠✨ where he would associate opposing pairs of letters per finger
+with words in English as well as his native language, Swedish:
+
+
+
“I remember the two characters by thinking that they are short hands. CN (china) KR (Korea) IS (Ice in swedish) ET (E.T phone home) and so on.”
+
+
+
KeyBr
+
I’ve used the KeyBr typing tutor to learn two different keyboard layouts in recent years. 👌🤓 It introduces a few new keys at a time (in English letter frequency order, so it’s layout agnostic 😎) until you gain proficiency, before advancing you further. Moreover, it gives you random English-like word fragments as you level up, so there’s a good variety for training compared to the typical fjfj style of rote exercises. I highly recommend it for learning a new layout.
+
You can hide the on-screen keyboard in KeyBr by pressing the Zoom button (next to the Settings button, with the gear icon). Then open another window below or beside it containing a reference image for the layout you’re learning. This way, you’re effectively visually replacing keybr’s on-screen keyboard with your own, and thus there’s no confusion. 🪄
+
+
Adaptation
+
Learning a new layout takes time (several days at least, because your brain internalizes the new concepts while you sleep — that’s what I’ve observed1 anyway) and this may be especially true coming from QWERTY and Colemak because they distribute vowels across both hands whereas Engram, Dvorak, and BEAKL cluster all vowels on the left hand, so it may take a little more effort to get used to it initially. 🧗 But the advantage of such clustering is that it improves hand balance & alternation, and is generally useful for international languages2.
+
+
Pinky finger usage
+
I’ve heard concerns about Engram’s placement of B on the left pinky finger – since most people are coming from QWERTY and their left pinky is relatively underutilized (and thus weaker) due to the fact that Q is one of the least frequent letters in English. In fact, I too observed this firsthand when I transitioned from Dvorak (which places the relatively infrequent apostrophe / single quote there) but my left pinky got stronger with practice and it hasn’t been an issue for me past the learning stage. Nevertheless, if this still remains a concern for you, consider these alternate placements for Q/Z and B/V.
+
In addition, I’ve found that the left pinky column in Engram (BCG) isn’t a problem in practice on contoured columnar keyboards (such as the Glove80, Dactyl, etc.) compared to traditional row-staggered keyboards (which the Alt-Layouts discord appears to be more concerned with optimizing). See this Reddit comment for further elaboration.
+
+
Cardinal directions (HJKL)
+
I presume you are accustomed to Vim’s traditional HJKL order? 🙂 I map the 4 right-hand fingers on the Cursor layer’s home row as Left, Up, Down, Right to according to the inward-rising curvature of the Glove80 keyboard. This is a longstanding preference that I formed 17 years ago, in my early days of using the Kinesis Advantage with the Dvorak layout, whose lack of HJKL provided the freedom to reimagine the arrangement of arrow keys on the home row.
+
In particular, I find it more natural to follow the inward-rising curve of the keyboard’s curved 3D keywell, which elevates the thumb above the pinky finger and, similarly, the middle finger (up arrow) above the ring finger (down arrow). As a bonus, this arrangement avoids double-duty on the index finger (for H & J) such that each home row finger is responsible for exactly one cardinal direction. Curiously, there is one thing in common (identical, actually) between HJKL and my arrow arrangement: K/up is on the middle finger.
+
In addition, my arrangement is consistent with the left-to-right (LTR) directionality of the Home, Page Up/Down, End keys on the lower row just beneath. Similarly, the clustering of Up with Left and Down with Right also reflects LTR because that’s how, say, a document shrinks & grows respectively in a text editor or word processor. See also this Reddit comment for additional context.
+
+
Variations
+
Here’s the baseline layout with which to compare the variations discussed below:
+
+
+
Depending on your ergonomic concerns, you might find these variations more
+comfortable, or at least preferable, on ergonomic keyboards such as the
+Glove80.
+
+
Engrammer
+
+
+
This variation helps maintain cross-proficiency with conventional keyboards:
+
+
+
Shifted pairs are standard (e.g. quotes don’t shift to parentheses).
+
Semicolon is placed relative to comma and period just like standard.
+
Equals and square brackets are placed near their standard locations.
+
+
In addition, application shortcuts that assume standard shifted pairs (such as
+Control-Equals for the “zoom in” operation in Web browsers) now work correctly.
+
+
Z and Q
+
The Z and Q keys overload the right pinky finger asymmetrically in the Engram
+layout due to the physical constraints of traditional row-staggered keyboards.
+This is an acceptable compromise because Z and Q are the least frequent letters
+in English: although they overload the right pinky finger, it seldom types them.
+
I was fond of variant ZQb, discovered by @x10an14, because it eliminates lateral movement of the right pinky finger, making the
+layout truly columnar. 💯 I tried switching to this personally but found that I
+use the CapsWord key more often than Z or Q, so the standard Engram layout was actually better for me. 😯
Similarly, some people dislike reaching up for the B and V keys with their pinky
+fingers or even, in some cases, reaching down for the G and P keys too!
Developed in 2021, Engram is a project completely created from scratch, focusing mainly on the best way to type diagrams, trigrams, or… Ngrams. The punctuation in the middle creates a clear gap between the hands, which makes it one of the best layouts for split columnar keyboards.
+
+
+
+
@Omnishambles asked on the Discord server for MoErgo Glove80 on April 20, 2023:
+
+
+
The Engram heatmap really makes the stacking of E and O on the same finger stand out. I know it’s one of the least-used vowel bigrams, but it’s still a vowel bigram. Does that combination ever feel awkward to you?
+
+
Not at all. 👍 I feel quite comfortable with the EO bigram in the Engram layout because it’s on the column assigned to the middle finger (the longest one), where ascending from E to O feels more like uncurling or lengthening (as opposed to reaching) the middle finger from its home row position. 🤔 Take a look at the rationale (and alternatives) that Engram considered for vowel clustering & placement.
+
As for the layout heatmaps in that Dygma article, they seem to have been created inconsistently: the Dvorak heatmap shows activity on the left shift key whereas the others don’t. Similarly, the Colemak, Dvorak, and Halmak heatmaps don’t highlight the O key (certainly not in red) as much as the others. Thus, it’s unclear what input text they typed on each layout when generating those heatmaps: each one seems to have had a different input.
+
A better source for heatmap comparison might be Ian Douglas’ database, which shows heatmaps for English, programming, bigrams, and so on. For example, here is the English heatmap for the Engram layout:
+
+
+
+
QWERTY
+
Jonathan Wheeler asked in the comments section of this article on July 8, 2023:
+
+
+
After reprogramming your brain to use different key layouts several times, when using someone else’s device, or a smartphone without a keyboard, etc, do you have any difficulty spontaneously switching back to the ol’ QWERTY when needed?
+
+
Not at all — in practice, I’ve found that it’s not a zero-sum game:
+
To my surprise, I still retain my QWERTY muscle memory, even nearly 2 decades after switching away from it! For instance, it’s especially strong when swiping (aka “glide typing”) on smartphones: I somehow Just Know the placement of all the letters perfectly, without conscious thought. Perhaps the physical interface of a smartphone’s virtual keyboard (behind a smooth glass display) is different enough from my ergonomic keyboards (split, columnar, with contoured keywells) that my brain doesn’t get confused.
+
However, actual touch-typing in QWERTY on conventional row-staggered keyboards is a bit rusty, but still workable. For instance, I was at my local public library recently where I had to fill out some forms on a computer kiosk: I was able to approximately touch-type (not as bad as hunt & peck, but not as good as blind touch-typing) on QWERTY even though I left it behind so long ago.
+
In contrast, I have no trouble typing in the Engram layout (and previously in BEAKL-15 and Dvorak) on conventional row-staggered keyboards (such as my laptop’s built-in keyboard) because the knowledge of where keys are located (along with the physical movements necessary to reach and press them) is ingrained in my muscle memory: it’s like a “device driver”. Whereas the actual key-to-letter mapping seems to be stored independently (stacked atop muscle memory, as a higher layer) because it can be overridden and reused on different physical keyboards: it’s like an “input method”.
+
In this manner, I’ve found that when you learn to type in a new layout or to use a different style of keyboard, it’s more like adding a new capability than replacing existing skills. Such that the very act of typing is effectively a transformation of your brain’s high-level thoughts and words into low-level physical keypresses by the appropriate combination of “input method” and “device driver” for the particular keyboard and layout you’re currently using. With sufficient practice, it all Just Works without conscious effort and on a variety of different physical keyboards (and layouts too, if you so wish). 🙌
+
Neuroplasticity for the win! (You’ll be fine 👍 — I’d highly recommend it.)
I’m curious what made you move away from BEAKL-15? Was there anything in particular that you did not like or struggle with.
+
+
With BEAKL-15, I ultimately felt that I didn’t experience the comfort that was advertised: I often felt like “climbing up a wall” when typing (for example, the word “spring” has me reaching up and down vertically). Also, I ended up forking my own variation after 3 months because the punctuation assignments weren’t as convenient as I liked for programming. Eventually, I found the obscure BEAKL-19bis layout which scored well on a comparison page and I was interested in learning more about its origins (how did the authors invent it? what principles guided them?) but I was too late: like an archaeologist pondering ancient civilizations, so too, I was left with more questions than I could investigate and was left demoralized. Even the BEAKL authors (Den and Ian) disappeared, along with the Shenafu forums where they discussed their creations, just a few months before I even heard about BEAKL. 😞
+
Thankfully, I found that Ian Douglas (now known as @iandoug on GitHub) was still active elsewhere and actually ran the keyboard-design.com website which I encountered while investigating BEAKL-19bis. On that site, he mentioned Arno Klein’s research publication and the resulting Engram layout. This caught my attention! Here was a layout, methodically & systematically derived from exhaustive comparison of permutations, guided by ergonomics principles. Not some golden result hidden behind AI algorithms like Halmak, or optimizers like AnDW for BEAKL-15, or CarpalX and MTGAP etc. Here was something that anyone could run on their own system and follow along in the Python notebook. (Plus the fact that Engram ended up on the same left-hand vowel layout as AnDW for BEAKL-15 greatly increased my confidence in the Engram methodology!)
+
+
+
Also did you use the BEAKL-15 number row and symbol suggestion?
+
+
No, I didn’t use the BEAKL-15 number row because it’s asymmetric: 40123 76598 (I naturally expected 87659 on right hand for symmetry). Moreover, as a programmer, I type numbers even less than prose, so rearranging the already intuitive native number row didn’t seem like a worthy trade-off in my case.
+
As for the BEAKL-15 symbol layer, I adapted parts of its design into my own symbol layer which is more optimized for Vim. (NOTE: My symbol layer has since evolved into this in the present day.) For example, I have ^()$ on the left hand home row of my Symbol layer which lets me quickly travel to BOL PrevSentence NextSentence EOL respectively. You can see how the BEAKL-15 symbol layer isn’t so convenient for Vim, although I do appreciate how logically it’s laid out.
+
+
Dvorak
+
I’ve typed exclusively in Dvorak for 16 years 👋 and Engram since the past 2 years. 🤓
+
I find Engram more comfortable because it eliminates lateral index finger movement when typing prose: Engram places all punctuation in the central columns, so my 8 touch typing fingers go straight up and down, effectively staying in their columns all the time. Moreover, Engram adds inward rolls (like MTGAP and Colemak) and accounts for finger strength differences, in addition to Dvorak’s concept of hand alternation. This combines the best of both worlds (rolling and alternation) for a rapid, yet comfortable, typing experience.
+
Finally, Engram has some important similarities with Dvorak: E and T/H and J/K placement is the same in both layouts. As a die-hard Vim user (since the last 14 years), I find that Engram has an even better L placement (directly above H; both on the strong index finger) than Dvorak while retaining its convenient J/K placement. See my review above for details.
+
+
Colemak
+
@formlessdao asked in Arno’s Engram discussion board on GitHub on June 8, 2021:
+
+
+
What are the improvements of Engram over Colemak Mod-DH and Halmak?
+
+
I would say the main advantage is that Engram was designed from a clean slate with support for columnar split/ortholinear keyboards in mind. 💡 Moreover, it doesn’t limit itself to the historical legacy and physical constraints of typewriter style row-staggered keyboards.
+
For example, Engram takes full advantage of the former style of columnar keyboards (where each finger can travel straight up and down along its own dedicated column) by eliminating lateral movement of the index fingers into the central columns for typing letters (A-Z).
+
In contrast, Colemak assumes the latter style of row-staggered keyboards because it retains the ZXCV sequence from QWERTY legacy for application & clipboard shortcuts (which become far less important when you have a fully programmable keyboard that lets you place such shortcuts on dedicated keys, layers, combos, macros, etc. anywhere you like) and also because Colemak’s variations (AngleMod, DH-mod, etc.) balance & redistribute finger workloads in a way that might not provide much benefit on columnar keyboards. For instance, consider the AngleMod: how would you use that “angle” on keyboards with curved 3D keywells (e.g. Glove80, Dactyl, Kinesis, Maltron) anyway? 😵💫
+
As an overall comparison of Colemak and Engram statistics, what do you think of page 113 in the Alt Keyboard Layouts community’s document on all things AKL? On that page, I see that Engram brings a modest reduction in SFS%, a significant reduction in LSB% (since it doesn’t have any letters in the central columns), better balance of left/right hand workload, significantly less redirects, and more left/right hand alternation (like Dvorak) along with more than double the amount of inward rolls — all at the cost of a negligible increase in SFB% compared to Colemak and Colemak-DH (seen on the following page) alike. (And also, not mentioned on that page: the Vim friendliness it coincidentally brings with JK and HL adjacency as well as B on left hand, W on right hand, T to the left of F, comma to the left of semicolon, and ease of cw, dw, yw, etc.).
+
There are many more keyboard layouts in the ecosystem, neatly quantized by SFB% in that document’s table of contents, available to be explored. But my feeling is that, if Colemak is considered to be a good enough campsite for travelers (welcomed by familiarity of conventional row staggered keyboards: ZXCV preservation, angle mods, and alt-fingering options to choose from) to settle down at, couldn’t Engram also be considered in the same light for split columnar keyboards (forgo ZXCV in favor of semantic undo/redo/cut/copy/paste keys such as those in Miryoku’s navigation layer, and forgo angle mods along with alt-fingering because columnar keyboards practically enforce proper touch-typing)?
+
+
Halmak
+
Similarly, Halmak’s AI was trained on a MacBook laptop’s row-staggered keyboard, so it literally assumes that exact physical key placement as well as its trainer’s natural home row finger placement (see “fingers are not perfect” at timestamp 5:11). Thus, it might not provide as much benefit for columnar split/ortho keyboards or for those who have a different natural home row placement than the AI’s trainer. 🤔
+
+
+
+
+
+
+
I’ve found that time away from practice (especially sleep) actually gives the brain a chance to reinforce and optimize all those new neurological pathways you’re building in the form of muscle memory. You might be a little rusty for the first few minutes but soon it’ll all come rushing back. 🌊 That’s what I observed recently after a week-long break away from computers. ↩
+
+
+
+
Specifically, I would consider layouts such as Engram and Dvorak that cluster all the vowels on one hand because words in Indo-European languages are composed of consonant+vowel formations, so you’ll get a more balanced workload distribution across both hands this way. Moreover, these improvements would benefit all of the languages you type in — whereas right now, they’re all encumbered by QWERTY legacy. :disappointed_relieved: ↩
+
+
+
+
+
Updates
\ No newline at end of file
diff --git a/engrammer-keyboard-layout.html b/engrammer-keyboard-layout.html
new file mode 100644
index 0000000..d68f0c9
--- /dev/null
+++ b/engrammer-keyboard-layout.html
@@ -0,0 +1,30 @@
+Engrammer - Arno's Engram layout for programmers - The Terminal Programmer
After 1.25 years of using Engram exclusively, I recently needed to type on a standard laptop keyboard featuring the QWERTY layout. I found myself struggling to locate symbols and punctuation marks (especially those on shifted keys) as I reluctantly resorted to hunt-and-peck typing. 😩 However, I was pleasantly surprised to find that it was significantly easier to type certain punctuation marks used in programming (such as backtick, backslash, semicolon, and equals) because they were unshifted: directly available, without needing to press Shift!
+
Having tasted the renewed ease of access to those aforementioned symbols (which happen to be used heavily in programming editors, such as Vim, and also more generally in application shortcuts such as Control-Equals for the “zoom in” operation), I could no longer return to Engram verbatim. 🤩 Instead, I was motivated to rectify the situation by harnessing the best of both worlds. Behold, the Engrammer layout: a variation of Arno’s Engram for programmers! :)
+
+
+
`~ 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) [{ ]}
+ bB yY oO uU '" ;: lL dD wW vV zZ =+ \|
+ cC iI eE aA ,< .> hH tT sS nN qQ
+ gG xX jJ kK -_ /? rR mM fF pP
+
Legend: Gold is Arno’s Engram; Blue is Engram-like; Pink is new.
+
+
+
+
This variation helps maintain cross-proficiency with standard keyboards because:
+
+
+
Shifted pairs are standard (e.g. quotes don’t shift to parentheses).
+
Semicolon is placed relative to comma and period just like standard.
+
Equals and square brackets are placed near their standard locations.
+
+
In addition, application shortcuts that assume standard shifted pairs (such as
+Control-Equals for the “zoom in” operation in Web browsers) now work correctly.
+
\ No newline at end of file
diff --git a/ergohaven-remnant-keyboard.html b/ergohaven-remnant-keyboard.html
new file mode 100644
index 0000000..c856d84
--- /dev/null
+++ b/ergohaven-remnant-keyboard.html
@@ -0,0 +1,441 @@
+Ergohaven Remnant programmable ergonomic keyboard - The Terminal Programmer
Here is my review of this keyboard, as published on Ergohaven’s reviews channel.
+
+
+
I’m a happy customer of two Ergohaven keyboards: the Dactyl Manuform 5x6 (top in photo) from 2021 and the Remnant (bottom in photo) from 2022.
+
As a professional software engineer, I heartily recommend the Remnant to my colleagues and friends as my “end game” keyboard after having used an Ergohaven Dactyl Manuform 5x6 (split and contoured) for 1 year, a ZSA ErgoDox EZ (split but not contoured) for 6 years, and a Kinesis Advantage (contoured but not split) for 11 years before that.
+
In terms of aesthetics, the Remnant’s fine lines and starlight material stand out elegantly (like a tuxedo) in contrast to the Dactyl Manuform 5x6’s thick lines and muted colors (like a trenchcoat). This shows just how much Ergohaven’s 3D printing skill and product quality has improved over the past year.
+
In terms of acoustics, the Remnant resonates with a pleasing “thock” on most keystrokes (especially on the thumb cluster keys) and wholly eliminates the hollow sound of the Dactyl Manuform 5x6. To my ears, the Remnant produces a premium sound like those fancy modded keyboards you see on YouTube reviews.
+
In terms of design, the Remnant greatly improves upon the Dactyl Manuform 5x6 by reducing the overall height of the keyboard (low profile), removing two vestigial keys from the bottom row of the thumb cluster (they were difficult to reach and I only pressed central one with the metacarpal joint of my thumb anyway), and realigning the thumb cluster to make all 3 keys easier to reach.
+
In terms of firmware, the Remnant’s VIAL support makes it very easy for newcomers to remap their keyboard using a desktop app or Web browser. For advanced users, the Remnant also supports QMK natively, so you can still “qmk flash” your own custom firmware onto it. Notably, I’m able to use my custom QMK implementation of home row mods based on the bilateral combinations concept from the legendary Miryoku layout (which works beautifully with the Remnant’s 3-key thumb clusters, by the way) on both the Remnant and the Dactyl Manuform 5x6.
+
In terms of hardware, the Remnant features per-key RGB lighting, a powerful ARM processor with 62x more onboard memory (for tapdance, combos, macros, lighting effects and custom firmware), and most impressively a curved PCB that houses hot-swappable switches! This makes the Remnant significantly more reliable than my Dactyl Manuform 5x6, which was an early hand-wired version that unfortunately experienced some electrical disconnects over time (but thankfully they were simple enough that I could debug and re-solder them by myself).
+
Overall, the Remnant is an excellent upgrade from the Dactyl Manuform 5x6, and both Ergohaven keyboards are lightyears ahead of the status quo. I’m very happy with my purchases and to see the Ergohaven team improve so much! Thank you and best regards.
+
+
Layers
+
The keyboard boots up into the following default “base” layer when powered on.
+When held, the purple keys in the thumb
+clusters activate the subsequent layers according to the legendary Miryoku’s
+6-layer design with 3-key thumb activation.
+
+
+
Interactive: Hover your mouse over the purple keys to see each layer!
+
Going beyond Miryoku, I have added custom “sticky layer toggle” functionality to the
+Shift keys of each Miryoku layer for times when I need use a layer for longer
+than a brief moment. For example, pressing Shift after activating a Miryoku layer
+with my thumb keeps that layer activated henceforth (thus making it “sticky”),
+allowing me to release my thumb off the Miryoku layer key. Similarly, pressing
+Shift again deactivates the Miryoku layer (thus making it “unsticky”) and sends
+me back home to the base layer.
+
+
Cursor layer
+
+
+
+
+
+
Arrow keys
+
+
The up/down arrow keys on the right-hand home row diverge from Vim’s HJKL order
+because it feels more natural to follow the inward-rising curve of the keyboard’s
+contoured keywell, which elevates the thumb above the pinky finger and, similarly,
+the middle finger (up arrow) above the ring finger (down arrow).
+
This is a longstanding preference that I formed 17 years ago, in my early days of
+using the Kinesis Advantage with the Dvorak layout, whose lack of HJKL provided
+the freedom to reimagine the arrangement of arrow keys on the home row.
+
+
+
+
Select & edit
+
+
Editing (index finger) and selection (thumb cluster) keys line the inner wall.
+This opposition allows for pinching, where selections can be followed by edits.
+For example, to copy everything, I would first tap the “Select all” key with my
+thumb and then pinch slightly inward to tap the “Copy” key with my index finger.
+
The copy and paste keys are stacked vertically, in that order, to allow the index
+finger to rake down upon them in a natural curling motion toward the palm. This
+order is also logical, since pasting requires something to be copied first.
+
The versatile “Select word/line” key at the thumb cluster’s home position is powered
+by Pascal Getreuer’s word selection QMK macro, which automates common
+selection tasks that require holding down Control and Shift with the arrow keys:
+
+
+
+
Tapping it selects the word under the cursor; shift-tapping it selects the line.
+Further taps extend the selection by another word (unshifted) or line (shifted).
+
+
Number layer
+
+
+
A 3x3 numeric keypad (using the standard 10-key layout) occupies the home block.
+The period and comma keys are positioned near the zero key on the thumb cluster.
+Square brackets from the base layer are replaced with parentheses for grouping.
+
+
+
+
Date & time
+
+
The slash and minus keys are positioned for MM/DD and YYYY-MM-DD date entry.
+Similarly, the colon key is positioned above them for HH:MM:SS time stamp entry.
+
+
+
: time stamp separator
+
- ISO-8601 date separator
+
/ American date separator
+
+
+
+
+
Arithmetic
+
+
Common arithmetic operators pair along the sides of the 3x3 numeric keypad.
+
+
+
% and : for percentages and proportions
+
+ and - for addition and subtraction
+
* and / for multiplication and division
+
+
+
+
+
Prefix signs
+
+
Signs that commonly prefix numbers line the top of the 3x3 numeric keypad.
+
+
+
~ approximately
+
# literal number
+
$ dollar amount
+
@ at the rate of
+
+
+
+
+
Inequalities
+
+
Comparison operators are positioned along the perimeter of the home block.
+
+
+
< less than
+
> greater than
+
= equal to
+
~ approximately
+
+
+
Function layer
+
+
+
The function keys are arranged in the same 10-key layout as the Number
+layer’s 3x3 numeric keypad so that you can develop common muscle
+memory for both layers. The remaining F10-F12 keys wrap around the home block
+because they’re found in shortcuts such as BIOS save/quit, fullscreen toggle,
+and devtools, respectively.
+
+
Symbol layer
+
This is the crown jewel of my keyboard’s configuration: an entire layer dedicated
+to the entry of symbols that are essential for computer programming.
+It’s the result of several hundreds of layout iterations over the last 8+ years.
@ is at the same relative position as the Tab key on standard keyboards.
+They pair well: the former denotes references and the latter expands them.
+
! is on the same key as Shift on the base layer: they both invert things.
+
(); sits just above the inward-rolling “you” sequence in the Engram layout.
+
+
+
+
+
Vim editor shortcuts
+
+
+
+
^ and $ are on the home row, for jumping to the start/end of current line.
+
# and * are on the home row, to search behind/ahead for word under cursor.
+
= is on the home row, to automatically indent current line or selection.
+
{ and } are on the home block, for jumping to previous/next paragraph.
+
< and > are on the home block, for decreasing/increasing indentation.
+
, and ; are in proper order for previous/next repetition of f/F/t/T jumps.
+
? and / are stacked vertically, to search behind/ahead for regex pattern.
+
: is on the inner thumb key, for entering Vim’s command mode comfortably.
+
% is on the outer thumb key, for jumping to cursor’s matching delimiter.
+
+
+
+
+
Adjacent key bigrams
+
+
+
+
#! for shebang lines in UNIX scripts.
+
-> for thin arrows in C, C++, and Elixir.
+
() for parentheses.
+
.* for filesystem globs.
+
*. for regular expressions.
+
+
+
+
+
Outer corner bigrams
+
+
These are easy to find because they’re on the outer corners of the keyboard.
+
+
+
!~ for regular expression “not matching” in Perl, Ruby, and Elixir.
+
/* and */ for multi-line comments in C, CSS, and JavaScript.
+
\/ for escaped regular expression delimiters in Vim.
+
~/ for home directory paths in UNIX.
+
?! for interrobang in English prose.
+
+
+
+
+
Inward rolling bigrams
+
+
+
+
(); for zero-arity function calls in C and related languages.
+
); for function call statements in C and related languages.
+
() for parentheses.
+
.* for regular expressions.
+
~/ for home directory paths in UNIX.
+
<- for assignment in R and in Elixir’s with statements.
+
-> for thin arrows in C, C++, and Elixir.
+
=> for fat arrows in Perl, Ruby, and Elixir.
+
!= for “not equal to” value comparison in many languages.
+
<= for “less than or equal to” comparison in many languages.
+
^= for bitwise XOR assignment in C and related languages.
+
|> for the pipe operator in Elixir.
+
!( for negating a group in Boolean expressions.
+
"$ for quoted variable substitution in Bourne shell.
+
!$ for last argument of previous command in Bourne shell.
+
$? for exit status of previous command in Bourne shell.
+
<% for directive tags in Ruby’s ERB and Elixir’s EEx templates.
+
#{ for string interpolation in Ruby and Elixir.
+
#[ and ![ for metadata attributes in Rust.
+
`' for legacy curly quotes.
+
/* for starting comments in C, CSS, and JavaScript.
+
</ for element closing tags in XML and HTML.
+
<> for angle brackets.
+
{} for curly braces.
+
+
+
+
+
Outward rolling bigrams
+
+
+
+
/? for query parameters in URLs.
+
~> for pessimistic version constraint in SemVer.
+
=~ for regular expression matching in Perl, Ruby, and Elixir.
+
-= for negative accumulation in C and related languages.
+
+= for accumulation in C and many languages.
+
%= for modulo assignment in C and related languages.
+
>= for “greater than or equal to” value comparison.
+
>& and &< for file descriptor redirection in Bourne shell.
+
$_ for value of last argument of previous command in Bourne shell.
+
%> for directive tags in Ruby’s ERB and Elixir’s EEx templates.
+
${ for variable interpolation in Bourne shell.
+
%{ for maps (hash tables) in Elixir.
+
*/ for closing comments in C, CSS, and JavaScript.
+
+
+
Mouse layer
+
+
+
Movement keys are located centrally in the home block, resembling WASD keys, and
+mouse acceleration controls are poised for pinky finger access, so you can
+independently move the mouse pointer and also change its speed at the same time.
+
Mousewheel down/up keys are also placed on the home block, specifically on
+the same keys as J/K (down/up in Vim) on the base layer for muscle memory reuse.
+
+
System layer
+
+
+
Keys for controlling RGB matrix settings line the central rows of home block,
+whereas keys for applying lighting modes line the perimeter of the home block.
+
+
Firmware
+
My keyboard’s entire firmware, as described in this article, is
+available on GitHub in the sunaku_remnant branch of my personal
+fork of QMK as well as Vial.
You can upload the provided QMK Keymap JSON file named
+keymap_config.json into the QMK Configurator app to view or
+customize the keymap and all of its layers. When you’re finished, download the
+keymap back to the same file, overwriting it.
+
+
Building the firmware
+
Navigate into the directory shown in the Firmware section above and run make to:
+
+
+
Convert the keymap_config.json file into C source code.
+
Wrap the C source code with a custom header and footer.
+
Compile the wrapped up C source code using qmk compile.
+
Flash the compiled firmware (UF2 file) to the keyboard.
+
+
All these steps are handled by the provided Makefile, shown below for reference:
+
# NOTE: Don't name the QMK Configurator JSON keymap file as "keymap.json"
+# because `qmk compile` directly translates it into C and compiles it too,
+# thereby completely bypassing this Makefile and our keymap header/footer!
+
+TOPLEVEL=`git rev-parse --show-toplevel`
+KEYBOARD=ergohaven/remnant
+KEYMAP=sunaku
+
+all:flash
+
+flash:build
+ qmk flash -kb$(KEYBOARD)-km$(KEYMAP)
+
+build:keymap.c config.h rules.mk
+ test!-e keymap.json # see comment at the top of this Makefile
+ qmk compile -kb$(KEYBOARD)-km$(KEYMAP)-j 0
+
+keymap.c:keymap_config_converted.json keymap_header.c keymap_footer.c config.h
+ qmk json2c -o$@$<
+ spot=$$(awk'/THIS FILE WAS GENERATED/ { print NR-1 }'$@)&&\
+ sed-e"$$spot r keymap_header.c"-e"$$ r keymap_footer.c"-i$@
+
+keymap_config_converted.json:keymap_config.json
+ sed's|\("keyboard": *"\)[^"]*|\1$(KEYBOARD)|'$^>$@
+
+clean:
+ qmk clean
+
+clobber:clean
+ rm-fv keymap.c keymap_config_converted.json
+ rm-fv$(TOPLEVEL)/$$(echo$(KEYBOARD) | tr / _ )_$(KEYMAP).*
+
+.PHONY:clean clobber build flash
+
\ No newline at end of file
diff --git a/extjs4-mvc-rails31-asset-pipeline.html b/extjs4-mvc-rails31-asset-pipeline.html
new file mode 100644
index 0000000..24b925f
--- /dev/null
+++ b/extjs4-mvc-rails31-asset-pipeline.html
@@ -0,0 +1,52 @@
+Putting ExtJS 4 into Rails 3.1's asset pipeline - The Terminal Programmer
ExtJS 4’s MVC architecture organizes
+source files in the following structure so that its Ext.Loader class can
+dynamically fetch and load the necessary Javascript files at runtime.
+
+
+
app/
+
+
+
controller/
+
model/
+
store/
+
view/
+
+
app.js
+
+
In a Rails 3.1 application, you can place the above structure under the
+app/assets/javascripts directory and package it as a monolithic
+Javascript asset using Rails 3.1’s asset pipeline by adding the
+following snippet to the app/assets/javascripts/application.js file:
Next, add an .erb extension to the app/assets/javascripts/app.js file
+and then register the packaged ExtJS 4 MVC components by adding the
+following snippet to the app/assets/javascripts/app.js.erb file:
+
Ext.application({
+<%Dir[File.expand_path('../app/*/',__FILE__)].eachdo|subdir|%>
+ <%=File.basename(subdir)%>s: <%=
+ # omit all file extensions (js, js.erb, etc.) from the file name
+ Dir[subdir+'/*.js*'].map{|f|File.basename(f)[/^[^.]+/]}.to_json
+ %>,
+<%end%>
+// ... the rest of your ExtJS 4 application definition goes here ...
+});
+
You now have ExtJS 4’s MVC architecture in Rails 3.1’s asset pipeline. :-)
+
Note that, during development, the asset pipeline caches the result of
+rendering the app/assets/javascripts/app.js.erb file in memory. As a
+result, if you add new files to or remove existing files from the
+app/assets/javascripts/app/ directory tree, then you must either (1)
+modify the public/javascripts/app.js file and reload your browser or (2)
+restart your Rails application (web server) to force the asset pipeline to
+render and cache the new output.
+
Updates
\ No newline at end of file
diff --git a/extracting-archives-without-making-a-mess.html b/extracting-archives-without-making-a-mess.html
new file mode 100644
index 0000000..b096d67
--- /dev/null
+++ b/extracting-archives-without-making-a-mess.html
@@ -0,0 +1,242 @@
+Extracting archives without making a mess - The Terminal Programmer
It is common practice to place all files related to a single project into
+a single directory, and then to make an archive of that directory.
+
This practice ensures that the archive does not make a mess of someone’s
+working directory when extracted.
+
Unfortunately, such benevolence cannot always be expected of the archive
+creator, so care must be taken by the user to prevent a mess.
+
+
Approach
+
Naturally, I automated this process by writing a script which takes care
+to always extract an archive neatly into its own subdirectory. This
+script remained as an example for my For each File project for a few years. However, I
+converted it into Ruby yesterday due to the inability of transforming a
+relative path into an absolute one in GNU BASH.
+
+
Solution
+
Now, behold the sparkling powers of the extract-archive script, listed
+below and available on GitHub, and enjoy
+a tidy working directory. :-)
+
+
Before the tool
+
Consider the two archives illustrated below.
+
$ tree
+.
+|-- good_archive.tar
+`-- evil_archive.tar
+
+0 directories, 2 files
+
+$ tar tf evil_archive.tar
+bar
+baz
+foo
+
+$ tar tf good_archive.tar
+good_archive/
+good_archive/bar
+good_archive/baz
+good_archive/foo
+
Watch what happens to our working directory as we extract them.
+
$ tar xf evil_archive.tar
+
+$ tree
+.
+|-- bar
+|-- baz
+|-- good_archive.tar
+|-- foo
+`-- evil_archive.tar
+
+0 directories, 5 files
+
+$ tar xf good_archive.tar
+
+$ tree
+.
+|-- bar
+|-- baz
+|-- good_archive
+| |-- bar
+| |-- baz
+| `-- foo
+|-- good_archive.tar
+|-- foo
+`-- evil_archive.tar
+
+1 directory, 8 files
+
The evil archive carelessly littered its contents within our working
+directory, whereas the good archive politely placed its contents
+into its own subdirectory.
#!/usr/bin/env ruby
+#
+# Extracts various compressed and uncompressed file archives (see
+# http://en.wikipedia.org/wiki/List_of_archive_formats) into their
+# *own* output directories so that they do not make a mess by
+# extracting directly into your working directory.
+#
+# Written in 2003 by Suraj N. Kurapati <https://github.com/sunaku>
+
+require'tmpdir'
+
+require'fileutils'
+includeFileUtils::Verbose
+
+# Extracts the given source archive relative to the given destination path,
+# and returns the path of the directory containing the extracted contents.
+defextractsrc_path,dst_path=File.dirname(src_path)
+ src_path=File.expand_path(src_path)
+ src_name=File.basename(src_path)
+ src_suffix=File.extname(src_name)
+ src_prefix=File.basename(src_name,src_suffix)
+
+ Dir.mktmpdir(nil,dst_path)do|tmp_dir|
+ # decompress the archive
+ cdtmp_dirdo
+ casesrc_name.sub(/\.part$/,'')
+ when/\.(tar\.gz|tar\.Z|tgz|taz)$/i
+ system'tar','-zxf',src_path
+
+ when/\.(tar\.bz|tar\.bz2|tbz|tbz2)$/i
+ system'tar','-jxf',src_path
+
+ when/\.(tar\.xz|txz)$/i
+ system'tar','-Jxf',src_path
+
+ when/\.(tar|cpio|gem)$/i
+ system'tar','-xf',src_path
+
+ when/\.(tar.lzo|tzo)$/i
+ system"lzop -xc #{src_path.inspect} | tar -xf -"
+
+ when/\.(lzo)$/i
+ system'lzop','-x',src_path
+
+ when/\.(gz)$/i
+ system"gunzip -c #{src_path.inspect} > #{src_prefix.inspect}"
+
+ when/\.(bz|bz2)$/i
+ system"bunzip2 -c #{src_path.inspect} > #{src_prefix.inspect}"
+
+ when/\.(shar)$/i
+ system'sh',src_path
+
+ when/\.(7z)$/i
+ system'7zr','x',src_path
+
+ when/\.(zip)$/i
+ system'unzip',src_path
+
+ when/\.(jar)$/i
+ system'jar','xf',src_path
+
+ when/\.(rz)$/i
+ lnsrc_path,src_name# rzip removes the archive after extraction
+ system'rzip','-d',src_name
+
+ when/\.(rar)$/i
+ system'unrar','x',src_path
+
+ when/\.(ace)$/i
+ system'unace','x',src_path
+
+ when/\.(arj)$/i
+ system'arj','x',src_path
+
+ when/\.(arc)$/i
+ system'arc','x',src_path
+
+ when/\.(lhz|lha)$/i
+ system'lha','x',src_path
+
+ when/\.(a|ar)$/i
+ system'ar','-x',src_path
+
+ when/\.(Z)$/
+ system"uncompress -c #{src_path.inspect} > #{src_prefix.inspect}"
+
+ when/\.(z)$/
+ system"pcat #{src_path.inspect} > #{src_prefix.inspect}"
+
+ when/\.(zoo)$/i
+ system'zoo','x//',src_path
+
+ when/\.(cab)$/i
+ system'cabextract',src_path
+
+ when/\.(deb)$/i
+ system'ar','x',src_path
+
+ when/\.(rpm)$/i
+ system"rpm2cpio #{src_path.inspect} | cpio -i --make-directories"
+
+ else
+ warn"I do not know how to extract #{src_path.inspect}"
+ end
+ end
+
+ # clean any mess made by decompression
+ manifest=Dir.new(tmp_dir).entries-%w[ . .. ]
+
+ ifmanifest.length==1# there was no mess!
+ adj_dst=File.join(dst_path,manifest.first)
+ adj_src=File.join(tmp_dir,manifest.first)
+ else
+ adj_src=tmp_dir
+ adj_dst=File.join(dst_path,src_name[/.*(?=\..*?)/])
+ end
+
+ adj_dst<<"+#{Time.now.to_i}"until
+ notFile.exist?adj_dstand
+ mv(adj_src,adj_dst,:force=>true)
+
+ touchtmp_dir# give Dir.mktmpdir() something to remove
+
+ adj_dst
+ end
+end
+
+if$0==__FILE__
+ prefix=File.basename(__FILE__)
+
+ ARGV.eachdo|src|
+ dst=extract(src)
+ puts"#{prefix}: '#{src}' => '#{dst}'"
+ end
+end
+
Updates
\ No newline at end of file
diff --git a/ftv01-la-jetee-effects.html b/ftv01-la-jetee-effects.html
new file mode 100644
index 0000000..4462008
--- /dev/null
+++ b/ftv01-la-jetee-effects.html
@@ -0,0 +1,35 @@
+On the effectiveness of La Jetée - The Terminal Programmer
La Jetée, a film by the French new wave artist Chris Marker, was truly
+fascinating because of its clever means of storytelling and its brilliant
+ending.
+
This film is composed almost wholly of tinted black and white stills. In
+particular, the tint used was a pale amber brown, which reminded me of a
+rusted sewer pipe wasting away in some ancient drainage system beneath a
+modern metropolis. In fact, this kind of feeling suited the film perfectly as
+the story took place underground. In this respect, Marker chose an effective
+tint for his stills which created an appropriate mood for his film.
+
The sound in this film was composed mostly of sound effects and perhaps a
+melodious background score during the protagonist’s encounters with the
+smiling woman from the past.
+
One instance where sound was used effectively was during the protagonist’s
+participation in the time-travelling experiment. Here, Marker added ambient
+but incomprehensible whispers spoken by the scientists. These whispers
+surround and overwhelm the viewer, much like the protagonist is overwhelmed by
+the scientists. Had the whispers been comprehensible, the viewer’s attention
+would be diverted away from the protagonist and towards the scientists. In
+this manner, Marker chose the sound effects well to help the viewer feel the
+protagonist’s helplessness.
+
The montage in this film is excellent. After the first few images, I no longer
+noticed any sharp transitions in the montage. The film gracefully flowed from
+one image to other next, as the rich photography drew me into the film’s
+world.
+
The film’s twist ending was quite brilliant and unexpected. It establishes the
+goal of the inward-looking journey of the protagonist, which ironically was
+himself, and serves as a very nice dénouement to the plot. That is, in order
+to discover who the smiling woman and the mysterious man who was murdered in
+his childhood, the protagonist unwittingly embarked on this time-travelling
+expedition.
+
\ No newline at end of file
diff --git a/ftv01-the-power-of-film.html b/ftv01-the-power-of-film.html
new file mode 100644
index 0000000..3c17085
--- /dev/null
+++ b/ftv01-the-power-of-film.html
@@ -0,0 +1,54 @@
+The power of film - The Terminal Programmer
Western culture is notorious for dichotomizing everything: good and bad, black
+and white, day and night, left and right, and so on; the list is endless.
+Influential films of the early 20th century, specifically D. W. Griffith’s
+Birth of a Nation (BON), are characteristic of such dichotomy as they
+establish racial and political stereotypes of the day. Fortunately, films of
+the early 21st century, such as City of God (COG), are making huge
+strides in breaking away from past legacy.
+
In BON, the protagonists are the heroic white citizens of the Ku Klux Klan
+(KKK) whereas the antagonists, led by their leader who attempts to forcibly
+marry a white woman, are a group of African Americans. This story is stale,
+cut and dry, and leaves little room for the imagination. That is, it simply
+regurgitates social beliefs (racial and political stereotypes) of its day.
+
In contrast, COG presents a psychotic antagonist and an optimistic protagonist
+who both happen to be of African descent to make the point that it is the
+human being who is both good and evil: not a “good” white man against an
+“evil” black man. Furthermore, the film portrays this message through various
+characters.
+
For example, consider the “Tender Trio” who are a group of bandits. Western
+culture would paint these men as “evil” or “bad” simply because they are
+bandits, but COG shows them in other angles: people who steal but give the
+loot to the poor; people who help the underprivileged gain access to basic
+necessities like gasoline for cooking and heating water.
+
Another example is the “runts”. These are children who terrorize and loot
+shops in the town, so they are painted as delinquents in Western culture.
+However, when they avenge their comrades by killing Zé Pequeño at the end,
+they are painted as victors and good citizens for ridding the town of the
+gangster menace.
+
Another example is “Knockout Ned”, the ex-army lieutenant who now works as a
+ticket conductor in the local bus routes. Ned is optimistic, friendly, and
+serves as a role model for Rocket and his friend when they ride the bus. He
+tells the young men that if they study in school and work hard, they can make
+it out of the slums and reach a better future: the “American Dream”, if you
+will. However, the same Ned becomes a ruthless killer when he joins up with
+Carrot’s gang and loots banks. Here, his initial no-killing policy
+(representing Western culture’s “good” qualities) changes to killing anyone
+who gets in the way (representing Western culture’s “bad” qualities).
+
Finally, consider Zé Pequeño himself. Although he is a psychotic menace
+according to Western culture, he brought peace to Cidade de Deus for a period
+of time when he first took power: an accomplishment few Western politicians
+can claim.
+
In all these examples, COG demolishes racial and political stereotypes and
+brings light to the fact that all humans have both good and bad qualities.
+Western culture’s dichotomy is, in fact, a tool used to dehumanize people in
+order to cause harm to them whilst maintaining a clear conscience.
+
By showing all sides of human nature (greed, compassion, kindness, love, etc.)
+COG dispels many stereotypes established by the ever-dichotomizing Western
+culture. I feel that the true power of films lies here and sincerely hope that
+future films strive to dispel stereotypes and bring light to the ever-complex
+and ever-changing face of humanity.
+
\ No newline at end of file
diff --git a/git-rebase-autocon.html b/git-rebase-autocon.html
new file mode 100644
index 0000000..1f757f7
--- /dev/null
+++ b/git-rebase-autocon.html
@@ -0,0 +1,112 @@
+Automatic merge conflict resolution for git-rebase(1) - The Terminal Programmer
Let’s say you have a copy of some software (a clone of a Git repository, to be
+precise) that you’ve modified in some way, locally. Some time later, that
+software is changed upstream, thereby rendering your local copy out of date.
+How can you bring your local copy up to date while also retaining your
+local modifications?
+
The answer is to use git-rebase(1).
+However, if your local modifications conflict with any upstream changes, then
+you’ll need to resolve those conflicts
+manually: a cumbersome process that can be daunting for inexperienced users.
+
This is particularly problematic for configurable software distributed through
+Git, such as my text editor and
+window manager configurations,
+where the occasional need for manual conflict resolution greatly impedes users
+who aren’t technically inclined or lack the time necessary to manually resolve
+such conflicts.
+
+
Solution
+
The following shell script automates conflict resolution for git-rebase(1) by
+simply setting aside local modifications that conflict with upstream changes
+and by offering users a means of easily undoing such actions, through git
+reset --hard.
+
Afterwards, the user may choose to inspect the automatically set-aside
+conflicts, recorded in empty-tree commits labeled “fixup!”, and restore
+portions thereof: thereby enacting manual conflict resolution, but willingly
+performed at one’s leisure.
+
+
~/bin/git-rebase-autocon
+
#!/bin/sh -e
+#
+# Usage: git-rebase-autocon [TARGET] [ARGUMENTS_FOR_GIT_REBASE...]
+#
+# Rebases the given TARGET while automatically resolving conflicts
+# by substituting empty-tree commits labeled "fixup!" that log all
+# conflicting hunks in their commit messages in git-diff(1) format.
+#
+# If TARGET is not specified, the upstream tracking branch is used.
+# Optional ARGUMENTS_FOR_GIT_REBASE... are passed to git-rebase(1).
+#
+# Written in 2010 by Suraj N. Kurapati <https://github.com/sunaku>
+# Documented at <https://sunaku.github.io/git-rebase-autocon.html>
+
+# ensure working tree is clean
+git rebase HEAD --quiet
+commit=$(git rev-parse --short HEAD)
+
+# parse command-line arguments
+test$# -gt 0 &&target=$1&&shift||target='@{u}'
+target=$(git name-rev --name-only"$target")
+target=${target#remotes/}
+
+# rebase target and ensure that only merge conflicts made it fail
+git rebase --fork-point"$target""$@"&&exit||test-d .git/rebase-apply
+
+# solve merge conflicts by absorbing leftover commits from prior
+# versions of the target or by setting aside conflicting commits
+trap'git rebase --abort' TERM INT
+while test-d .git/rebase-apply;do
+ headline=$(head-1 .git/rebase-apply/final-commit)
+ conflict=$(cat .git/rebase-apply/original-commit)
+ shortish=$(git rev-parse --short"$conflict")
+
+ # in place of each conflicting commit, record an empty commit whose
+ # message contains the changes introduced by the conflicting commit
+ git reset --mixed--quiet# empty the index so we can make a commit
+ git commit --allow-empty--reuse-message="$conflict"--quiet
+ {
+ printf'fixup! %s %s\n\n'"$shortish""$headline"
+ git format-patch --stdout"$conflict~..$conflict"
+ } |
+ git commit --amend--allow-empty--file=-
+
+ git rebase --skip>/dev/null 2>&1 || :
+done
+
+cat<<END
+
+ +-------------------------IMPORTANT!----------------------------+
+ | |
+ | Some of YOUR COMMITS WERE SET ASIDE to solve merge conflicts: |
+ | empty commits labeled as "fixup!" have now taken their place. |
+ | But rest assured, THEY STILL EXIST in Git history at $commit, |
+ | and your working tree has all changes from those commits too! |
+ | |
+ | You can UNDO THIS REBASE at any time by running this command: |
+ | |
+ | git reset --hard $commit |
+ | |
+ | You can SEE WHAT CHANGED from before by running this command: |
+ | |
+ | git diff $commit |
+ | |
+ +-------------------------IMPORTANT!----------------------------+
+
+END
+
Updates
\ No newline at end of file
diff --git a/gruvbox-terminal-color-scheme.html b/gruvbox-terminal-color-scheme.html
new file mode 100644
index 0000000..6bb43c7
--- /dev/null
+++ b/gruvbox-terminal-color-scheme.html
@@ -0,0 +1,105 @@
+Gruvbox terminal color scheme - The Terminal Programmer
Having used the gorgeous gruvbox color scheme in (Neo)Vim for nearly two
+years, I thought: why not use the same colors in my terminal as well? And
+thus we have:
+
+
+
+
+
To use this color scheme in hterm, open the nassh extension’s settings page,
+open Chrome Developer Tools, and then run the following snippet in the console:
To use this color scheme in Rxvt or XTerm, save the following snippet to a file
+such as ~/.Xdefaults and then run xrdb -merge on that file to apply it. Any
+terminals launched thereafter will use this color scheme, but not existing ones.
+
! Colors from "gruvbox" colorscheme for Vim
+! https://github.com/morhetz/gruvbox#palette
+
+#define Ansi_0_Color #504945 /* fg2 *//* normal black */
+#define Ansi_1_Color #fb4934 /* red bright *//* normal red */
+#define Ansi_2_Color #b8bb26 /* green bright *//* normal green */
+#define Ansi_3_Color #fabd2f /* yellow bright *//* normal yellow */
+#define Ansi_4_Color #83a598 /* blue bright *//* normal blue */
+#define Ansi_5_Color #d3869b /* purple bright *//* normal magenta */
+#define Ansi_6_Color #8ec07c /* aqua bright *//* normal cyan */
+#define Ansi_7_Color #ebdbb2 /* fg *//* normal white */
+#define Ansi_8_Color #7c6f64 /* gray *//* bright black */
+#define Ansi_9_Color #fb4934 /* red bright *//* bright red */
+#define Ansi_10_Color #b8bb26 /* green bright *//* bright green */
+#define Ansi_11_Color #fabd2f /* yellow bright *//* bright yellow */
+#define Ansi_12_Color #83a598 /* blue bright *//* bright blue */
+#define Ansi_13_Color #d3869b /* purple bright *//* bright magenta */
+#define Ansi_14_Color #8ec07c /* aqua bright *//* bright cyan */
+#define Ansi_15_Color #ebdbb2 /* fg *//* bright white */
+#define Bold_Color #fabd2f /* yellow bright */
+#define Italic_Color #d3869b /* purple bright */
+#define Underline_Color #8ec07c /* aqua bright */
+#define Reverse_Color #d65d0e /* orange */
+#define Foreground_Color #d5c4a1 /* fg2 */
+#define Background_Color #282828 /* bg0 */
+#define Cursor_Color #fe8019 /* orange bright */
+
+Rxvt*color0 : Ansi_0_Color/* normal black */
+Rxvt*color1 : Ansi_1_Color/* normal red */
+Rxvt*color2 : Ansi_2_Color/* normal green */
+Rxvt*color3 : Ansi_3_Color/* normal yellow */
+Rxvt*color4 : Ansi_4_Color/* normal blue */
+Rxvt*color5 : Ansi_5_Color/* normal magenta */
+Rxvt*color6 : Ansi_6_Color/* normal cyan */
+Rxvt*color7 : Ansi_7_Color/* normal white */
+Rxvt*color8 : Ansi_8_Color/* bright black */
+Rxvt*color9 : Ansi_9_Color/* bright red */
+Rxvt*color10 : Ansi_10_Color/* bright green */
+Rxvt*color11 : Ansi_11_Color/* bright yellow */
+Rxvt*color12 : Ansi_12_Color/* bright blue */
+Rxvt*color13 : Ansi_13_Color/* bright magenta */
+Rxvt*color14 : Ansi_14_Color/* bright cyan */
+Rxvt*color15 : Ansi_15_Color/* bright white */
+Rxvt*colorBD : Bold_Color
+Rxvt*colorIT : Italic_Color
+Rxvt*colorUL : Underline_Color
+Rxvt*colorRV : Reverse_Color
+Rxvt*foreground : Foreground_Color
+Rxvt*background : Background_Color
+Rxvt*cursorColor : Cursor_Color
+
+XTerm*color0 : Ansi_0_Color/* normal black */
+XTerm*color1 : Ansi_1_Color/* normal red */
+XTerm*color2 : Ansi_2_Color/* normal green */
+XTerm*color3 : Ansi_3_Color/* normal yellow */
+XTerm*color4 : Ansi_4_Color/* normal blue */
+XTerm*color5 : Ansi_5_Color/* normal magenta */
+XTerm*color6 : Ansi_6_Color/* normal cyan */
+XTerm*color7 : Ansi_7_Color/* normal white */
+XTerm*color8 : Ansi_8_Color/* bright black */
+XTerm*color9 : Ansi_9_Color/* bright red */
+XTerm*color10 : Ansi_10_Color/* bright green */
+XTerm*color11 : Ansi_11_Color/* bright yellow */
+XTerm*color12 : Ansi_12_Color/* bright blue */
+XTerm*color13 : Ansi_13_Color/* bright magenta */
+XTerm*color14 : Ansi_14_Color/* bright cyan */
+XTerm*color15 : Ansi_15_Color/* bright white */
+XTerm*colorBD : Bold_Color
+XTerm*colorIT : Italic_Color
+XTerm*colorUL : Underline_Color
+XTerm*colorRV : Reverse_Color
+XTerm*foreground : Foreground_Color
+XTerm*background : Background_Color
+XTerm*cursorColor : Cursor_Color
+
+! enable coloring for bold and underlined text
+XTerm*vt100.boldColors : False
+XTerm*vt100.colorBDMode : True
+XTerm*vt100.colorULMode : True
+XTerm*vt100.colorRVMode : False
+XTerm*vt100.veryBoldColors : 6
+
Updates
\ No newline at end of file
diff --git a/guiltless-monkeypatching-with-uuid.html b/guiltless-monkeypatching-with-uuid.html
new file mode 100644
index 0000000..5837b18
--- /dev/null
+++ b/guiltless-monkeypatching-with-uuid.html
@@ -0,0 +1,36 @@
+Guiltless monkeypatching with UUID - The Terminal Programmer
Feeling guilty about
+monkeypatching? Then free
+yourself from fear of name collisions and peer ridicule by naming your
+monkeypatched constructs with universally unique identifiers
+(UUIDs) and
+be finally at peace with your inner, monkeypatching self!
+
uuidgen | sed'y/-/_/; s/^/_/'
+
The above command generates a UUID that conforms to /^\w+$/ and is
+therefore ready for use in mainstream programming languages today.
+
+
Example in Ruby
+
Although you would typically monkeypatch in Ruby like this:
\ No newline at end of file
diff --git a/his29-essay1.html b/his29-essay1.html
new file mode 100644
index 0000000..98e5836
--- /dev/null
+++ b/his29-essay1.html
@@ -0,0 +1,180 @@
+Major changes in South Asian rituals in 1500-500 BCE - The Terminal Programmer
A brief account of major south Asian Vedic, Upanishadic, Buddhist, and Jainist
+rituals and their changes during 1500-500 BCE is given in this article. In
+particular, there is an overall shift from a utilitarian to a respect-for-
+persons ethical perspective in the belief systems as time progresses.
+
+
1 Introduction
+
The following sections outline major Vedic, Upanishadic, Buddhist, and Jainist
+rituals during the 1500-500 BCE period. Once these rituals have been
+introduced, a brief account of their major changes is given in Section 2.
+
Keep in mind that the chronological order (from most ancient to most recent)
+in which these belief systems emerged is roughly [1]:
+
+
+
Vedism
+
Upanishadism (Asceticism)
+
Buddhism and Jainism
+
+
+
1.1 Vedic Rituals
+
The most important Vedic ritual is the sacrifice [1], which is said (by
+brahmins) to maintain the universal order while preventing “chaos and
+calamity” [1]. This ritual is organized by wealthy sponsors who make
+preparations for [1]:
+
+
+
a spacious, reserved area of land upon which the ritual will be performed
+
the slaughter of many animals upon which the participants feast
+
a generous supply of the soma plant for creation of the soma drink
+
a fire (agni) through which the gods are summoned [2, p. 44]
+
numerous brahmins (specialists who chant the sacred Vedas and facilitate the sacrifice)
+
+
During the sacrifice, brahmins chant the sacred Vedas and invite the gods
+to join them. Concurrently, animals are slaughtered and a generous supply of
+soma drink is created from the soma plant [1]. Next, the gods arrive from
+the heavens and join the participants (brahmins, the sponsors of the
+sacrifice [1], and sometimes entire villages or tribes [2, p. 44]) in feasting
+on the freshly slaughtered animals and indulging in the hallucination-
+inducing1soma drink [1]. Finally, the sacrifice is often concluded with
+plentiful acts of sexual intercourse (commended by soma, the warrior God)
+amongst the participants [1].
+
As [1] eloquently states, the Vedic sacrifice can be summarized as “eat,
+drink, and be merry.”
+
+
1.2 Upanishads
+
The primary ritualistic focus of the Upanishads is to facilitate the
+attainment of knowledge and understanding [1]. Thus, the Upanishads brought
+about significant scientific and philosophical development.
+
Major Upanishadic rituals are [1]:
+
+
+
rejecting the Vedic sacrifice and varna system
+
upholding of virtues: ahimsa (non-violence), satya (honesty), etc.
+
regular practice of asceticism and meditation
+
rejecting materialism and living a simple life
+
+
+
1.3 Buddhism
+
The primary ritualistic focus of Buddhism is to become liberated (nirodha)
+from the cycle of rebirth (samsara) and from suffering (dukkha) induced by
+desires [1]. Thus, major Buddhist rituals are [1]:
+
+
+
following the eight-fold path
+
practicing the four noble truths
+
renouncing worldly possessions and status
+
living a simple life (similar to Asceticism)
+
following the middle path (madhyama) or practising moderation
+
+
+
1.4 Jainism
+
Similar to Buddhism, the primary ritualistic focus of Jainism is to become
+liberated from the cycle of rebirth (samsara) [1]. However, in contrast to
+Buddhism, the practice of non-violence (ahimsa) is given top priority in
+Jainism [1]. Thus, major Jainist rituals are [1]:
+
+
+
practicing non-violence (ahimsa) for all forms of life
+
renouncing worldly possessions and status
+
living a simple life (similar to Asceticism)
+
+
+
2 Major Changes
+
The following sections outline major changes and trends in concepts
+fundamental to the belief systems under discussion.
+
+
2.1 Cosmology and Sacrifice
+
During the heyday of the Vedic period, we see mass performances of the Vedic
+sacrifice because Vedic worshippers believed that proper performance of said
+rituals maintains the order of the universe [1], [2, p. 45]. However, towards
+the end of the Vedic period, when Upanishadic influence had established
+itself, we see questioning of earlier Vedic theories of cosmology–namely,
+that the universe resulted from the great Vedic sacrifice of the primeval
+being named purusha or prajaapati [1], [2, p. 45], [3, p. 35]. For
+example, [2, p. 45] shows an interesting quotation which illustrates such
+questioning:
+
+
+
But, after all, who knows, and who can say,
+Whence it all came, and how creation happened?
+The gods themselves are later than creation,
+So who knows truly whence it has arisen?
+
+
This quotation also shows a decline in the power of the Vedic gods because it
+suggests that they are secondary to some unmentioned entity which created the
+Vedic gods. This unmentioned entity might be the Upanishadic brahman, which
+transcends the universe and everything in it (including the Vedic gods).
+
+
2.2 Ethics and the After-Life
+
The early Vedic worshippers (associated with the Rig Veda) were not concerned
+with the after-life [1] whereas the Upanishads, Buddhism, and Jainism were.
+This concern in the latter belief systems emerged from the Upanishadic
+concepts of the seven underworlds [1], [3, p. 48]; seven heavens [1], [3, p.
+46]; and the numerous hells [1], [3, p. 49]. In the concept of the hells,
+Upanishadists believed that they would be punished in one or more hells by the
+God yama according to their kharma2 or conduct [1], [3, p. 49-52]. That
+is, if one exhibits bad (violent, materialistic, etc.) conduct before one
+dies, then one will be punished accordingly by God yama [1].
+
The new, Upanishadic idea of kharma heavily influenced Buddhism and Jainism
+for they too stress the importance of proper ethical conduct-however, the
+latter use it as a stepping stone in achieving liberation from the cycle of
+rebirth [1].
+
+
2.3 Suffering and Liberation
+
Upanishadism, Buddhism, and Jainism all promote the goal of being liberated
+from the cycle of rebirth, but they do so in slightly different ways. For
+example, consider the following distinctions.
+
+
+
Upanishadism focuses on knowledge and understanding of the brahman in achieving moksha or liberation [1].
+
Buddhism sees life as suffering and focuses on avoidance of suffering by achieving nirvaana or liberation [1].
+
Jainism focuses on absolute practice of ahimsa to all forms of life in achieving liberation [1].
+
+
Here we see a shift in goals from knowledge oriented Upanishadism to
+prevention of suffering in Buddhism and Jainism. Thus, the human condition or
+circumstance is given more importance as we move in time towards 500 BCE.
+
+
3 Conclusion
+
As we move chronologically from 1500 BCE to 500 BCE, we see a shift-more or
+less-from a utilitarian to a respect-for-persons ethical perspective or
+concern for the human condition. That is, the people of this time period
+became less concerned with actively maintaining the order of the universe and
+focused on improving their personal or inter-personal circumstance. This shift
+also brought about greater importance on personal responsibility in exhibiting
+and practicing proper3 ethical conduct.
+
+
References
+
+
+
D. Basu, “History 29: Traditional Histories of India,” presented at University of California, Santa Cruz, CA, March-June 2005.
+
R. Thapar, A History of India, vol. 1, Chippenham, Wiltshire, Great Britain: Penguin Books, 1966.
+
C. Dimmit and J.A.B. van Buitenen, Classical Hindu Mythology: A Reader in the Sanskrit Puranas, Philadelphia, PA: Temple University Press, 1978.
+
+
+
Footnotes
+
+
+
+
+
+
+
Upon ingesting the soma drink, participants are said to experience fantastic visions; uplifting, incredible, superhuman sensations; and a desire to engage in sexual intercourse [1]. ↩
+
+
+
+
The Upanishadic version of kharma is different from that of Vedism, for the latter used the term to justify the varna or caste system [1], [2, p. 46]. ↩
+
+
+
+
Each belief system under discussion has its own respective idea of what is proper or good ethical conduct. ↩
+
+
+
+
+
\ No newline at end of file
diff --git a/his29-essay2.html b/his29-essay2.html
new file mode 100644
index 0000000..c911f73
--- /dev/null
+++ b/his29-essay2.html
@@ -0,0 +1,152 @@
+Vedic and Upanishadic influence in the Bhagavadh Giitha - The Terminal Programmer
In the Bhagavadh Giitha1, Krishna draws upon and transforms the central tenets
+of Vedic and Upanishadic philosophy while defining his own [1]. In particular,
+he skillfully transforms the concepts of Aathman2, Brahman, Samsaara, Kharma,
+Dharma, and Moksha into two primary forms of discipline: Yoga and Bhakthi.
+
+
Introduction
+
Yoga, defined as an act or state of morality and contemplation [1], is the
+primary vehicle which delivers one from chaos, ignorance, and the cycle of
+rebirth into peace, wisdom, and the infinite spirit according to Krishna [2].
+Bhakthi, defined as devotion [1], is the religious aspect of Krishna’s
+dialogue in the Bhagavadh Giitha through which one becomes detached from
+desires, delusions, emotion, and acts of consciousness through thinking,
+praying, and meditating on the Gods [1, 2]. Yoga and Bhakthi should not be
+thought of as being two disjoint, unrelated concepts, for as Krishna explains
+[2, p. 67]:
+
+
+
The self tranquil, his fear dispelled,
+firm in his vow of celibacy, his mind restrained,
+let him sit with discipline,
+his thought fixed upon me, intent on me.
+
+
Here, the first three lines refer to Yoga while the last line refers to
+Bhakthi. One can see that Yoga is essential to performing Bhakthi optimally,
+and performance of Bhakthi enhances one’s Yoga because one’s mind is
+indifferent to environmental and personal influences when all of one’s
+thoughts are fixed away, upon something. Thus, in the subsequent discussion,
+one should keep in mind that whenever Yoga is detailed, Bhakthi is also
+detailed implicitly; and vice versa.
+
+
Vedic Influence
+
The Vedic concepts of Samsaara, Kharma, and Dharma are well supported in the
+Bhagavadh Giitha with numerous examples of social and personal
+responsibilities. For example, when Arjuna—dejected by the sight of his
+kinsmen ready to battle against him [2, p. 27–29]—is unwilling to fight, he
+says [2, p. 29]:
+
+
+
The sins of men who violate
+the family create disorder in society
+that undermines the constant laws
+of caste and family duty.
+
+
Here we see clearly the influence of Vedic Kharma in the phrase “the constant
+laws of caste and family duty,” because Vedic Kharma was used to support the
+caste system of the four Varnas [1]. Influence of Vedic Samsaara is also
+present with this quotation’s reference to bringing disorder as a sin, because
+it was believed in Vedic culture that without performance of Vedic fire
+sacrifices, the world would go into chaos and calamity [1]—an undesirable
+event in Vedic philosophy [1]. Finally, Vedic Dharma is present as Krishna
+encourages Arjuna to selflessly perform and fulfill his duties as a warrior
+because such is a warrior’s Dharma [2, p. 36]:
+
+
+
nothing is better for a warrior
+than a battle of sacred duty.
+
+
In fact, Arjuna’s reluctance to perform his warrior’s Dharma is the very
+problem the Bhagavadh Giitha strives to resolve.
+
+
Upanishadic Influence
+
The Upanishadic concepts of Aathman, Brahman, and Moksha are well supported
+through the treatment of numerous philosophical questions, concepts, and
+abstract thought in the Bhagavadh Giitha, primarily because the Upanishads
+themselves dealt with such abstract, philosophical concepts [1, 4, 3].
+
Aathman, or the inner self, is described by Krishna when he philosophically
+encourages Arjuna to follow his warrior’s Dharma by explaining that the
+Aathman cannot be killed [2, p. 34]:
+
+
+
Arjuna, when a man knows the self
+to be indestructible, enduring, unborn,
+unchanging, how does he kill
+or cause anyone to kill?
+
+
In this manner, Krishna motivates the practice of Dharma and Yoga because the
+self has nothing to lose, so to speak. That is, even if one dies while
+practicing one’s Dharma and Yoga, one’s self persists nevertheless [2, p. 71]:
+
+
+
Fallen in discipline, he reaches
+worlds made by his virtue, wherein he dwells
+for endless years, until he is reborn
+in a house of upright and noble men.
+
+
Brahman, or the infinite spirit, is thoroughly discussed in the eighteenth
+teaching of the Bhagavadh Giitha. Here, Krishna hints about the Upanishadic
+origin of Brahman when he describes it as “the state ascetics enter, freed
+from passion,” [2, p. 81]—the Upanishads were developed by ascetics [1]—and
+states that one can invoke it via the “eternal syllable OM” [2, p. 81]—the
+Vedic eternal syllable consisted only of the letter O until the Upanishads
+appended the letter M onto it [1]. In addition, Krishna frequently connects
+Brahman to Yoga for he states that one requires discipline to reach Brahman
+[2, p. 80]:
+
+
+
Disciplined through practice,
+his reason never straying,
+meditating, one reaches
+the supreme divine spirit of man.
+
+
Moksha, or liberation from the cycle of rebirth [1], is described by Krishna
+when he says [2, p. 80]:
+
+
+
with the mind immovable,
+armed with devotion
+[…]
+one attains the supreme
+divine spirit of man.
+
+
Here, Krishna relates Moksha to Yoga in the first line by requiring that only
+those who possess complete mental resolve through Yoga can achieve Moksha.
+Similarly, he relates Moksha to Bhakthi in the second line by requiring that
+only those with complete devotion can achieve Moksha.
+
+
Conclusion
+
As we have seen through various examples, the central tenets of Vedic and
+Upanishadic philosophy are borrowed from heavily and incorporated in the
+Bhagavadh Giitha; particularly in describing Bhakthi and Yoga, the two primary
+forms of discipline.
+
+
References
+
+
+
D. Basu, “History 29: Traditional Histories of India,” presented at University of California, Santa Cruz, CA, March–June 2005.
+
B. S. Miller, The Bhagavad-Gita: Krishna’s Counsel in Time of War, New York, NY: Bantam Dell, 2004.
+
C. Dimmit and J.A.B. van Buitenen, Classical Hindu Mythology: A Reader in the Sanskrit Puranas, Philadelphia, PA: Temple University Press, 1978.
+
R. Thapar, A History of India, vol. 1, Chippenham, Wiltshire, Great Britain: Penguin Books, 1966.
\ No newline at end of file
diff --git a/home-row-mods.html b/home-row-mods.html
new file mode 100644
index 0000000..f47e58b
--- /dev/null
+++ b/home-row-mods.html
@@ -0,0 +1,459 @@
+Taming home row mods with bilateral combinations - The Terminal Programmer
For the past 2 years, I’ve used home row mods designed by the legendary
+Miryoku, where the Super, Alt, Control, and Shift modifier keys are embedded
+in the home row as dual role “mod-tap” keys through my programmable keyboard’s QMK firmware.
+
+
+
+
+
+
These keys behave normally when tapped, sending their assigned character (such as
+the letter “A”) to the computer. But when held, they become modifier keys,
+sending their assigned modifier (such as Alt or Shift) to the computer instead.
+
This literally puts all modifier keys at your fingertips, thereby eliminating
+the need to move your fingers away from the home row to reach them.
The main issue with home row mods is the unintended activation of modifiers.
+When typing quickly, I sometimes press keys together like a piano chord instead
+of tapping and releasing keys individually like distinct notes on a piano scale.
+This can activate home row mods and trigger unwanted shortcuts on my computer,
+such as launching an app, closing a window, or sending an unfinished e-mail. 😱
+
Over time, the negative feedback from these misfires has trained me to type more
+slowly and thoughtfully, burdening my mind with uncertainty that impedes my
+natural typing rhythm and unconscious flow of thoughts onto the computer screen.
+
+
Same-hand chords
+
When typing the word “clock” in the Engram layout, I tend to chord “ck”
+by first holding down “c” with my left pinky finger, tapping “k” with my left
+index finger, and finally releasing them both simultaneously or in the reverse order.
+
+
+
+
+
+
Miryoku’s experimental “bilateral combinations” feature attempts to solve
+this problem by suppressing home row mods for chords that begin and end on the
+same side of the keyboard. This way, it only affects home row mods for chords that
+cross over to the other side of the keyboard; so they’re bilateral combinations.
+
However, it has a longstanding bug called “flashing mods” where
+the Super key would always be sent to the computer for same-hand chords, even
+though their home row mods should be suppressed. This makes me accidentally
+trigger the Start Menu when typing words like “clock” and “sock” due to my “ck”
+chording tendency.
+
With sleepless determination, I recently fixed this and contributed a patch.
+
+
Dual-hand chords
+
When typing the word “end” in the Engram layout, I tend to chord the
+entire word by first holding down “e” with my left middle finger and then holding
+down “n” with right pinky finger, before finally tapping “d” with my right middle
+finger. The first two keys essentially form a stable base from which the third
+can be reached. Similarly, I chord “est” in the same way when typing the word
+“best”.
+
+
+
+
+
+
Thanks to the increased typing speed made possible by my “flashing mods” patch
+for same-hand chords, I was able to make the above
+observations which revealed a blind spot in Miryoku’s bilateral combinations
+concept: it only intercepts same-hand home row mods and completely ignores
+the dual-handed ones!
+
This seemed like the key to solving the problem, so I plunged in with more
+sleepless determination and created a new feature called “crossover” bilateral
+combinations to intercept fast “rolls” that cross over the
+left/right boundary.
+
+
Typing streaks
+
When typing the word “stress” in the Engram layout, I tend to chord the
+end of the word (after typing “str”) by first holding down “e” with my left
+middle finger and then tapping “s” with right ring finger twice. If done too slowly,
+this will trigger the “es” bilateral combination (which sends the Ctrl-S
+shortcut to the computer, in my case) instead of tapping “e” and “s” separately.
+
+
+
+
+
+
There is a brilliant solution to this problem in ZMK’s global-quick-tap
+feature, which cleverly suppresses modifiers during periods of
+active typing. For instance, in the example above, I tap “r” immediately before
+the “es” chord: thus, by measuring the amount of time that has passed between the
+tap and chord, we can detect whether the chord occurred within a typing streak
+and suppress it!
+
Duly inspired, I’ve implemented this idea in QMK via a configurable typing
+streak timeout setting that automatically suppresses home row mods while typing:
However, this tends to obstruct the Shift modifier when typing parentheses or
+punctuation marks such as ! and ? at the end of a sentence; and it requires a
+dedicated Shift key as a workaround, per @urob’s “timeless” mods for
+ZMK. So I went further
+and exempted Shift modifiers from typing streaks in a bitwise mask:
And so, with all this, typing feels natural again! No more unconscious fears
+about accidentally triggering home row mods. 😌 It’s a complete game changer! 🤩
+
+
Solution
+
Armed with my patches for the aforementioned problems, I have finally tamed home
+row mods! But how does it even work, you ask? Well, it’s all quite coincidental:
Three main user actions drive the logic: tapping, holding, and releasing keys.
+Everything happens depending on what keys they hold and how long they hold them.
+
+
+
The green hexagons are external events representing a user’s actions.
+
The blue ovals are a chord’s main stages of life: begin, extend, end.
+
The pink folders are #define settings, documented individually below.
+
The yellow components are QMK functions, which I treat as system calls.
+
The gray boxes are internal storage mutations that track state changes.
+
+
+
Patches
+
Apply the following patch or check out a ready-to-use branch or working example.
This patch supersedes my previous patches, listed below, by adding support for
+chord tapping (multiple mod keys) and “eager mods” for mod-click mouse usage.
Below are the relevant parts of my configuration that put everything in action, producing the best typing experience I’ve felt
+since adopting Miryoku. Enjoy! 😎
+
+
+
+
rules.mk
+
+
DEFERRED_EXEC_ENABLE=yes
+
+
+
+
config.h
+
+
/* QMK */
+#define TAPPING_TERM 200
+#define IGNORE_MOD_TAP_INTERRUPT /* for rolling on mod-tap keys */
+
+/* Miryoku */
+#define BILATERAL_COMBINATIONS
+#define BILATERAL_COMBINATIONS_LIMIT_CHORD_TO_N_KEYS 4 /* GUI, Alt, Ctrl, Shift */
+#define BILATERAL_COMBINATIONS_DELAY_MODS_THAT_MATCH MOD_MASK_GUI
+#define BILATERAL_COMBINATIONS_DELAY_MATCHED_MODS_BY 120 /* ms */
+#define BILATERAL_COMBINATIONS_ALLOW_CROSSOVER_AFTER 80 /* ms */
+#define BILATERAL_COMBINATIONS_ALLOW_SAMESIDED_AFTER 3000 /* ms */
+#define BILATERAL_COMBINATIONS_TYPING_STREAK_TIMEOUT 160 /* ms */
+#define BILATERAL_COMBINATIONS_TYPING_STREAK_MODMASK (~MOD_MASK_SHIFT)
+
+
Usage
+
To enable bilateral combinations:
+
+
+
Add the following line to your config.h file:
+
#define BILATERAL_COMBINATIONS
+
+
Add the following line to your rules.mk file to enable QMK’s deferred execution facility.
+
DEFERRED_EXEC_ENABLE=yes
+
+
+
+
+
+
Allowing same-sided combinations
+
+
To enable same-sided combinations (which start on one side of the keyboard and end on the same side, such as RSFT_T(KC_J) and RCTL_T(KC_K) in the abbreviation “jk” which stands for “just kidding”), add the following line to your config.h and define a value: hold times greater than that value will permit same-sided combinations.
+
For example, if you typed RSFT_T(KC_J) and RCTL_T(KC_K) faster than the defined value, the keys KC_J and KC_K would be sent to the computer. In contrast, if you typed slower than the defined value, the keys RSFT(KC_K) would be sent to the computer.
To enable crossover bilateral combinations (which start on one side of the keyboard and cross over to the other side, such as RSFT_T(KC_J) and LGUI_T(KC_A) in the word “jam”), add the following line to your config.h and define a value: hold times greater than that value will permit crossover bilateral combinations.
+
For example, if you typed RSFT_T(KC_J) and LGUI_T(KC_A) faster than the defined value, the keys KC_J and KC_A would be sent to the computer. In contrast, if you typed slower than the defined value, the keys RSFT(KC_A) would be sent to the computer.
To delay the registration of certain modifiers (such as KC_LGUI and KC_RGUI, which are considered to be “flashing mods” because they suddenly “flash” or pop up the “Start Menu” in Microsoft Windows) during bilateral combinations, you can define a BILATERAL_COMBINATIONS_DELAY_MODS_THAT_MATCH setting specifying which modifiers should be delayed, and a BILATERAL_COMBINATIONS_DELAY_MATCHED_MODS_BY setting specifying how long that delay (measured in milliseconds) should be.
+
+
+
Add the following line to your config.h and define a bitwise mask that matches the modifiers you want to delay. For example, here we are defining the mask to only match the GUI and ALT modifiers.
Add the following line to your config.h and define a timeout value (measured in milliseconds) that specifies how long modifiers matched by BILATERAL_COMBINATIONS_DELAY_MODS_THAT_MATCH should be delayed. For example, here we are defining the timeout to be 100 milliseconds long.
To suppress mod-tap holds within a typing streak, add the following line to your config.h and define a timeout value: a typing streak ends when this much time passes after the last key in the streak is tapped. Until such time has passed, mod-tap holds are converted into regular taps. The default value of this definition is 0, which disables this feature entirely. Overall, this feature is similar in spirit to ZMK’s global-quick-tap feature.
If you wish to target only certain modifiers (instead of all possible modifiers) for the typing streak timeout setting described above, add the following line to your config.h and define a bit mask: only those modifiers that match this mask will be governed by the typing streak timeout. For example, to exempt Shift modifiers from the typing streak timeout while still targeting all other modifiers, you can specify the following mask.
I switched to a new keyboard powered by ZMK
+firmware recently and I feared it might take another 6 months to rewrite my QMK-based solution
+described thus far. Fortunately, I was able to port an essential subset of my
+QMK patches without having to modify ZMK source at all: I didn’t need to write
+a single line of C++!
+
By declaratively defining “custom behaviors” in ZMK, I was able to configure
+home row mods disambiguation using a combination of these behavioral components:
+
+
+
flavor="tap-preferred" for strictly time-based home row mods activation
+
flavor="balanced" for short-circuiting sequence-based layer activation
+
hold-trigger for cross-hand home row mods enforcement (positional hold-tap)
+
tapping-term-ms for home row mods detection, as governed by flavor setting
+
quick-tap-ms for automatic key repetition via “tap then hold” usage pattern
+
require-prior-idle-ms timeout for typing streak enforcement
+
+
The resulting ZMK configuration is posted here, with documentation in comments.
+It has separate custom behaviors per hand for crossover bilateral combinations;
+as well as per thumbs, index fingers, and the rest for differences in dexterity.
//
+// HOMEY_HOLDING_TYPE defines the flavor of ZMK hold-tap behavior to use
+// for the pinky, ring, and middle fingers (which are assigned to Super,
+// Alt, and Ctrl respectively in the Miryoku system) on home row keys.
+//
+#ifndef HOMEY_HOLDING_TYPE
+#define HOMEY_HOLDING_TYPE "tap-preferred"
+#endif
+
+//
+// HOMEY_HOLDING_TIME defines how long you need to hold (milliseconds)
+// home row mod keys in order to send their modifiers to the computer
+// (i.e. "register" them) for mod-click mouse usage (e.g. Ctrl-Click).
+//
+#ifndef HOMEY_HOLDING_TIME
+#define HOMEY_HOLDING_TIME 270 // TAPPING_TERM + ALLOW_CROSSOVER_AFTER
+#endif
+
+//
+// HOMEY_STREAK_DECAY defines how long you need to wait (milliseconds)
+// after typing before you can use home row mods again. It prevents
+// unintended activation of home row mods when you're actively typing.
+//
+#ifndef HOMEY_STREAK_DECAY
+#define HOMEY_STREAK_DECAY 230
+#endif
+
+//
+// HOMEY_REPEAT_DECAY defines how much time you have left (milliseconds)
+// after tapping a key to hold it again in order to make it auto-repeat.
+//
+#ifndef HOMEY_REPEAT_DECAY
+#define HOMEY_REPEAT_DECAY 300 // "tap then hold" for key auto-repeat
+#endif
+
To illustrate, here is a table of varying behaviors per hand and finger as
+arranged in Miryoku’s “G-A-C-S” order on the Engram keyboard layout’s home row.
+
+
+
+
Left Pinky
+
Left Ring
+
Left Middle
+
Left Index
+
(Finger)
+
Right Index
+
Right Middle
+
Right Ring
+
Right Pinky
+
+
+
+
LGUI
+
LALT
+
LCTRL
+
LSHFT
+
(Hold)
+
LSHFT
+
LCTRL
+
LALT
+
LGUI
+
+
+
C
+
I
+
E
+
A
+
(Tap)
+
H
+
T
+
S
+
N
+
+
+
Specifically, since index fingers are the most dexterous, the normal rules don’t apply to them well. They roll, tap, and hold rapidly and quite differently based on your typing rhythm and use-case. So the “balanced” flavor of hold-tap in ZMK wasn’t the best choice for them as I sometimes roll with my index fingers, which ends up triggering mods when I don’t intend to. Moreover, the “balanced” flavor’s requirement to release the modified (shifted in my case since I use shift on index fingers) key breaks my rhythm and speed when typing CamelCase variable names: I want it to trigger the mod instantly in this case. Instead, the strictly time-based “tap-preferred” flavor seems to better encapsulate their inconsistent complexity (sometimes I want tap, other times I want hold). And that’s essentially what my QMK implementation does, effectively reading my mind.
The way I’ve implemented bilateral combinations is with 2 levels of hold-taps
+bridged by a layer transition. The first level starts at the base layer, with
+the first home row mod that you hold taking you its respective layer on the
+second level. For example, holding the LeftIndex key takes you to the LeftIndex
+layer, which masks all same-sided keys with either (1) additional hold-taps for
+the remaining home row mods or (2) mod-cancelling taps for other remaining keys.
+
Effectively, this approach constructs a “sandwich” 🥪 of timing thresholds:
+
+
+
You hold the first modifier for at leastHOMEY_HOLDING_TIME milliseconds.
+
+
+
The first modifier is now “registered” (sent to the computer).
+
You are sent to the first modifier’s corresponding layer in the keymap.
+
+
You hold the second modifier for at leastCHORD_HOLDING_TIME milliseconds.
+
+
+
The second modifier is now “registered” (sent to the computer).
+
+
You can repeat step 2 to add even more modifiers to your multi-mod chord.
+
You tap a key on the opposite hand.
+
+
+
The tap is now “registered” (sent to the computer) and it’s nested under
+the modifiers you’re already holding (which have also already been
+“registered”).
+
+
+
NOTE: If you don’t hold the second modifier long enough at step #2, it will
+look like a same-sided tap, which will then trigger the bilateral combinations
+logic:
+
+
+
The first modifier will be released.
+
The first modifier’s normal keycode will be tapped.
+
The second modifier’s normal keycode will be tapped.
\ No newline at end of file
diff --git a/hterm-shift-pageup.html b/hterm-shift-pageup.html
new file mode 100644
index 0000000..cc5dcc8
--- /dev/null
+++ b/hterm-shift-pageup.html
@@ -0,0 +1,66 @@
+Enabling shifted page keys in hterm - The Terminal Programmer
hterm binds Shift+PageUp/Down keys for scrolling its own backbuffer by default.
+To override those defaults and allow the underlying terminal session to receive
+those keys, while also adding support for Alt and Control modifier keys, simply:
For example, I commonly use Shift+PageUp as a convenient shortcut to enter tmux’s
+copy mode and then immediately scroll up by one page (tmux copy-mode -u):
+
bind-key -n S-PPage copy-mode -u
+
But how could I possibly have known those esoteric keycodes in the first place?
+:-) It was only possible with the help of arcane knowledge in this MinTTY wiki, which
+explained the notation of special editing keys and how they are
+modified:
+
+
+
modifier keys Shift, Alt and Ctrl are … encoded as a one-digit number that becomes part of the keycode. To obtain that number, add the numbers for each pressed modifier to 1:
+Shift=1,
+Alt=2,
+Ctrl=4.
+
+
+
+
+
Key
+
plain
+
modified
+
+
+
+
PgUp
+
^[[5~
+
^[[5;m~
+
+
+
PgDn
+
^[[6~
+
^[[6;m~
+
+
+
Using that formula, I calculated the one-digit numbers for my modifier combos:
+
+
+
Shift: (1) + 1 = 2
+
Alt+Shift: (2 + 1) + 1 = 4
+
Ctrl+Shift: (4 + 1) + 1 = 6
+
Ctrl+Alt+Shift: (4 + 2 + 1) + 1 = 8
+
+
With these results, I was able to override the hterm keybindings setting above.
+
\ No newline at end of file
diff --git a/iex-eval-stdin.html b/iex-eval-stdin.html
new file mode 100644
index 0000000..0561c6b
--- /dev/null
+++ b/iex-eval-stdin.html
@@ -0,0 +1,118 @@
+Automating interactive Elixir evaluation via stdin - The Terminal Programmer
Newcomers to the Elixir language begin their
+official instruction using the iex tool,
+which places the user in Elixir’s interactive mode.
+Inside this mode, all of the Elixir commands the user types in are short-lived,
+disappearing entirely (or only partially, if history is enabled) when the user quits the
+iex session.
To avoid repeating the manual labor of retyping all of their Elixir commands (or
+reinserting them from saved history) when starting a new iex
+session, the clever user naturally attempts to store all of their Elixir commands
+in a file and to then run that file with iex. This way, each line in the file
+would be treated as if the user had manually typed it all in. :-) Problem
+solved, right?
Alas, iex merely prints the results of the clever user’s Elixir commands. :-(
+Whereas this whole method of interaction would be far more useful if iex had
+also printed each of the user’s Elixir commands along with their result, right?
That would make it easier to learn Elixir, if both cause and effect were shown.
+
+
Approach
+
Since iex is an interactive tool, it naturally expects to talk to the user’s
+terminal device instead of the plain-text standard input and output streams.
+For example, syntax highlighting the user’s Elixir command results only makes
+sense when iex is talking to a terminal device; not to the plain-text stdout.
+
Thus, I needed to create a fake terminal device for iex to talk to, through
+which I would send each of the user’s Elixir commands, read from stdin, to iex.
+Luckily, the Expect tool allows us to perform
+exactly this kind of automation.
+
To start off, I wrote a Bourne shell function that generates an Expect script
+from the contents of the user’s file (provided on stdin), taking care to escape
+each line of input so that Expect would pass them through as-is to iex:
#!/usr/bin/expect -f
+#
+# Usage: iex-eval-stdin -- [ARGUMENTS_FOR_IEX...]
+# Usage: iex-eval-stdin < YOUR_ELIXIR_SCRIPT_FILE
+# Usage: echo YOUR_ELIXIR_SCRIPT | iex-eval-stdin
+#
+# Runs each line from the standard input stream using iex(1), as if
+# each line were interactively typed into iex(1) in the first place.
+#
+# Written in 2018 by Suraj N. Kurapati <https://github.com/sunaku>
+# and documented at <https://sunaku.github.io/iex-eval-stdin.html>
+
+setprompt{\n(iex|\.\.\.)(\(\d+\)|\(.+@.+\)\d+)>$}
+
+evalspawn-noechoiex$argv
+while{[getsstdinline]>=0}{
+ expect-re$prompt
+ send--"$line\r"
+}
+expect-re$prompt;# await last result
+puts"\033\[1K";# clear last prompt
+
To install this script, copy/paste it into a file or download it from GitHub.
+Then, mark it as executable so that you can run it just by typing in its name:
+
$ chmod +x ./iex-eval-stdin
+
Now, you can run it: (if you don’t like typing the ./, move it into your $PATH)
If you get an error saying that Expect couldn’t be found, try installing it:
+
$ sudo apt-get install expect
+
That’s all. :-) Enjoy learning Elixir!
+
\ No newline at end of file
diff --git a/index.atom b/index.atom
new file mode 100644
index 0000000..2d01f74
--- /dev/null
+++ b/index.atom
@@ -0,0 +1,300 @@
+https://sunaku.github.io/The Terminal Programmer
+
+
+
I program, incessantly,
+and I fear it will be the end of me.
+I cannot stop, for it commands my will,
+thus I am the terminal programmer, nil.
+]]>2024-02-04T23:34:44-08:00Suraj N. Kurapatihttps://sunaku.github.io/engram-keyboard-layout.htmlSwitching to Arno's Engram 2.0 keyboard layout2024-02-04T23:34:44-08:002021-05-07T06:45:13ZSuraj N. Kurapati
Added a new Comparison section with my feedback posted online about Engram.
+
]]>https://sunaku.github.io/home-row-mods.htmlTaming home row mods with bilateral combinations2024-01-01T18:47:17-08:002022-10-16T05:06:31-07:00Suraj N. Kurapati
I’ve finally implemented bilateral combinations in
+ZMK! ❤️🔥 It’s still a hair shy 🧐
+(lacking multi-key chord unrolling during unilateral cancellation) compared to
+my QMK endgame patch, but that’s such a theoretical corner case that I don’t
+think anyone would actually notice in practice. 🙃 Enjoy!
+
In my ZMK port:
+
+
+
Allow the user to override all #defines at the very top of the “Custom
+Defined Behaviors” ZMK snippet, by guarding each #define with #ifdef.
+
+
In my ZMK port, I’ve incorporated the new require-prior-idle-ms feature that
+was recently merged into ZMK to produce the crispest, most responsive typing
+I’ve ever experienced outside of the typing layer! 🫰🤯✨ ZMK docs say this can
+eliminate input lag when typing quickly, and they’re right! 💯
+
Additionally, what this gives us in terms of configuration is the separation of
+streak vs. repeat decay timers — specifically for homey mods and index finger
+shifts, but potentially also for thumbs and any other hold-taps imaginable! ️ Thanks to Bryan Forbes for the use-case behind this.
Thumb keys: disable retro-tap for all thumb keys except spacebar. The backspace thumb key for cursor layer was accidentally navigating the browser back in history for me sometimes.
+
Thumb keys: increase hold time to 200ms for non-space thumb keys to avoid layer activation where they map you into &none.
+
Thumb keys: increase repeat decay time to 300ms for non-space thumb keys so it’s easier to hold backspace for repetition.
+
Rename INDEX_* settings to SHIFT_* because it’s semantically shift: it needs to be fast regardless of the finger it’s on (not just the indexes). Also rename &thumb_space to &space for same reason.
+
+
I was able to enable typing streaks for index finger shifts while still
+maintaining fast activation for CamelCase typing. 😤 Next, I separated
+index fingers’ typing streak from the other home row mods and fine-tuned
+the timing per my taste: apparently, 70ms is my magic number… 🪄 it
+just boosted me to my highest raw speed ever (115) measured with home
+row mods enabled! 🤩 The typefeel is also very crisp 🫰 now, and I
+must say that I’m quite pleased with this. 👌 See updated ZMK snippet
+in the Porting to ZMK section.
+
Still not completely satisfied by the robustness of home row mods
+disambiguation in my ZMK port, I’ve switched back from the “balanced”
+flavor of hold-tap to the purely time-based “tap-preferred” flavor in
+order to be more in line with my endgame QMK patch… and oh my, am I
+impressed! 😍
+
This change has significantly improved typing responsiveness (less delay
+from keystroke to letter showing up on the computer screen) and also
+increased my confidence in the home row mods disambiguation system.
+😎👌 I mean, typing is fun now — it’s unexpectedly satisfying to
+see letters appear instantly on the screen when I press a key. 👏
+Hooray for latency! ✨
+
I have added a ZMK port of my QMK patches for home row mods disambiguation.
+
Inspired by ZMK’s global-quick-tap feature, I’ve
+implemented a typing streak timeout setting that suppresses home row mods
+while actively typing.
+
#define BILATERAL_COMBINATIONS_TYPING_STREAK_TIMEOUT 160 /* ms */
+
However, this obstructs the Shift modifier when typing parentheses or
+punctuation marks such as ! and ? at the end of a sentence; and it requires
+a dedicated Shift key as a workaround, per @urob’s “timeless” mods for
+ZMK. So I went on to
+exempt Shift modifiers from typing streaks using a bitmask:
With all this, typing feels natural again! No more unconscious fears
+about accidentally triggering home row mods. 😌 It’s a complete game changer! 🤩
+
I have fixed some corner cases, simplified the configuration, improved chording support, and upgraded to QMK 0.19.10. Eager mods are now enabled by default (so that mod-clicks Just Work out of the box) but you can delay them via #define settings.
+
+
+
Removed CHORDSIZE setting since it’s now hard-coded to the maximum allowed value in QMK.
+
Removed EAGERMODS setting since “eager mods” are now always enabled by default.
+
Renamed EAGERMASK setting to DELAY_MODS_THAT_MATCH and thereby inverted its meaning (it now specifies modifiers that should be delayed, not made eager).
+
Renamed DEFERMODS setting to DELAY_MATCHED_MODS_BY.
+
Renamed CROSSOVER setting to ALLOW_CROSSOVER_AFTER.
+
Renamed SAMESIDED setting to ALLOW_SAMESIDED_AFTER.
+
+
For example, here is a diff showing how my personal configuration has changed since my last update to this article and associated set of patches:
I have added support for chord tapping (multiple mod keys) and “eager mods” for
+mod-click mouse usage. The overall logic is illustrated visually as a
+flowchart and the various #define settings are now
+documented here as well.
+
This is, by far, the crispest typing experience I’ve felt in years! :)
+
I have replaced the FLASHMODS mask definition with a
+new DEFERMODS timeout in order to implement Miryoku’s
+suggestion of (1) suppressing all modifiers as “flashing mods” and
+(2) registering them later, after a timeout, for mod-click mouse usage.
]]>https://sunaku.github.io/moergo-glove80-keyboard.htmlMoErgo Glove80 programmable ergonomic keyboard2023-12-01T18:12:00-08:002023-07-04T05:00:41-07:00Suraj N. Kurapati
I’ve published a video tour of my symbol layer, with examples in Vim:
+
+
+
+
+
I’ve released version 31 featuring mouse keys (keyboard control of mouse pointer & wheel), Unicode curly quotes, and improved Number layer affinity.
+
I’ve released version 30 featuring an improved symbol layer with balanced triangles and support for spacegrams; and support for QWERTY and a few other popular keyboard layouts (which you can activate dynamically via Magic+N or set as the base layer when compiling your keymap) in order to welcome other layout users on the path to a glorious typing experience. 🖖
+
I’ve released version 29 featuring
+an improved symbol layer (as follows), and I’ve added an
+explanation of the design methodology I appear to follow.
+Also, I’ve added interactive usage examples to most of the layer diagrams.
+
I released version 25 featuring Emoji, international characters, and more!
Symbol layer: swap backtick and double quotes so that I can inward roll from equals to double quote in one swoop.
+
+
+
(" for starting a string value argument for a function call.
+
=" for HTML attributes and Bourne shell variable assignments.
+
+
Cursor layer: easier one-handed SelectAll -> Cut/Copy/Paste; put undo/redo on same upper row as right hand; adjust find keys accordingly.
+
+
I’ve observed that I don’t really use , and ; from the bottom row in
+practice for navigating Vim’s f/F/t/T jumps, so I’ve been thinking about
+a better use for them. Having comma and dot paired together on a single
+hand (like their relative positioning in QWERTY) might be useful for
+shortcuts that assume their adjacency — for instance, I have Alt+,
+and Alt+. navigate to the prev/next window in tmux. And apostrophe
+has been missing from the symbol layer because it’s readily accessible
+from the base layer, but it may be nice to add it anyway for
+completeness. Hence this design iteration:
+
+
+
Symbol layer: put apostrophe, comma, dot (like the Dvorak layout) on
+bottom row. The comma and dot align with the angle brackets above them.
+The apostrophe is just there for completeness; and '<,'> ranges in Vim.
+
+
A minor semantic tweak to the symbol layer (backslash on ESC key) and some documentation about index finger home row mods (as inward-rolling chords):
+
+
+
Symbol layer: swap backslash and percentage to place backslash on the same key as ESC. Percentage acts as the “matchit” operator in Vim.
+
Custom Defined Behaviors: add a CAUTION note about the inward-rolling requirement for index fingers, which disable hold-trigger-on-release:
+
+
+
+
CAUTION: You’ll need to perform inward rolls from pinky->ring->middle
+fingers toward the index fingers when activating multiple modifiers
+because hold-trigger-on-release is disabled for the index fingers.
+Otherwise, you may be surprised that the index fingers’ modifier is
+sent immediately without the rest of your multi-mod chord when you
+perform outward rolls from your index fingers toward your pinkies.
+
+
Improved the gaming layer and right-handed mousing on the cursor layer. Also
+added lower, gaming, and
+typing layers to the interactive layer map.
+
+
+
Gaming layer: Move ZXCV to bottom-most row; add BNM on the top row of home block; put ESC/Enter/Win on thumb cluster; restore tilde above tab; move Pause to furthest key in lower arc of thumb cluster.
+
Cursor layer: Move left half’s undo/redo keys to J/K position. Add word/line selection macros and find/next/prev/replace keys on left half for right-handed mouse usage. Also remove the ScrollLock shortcut from the Cursor layer (it’s still available on the left thumb cluster of the System layer). Thanks to @luminous_echidna for the suggestions.
+
+
Better right-handed mouse usage, easier Vim line/column jumping, and more:
+
+
+
Miryoku: Move ring finger AltGr modifiers to upper row to avoid accidental triggers since I tend to hold X/F down longer than necessary when curling my ring fingers compared to when I extend them out to reach Y/W on the upper row.
+
Base layer: Swap = and \ so that = alternates with space bar on right thumb when typing = assignments.
+
Cursor layer: Move insert to rightmost bottom key and replace with backspace above left arrow on home row.
+
Cursor layer: Mirror cut/copy/paste and undo/redo keys on left hand for right-handed mouse usage. Thanks to @n0rvegan for this great idea!
+
Cursor layer: Surround find prev/next keys with find and find/replace.
+
Symbol layer: Wrap parentheses () with square brackets [] and replace innermost , position with ;. This splits the (); sequence apart but still maintains it as an inward roll. This also establishes Vim’s ,; order for f/F/t/T jump repetition.
+
Number layer: Replace <>= in rightmost column with kjG for jumping to line numbers in Vim.
+
Number layer: Add | on the rightmost bottom corner key for jumping to column numbers in Vim.
]]>https://sunaku.github.io/tmux-yank-osc52.htmlCopying to clipboard from tmux and Vim using OSC 522023-07-04T15:37:55-07:002014-04-19T13:04:46-07:00Suraj N. Kurapati
Verify the $DISPLAY environment variable before attempting xsel and xclip.
+
Pipe the text to be copied into tmux load-buffer instead of passing it as an
+argument to tmux set-buffer, thereby avoiding any shell (mis)interpretation.
+Thanks to Jonathan Wheeler for reporting this issue.
+
Restored X11 support in the yank script because its removal didn’t prevent
+the entire terminal screen to be copied to tmux clipboard when using Vim.
+
Removed X11 support from the yank script because running it from within Vim
+caused the entire terminal screen to be copied to tmux clipboard. Also, removed
+the workaround of clearing the KiTTY terminal clipboard before yanking; use the
+clipboard_control no-append setting in ~/.config/kitty/kitty.conf instead:
Larry Sanderson contributed this Vim configuration snippet that automates
+yanking to system clipboard by calling the yank(1) script in this article:
+
" Set it up so all yanks copy to system clipboard
+function! CopyYank() abort
+ call Yank(join(v:event.regcontents,"\n"))
+endfunction
+autocmd TextYankPost * call CopyYank()
+
Fixed tmux detection under nested SSH. Added support for KiTTY terminal.
+
When you start an SSH session from inside tmux, the $TMUX environment
+variable does not carry forward into that newly started SSH session. As a
+result, when you run the yank script inside that SSH session, it doesn’t
+realize that it’s actually attached to a tmux client (since $TMUX is not set)
+and it therefore doesn’t specially wrap the OSC 52 sequence for tmux.
+As a result, tmux discards the bare OSC 52 sequence and it all falls apart.
+
To solve this, we instead look to the $TERM environment variable, which
+begins with the word “screen” for all terminal sessions started under tmux.
+
Vim snippet now handles /bin/sh: /dev/tty: No such device or address errors
+on some systems by using writefile() instead of shell redirection. It also
+provides a convenient Yank() function to abstract away the yank system call.
+
Updated configuration snippet for tmux 2.4, using send-keys -X copy-pipe.
+
Gary Bishop discovered that the Vim mapping
+presented in this article needed to explicitly redirect the OSC 52 escape
+sequence generated by the yank(1) script to the terminal device in order to
+work correctly in certain environments (Linux => tmux => ssh => Chromebook):
+
:callsystem('yank > /dev/tty', @0)
+
See the comments thread at the bottom of this article for more information.
+
Updated yank(1) script. Added M-Y and y copy-pipe shortcuts for tmux.
+
I have updated the yank(1) script in this article to copy its input text
+into tmux’s clipboard in addition to the operating system clipboard.
+
]]>https://sunaku.github.io/ergohaven-remnant-keyboard.htmlErgohaven Remnant programmable ergonomic keyboard2023-01-28T23:57:21-08:002023-01-28T23:57:21-08:00Suraj N. Kurapatihttps://sunaku.github.io/dactyl-manuform-5x6-keyboard.htmlDactyl Manuform 5x6 programmable ergonomic keyboard2022-10-23T03:29:39-07:002022-10-23T03:29:39-07:00Suraj N. Kurapatihttps://sunaku.github.io/engrammer-keyboard-layout.htmlEngrammer - Arno's Engram layout for programmers2022-08-28T09:50:40Z2022-08-28T09:50:40ZSuraj N. Kurapatihttps://sunaku.github.io/tmux-select-pane.htmlIntelligently navigating tmux panes and Vim splits2020-04-25T22:03:25-07:002015-05-05T17:43:38-07:00Suraj N. Kurapati
I’ve published the code in this article as an installable tmux/Vim plugin:
Thanks to @bradleyharden for contributing a
+working example of TPM plugin conversion and giving me the opportunity to host
+this plugin in my GitHub.
+
Erase stray ESC + hjkl keys for panes that are misidentified as containing Vim.
+This occurs when holding down the shortcut keys or using an old Vim that leaves
+behind a dirty window title labeled “Thanks for flying Vim!”.
+
Use the #{q:} shell quoting directive in tmux-2.9a when interpolating tmux
+variables to properly support pane titles that contain quotation marks.
+
When Vim is launched from within git commit as the editor for a git commit
+message, the value of #{pane_current_command} is git instead of vim.
+
Use-b option for run-shell instead of the & shell background operator:
Added support for seamless navigation of embedded NeoVim terminal sessions.
+
I’ve rolled my own replacement for the vim-tmux-navigator plugin: the tmux
+keybindings presented in this article now handle Vim navigation without the
+need for any special Vim plugins (or configuration thereof). It Just Works!
+
To illustrate, here is how the code used to appear before this update:
# in ~/.tmux.conf
+bind-key -n M-h run-shell ' \
+ case "#{pane_current_command}" in (*vim*) inside=1;; esac; \
+ case "${inside:-0}#{window_zoomed_flag}" in \
+ (00) tmux select-pane -L ;; \
+ (10) tmux send-keys M-h ;; \
+ (11) tmux send-keys C-w h ;; \
+ esac \
+'
+
Improved the specificity of the glob pattern for detecting Vim’s presence.
+
Replaced the positional argument arithmetic with case statements. The code
+still uses binary encoding but it’s more compact and easier to read now. :)
+
To illustrate, here is how the code appeared before this change:
In contrast, here is how the code appears now, after the change:
+
bind-key -n M-h run-shell ' \
+ case "#{pane_current_command}" in (*vi*) vim=1;; esac; \
+ case "${vim:-0}#{window_zoomed_flag}" in \
+ (00) tmux select-pane -L ;; \
+ (10) tmux send-keys M-h ;; \
+ (11) tmux send-keys C-w h ;; \
+ esac \
+'
+
I’ll need to update the explanation section accordingly, someday!
+
]]>https://sunaku.github.io/gruvbox-terminal-color-scheme.htmlGruvbox terminal color scheme2018-12-20T19:17:07-08:002016-03-03T16:37:00-08:00Suraj N. Kurapati
Changed blacks to grays because they were unreadable against background.
+
]]>https://sunaku.github.io/elixir-fileio-speedup.htmlSpeeding up I/O on very large files in Elixir2018-12-18T20:40:41-08:002017-03-07T16:35:00-08:00Suraj N. Kurapati
]]>https://sunaku.github.io/iex-eval-stdin.htmlAutomating interactive Elixir evaluation via stdin2018-10-28T17:33:46-07:002018-10-28T17:33:46-07:00Suraj N. Kurapati
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..4dcce23
--- /dev/null
+++ b/index.html
@@ -0,0 +1,10 @@
+The Terminal Programmer
I program, incessantly,
+and I fear it will be the end of me.
+I cannot stop, for it commands my will,
+thus I am the terminal programmer, nil.
+
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..e02b0ff
--- /dev/null
+++ b/index.js
@@ -0,0 +1,70 @@
+$(function() {
+ // create links to preceding and following entries in navigation chain
+ var chain_by_entry_id = {"engram-keyboard-layout":{"following":{"url":"home-row-mods.html","title":"Taming home row mods with bilateral combinations"}},"home-row-mods":{"preceding":{"url":"engram-keyboard-layout.html","title":"Switching to Arno's Engram 2.0 keyboard layout"},"following":{"url":"moergo-glove80-keyboard.html","title":"MoErgo Glove80 programmable ergonomic keyboard"}},"moergo-glove80-keyboard":{"preceding":{"url":"home-row-mods.html","title":"Taming home row mods with bilateral combinations"},"following":{"url":"tmux-yank-osc52.html","title":"Copying to clipboard from tmux and Vim using OSC 52"}},"tmux-yank-osc52":{"preceding":{"url":"moergo-glove80-keyboard.html","title":"MoErgo Glove80 programmable ergonomic keyboard"},"following":{"url":"ergohaven-remnant-keyboard.html","title":"Ergohaven Remnant programmable ergonomic keyboard"}},"ergohaven-remnant-keyboard":{"preceding":{"url":"tmux-yank-osc52.html","title":"Copying to clipboard from tmux and Vim using OSC 52"},"following":{"url":"dactyl-manuform-5x6-keyboard.html","title":"Dactyl Manuform 5x6 programmable ergonomic keyboard"}},"dactyl-manuform-5x6-keyboard":{"preceding":{"url":"ergohaven-remnant-keyboard.html","title":"Ergohaven Remnant programmable ergonomic keyboard"},"following":{"url":"engrammer-keyboard-layout.html","title":"Engrammer - Arno's Engram layout for programmers"}},"engrammer-keyboard-layout":{"preceding":{"url":"dactyl-manuform-5x6-keyboard.html","title":"Dactyl Manuform 5x6 programmable ergonomic keyboard"},"following":{"url":"tmux-select-pane.html","title":"Intelligently navigating tmux panes and Vim splits"}},"tmux-select-pane":{"preceding":{"url":"engrammer-keyboard-layout.html","title":"Engrammer - Arno's Engram layout for programmers"},"following":{"url":"gruvbox-terminal-color-scheme.html","title":"Gruvbox terminal color scheme"}},"gruvbox-terminal-color-scheme":{"preceding":{"url":"tmux-select-pane.html","title":"Intelligently navigating tmux panes and Vim splits"},"following":{"url":"elixir-fileio-speedup.html","title":"Speeding up I/O on very large files in Elixir"}},"elixir-fileio-speedup":{"preceding":{"url":"gruvbox-terminal-color-scheme.html","title":"Gruvbox terminal color scheme"},"following":{"url":"iex-eval-stdin.html","title":"Automating interactive Elixir evaluation via stdin"}},"iex-eval-stdin":{"preceding":{"url":"elixir-fileio-speedup.html","title":"Speeding up I/O on very large files in Elixir"},"following":{"url":"vegan-for-life.html","title":"Vegan: Healthy, Sustainable, Compassionate Living"}},"vegan-for-life":{"preceding":{"url":"iex-eval-stdin.html","title":"Automating interactive Elixir evaluation via stdin"},"following":{"url":"hterm-shift-pageup.html","title":"Enabling shifted page keys in hterm"}},"hterm-shift-pageup":{"preceding":{"url":"vegan-for-life.html","title":"Vegan: Healthy, Sustainable, Compassionate Living"},"following":{"url":"papercolor-terminal-color-scheme.html","title":"PaperColor terminal color scheme"}},"papercolor-terminal-color-scheme":{"preceding":{"url":"hterm-shift-pageup.html","title":"Enabling shifted page keys in hterm"},"following":{"url":"meditation-five-steps.html","title":"Meditation in five steps"}},"meditation-five-steps":{"preceding":{"url":"papercolor-terminal-color-scheme.html","title":"PaperColor terminal color scheme"},"following":{"url":"zabutom-zeta-force-level-2.html","title":"zabutom's Zeta Force Level 2, rearranged"}},"zabutom-zeta-force-level-2":{"preceding":{"url":"meditation-five-steps.html","title":"Meditation in five steps"},"following":{"url":"elixir-parallel-grep.html","title":"A parallel grep(1) prototype in Elixir"}},"elixir-parallel-grep":{"preceding":{"url":"zabutom-zeta-force-level-2.html","title":"zabutom's Zeta Force Level 2, rearranged"},"following":{"url":"git-rebase-autocon.html","title":"Automatic merge conflict resolution for git-rebase(1)"}},"git-rebase-autocon":{"preceding":{"url":"elixir-parallel-grep.html","title":"A parallel grep(1) prototype in Elixir"},"following":{"url":"tmux-layout-dwindle.html","title":"Porting dwm's dwindle layout to tmux"}},"tmux-layout-dwindle":{"preceding":{"url":"git-rebase-autocon.html","title":"Automatic merge conflict resolution for git-rebase(1)"},"following":{"url":"tmux-24bit-color.html","title":"Adding 24-bit TrueColor RGB escape sequences to tmux"}},"tmux-24bit-color":{"preceding":{"url":"tmux-layout-dwindle.html","title":"Porting dwm's dwindle layout to tmux"},"following":{"url":"startx-login-tty1.html","title":"Automatically starting X after logging into a Linux virtual terminal"}},"startx-login-tty1":{"preceding":{"url":"tmux-24bit-color.html","title":"Adding 24-bit TrueColor RGB escape sequences to tmux"},"following":{"url":"4digit-base36-timestamp.html","title":"Encoding four base 36 digits as a timestamp"}},"4digit-base36-timestamp":{"preceding":{"url":"startx-login-tty1.html","title":"Automatically starting X after logging into a Linux virtual terminal"},"following":{"url":"xfce-enlarge-titlebar-buttons.html","title":"Enlarging titlebar buttons in XFCE"}},"xfce-enlarge-titlebar-buttons":{"preceding":{"url":"4digit-base36-timestamp.html","title":"Encoding four base 36 digits as a timestamp"},"following":{"url":"acer-c720-linux.html","title":"Lubuntu 14.10 on the Acer C720 Chromebook"}},"acer-c720-linux":{"preceding":{"url":"xfce-enlarge-titlebar-buttons.html","title":"Enlarging titlebar buttons in XFCE"},"following":{"url":"minitest-colordiff.html","title":"Colorful assertion failure diffs in MiniTest"}},"minitest-colordiff":{"preceding":{"url":"acer-c720-linux.html","title":"Lubuntu 14.10 on the Acer C720 Chromebook"},"following":{"url":"tomorrow-terminal-color-scheme.html","title":"Tomorrow terminal color scheme"}},"tomorrow-terminal-color-scheme":{"preceding":{"url":"minitest-colordiff.html","title":"Colorful assertion failure diffs in MiniTest"},"following":{"url":"tmux-refocus-patch.html","title":"Remembering window pane focus in tmux"}},"tmux-refocus-patch":{"preceding":{"url":"tomorrow-terminal-color-scheme.html","title":"Tomorrow terminal color scheme"},"following":{"url":"tmux-searchable-scrollback.html","title":"Searching the scrollback buffer in tmux"}},"tmux-searchable-scrollback":{"preceding":{"url":"tmux-refocus-patch.html","title":"Remembering window pane focus in tmux"},"following":{"url":"tmux-half-screen-tiling-layouts.html","title":"Half-screen tiling layouts in tmux"}},"tmux-half-screen-tiling-layouts":{"preceding":{"url":"tmux-searchable-scrollback.html","title":"Searching the scrollback buffer in tmux"},"following":{"url":"tamsyn-1.7b-font-review.html","title":"Tamsyn 1.7b programming font review"}},"tamsyn-1.7b-font-review":{"preceding":{"url":"tmux-half-screen-tiling-layouts.html","title":"Half-screen tiling layouts in tmux"},"following":{"url":"slim-tilt-redcarpet.html","title":"Using Redcarpet for Markdown in Slim"}},"slim-tilt-redcarpet":{"preceding":{"url":"tamsyn-1.7b-font-review.html","title":"Tamsyn 1.7b programming font review"},"following":{"url":"tmux-pane-zoom.html","title":"Temporarily zooming panes in tmux"}},"tmux-pane-zoom":{"preceding":{"url":"slim-tilt-redcarpet.html","title":"Using Redcarpet for Markdown in Slim"},"following":{"url":"rails3-gui-api.html","title":"Decoupling GUI from API in Ruby on Rails 3"}},"rails3-gui-api":{"preceding":{"url":"tmux-pane-zoom.html","title":"Temporarily zooming panes in tmux"},"following":{"url":"sencha-architect2-rails31-asset-pipeline.html","title":"Putting Sencha Architect 2 into Rails 3.1's asset pipeline"}},"sencha-architect2-rails31-asset-pipeline":{"preceding":{"url":"rails3-gui-api.html","title":"Decoupling GUI from API in Ruby on Rails 3"},"following":{"url":"vim-256color-bce.html","title":"Fixing Vim's Background Color Erase for 256-color tmux and GNU screen"}},"vim-256color-bce":{"preceding":{"url":"sencha-architect2-rails31-asset-pipeline.html","title":"Putting Sencha Architect 2 into Rails 3.1's asset pipeline"},"following":{"url":"4-reminders-6-paramitas-1-universe.html","title":"Four reminders, Six paramitas, and One universe"}},"4-reminders-6-paramitas-1-universe":{"preceding":{"url":"vim-256color-bce.html","title":"Fixing Vim's Background Color Erase for 256-color tmux and GNU screen"},"following":{"url":"extracting-archives-without-making-a-mess.html","title":"Extracting archives without making a mess"}},"extracting-archives-without-making-a-mess":{"preceding":{"url":"4-reminders-6-paramitas-1-universe.html","title":"Four reminders, Six paramitas, and One universe"},"following":{"url":"extjs4-mvc-rails31-asset-pipeline.html","title":"Putting ExtJS 4 into Rails 3.1's asset pipeline"}},"extjs4-mvc-rails31-asset-pipeline":{"preceding":{"url":"extracting-archives-without-making-a-mess.html","title":"Extracting archives without making a mess"},"following":{"url":"test-loop.html","title":"Continuous testing for Ruby with fork/eval"}},"test-loop":{"preceding":{"url":"extjs4-mvc-rails31-asset-pipeline.html","title":"Putting ExtJS 4 into Rails 3.1's asset pipeline"},"following":{"url":"dwm-spawn-cwd-patch.html","title":"Spawn from current working directory in DWM"}},"dwm-spawn-cwd-patch":{"preceding":{"url":"test-loop.html","title":"Continuous testing for Ruby with fork/eval"},"following":{"url":"vim-script-management-system.html","title":"Vim script management system"}},"vim-script-management-system":{"preceding":{"url":"dwm-spawn-cwd-patch.html","title":"Spawn from current working directory in DWM"},"following":{"url":"zenburn-terminal-color-scheme.html","title":"Zenburn terminal color scheme"}},"zenburn-terminal-color-scheme":{"preceding":{"url":"vim-script-management-system.html","title":"Vim script management system"},"following":{"url":"oniguruma-negated-regexps.html","title":"Negated regular expressions in Oniguruma and Ruby"}},"oniguruma-negated-regexps":{"preceding":{"url":"zenburn-terminal-color-scheme.html","title":"Zenburn terminal color scheme"},"following":{"url":"ruby-1.9.3-p0-power-efficiency.html","title":"Ruby 1.9.3 power efficiency ends wmiirc woes"}},"ruby-1.9.3-p0-power-efficiency":{"preceding":{"url":"oniguruma-negated-regexps.html","title":"Negated regular expressions in Oniguruma and Ruby"},"following":{"url":"switching-from-fish-to-zsh.html","title":"Switching from Fish to ZSH"}},"switching-from-fish-to-zsh":{"preceding":{"url":"ruby-1.9.3-p0-power-efficiency.html","title":"Ruby 1.9.3 power efficiency ends wmiirc woes"},"following":{"url":"xoria256-terminal-color-scheme.html","title":"xoria256 terminal color scheme"}},"xoria256-terminal-color-scheme":{"preceding":{"url":"switching-from-fish-to-zsh.html","title":"Switching from Fish to ZSH"},"following":{"url":"yaml-json-validation-kwalify-ruby19.html","title":"Validating machine-generated YAML and JSON using Kwalify in Ruby 1.9"}},"yaml-json-validation-kwalify-ruby19":{"preceding":{"url":"xoria256-terminal-color-scheme.html","title":"xoria256 terminal color scheme"},"following":{"url":"contest-rails-testing-monkeypatch.html","title":"Using Contest's context() in Rails' controller tests"}},"contest-rails-testing-monkeypatch":{"preceding":{"url":"yaml-json-validation-kwalify-ruby19.html","title":"Validating machine-generated YAML and JSON using Kwalify in Ruby 1.9"},"following":{"url":"xoria256-menu-color-patch.html","title":"xoria256 menu color patch"}},"xoria256-menu-color-patch":{"preceding":{"url":"contest-rails-testing-monkeypatch.html","title":"Using Contest's context() in Rails' controller tests"},"following":{"url":"joe-hisaishi-sheeta-no-ketsui.html","title":"Transcription of Joe Hisaishi's \"Sheeta no Ketsui\""}},"joe-hisaishi-sheeta-no-ketsui":{"preceding":{"url":"xoria256-menu-color-patch.html","title":"xoria256 menu color patch"},"following":{"url":"ryuichi-sakamoto-solitude.html","title":"Transcription of Ryuichi Sakamoto's \"Solitude\""}},"ryuichi-sakamoto-solitude":{"preceding":{"url":"joe-hisaishi-sheeta-no-ketsui.html","title":"Transcription of Joe Hisaishi's \"Sheeta no Ketsui\""},"following":{"url":"guiltless-monkeypatching-with-uuid.html","title":"Guiltless monkeypatching with UUID"}},"guiltless-monkeypatching-with-uuid":{"preceding":{"url":"ryuichi-sakamoto-solitude.html","title":"Transcription of Ryuichi Sakamoto's \"Solitude\""},"following":{"url":"wmii-3.1-ruby-config.html","title":"wmii-3.1 configuration in Ruby"}},"wmii-3.1-ruby-config":{"preceding":{"url":"guiltless-monkeypatching-with-uuid.html","title":"Guiltless monkeypatching with UUID"},"following":{"url":"jedit-max-width-patch.html","title":"Truly elastic word-wrapping in jEdit"}},"jedit-max-width-patch":{"preceding":{"url":"wmii-3.1-ruby-config.html","title":"wmii-3.1 configuration in Ruby"},"following":{"url":"telugu-rts-with-scim.html","title":"Telugu RTS with SCIM"}},"telugu-rts-with-scim":{"preceding":{"url":"jedit-max-width-patch.html","title":"Truly elastic word-wrapping in jEdit"},"following":{"url":"defining-methods-for-erb-templates.html","title":"Defining methods for ERB templates"}},"defining-methods-for-erb-templates":{"preceding":{"url":"telugu-rts-with-scim.html","title":"Telugu RTS with SCIM"},"following":{"url":"switching-from-jedit-to-vim.html","title":"Switching from jEdit to Vim"}},"switching-from-jedit-to-vim":{"preceding":{"url":"defining-methods-for-erb-templates.html","title":"Defining methods for ERB templates"},"following":{"url":"copyleft-mit-license.html","title":"Copyleft variation of the MIT license"}},"copyleft-mit-license":{"preceding":{"url":"switching-from-jedit-to-vim.html","title":"Switching from jEdit to Vim"},"following":{"url":"nanase-hikaru-honoka-na-omoi.html","title":"Transcription of Nanase Hikaru's \"Honoka na Omoi\""}},"nanase-hikaru-honoka-na-omoi":{"preceding":{"url":"copyleft-mit-license.html","title":"Copyleft variation of the MIT license"},"following":{"url":"darcs-2-enhances-amend-record.html","title":"Darcs 2 enhances amend-record"}},"darcs-2-enhances-amend-record":{"preceding":{"url":"nanase-hikaru-honoka-na-omoi.html","title":"Transcription of Nanase Hikaru's \"Honoka na Omoi\""},"following":{"url":"openag5-typing-microcontroller-pins.html","title":"Tying wires to microcontroller pins"}},"openag5-typing-microcontroller-pins":{"preceding":{"url":"darcs-2-enhances-amend-record.html","title":"Darcs 2 enhances amend-record"},"following":{"url":"ruby-vpi-c-extension-breakthrough.html","title":"Breakthrough in Ruby-VPI's C extension and Ruby 1.9 support"}},"ruby-vpi-c-extension-breakthrough":{"preceding":{"url":"openag5-typing-microcontroller-pins.html","title":"Tying wires to microcontroller pins"},"following":{"url":"chess-diagrams-in-lyx-with-pdflatex.html","title":"Chess diagrams in LyX with PDFLaTeX"}},"chess-diagrams-in-lyx-with-pdflatex":{"preceding":{"url":"ruby-vpi-c-extension-breakthrough.html","title":"Breakthrough in Ruby-VPI's C extension and Ruby 1.9 support"},"following":{"url":"masters-thesis-completed.html","title":"Masters Thesis completed"}},"masters-thesis-completed":{"preceding":{"url":"chess-diagrams-in-lyx-with-pdflatex.html","title":"Chess diagrams in LyX with PDFLaTeX"},"following":{"url":"using-telugu-in-ubuntu-linux.html","title":"How to use Telugu in Ubuntu Linux"}},"using-telugu-in-ubuntu-linux":{"preceding":{"url":"masters-thesis-completed.html","title":"Masters Thesis completed"},"following":{"url":"ruby-vpi-using-ruby-debug.html","title":"Using ruby-debug with ruby-vpi"}},"ruby-vpi-using-ruby-debug":{"preceding":{"url":"using-telugu-in-ubuntu-linux.html","title":"How to use Telugu in Ubuntu Linux"},"following":{"url":"one-tab-is-two-spaces.html","title":"One tab is two spaces"}},"one-tab-is-two-spaces":{"preceding":{"url":"ruby-vpi-using-ruby-debug.html","title":"Using ruby-debug with ruby-vpi"},"following":{"url":"darcs-migration-tailor-ascii-error.html","title":"ASCII/Unicode error in Tailor"}},"darcs-migration-tailor-ascii-error":{"preceding":{"url":"one-tab-is-two-spaces.html","title":"One tab is two spaces"},"following":{"url":"making-erb-behave-like-php.html","title":"Making ERB behave like PHP"}},"making-erb-behave-like-php":{"preceding":{"url":"darcs-migration-tailor-ascii-error.html","title":"ASCII/Unicode error in Tailor"},"following":{"url":"text-formatting-with-smart-crown-margins.html","title":"Text formatting with smart crown margins"}},"text-formatting-with-smart-crown-margins":{"preceding":{"url":"making-erb-behave-like-php.html","title":"Making ERB behave like PHP"},"following":{"url":"revival-of-the-ruby-vpi-project.html","title":"Revival of the ruby-vpi project"}},"revival-of-the-ruby-vpi-project":{"preceding":{"url":"text-formatting-with-smart-crown-margins.html","title":"Text formatting with smart crown margins"},"following":{"url":"compiling-jedit-in-ubuntu-linux.html","title":"Compiling jEdit under Ubuntu"}},"compiling-jedit-in-ubuntu-linux":{"preceding":{"url":"revival-of-the-ruby-vpi-project.html","title":"Revival of the ruby-vpi project"},"following":{"url":"organizing-firefox-bookmarks-with-freemind.html","title":"Organizing Firefox bookmarks with Freemind"}},"organizing-firefox-bookmarks-with-freemind":{"preceding":{"url":"compiling-jedit-in-ubuntu-linux.html","title":"Compiling jEdit under Ubuntu"},"following":{"url":"telugu-rts-with-scim-demo.html","title":"Video demonstration of Telugu RTS with SCIM"}},"telugu-rts-with-scim-demo":{"preceding":{"url":"organizing-firefox-bookmarks-with-freemind.html","title":"Organizing Firefox bookmarks with Freemind"},"following":{"url":"telugu-vocabulary.html","title":"Telugu vocabulary for modern Telugu people"}},"telugu-vocabulary":{"preceding":{"url":"telugu-rts-with-scim-demo.html","title":"Video demonstration of Telugu RTS with SCIM"},"following":{"url":"telugu-fonts-in-ubuntu-breezy.html","title":"Proper rendering of Indic fonts in Ubuntu Breezy"}},"telugu-fonts-in-ubuntu-breezy":{"preceding":{"url":"telugu-vocabulary.html","title":"Telugu vocabulary for modern Telugu people"},"following":{"url":"the-umbrella-of-sacrifice.html","title":"The umbrella of sacrifice"}},"the-umbrella-of-sacrifice":{"preceding":{"url":"telugu-fonts-in-ubuntu-breezy.html","title":"Proper rendering of Indic fonts in Ubuntu Breezy"},"following":{"url":"making-jedit-and-wmii-2-cooperate.html","title":"Making jEdit and WMII-2 cooperate"}},"making-jedit-and-wmii-2-cooperate":{"preceding":{"url":"the-umbrella-of-sacrifice.html","title":"The umbrella of sacrifice"},"following":{"url":"soundblaster-audigy-2-value-in-ubuntu-hoary.html","title":"SoundBlaster Audigy 2 Value and Ubuntu Hoary"}},"soundblaster-audigy-2-value-in-ubuntu-hoary":{"preceding":{"url":"making-jedit-and-wmii-2-cooperate.html","title":"Making jEdit and WMII-2 cooperate"},"following":{"url":"ftv01-la-jetee-effects.html","title":"On the effectiveness of La Jetée"}},"ftv01-la-jetee-effects":{"preceding":{"url":"soundblaster-audigy-2-value-in-ubuntu-hoary.html","title":"SoundBlaster Audigy 2 Value and Ubuntu Hoary"},"following":{"url":"ftv01-the-power-of-film.html","title":"The power of film"}},"ftv01-the-power-of-film":{"preceding":{"url":"ftv01-la-jetee-effects.html","title":"On the effectiveness of La Jetée"},"following":{"url":"compiling-workrave-in-fedora-core-4.html","title":"Compiling Workrave in Fedora Core 4"}},"compiling-workrave-in-fedora-core-4":{"preceding":{"url":"ftv01-the-power-of-film.html","title":"The power of film"},"following":{"url":"his29-essay2.html","title":"Vedic and Upanishadic influence in the Bhagavadh Giitha"}},"his29-essay2":{"preceding":{"url":"compiling-workrave-in-fedora-core-4.html","title":"Compiling Workrave in Fedora Core 4"},"following":{"url":"his29-essay1.html","title":"Major changes in South Asian rituals in 1500-500 BCE"}},"his29-essay1":{"preceding":{"url":"his29-essay2.html","title":"Vedic and Upanishadic influence in the Bhagavadh Giitha"},"following":{"url":"educ92a-essay.html","title":"Forced assimilation in American public education"}},"educ92a-essay":{"preceding":{"url":"his29-essay1.html","title":"Major changes in South Asian rituals in 1500-500 BCE"},"following":{"url":"telugu-rts-with-iiimf.html","title":"Telugu RTS with IIIMF"}},"telugu-rts-with-iiimf":{"preceding":{"url":"educ92a-essay.html","title":"Forced assimilation in American public education"},"following":{"url":"mac-address-rotation.html","title":"MAC address rotation"}},"mac-address-rotation":{"preceding":{"url":"telugu-rts-with-iiimf.html","title":"Telugu RTS with IIIMF"},"following":{"url":"socy15-essay3.html","title":"Changes in Womens' roles due to Industrialization in Northern Europe"}},"socy15-essay3":{"preceding":{"url":"mac-address-rotation.html","title":"MAC address rotation"},"following":{"url":"musc80v-midterm.html","title":"From \"A Hard Day's Night\" to \"Help!\": changes in The Beatles' music and personality"}},"musc80v-midterm":{"preceding":{"url":"socy15-essay3.html","title":"Changes in Womens' roles due to Industrialization in Northern Europe"},"following":{"url":"socy15-essay1.html","title":"What is Globalization?"}},"socy15-essay1":{"preceding":{"url":"musc80v-midterm.html","title":"From \"A Hard Day's Night\" to \"Help!\": changes in The Beatles' music and personality"},"following":{"url":"socy15-essay2.html","title":"Chinese World Domination in the 19th Century"}},"socy15-essay2":{"preceding":{"url":"socy15-essay1.html","title":"What is Globalization?"},"following":{"url":"the-sixth-sense.html","title":"Review of M. Night Shyamalan's film \"The Sixth Sense\""}},"the-sixth-sense":{"preceding":{"url":"socy15-essay2.html","title":"Chinese World Domination in the 19th Century"},"following":{"url":"ebola-virus.html","title":"Investigating the Ebola HF virus"}},"ebola-virus":{"preceding":{"url":"the-sixth-sense.html","title":"Review of M. Night Shyamalan's film \"The Sixth Sense\""}}};
+ var entry_id = $('#body').data('entry-id');
+ if (entry_id) {
+ var chain = chain_by_entry_id[entry_id];
+ if (chain) {
+ // element to surround with navigation links
+ var $target = $('article > header .navigation > a.rootlink');
+
+ var preceding = chain.preceding;
+ $target.before($('').
+ attr('class', 'prevlink').
+ attr('href', preceding && preceding.url).
+ attr('title', preceding && preceding.title));
+
+ var following = chain.following;
+ $target.after($('').
+ attr('class', 'nextlink').
+ attr('href', following && following.url).
+ attr('title', following && following.title));
+ }
+ }
+
+ // make alternate texts on images appear as tooltip labels on mouse-over
+ $('img[alt]:not([title])').each(function() {
+ $(this).attr('title', $(this).attr('alt'));
+ });
+
+ // filter entries on index page based on user input in the search box
+ var $entries = $('nav.entries > ul > li');
+ var $headings = $('nav.entries > h2');
+ $('article > header > form.search > input[type=search]').on(
+ 'keyup change focus blur search', function(event) {
+ var input = event.target.value;
+ if (input.match(/\S/)) {
+ var words = input.replace(/^\s+|\s+$/g, '').split(/\s+/g);
+ var regexp = new RegExp(words.join('|'), 'ig');
+ $headings.hide();
+ $entries.each(function() {
+ var $entry = $(this);
+ if (regexp.test($entry.text())) {
+ $entry.show();
+ $entry.parent('ul').prev('h2').show();
+ }
+ else {
+ $entry.hide();
+ }
+ });
+ }
+ else {
+ $entries.show();
+ $headings.show();
+ }
+ }
+ );
+
+ // replace embedded YouTube video thumbnail links with