Finally out of stealth, here's Causata, Inc.
Wow, it's been months and months that we've been working quietly to build our software platform, recruit more team members, design the UI, do user testing, ... But, finally, Causata is here.
I won't be blogging about Causata stuff here because there's a corporate blog where you can read my first blog post about working at Causata. My biggest concern right now is recruiting. If you know a lot about Java or JavaScript and are based in London or Silicon Valley please get in contact. Labels: hiring, machine learning, mathematics, pseudorandomness
Any sufficiently simple explanation is indistinguishable from magic
Well, that's true if you are a fool. Take for example the mystical belief that the number 11 or 11:11 is somehow significant. Uri Geller goes on about this on his web site. To quote from Geller's web site (and you'll find other similar thinking on many 11:11 web sites): String theory is said to be the theory of everything. It is a way of describing every force and matter regardless of how large or small or weak or strong it is. There are a few eleven's that have been found in string theory.
I find this to be interesting since this theory is supposed to explain the universe! The first eleven that was noticed is that string theory has to have 11 parallel universes (discussed in the beginning of the "11.11" article) and without including these universes, the theory does not work.
The second is that Brian Greene has 11 letters in his name. For those of you who do not know, he is a physicist as well as the author of The Elegant Universe, which is a book explaining string theory. (His book was later made into a mini series that he hosted.) Another interesting find is that Isaac Newton (who's ideas kicked off string theory many years later) has 11 letters in his name as well as John Schwarz. Schwarz was one of the two men who worked out the anomalies in the theory. Plus, 1 person + 1 person = 2 people = equality.
Also, the two one's next to each other is 11. The two men had to find the same number (496) on both sides of the equation in order for the anomalies to be worked out, so the equation had to have equality! There were two matching sides to the equation as well because they ultimately got 496 on both sides. So, the 1 + 1 = 2 = equality applies for the equation as well.
I added a little bold type there because it amused me; pity that Mr Geller didn't look up the definition of equation before writing that line. But key to this whole belief is that the number 11 keeps turning up at random. When I first read about this I looked up at the clock and it was 11:43. Whoa! Spooky! But then I remembered Benford's Law. Benford's Law is essentially that in lots of reallife data the leading digit is 1 with a probability of about 30% (instead of the 10% you'd expect if the first digit was random from 0 through 9) and hence numbers beginning with 1 occur more often than numbers starting with any other digit. A simple illustration is my clock experience. What's the probability that if you look at a clock at random that the first digit is a 1? Well it's more likely than any other number. For a clock showing 12 hour time it cycles through: 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11. A simple count will show you that the number 1 is the first digit for 8 out of the 24 hours and that all the other digits occur 2 times in 24 hours. So what's the probability that if I glance at a clock at random I'll see a 1 at the beginning? 8/24 or 1/3 of the time... which is Benford's Law. Now, Benford's Law isn't restricted to time. It occurs all over the place (Wikipedia lists: electricity bills, street addresses, stock prices, population numbers, death rates, lengths of rivers, physical and mathematical constants) and so if you walk through life looking at random numbers you'll see numbers starting with a 1 more often than any other number. In 1988 a mathematician named Ted Hill showed why this is the case for many realworld systems. But, what about 11? I hear you ask. Well if the first digit is more likely to be 1 than any other than it's clear that you are more likely to see numbers in the range 10 through 19 more than other two digit numbers, but a more interesting offshoot of Benford's Law is explained here. Essentially as you walk through the digits of a number you are more likely to see a 1 than another digit, but that effect diminishes the longer the number gets. The probability that the the second digit is a 1 is about 11% (instead of the expected 10%) and given that the probability that the first digit is a 1 is 30% you are bound to come across 11 more frequently than you'd expect (if numbers were random). So, it's no surprise that we see lots of 11s, and hence there's a simple explanation for all those 11s. Either that or I've been missing the call of the 11:11 Spirit Guardians all these years: These 11:11 WakeUp Calls on your digital clocks, mobile phones, VCR’s and microwaves are the "trademark" prompts of a group of just 1,111 funloving Spirit Guardians, or Angels. Once they have your attention, they will use other digits, like 12:34, or 2:22 to remind you of their presence. Invisible to our eyes, they are very real.
Labels: mathematics, pseudorandomness
The sum of the first n odd numbers is always a square
I was staring at the checked pattern on the back of an airline seat the other day when I suddenly saw that the sum of the first n odd numbers is always a square. For example, 1 1 + 3 = 4 1 + 3 + 5 = 9 1 + 3 + 5 + 7 = 16
And, of course, it occurred to me that it would be nice to be able to prove it. There are lots of ways to do that. Firstly, this is just the sum of an arithmetic progression starting at a = 1 with a difference of d = 2. So the standard formula gives us: sum_odd(n) = n(2a + (n1)d)/2 = n(2 + (n1)2)/2 = n(1 + n  1) = n^2
So, the sum of the first n odd numbers is n^2. But using standard formulae is annoying, so how about trying a little induction. sum_odd(1) = 1
sum_odd(n+1) = sum_odd(n) + (2n + 1) = n^2 + 2n + 1 = (n+1)^2
But back to the airline seat. Here's what I saw (I added the numbering, Lufthansa isn't kind enough to do that for you :):
The other thing I noticed was this:
You can view the square as the sum of two simpler progressions (the sum of the first n numbers and the sum of the first n1 numbers): 1 + 3 + 5 + 7 = 1 + 2 + 3 + 4 + 1 + 2 + 3
And given that we know from Gauss the sum of the first n numbers if n(n+1)/2 we can easily calculate: sum_odd(n) = sum(n) + sum(n1) = n(n+1)/2 + (n1)n/2 = (n^2 + n + n^2  n)/2 = n^2
What do you do on long flights? Labels: mathematics
Proof that the sum of the squares of the first n whole numbers is n^3/3 + n^2/2 + n/6
A recent thread on Hacker News that I started with a flippant comment turned into a little mathematical puzzle. What's the sum of the square of the first n whole numbers? It's well known that the sum of the first n whole numbers is n(n+1)/2. But what's the value of sum(i=1..n) n^2? (I'll call this number S for the remainder of this post). It turns out that it's easy to prove that S = n^3/3 + n^2/2 + n/6 by induction. But how is the formula derived? To help with reasoning here's a little picture of the first 4 squares stacked up one on top of the other:
If we fill in the blank squares to make a rectangle we have the basis of a derivation of the formula:
Looking at the formerly blank squares (that I've numbered to assist with the thinking) we can see that the columns have 1 then 1+2 then 1+2+3 and finally 1+2+3+4 squares. Thus the columns are sums of consecutive whole numbers (for which we already have the n(n+1)/2 formula. Now the total rectangle is n+1 squares wide (in this case 5) and its height is the final sum of whole numbers up to n or n(n+1)/2 (in the image it's 4 x 5 / 2 = 10. So the total number of squares in the rectangle is (n+1)n(n+1)/2 (in the example that's 5 x 10 = 50). So we can calculate S as the total rectangle minus the formerly blank squares which gives: S = (n+1)n(n+1)/2  sum(i=1..n)sum(j=1..i) j = (n(n+1)^2)/2  sum(i=1..n) i(i+1)/2 2S = n(n+1)^2  sum(i=1..n) i(i+1) = n(n+1)^2  sum(i=1..n) i^2  sum(i=1..n) i = n(n+1)^2  S  n(n+1)/2 3S = n(n+1)^2  n(n+1)/2 = n(n+1)( n+1  1/2 ) = n(n+1)(n+1/2) = (n^2+n)(n+1/2) = n^3 + n^2/2 + n^2 + n/2 = n^3 + 3n^2/2 + n/2 S = n^3/3 + n^2/2 + n/6
Labels: mathematics
First release of my 'shimmer' project
A couple of months ago I blogged about a system for open and closing ports based on a crytographic algorithm that makes it hard for an attacker to guess the right port. It's a sort of port knocking scheme that I called C3PO. Many commentators via email or on the blog and in other forums requested that I open source the code. I couldn't do that because the code was a nasty hack put together for my machine, but I gone one better. Today I'm releasing the first version of shimmer. shimmer is completely new, GPLlicensed code, implementing my original idea. Read more about it on the site.
Hit the right port and you're in, hit the wrong one and your blacklisted. Ports change every 60 seconds. Labels: mathematics, perl, security
Transitive decay in social networks
When I was doing research in computer security we often used to say "trust isn't transitive". What we meant was that if Alice trusts Bob and Bob trusts Charlie then we can't assume that Alice trusts Charlie. Another way to think of this is to look at your own friends and friends of friends; do you trust the friends of friends? It's likely that you do not (if you did then it's likely that they would actually already be a friend and not a FOAF). Clearly, trust is not a constant value across all friends, so each of your N friends will have a trust value ("how much you trust them"), which I'll call Ti, assigned to them. A friend you'd trust with your life has Ti = 1 (perhaps they're a candidate for a BFF), and a friend you don't trust at all has Ti = 0. (I'll ignore the question of why you even have friends with Ti = 0, but in the context of computer social networks you probably do have some). In social situations we are only exposed to this FOAF trust problem occasionally, but with 'social networking' a current web buzzword we see social networks, or social graphs, and can traverse them. Many web sites are trying to use this graph traversal to build services (e.g. LinkedIn allows you to send requests across the network, or ask questions; Facebook is hoping that graph traversal will be the new application distribution method). But any graph traversal suffers from the FOAF trust problem. In a social network online this gets expressed by statements like "Just because Alice likes the Werewolf application and shares it with Bob and Charlie is friends with Bob, that doesn't mean that Charlie wants to be a Werewolf", or "A message crossing between more than one hop won't get passed all the time". I dare say that LinkedIn, Facebook and others could actually characterize the rate at which the FOAF attenuates messages (be they actual messages, application installations, or any other meme) passing through the network. I'm going to posit that the amount of trust a user would place in a FOAF (and a FOAFOAF, a FOAFOAFOAF, ... ) decays rapidly with the number of FOAF hops traversed. Intuitively, if Alice trusts Bob with Talice,bob and Bob trusts Charlie with Tbob,charlie then how much does Alice trust Charlie? Less than she trusts Bob because Charlie is not her friend, and she can only evaluate Charlie based on Bob's recommendation. The more she trusts Bob the more she should trust Charlie. So some sort of estimate Talice,charlie is created from Talice,bob and Tbob,charlie taking into account these trust estimates. A simple combination would be Talice,charlie = Talice,bob * Tbob,charlie (this assumes quite the opposite of the original declaration above: here trust is transitive to a certain degree). The problem with this is that it treats all trust relationships as having equal weight, no matter how far they are from the original person (in this case, Alice). Imagine the case where Alice trusts Bob with Talice,bob = 1, Bob trusts Charlie Tbob,charlie = 1. This formula gives Talice,charlie = 1 which would probably not reflect most people's intuitive grasp of trust. If in addition, Charlie trusts Dave with Tcharlie,dave = 1 then we get Talice,dave = 1 which seems even more unlikely. What's needed is a way to decay trust the further apart people are. One way to to this is for each person to have their own damping factor the encodes how much they trust another person's trust. So Alice might trust other people's recommendations with factor Dalice (in the range [0,1]). The formula would be updated to have Talice,charlie = Dalice * Talice,bob * Tbob,charlie Talice,dave = Dalice * Talice,charlie = Dalice * Talice,bob * Dbob * Tbob,charlie * Tcharlie,dave But that's still essentially linear. I think trust looks more like an inverse square law so that distance is explicitly encoded. With that Talice,charlie = Dalice * Talice,bob * Tbob,charlie / 1^2 Talice,dave = Dalice * Talice,charlie / 2^2 = Dalice * Talice,bob * Dbob * Tbob,charlie * Tcharlie,dave / 4 This seems to fit better intuition because trust of distant people drops away very rapidly. Now, since this is only a hypothesis it would be interesting to measure the reach of messages passing inside a social network to look at the actual 'pass it on' rates to see if they match intuition. Anyone out there got lots of social network data I could analyze? Perhaps there's a Facebook application developer who's tracked enough invite/install data that this could be verified. Labels: mathematics, pseudorandomness
Facebook meets the Birthday Paradox
Logging in to Facebook this morning there was a great demonstration of the Birthday Paradox (which isn't actually a paradox, it's just that people get surprised by it). I have 95 'friends' on Facebook, and this Thursday three of them have a birthday. Wow! Or not, wow, in fact since the calculation in the birthday paradox shows us that this is very likely to happen. Once you reach 57 friends there's a 99% likelihood that two share he same birthday, with 95 friends your getting very close to 100%. So the fact that three people have the same birthday is not at all unlikely. In fact the birthday paradox can be generalized to cover more than two birthday's being the same. My three birthday example with 95 friends would happen with probability well over 50% (which happens with 88 friends). Labels: mathematics
Cryptographically and Constantly Changing Port Opening (or C3PO)
In another forum I was just talking about a little technique that I came up with for securing a server that I want on the Internet, but to be hard for hackers to get into. I've done all the right things with firewalling and shutting down services so that only SSH is available. But that still leaves port 22 sitting there open for someone to bang on. So what I wanted was something like port knocking (for an introduction to that you can read my DDJ article Practical Secure Port Knocking). To avoid doing the classic port knocking where you have to knock the right way to open port 22 I came up with a different scheme which I call Cryptographically and Constantly Changing Port Opening or C3PO. The server and any SSH client who wish to connect to it share a common secret. In my case that's just a long passphrase that we both know. Both bits of software hash this secret with the current UTC time in minutes (using SHA256) to get 256 bits of random data. This data changes every minute. The 256 bits gives me 16 words of random data. Those 16 words can be interpreted as 16 different port numbers. Once a minute the server reconfigures iptables to open those 16 ports forwarding one of them (which corresponds to word[0] in the hash) to SSH and the other 15 to a blacklist service. At any one time 16 ports are open (i.e. respond to a SYN) with only one being SSH and the other 15 being a trap to be sprung by an attacker. The 16 ports change once a minute. Since both sides can compute the hash the client is able to compute where the SSH server is residing at that moment and contact it. Once contact is established the connection remains open for the duration of the session. New sessions, of course, will need to recompute the hash once a minute. The blacklist service serves to tarpit an attacker. Any connection attempt to one of the other 15 sockets causes the IP address of the attacker to be blacklisted (again an iptables change) which means that hitting any of the 15 ports causes the attacker to shut off their access to the SSH server for the next 15 minutes. A casual NMAP of my machine gets your IP address blacklisted and shows up a random selection of open ports. A real user connects to the SSH server first time because they know where it resides. Of course, this doesn't replace making sure that the SSH server is up to date, and that passwords are generated carefully, but it seriously frustrates a potential attacker. If this is of interest to others I'd be happy to release my code (which is currently in Perl) as a GPL2 project. Labels: mathematics, pseudorandomness, security
PPPv2 in Java and C
Recently, I released C and Java versions of Steve Gibson's PPP system for password generation. Steve updated the algorithm to PPPv2 which uses a different hash (SHA256 instead of SHA384) and a slightly different plain text generation algorithm (see the PPP pages for details). I've now updated my code to PPPv2 and am releasing it here. The C source code is available in pppc.zip. The Java source code is available in pppjava.zip. The compiled Java is available in ppp.jar. Read my original blog posts for details. All source code is released under the BSD License. Labels: mathematics, pseudorandomness
A Java client implementation of Steve Gibson's PPP
I recently produced an open source implementation of Steve Gibson's Perfect Paper Passwords system in C. It occurred to me that a better implementation would be a Java client for my mobile phone (thus eliminating the need for printing and carrying the paper passwords). Here's my PPP client implementation running on my Motorola RAZR. It's written in Java using CLDC 1.0 and MIDP 2.0. You can download and install the JAR file. The current version is 1.0.0. Labels: mathematics, pseudorandomness
An open source implementation of Steve Gibson's PPP algorithm
Steve Gibson has come up with a simple twofactor password scheme that relies on printed cards of passcodes generated using a combination of SHA384 and Rijndael. The idea is that a system could prompt the user for one of the passcodes in addition to their normal password. Steve calls this his Perfect Paper Passwords system and has given a detailed description of the algorithm. As usual he's released code written in assembly language as a DLL for Windows. He hasn't released his source code (he never does), so I thought it would be interesting to write my own implementation of his algorithm. Here's the C code: #include <sys/time.h> #include <string.h> #include <stdio.h> #include <stdlib.h>
#include "rijndael.h" #include "sha2.h"
#pragma pack(1)
typedef unsigned char Byte;
typedef union __Passcode { unsigned long as_long; struct { Byte byte[4]; } bytes; } Passcode;
typedef struct __PasscodeString { char character[5]; } PasscodeString;
typedef unsigned long long SixtyFour;
typedef union __OneTwoEight { struct { SixtyFour low; SixtyFour high; } sixtyfour; Byte byte[16]; } OneTwoEight;
typedef struct __SequenceKey { Byte byte[SHA384_DIGEST_SIZE]; } SequenceKey;
typedef unsigned long DWord;
const char * alphabet = "[email protected]#%+=:?abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ";
void inc( OneTwoEight * i ) { ++i>sixtyfour.low; if ( i>sixtyfour.low == 0 ) { ++i>sixtyfour.high; } }
void add( OneTwoEight * to, OneTwoEight * addend ) { SixtyFour low = to>sixtyfour.low;
low += addend>sixtyfour.low;
if ( ( low < to>sixtyfour.low )  ( low < addend>sixtyfour.low ) ) { ++to>sixtyfour.high; }
to>sixtyfour.low = low; }
void ConvertPasscodeToString( PasscodeString * passcodeString, Passcode passcodeValue ) { Byte bytes[4];
bytes[0] = passcodeValue.bytes.byte[0] & 0x3f; bytes[1] = ( ( passcodeValue.bytes.byte[0] & 0xc0 ) >> 6 ) + ( ( passcodeValue.bytes.byte[1] & 0x0f ) << 2 ); bytes[2] = ( ( passcodeValue.bytes.byte[1] & 0xf0 ) >> 4 ) + ( ( passcodeValue.bytes.byte[2] & 0x03 ) << 4 ); bytes[3] = ( ( passcodeValue.bytes.byte[2] & 0xfc ) >> 2 );
int i; for ( i = 0; i < 4; ++i ) { passcodeString>character[i] = alphabet[bytes[i]]; }
passcodeString>character[4] = '\0'; }
void RetrievePasscodes( Passcode passcodeListBuffer[], OneTwoEight firstPasscodeNumber, int passcodeCount, SequenceKey * sequenceKey ) { int i;
#define KEY_BITS (int)256 Byte key[KEYLENGTH(KEY_BITS)];
for ( i = 0; i < KEYLENGTH(KEY_BITS); ++i ) { key[i] = sequenceKey>byte[i+16]; }
unsigned long rk[RKLENGTH(KEY_BITS)]; OneTwoEight plain;
for ( i = 0; i < 16; ++i ) { plain.byte[i] = sequenceKey>byte[i]; }
OneTwoEight block = firstPasscodeNumber; unsigned int skip = (unsigned int)(block.sixtyfour.low & 0xF);
SixtyFour carry = block.sixtyfour.high & 0xF; block.sixtyfour.high >>= 4; block.sixtyfour.low >>= 4; block.sixtyfour.low = (carry << 60);
OneTwoEight temp = block; add( &block, &temp ); add( &block, &temp ); add( &plain, &block );
int nrounds = rijndaelSetupEncrypt( rk, key, KEY_BITS ); Byte cipher[16*3];
int c = 0;
while ( passcodeCount > 0 ) { rijndaelEncrypt( rk, nrounds, (Byte *)&plain.byte[0], &cipher[0] ); inc( &plain ); rijndaelEncrypt( rk, nrounds, (Byte *)&plain.byte[0], &cipher[16] ); inc( &plain ); rijndaelEncrypt( rk, nrounds, (Byte *)&plain.byte[0], &cipher[32] ); inc( &plain ); for ( i = skip; ( i < 16 ) && ( passcodeCount > 0 ); ++i ) { passcodeListBuffer[c].bytes.byte[0] = cipher[i*3]; passcodeListBuffer[c].bytes.byte[1] = cipher[i*3+1]; passcodeListBuffer[c].bytes.byte[2] = cipher[i*3+2]; ++c; passcodeCount; }
skip = 0; } }
void GenerateSequenceKeyFromString( char * string, SequenceKey * sequenceKey ) { sha384( (const unsigned char *)string, strlen( string ), (unsigned char *)sequenceKey ); }
void GenerateRandomSequenceKey( SequenceKey * sequenceKey ) { struct timeval t; gettimeofday( &t, 0 );
char t_buffer[61]; strftime( t_buffer, 60, "%c%d%e%H%I%j%m", localtime( &t.tv_sec ) ); char msecs_buffer[32]; sprintf( msecs_buffer, "%ld", t.tv_usec );
char hostname_buffer[256]; gethostname( hostname_buffer, 255 );
char pointer_buffer[16]; sprintf( pointer_buffer, "%p", sequenceKey );
char loadavg_buffer[256]; double samples[3]; getloadavg( samples, 3 ); sprintf( loadavg_buffer, "%f%f%f", samples[0], samples[1], samples[2] );
char buffer[1024]; sprintf( buffer, "%s%s%s%s%s", t_buffer, msecs_buffer, hostname_buffer, pointer_buffer, loadavg_buffer );
GenerateSequenceKeyFromString( buffer, sequenceKey ); }
int ConvertHexToKey( char * hex, SequenceKey * key ) { int i, j;
for ( i = 0, j = 0; i < 96; i += 2, ++j ) { char pair[3]; sprintf( pair, "%c%c", hex[i], hex[i+1] ); int x; sscanf( pair, "%x", &x ); key>byte[j] = (Byte)x; } }
int main( int argc, char * argv[] ) { if ( argc == 1 ) { printf( "Error: You must provide the passphrase or sequence key as the first parameter\n" ); return 1; }
SequenceKey key;
if ( strlen( argv[1] ) == 0 ) { printf( "Generating random sequence key\n" ); GenerateRandomSequenceKey( &key ); } else { if ( ( strlen( argv[1] ) == 96 ) && ( ConvertHexToKey( argv[1], &key ) ) ) { printf( "Using entered sequence key\n" ); } else { printf( "Generating sequence key from passphrase\n" ); GenerateSequenceKeyFromString( argv[1], &key ); } }
printf( "Sequence Key: " ); int i; for ( i = 0; i < SHA384_DIGEST_SIZE; ++i ) { printf( "%2.2x", key.byte[i] ); } printf( "\n" );
if ( argc == 4 ) { OneTwoEight firstPasscode; // Warning! This only uses the bottom 64bits of argv[2] and hence // can't convert a much higher number
firstPasscode.sixtyfour.low = atoi( argv[2] ); firstPasscode.sixtyfour.high = 0;
int count = atoi( argv[3] );
Passcode * pcl = malloc( sizeof( Passcode ) * count );
RetrievePasscodes( pcl, firstPasscode, count, &key );
for ( i = 0; i < count; ++i ) { PasscodeString str; ConvertPasscodeToString( &str, pcl[i] ); printf( "%s ", &str.character[0] ); } printf( "\n" ); }
return 0; }
Now that's a little less flexible than all the options given in Steve's ppp.exe implementation, but it does compute the correct output and can easily be modified if you want your own implementation with source of PPP. It uses this SHA384 implementation and this Rijndael implementation. Here's the output of my ppp program producing the first 70 passcodes: $ ./ppp 53303f97ddcf91ed74391fc5c3661246 32427e1c93c1a2e2836d006fa2653dc1 fb94f8fbeefa5f1e9263c12878e0a95e 0 70 Using entered sequence key Sequence Key: 53303f97ddcf91ed74391fc5c3661246 32427e1c93c1a2e2836d006fa2653dc1 fb94f8fbeefa5f1e9263c12878e0a95e VJNV gHoF PaRp T8FS tGw2 s%iT u7rp [email protected] MWGb %574 ?DVF btRq PLTA DDtm C2TP Yin8 [email protected] a8%H zHvq Uwxc qkF7 YuUk 8Ca? :ZvZ T9:? wki+ KiHq d?9b GY%5 !igR [email protected] [email protected] eyVm 5PwY CAVs oKzK 43Mc nR%? [email protected] oZUs Tbec xn6B 9bVA UvJt DfAX =Gqp 7Abj M:6Y ENRs aXX= Eokx WjTj %MPV McSA GFTK XMdY 49?Z Z?Hk G+A? zoK5 :Z8N z8NU WpM! =AB% RrSq %7:Y %=P8 RKXr di#5 4T3L
Feel free to take my code and use it under the BSD license. Labels: mathematics, pseudorandomness
BOUTS: Best of UseTheSource
Long, long ago, OK, 1999, I registered the domain name usethesource.com (as in Use the source, Luke!) and used it to start a web site which would these days be called a blog. The site was powered by Slashdot's code Slashcode and featured a mix of my commentary on the news and original articles. You can still read the old site at archive.org. The site even got me an appearance with Leo Laporte on The Screen Savers. Most of the articles published are irrelevant today. The commentary is often on start ups that have fizzled out long ago, and I shut down the site in 2004. But some of the articles are worth repeating. So, from time to time, I'll be republishing original pieces from UseTheSource as BOUTS entries in this blog. To get things rolling here's an article I wrote back in 2002 about calculating the area of an annulus based on the length of a tangent. Originally published June 12, 2002Take a look at the following shape. It's an annulus: two concentric circles, something like a simple washer or donut. Imagine that you know only one fact about this shape, the length of a tangent of the inner circle where it touches the edges of the outer circle. Call that length x. Can you calculate the area of the yellow shaded part? This problem was presented on the NPR radio show CarTalk a few weeks back and after I solved it I realized that there were a couple of interesting ways of calculating the area. Both require knowledge of the formula for the area of a circle: πr^{2}, where r is the radius of the circle. One requires remembering Pythagoras' Theorem, the other a little logical reasoning. Solution by logical reasoningInsight: there must be many such concentric circles where it's possible that the tangent has length x. In fact if I start with the small circle in the middle it must always be possible to choose the size of the outer circle so that the tangent is x. So what if I make then inner circle have size zero. Then all I need is an outer circle with diameter x. Since we know there's only one solution (surely the person posing this question knew that there was only one solution), then we can just calculate the area of the outer circle when the inner circle has zero radius. The outer circle in that case has diameter x or radius x/2 and so the area is π(x/2)^{2} or πx^{2}/4. Solution by PythagorasTo calculate the area of the annulus we need to calculate the area of the big circle and subtract the area of the small circle. If we name the radius of the big circle r and the radius of the small circle s then we need to calculate πr^{2}  πs^{2} or π(r^{2}  s^{2}). Hmm. That r^{2}  s^{2} bit looks a lot like something we might get from Pythagoras' Theorem (the square on the hypotenuse is equal to the sum of the squares on the other two sides). For Pythagoras we need a right angle triangle. Low and behold we have one. Since we have a tangent we know it's at right angles to a radius of the inner circle. The complete triangle has sides r, s and x/2. So run through Pythagoras on this triangle and we get r^{2} = (x/2)^{2} + s^{2}. Subtract s^{2} from both sides and you've got r^{2}  s^{2} = (x/2)^{2}. Now we know how to calculate r^{2}  s^{2}, it's (x/2)^{2} and so the area of the annulus is π(x/2)^{2} or πx^{2}/4. Labels: bouts, mathematics
