# this program copyright 1995-2005 Philip Greenspun (philg@mit.edu) # redistribution and reuse permitted under # the GNU General Public License, http://www.gnu.org/copyleft/gpl.html # this is the source code for the Bill Gates Personal Wealth Clock, operating # at http://philip.greenspun.com/WealthClock and requiring the AOLserver # Web server program as well as some components of the ArsDigita Community # System version 3.x. # this is an ArsDigita Community System feature in which the loading # of these procedures is recorded in the server error log at startup util_report_library_entry # tell AOLserver to associate requests for "/WealthClock" with calls to the procedure wealth_Top ad_register_proc GET /WealthClock wealth_Top ad_register_proc GET /WealthClockRealTime wealth_TopRealTime ad_register_proc GET /WealthClockIntl wealth_IntlTop # helper procedures proc wealth_USPopulation {} { # use the AOLserver API procedure ns_httpget to fetch a Web page from another server set population_html [ns_httpget "http://www.census.gov/cgi-bin/popclock"] # use the Tcl regular expression facility to pull just the population figure out of the # page (the Census folks have surrounded it in an H2 tag) regexp {

[^0-9]*([0-9]+),([0-9]+),([0-9]+).*

} $population_html match millions thousands units # we have to trim the leading zeros because Tcl thinks anything # starting with a "0" is an octal number, which means that # something like "039" will raise an error set trimmed_millions [string trimleft $millions 0] set trimmed_thousands [string trimleft $thousands 0] set trimmed_units [string trimleft $units 0] return [list [expr ($trimmed_millions * 1000000) + ($trimmed_thousands * 1000) + $trimmed_units] "$millions,$thousands,$units"] } # this procedure uses ns_httpget to grab a page from Yahoo! Finance. We ask for the # quote in comma-separated values format (".csv"), which makes it very easy to parse # (the regular expression just looks for a comma followed by some combination of digits # and decimal point proc wealth_get_msft_price {} { catch { ns_httpget "http://finance.yahoo.com/d/quotes.csv?f=sl1&e=.csv&s=MSFT" } quote_html if [regexp {,([0-9\.]+)} $quote_html match the_price] { # Yahoo is good; they give it to us in decimal return $the_price } } # this procedure adjusts a roughly calculation of Gates's wealth for extraordinary # dividends paid out by Microsoft, notably the $3.29 billion dividend Bill Gates # received at the end of 2004. proc wealth_adjust_for_extraordinary_dividends {basic_stock_price_times_shares} { return [expr $basic_stock_price_times_shares + double(3.29e9)] } # This procedure returns an HTML string to its caller rather than # a page to the user. This will enable use to build a caching mechanism # so that if we get thousands of requests per hour we aren't making # thousands of requests of the population and stock quote services. proc wealth_ReturnWholePage {} { set msft_stock_price [wealth_get_msft_price] set population_list [wealth_USPopulation] set population [lindex $population_list 0] set pretty_population [lindex $population_list 1] # Tcl is NOT Lisp and therefore if the stock price and shares are # both integers, you get silent overflow and Bill Gates comes out as a # pauper (< $1 billion). We hammer the problem by converting to double # precision floating point right here. Can someone please take us # back to the 1960s so that we can use a computer language with a decent # model of arithmetic? set gates_shares_pre_split [expr double(141159990)] # adjusted for four splits since 1995 set gates_shares [expr $gates_shares_pre_split * 16] set gates_wealth [wealth_adjust_for_extraordinary_dividends [expr $gates_shares * $msft_stock_price]] set gates_wealth_billions [format "%0.6f" [expr $gates_wealth / 1.0e9]] set personal_share [format "%0.2f" [expr $gates_wealth / $population]] set pretty_date [exec /bin/date] return " Bill Gates Personal Wealth Clock

Bill Gates Personal Wealth Clock

just a small portion of Why Bill Gates is Richer than You by Philip Greenspun


$pretty_date
Microsoft Stock Price: \$$msft_stock_price
Bill Gates's Wealth: \$$gates_wealth_billions billion
U.S. Population: $pretty_population
Your Personal Contribution: \$$personal_share

\"If you want to know what God thinks about money, just look at the people He gives it to.\"
-- Old Irish Saying

Sources

The Clock attempts to accurately display Bill Gates's wealth, not the value of his current holdings of Microsoft stock. We take as a baseline of his wealth the shares of Microsoft that he held in 1995. This is an understatement because it doesn't include the multi-million dollar trust funds he received at birth from his grandparents, houses, stock, and other gifts from his wealthy parents, or investments he purchased with sales of Microsoft shares sold prior to 1995. The clock adjusts for the extraodinary \$3.29 billion dividend that Bill Gates received from Microsoft at the end of 2004.

What about shares sold subsequent to 1995? Don't they balance out this understatement of wealth? No. If Gates sold Microsoft shares to purchase shares in cable TV companies, Corbis, or whatever, we assume that these investments have performed about as well as Microsoft. What about charity? There are two ways to look at this. One is that Bill Gates is directly involved in managing his charitable foundation. So he still controls the money, though of course it will be used only for certain kinds of purposes. If you were a real cynic you might note that Bill's charitable inclinations remained, uh, undiscovered until the Federal Government began to file anti-trust lawsuits. You would then see his charitable contributions as investments in the maintenance of Microsoft's monopoly and not reductions in wealth.

Multi-Nationalism

As the author of such books as Canada: More than Just a Brand Name?, I am well aware of the importance of multi-nationalism. You are invited to try an international version of the Clock.

How it Works

... is explained in somewhat simplified form, including source code, in Chapter 10 of Philip and Alex's Guide to Web Publishing. The program took about one hour from start to finish and was built back in 1995 as an example for MIT students of the future of Web service design: servers that combine information and services from other servers (see http://philip.greenspun.com/teaching/teaching-software-engineering). Ironically this approach to distributed computing over the Internet was ignored by most of the rest of the world except for one company: Microsoft! If you look at Microsoft .NET you'll see that it provides extensive support for building applications like this wealth clock.

In order to provide you with faster service, and to reduce the load on the subsidiary Web sites, the program caches the page. However, you can also operate the clock in real time mode, which will update the cache for everyone else.

The actual source code is available and is intended primarily for Computer Science majors working through the textbook Software Engineering for Internet Applications.

Index


philg@mit.edu
" } proc wealth_BrokenMessage {} { return " Wealth Clock -- We're Broken

We're Broken

here at the Bill Gates Personal Wealth Clock
Probably the Wealth Clock is broken because something has gone wrong with the underlying real-time sources of data: In the meantime, you can amuse yourself by reading Why Bill Gates is Richer than You and/or the perennially popular Travels with Samantha
philg@mit.edu
" } # here is the procedure that is invoked when someone asks AOLserver # for the "/WealthClock" URI. wealth_Top tries to construct the page, # wrapping the attempt in a Tcl catch because the availability of the # underlying data sources cannot be guaranteed. # one interesting point is that the construction of the page is wrapped # in a "util_memoize" procedure, which keeps a record of Tcl statements that # it has seen and their return values. If it has already seen a statement # since the server started up (midnight), it will returned the cached value # rather than reevaluating the statement. Note that the procedure # wealth_TopRealTime calls util_memoize_flush so that the next time util_memoize # is called it will call wealth_ReturnWholePage again. proc wealth_Top {ignore} { if [catch {set moby_string [util_memoize wealth_ReturnWholePage]} errmsg] { # something went wrong with our sources # the catch means that the failure will never be recorded in the error log # so if we're debugging it might be very helpful to write what happened # into the log as a "notice" # ns_log Notice "Wealth clock got $errmsg" ns_return 200 text/html [wealth_BrokenMessage] } else { ns_return 200 text/html $moby_string } } proc wealth_TopRealTime {ignore} { # flush the cached value for the page (a big HTML string) util_memoize_flush wealth_ReturnWholePage # recalculate the page, which will populate the cache for the next # person, then return the big HTML string to the user ns_return 200 text/html [util_memoize wealth_ReturnWholePage] } ### international # see comments on wealth_USPopulation for explanations proc wealth_IntlPopulation {} { set population_html [ns_httpget "http://www.census.gov/cgi-bin/ipc/popclockw"] regexp {

[^0-9]*([0-9]+),([0-9]+),([0-9]+),([0-9]+).*

} $population_html match billions millions thousands units set trimmed_billions [string trimleft $billions 0] set trimmed_millions [string trimleft $millions 0] set trimmed_thousands [string trimleft $thousands 0] set trimmed_units [string trimleft $units 0] return [list [expr ($trimmed_billions * 1e+09) + ($trimmed_millions * 1000000) + ($trimmed_thousands * 1000) + $trimmed_units] "$billions,$millions,$thousands,$units"] } proc wealth_ReturnIntlPage {} { set msft_stock_price [wealth_get_msft_price] set population_list [wealth_IntlPopulation] set population [lindex $population_list 0] set pretty_population [lindex $population_list 1] set gates_shares_pre_split [expr double(141159990)] set gates_shares [expr $gates_shares_pre_split * 16] set gates_wealth [wealth_adjust_for_extraordinary_dividends [expr $gates_shares * $msft_stock_price]] set gates_wealth_billions [format "%0.6f" [expr $gates_wealth / 1.0e9]] set personal_share [format "%0.2f" [expr $gates_wealth / $population]] set pretty_date [exec /bin/date] return " Bill Gates Personal Wealth Clock (International Version)

Bill Gates Personal Wealth Clock

(International Version)

just a small portion of Why Bill Gates is Richer than You by Philip Greenspun




$pretty_date
Microsoft Stock Price: \$$msft_stock_price
Bill Gates's Wealth: \$$gates_wealth_billions billion
World Population: $pretty_population
Your Personal Contribution: \$$personal_share



Sources

If you are a U.S. resident, you might be interested in viewing the original Americentric version.

Index


philg@mit.edu
" } # we memoize the page for 1800 seconds (30 minutes) because there is no real-time version # of this clock proc wealth_IntlTop {ignore} { if [catch {set moby_string [util_memoize wealth_ReturnIntlPage 1800]} errmsg] { # something went wrong with our sources ns_log Notice "Wealth clock got $errmsg" ns_return 200 text/html [wealth_BrokenMessage] } else { ns_return 200 text/html $moby_string } } # old procedure, no longer needed because we're able to get quotes in decimal rather # than "34 and 7/8ths" # proc wealth_RawQuoteToDecimal {raw_quote} { # if { [regexp {(.*) (.*)} $raw_quote match whole fraction] } { # # there was a space # if { [regexp {(.*)/(.*)} $fraction match num denom] } { # # there was a "/" # set extra [expr double($num) / $denom] # return [expr $whole + $extra] # } # # we couldn't parse the fraction # return $whole # } else { # # we couldn't find a space, assume integer # return $raw_quote # } # } util_report_successful_library_load