<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-424507293146612898</id><updated>2011-10-04T06:07:32.788-04:00</updated><category term='Investing'/><category term='Business'/><category term='Innovation'/><category term='GeekFun'/><category term='economics'/><category term='Learning'/><category term='Computer Science'/><category term='Mathematics'/><category term='FATrecover'/><category term='Career'/><category term='History'/><category term='Writing'/><category term='LifeHacks'/><category term='Science'/><category term='BookReview'/><category term='Lisp'/><category term='Programming'/><category term='DataMining'/><category term='Finance'/><category term='BugHunts'/><category term='Meta'/><title type='text'>JCardente</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jcardente.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>87</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3337558567723527262</id><published>2011-07-22T08:30:00.001-04:00</published><updated>2011-07-22T08:32:54.963-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataMining'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>A Busy Summer</title><content type='html'>&lt;p&gt;The summer is proving busier than expected. In June, I started taking
remote classes at Stanford in pursuit of a &lt;a href="http://scpd.stanford.edu/public/category/courseCategoryCertificateProfile.do?method=load&amp;amp;certificateId=1209602#searchResults"&gt;Graduate Certificate in Data Mining and Applications&lt;/a&gt; through the Stanford Center for
Professional Development (&lt;a href="http://scpd.stanford.edu/publicViewHome.do?method=load"&gt;SCPD&lt;/a&gt;). So far, I am enjoying my first class,
&lt;a href="http://sites.google.com/site/stats202/"&gt;stats202&lt;/a&gt;.  As I suspected, I really like working with data to find
hidden patterns, trends, etc.
&lt;/p&gt;
&lt;p&gt;
At the same time, I have a project at work with an aggressive mid-August
deadline. So I've been putting in extra hours. The good news is that
the project is going well and I'm doing lots of &lt;a href="http://www.r-project.org/"&gt;R programming&lt;/a&gt; to analyze
performance data. More fun working with data. I like R and am gaining
a lot of experience with it through work and school projects.
&lt;/p&gt;
&lt;p&gt;
Any spare time not spent with family is being use to work on a few
hobby projects. I'm building a &lt;a href="http://www.zentoolworks.com/product_info.php?products_id=74"&gt;Zen Toolworks desktop CNC machine&lt;/a&gt; so
that my son and I can make cool things. I bought an &lt;a href="http://www.arduino.cc/"&gt;Arduino&lt;/a&gt; and
&lt;a href="http://www.makershed.com/ProductDetails.asp?ProductCode=MSUMP"&gt;Ultimate Microcontoller Pack&lt;/a&gt; from the &lt;a href="http://www.makershed.com/"&gt;MakerShed&lt;/a&gt; that I'm having fun
with. If that wasn't enough, I'm slowly putting together a plan
to build a &lt;a href="http://www.rcexplorer.se/projects/tricopter/tricopter.html"&gt;tricopter&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
A busy summer indeed.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3337558567723527262?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3337558567723527262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3337558567723527262'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/07/busy-summer.html' title='A Busy Summer'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2219192573919297792</id><published>2011-05-06T19:39:00.006-04:00</published><updated>2011-05-06T19:42:56.643-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>TomTom Foolery</title><content type='html'>&lt;p&gt;Three years ago I bought a TomTom ONE XL for a family trip. It worked
great but I haven't used it much since then. We took another family
trip a couple of weeks ago and I again wanted to take the TomTom.  I
decided to update its maps and thus ensued an unexpected adventure.
&lt;/p&gt;
&lt;p&gt;
I bought an updated (and overpriced) map via the TomTom Home desktop
application. Part way through copying the map to the device, it
complained that it was out of space. A little digging revealed that
there wasn't room for both the old and new maps. I looked for a way to
uninstall the original map via the desktop but couldn't find
one. Luckily, OSX mounted the TomTom as a &lt;code&gt;FAT&lt;/code&gt; formatted disk so I
just deleted the old map files. No more out-of-space problem.
&lt;/p&gt;
&lt;p&gt;
I confidently re-started copying the map to the TomTom but ran into
another problem. The TomTom suddenly disconnected.  Repeated attempts
all ended with the same error message that the USB device had
unexpectedly disconnected. I tried different USB cables and ports
but nothing worked. With the trip looming, it was time for some hacking.
&lt;/p&gt;
&lt;p&gt;
I opened an OSX &lt;code&gt;Terminal&lt;/code&gt; and used &lt;a href="http://en.wikipedia.org/wiki/Dd_(Unix)"&gt;&lt;code&gt;dd&lt;/code&gt;&lt;/a&gt; to write files with increasing
sizes to the TomTom. It consistently disconnected when writing files
larger than 100MB. Writing smaller files with intervening pauses
seemed to work OK. Now that I knew what I &lt;i&gt;could&lt;/i&gt; do, I turned my
attention to what I &lt;i&gt;needed&lt;/i&gt; to do.
&lt;/p&gt;
&lt;p&gt;
I figured out that TomTom Home put the new map files at the path,
&lt;/p&gt;
&lt;pre&gt;
~/Documents/TomTom/HOME/Download/complete/map/USA_and_Canada/
&lt;/pre&gt;
&lt;p&gt;
The directory contained the files,
&lt;/p&gt;



&lt;pre class="example"&gt;$ls -lh 
total 1790632
-rw-r--r--  jcardent  staff   7.7K Apr 13 16:25 USA_and_Canada-1.gif
-rw-r--r--  jcardent  staff   1.6K Apr 13 17:22 USA_and_Canada.gif
-rw-r--r--  jcardent  staff   2.6K Apr 13 17:30 USA_and_Canada.toc
-rw-r--r--  jcardent  staff   874M Apr 13 17:30 USA_and_Canada.zip
-rw-r--r--  jcardent  staff   305B Apr 13 17:22 activation.zip
&lt;/pre&gt;



&lt;p&gt;
Clearly, the file &lt;code&gt;USA_and_Canada.zip&lt;/code&gt; contained the majority of the
data. So I unzipped it and found,
&lt;/p&gt;



&lt;pre class="example"&gt;$ls -lh
total 1790504
-rw-r--r--  jcardent  staff   252K Jan 10 11:05 USA_and_Canada-308.meta
-rwxr-xr-x  jcardent  staff    60B Jan 10 11:05 USA_and_Canada.pna
drwxr-xr-x  jcardent  staff   3.1K Jan 10 11:04 brand
-rwxr-xr-x  jcardent  staff   446M Jan 10 11:04 cline.dat
-rwxr-xr-x  jcardent  staff   113M Jan 10 11:05 cname.dat
-rwxr-xr-x  jcardent  staff   123M Jan 10 11:05 cnode.dat
-rwxr-xr-x  jcardent  staff    23M Jan 10 11:05 cphoneme.dat
-rwxr-xr-x  jcardent  staff    56M Jan 10 11:05 faces.dat
-rwxr-xr-x  jcardent  staff    30B Jan 10 11:05 mapinfo.dat
-rwxr-xr-x  jcardent  staff    92M Jan 10 11:05 poi.dat
-rwxr-xr-x  jcardent  staff    15M Feb 18 10:53 tables.dat
-rwxr-xr-x  jcardent  staff   4.9M Jan 10 11:05 tmccodes.dat
-rwxr-xr-x  jcardent  staff   129B Jan 10 11:05 traffic.dat
&lt;/pre&gt;



&lt;p&gt;
Three files over 100MB, oh bother. But there was no need to fear for I
was armed with &lt;code&gt;dd&lt;/code&gt;. I proceeded to use a command like the following
to copy the large files to the TomTom in 50MB chunks,
&lt;/p&gt;



&lt;pre class="example"&gt;dd if=./&amp;lt;map file&amp;gt; of=/&amp;lt;TomTom path&amp;gt; bs=1024 count=52428800 \
  iseek=&amp;lt;offset&amp;gt; oseek=&amp;lt;offset&amp;gt;
&lt;/pre&gt;



&lt;p&gt;
After an hour or so, all the data was on the TomTom. I disconnected
and rebooted it only to get a "map not authorized" error. After a some
curses, I recalled the other downloaded file, &lt;code&gt;activation.zip&lt;/code&gt;. I
unzipped the file, copied the contents to a couple of places on the
TomTom - I wasn't sure where it belonged - and rebooted. Woot! The
updated map worked!
&lt;/p&gt;
&lt;p&gt;
I'm happy to report that the TomTom worked flawlessly for our
vacation.  
&lt;/p&gt;
&lt;p&gt;
Moral of the lesson, know and use your &lt;code&gt;UNIX&lt;/code&gt; command line tools.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2219192573919297792?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2219192573919297792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2219192573919297792'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/05/tomtom-foolery.html' title='TomTom Foolery'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2905779436475304922</id><published>2011-04-29T06:20:00.001-04:00</published><updated>2011-04-29T06:23:54.671-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataMining'/><title type='text'>Diving into R</title><content type='html'>&lt;p&gt;I've wanted to learn &lt;a href="http://www.r-project.org/"&gt;R&lt;/a&gt; for a long time. A new project at work is
providing an ideal opportunity to finally use it. So far, it's been a
great experience. R is an incredibly powerful tool for data
analysis. It's allowed to me dive deep into the project's data and
automate much of the analysis process.
&lt;/p&gt;
&lt;p&gt;
Programming in R has been easier than expected. I've previously
programmed in Matlab which has helped greatly. Some of the concepts
are still foreign but I'm confident that they will become less so with
time.
&lt;/p&gt;
&lt;p&gt;
The greatest joy has been getting "lost" for hours writing R functions
to analyze the data and produce reports. R's interactive interface has
made it easy to build up code in an exploratory manner. This is my
preferred programming methodology that, I find, allows me to stay in a
&lt;a href="http://en.wikipedia.org/wiki/Flow_(psychology)"&gt;flow state&lt;/a&gt; for long periods of time. The experience has been very
similar to programming in Lisp dialects which I also deeply enjoy.
&lt;/p&gt;
&lt;p&gt;
Although there is a lot of good information about R available for free
on the web, I've found the following O'Reilly books the best resource
for coming up to speed quickly,
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://oreilly.com/catalog/9780596801717/"&gt;R in a Nutshell&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://oreilly.com/catalog/9780596809164/"&gt;R Cookbook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://oreilly.com/catalog/0636920018315/"&gt;25 Recipes for Getting Started with R&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://oreilly.com/catalog/0636920018438/"&gt;Data Mashups in R&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
A particularly powerful library is &lt;a href="http://had.co.nz/ggplot2/"&gt;ggplot2&lt;/a&gt; by Hadley Wickham. With it,
I've been able to create very complex graphs and charts with minimal
code. ggplot2 uses a grammar to create graphics in layers that, at
first, can be challenging to learn. The website is informative but
the book has been the best resource and well worth the money.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/gp/product/0387981403"&gt;ggplot2: Elegant Graphics for Data Analysis (Use R)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Another useful library is &lt;a href="http://cran.r-project.org/web/packages/brew/"&gt;brew&lt;/a&gt; which I am using to auto-generate pleasant
looking reports in PDF via &lt;a href="http://en.wikipedia.org/wiki/LaTeX"&gt;LaTex&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I look forward to working more with R. &lt;a href="http://radar.oreilly.com/2010/06/what-is-data-science.html"&gt;Data science&lt;/a&gt; is a growing
interest of mine and this opportunity to use R is adding to the
momentum.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2905779436475304922?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2905779436475304922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2905779436475304922'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/04/diving-into-r.html' title='Diving into R'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-679028753941282694</id><published>2011-04-11T19:39:00.001-04:00</published><updated>2011-04-11T19:40:35.142-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Book Review: Final Jeopardy</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=CwLSrDUoVB0C&amp;amp;printsec=frontcover&amp;amp;dq=final+jeopardy&amp;amp;hl=en&amp;amp;ei=24WjTe3ILsTagQed9t3aBQ&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;ct=result&amp;amp;resnum=2&amp;amp;ved=0CDQQ6AEwAQ#v=onepage&amp;amp;q&amp;amp;f=false"&gt;Final Jeopardy: Man vs Machine and the Quest to Know Everything&lt;/a&gt; by
&lt;a href="http://thenumerati.net/?catID=8"&gt;Stephen Baker&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I found the Watson exhibition &lt;a href="http://jcardente.blogspot.com/2011/02/watson-wins.html"&gt;very&lt;/a&gt; &lt;a href="http://jcardente.blogspot.com/2011/02/more-watson.html"&gt;exciting&lt;/a&gt;.  I was therefore eager to
read Baker's new book, &lt;i&gt;Final Jeopardy&lt;/i&gt;, that accounts the inception
of IBM's Jeopardy Grand Challenge and the software team that completed
it by creating &lt;a href="http://www-03.ibm.com/innovation/us/watson/index.html"&gt;Watson&lt;/a&gt;. Although light on technical details, the book
provides a good overview of the primary challenges. It also discusses
the non-technical issues that the Watson and Jeopardy teams struggled
with in staging the man-machine competition. Overall, a very good and
enjoyable book. If you enjoyed Baker's Numerati, you'll probably enjoy
this book too. 
&lt;/p&gt;
&lt;p&gt;
The next challenge is to create a computer that can write Jeopardy
questions rather than &lt;i&gt;just&lt;/i&gt; answering them.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-679028753941282694?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/679028753941282694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/679028753941282694'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/04/book-review-final-jeopardy.html' title='Book Review: Final Jeopardy'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3216103165568799832</id><published>2011-04-04T18:35:00.001-04:00</published><updated>2011-04-04T18:37:52.032-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Seymour Cray Videos</title><content type='html'>&lt;p&gt;
I've long admired &lt;a href="http://en.wikipedia.org/wiki/Seymour_Cray"&gt;Seymour Cray&lt;/a&gt; as the genius behind early super
computers such as the &lt;a href="http://en.wikipedia.org/wiki/CDC_6600"&gt;CDC6600&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Cray-1"&gt;Cray-1&lt;/a&gt;, and later Cray
systems. However, I know little about Cray himself. So, I was happy
to discover two YouTube videos of Cray speaking about his career and 
systems.
&lt;/p&gt;
&lt;p&gt;
In &lt;a href="http://www.youtube.com/watch?v=vtOA1vuoDgQ"&gt;this 1976 talk&lt;/a&gt;, Cray describes the design of the Cray-1. Among
other topics, he describes the factors that gave rise to the Cray-1's
&lt;a href="http://en.wikipedia.org/wiki/File:Cray-1_(1).jpg"&gt;iconic shape&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Thirteen years later, Cray discusses the design of the Cray-3 and
Cray-4 systems in &lt;a href="http://www.youtube.com/watch?v=xW7j2ipE2Ck"&gt;this talk&lt;/a&gt; and his decision to use Gallium Arsenide,
then a leading edge material. I wasn't aware of the &lt;a href="http://en.wikipedia.org/wiki/Cray-3#Technology"&gt;three dimensional modules&lt;/a&gt; used in the Cray-3. Cool stuff.
&lt;/p&gt;
&lt;p&gt;
I enjoyed both talks. Cray was much more personable than I
expected. He was very humble and claimed ignorance in a number of
areas related to computing. It was refreshing to see someone of Cray's
caliber display these characteristics.
&lt;/p&gt;
&lt;p&gt;
It was amusing to see that the fundamental problems of building computing
systems have remained the same for decades: speed, size, and power. The 
more things change, the more they stay the same.  
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3216103165568799832?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3216103165568799832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3216103165568799832'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/04/seymour-cray-videos.html' title='Seymour Cray Videos'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3639202918480012446</id><published>2011-03-06T09:14:00.001-05:00</published><updated>2011-03-06T09:16:00.589-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Quants: The Alchemists of Wall Street</title><content type='html'>&lt;p&gt;Last week, I stumbled across a &lt;a href="http://www.youtube.com/watch?v=ed2FWNWwE3I&amp;amp;NR=1"&gt;good documentary&lt;/a&gt; by &lt;a href="http://en.wikipedia.org/wiki/VPRO"&gt;VPRO&lt;/a&gt; on
&lt;a href="http://en.wikipedia.org/wiki/Quantitative_analyst"&gt;quantitative analysts&lt;/a&gt;. It features a couple of famous "quants", &lt;a href="http://en.wikipedia.org/wiki/Paul_Wilmott"&gt;Paul Wilmott&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Emanuel_Derman"&gt;Emanuel Derman&lt;/a&gt;, as well as Michael Osinki &lt;a href="http://nymag.com/news/business/55687/"&gt;who wrote the software&lt;/a&gt; used by many banks to &lt;a href="http://en.wikipedia.org/wiki/Mortgage-backed_security"&gt;securitize mortgages&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The documentary discusses the challenges associated with financial
modeling. For example,
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Many models were based on limited historical data that was
insufficient to represent macro-economic swings.

&lt;/li&gt;
&lt;li&gt;
Many executives did not understand the technical aspects of
financial modeling and were therefore unable to recognize the
associated risks that led to the subprime crisis.
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
I strongly agree with Paul Wilmott on the following (paraphrased)
point,
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;People that take risk should be compensated. But they should not be
compensated for taking risk with other people's money.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
Here here. Wilmott is extremely impressive. When the subprime crisis
hit, I was surprised to find out that he had been warning against
model related risks. Given his high regard in quant circles, I'm
surprised his warnings were not better heeded.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3639202918480012446?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3639202918480012446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3639202918480012446'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/03/quants-alchemists-of-wall-street.html' title='Quants: The Alchemists of Wall Street'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7412415223048507250</id><published>2011-02-26T19:14:00.003-05:00</published><updated>2011-02-26T19:17:20.902-05:00</updated><title type='text'>Commemorating Discovery's Last Launch</title><content type='html'>&lt;p&gt;In commemoration of the &lt;a href="http://en.wikipedia.org/wiki/Space_Shuttle_Discovery"&gt;Space Shuttle Discovery&lt;/a&gt;'s &lt;a href="http://en.wikipedia.org/wiki/STS-133"&gt;last flight&lt;/a&gt;, I
decided to post a link to the &lt;a href="http://www.youtube.com/view_play_list?p=35721A60B7B57386"&gt;YouTube videos&lt;/a&gt; of MIT's Fall 2005
session of &lt;a href="http://ocw.mit.edu/courses/aeronautics-and-astronautics/16-885j-aircraft-systems-engineering-fall-2005/"&gt;Aircraft Systems Engineering&lt;/a&gt; (16.885J). The course was
co-taught by ex-shuttle astronaut &lt;a href="http://en.wikipedia.org/wiki/Jeffrey_Hoffman"&gt;Jeffrey Hoffman&lt;/a&gt; and ex-NASA
official &lt;a href="http://en.wikipedia.org/wiki/Aaron_Cohen_(Deputy_NASA_administrator)"&gt;Aaron Cohen&lt;/a&gt;. It featured many guest speakers from the Shuttle
program who went into a lot of technical detail about the system's
design and operations. 
&lt;/p&gt;
&lt;p&gt;
All of the videos are good but my favorites are,
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=uow6v1EuybE&amp;amp;p=35721A60B7B57386"&gt;Lecture 6&lt;/a&gt;: Space Shuttle Main Engines. 
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=KFOv1WtlAow&amp;amp;p=35721A60B7B57386"&gt;Lecture 15&lt;/a&gt;: Space Shuttle Accidents. An excellent guest lecture by
&lt;a href="http://en.wikipedia.org/wiki/Sheila_Widnall"&gt;Sheila Widnall&lt;/a&gt; describing the investigation of the &lt;a href="http://en.wikipedia.org/wiki/Columbia_space_shuttle_disaster"&gt;Columbia accident&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=OksC02Xqe7Q&amp;amp;p=35721A60B7B57386"&gt;Lecture 16&lt;/a&gt;: Guidance, Navigation, and Control. Great overview of the
Shuttle's computer systems. 
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=Fo8v7juSgRw&amp;amp;p=35721A60B7B57386"&gt;Lecture 17&lt;/a&gt;: Mission Control. Guest lecture by &lt;a href="http://en.wikipedia.org/wiki/Chris_Kraft"&gt;Chris Kraft&lt;/a&gt; who held
prominent positions in Mission Control during the &lt;a href="http://en.wikipedia.org/wiki/Project_Mercury"&gt;Mercury&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Gemini_space_program"&gt;Gemini&lt;/a&gt;,
and &lt;a href="http://en.wikipedia.org/wiki/Apollo_program"&gt;Apollo&lt;/a&gt; programs.
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Whenever I need a hard-core technical fix, I watch one of these
videos. Works every time. These were real engineers. 
&lt;/p&gt;
&lt;p&gt;
Other materials from this course &lt;a href="http://ocw.mit.edu/courses/aeronautics-and-astronautics/16-885j-aircraft-systems-engineering-fall-2005/"&gt;are available&lt;/a&gt; on MIT's OCW website.
&lt;/p&gt;
&lt;p&gt;
Thank you Discovery for twenty seven years of service. It's disappointing
that the space program is returning to rockets. It's just so 20th century. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7412415223048507250?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7412415223048507250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7412415223048507250'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/commemorating-discoverys-last-launch.html' title='Commemorating Discovery&apos;s Last Launch'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-928047498362366863</id><published>2011-02-26T05:48:00.002-05:00</published><updated>2011-02-26T05:49:55.695-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='economics'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Rickards on Global Sovereign Debt</title><content type='html'>&lt;p&gt;It's one thing to read tin-foil hat blog posts about the impending
collapse of the global economy. It's another &lt;a href="http://outerdnn.outer.jhuapl.edu/rethinking/VideoArchives/MrJamesGRickardsPresentationVideo.aspx"&gt;to watch&lt;/a&gt; the same message
from James G. Rickards, a (seemingly) intelligent and credible source.
&lt;/p&gt;
&lt;p&gt;
I liked Rickard's approach of using &lt;a href="http://en.wikipedia.org/wiki/Dynamical_systems_theory"&gt;dynamic systems theory&lt;/a&gt; to analyze
global markets and economies. The technique changes the conversation
from a philosophical one (i.e. &lt;a href="http://en.wikipedia.org/wiki/Keynesian_economics"&gt;Keynesian&lt;/a&gt; vs.  &lt;a href="http://en.wikipedia.org/wiki/Monetarism"&gt;Monetarists&lt;/a&gt; vs. &lt;a href="http://en.wikipedia.org/wiki/Austrian_School"&gt;Austrian&lt;/a&gt;)
to a scientific, empirical one. 
&lt;/p&gt;
&lt;p&gt;
To summarize, Rickards says we're screwed. Decades of deficit spending
and the bailout have led to the accumulation of an unsustainable
amount of debt - no combination of growth and taxes that can satisfy
the liability.  Rickard's makes the point that this debt can't
magically disappear, it has to be flushed from the system either
through default, inflating currency, or debtors and creditors going to
war. This sounds sensational until Rickards explains how this happened
multiple times in the 20th century alone.
&lt;/p&gt;
&lt;p&gt;
Scary stuff but worth watching. Wishful thinking isn't going to fix the
current economic situation. I much prefer a data based approach to 
understanding and solving the problem. I hope Rickard's is wrong but, if
not, I hope someone is listening. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-928047498362366863?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/928047498362366863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/928047498362366863'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/rickards-on-global-sovereign-debt.html' title='Rickards on Global Sovereign Debt'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7502602554657928209</id><published>2011-02-25T06:40:00.000-05:00</published><updated>2011-02-25T06:41:14.485-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Shared Keyboard Madness</title><content type='html'>&lt;p&gt;I'm not alone in my mechanical &lt;a href="http://jcardente.blogspot.com/2010/03/keyboard-madness.html"&gt;keyboard&lt;/a&gt; &lt;a href="http://jcardente.blogspot.com/2011/01/more-keyboard-madness.html"&gt;madness&lt;/a&gt;. &lt;a href="http://arstechnica.com/"&gt;Arstechnica&lt;/a&gt; posted a
&lt;a href="http://arstechnica.com/gadgets/news/2011/02/ask-ars-ergonomic-keyboards-101.ars "&gt;good video&lt;/a&gt; about ergonomic keyboards. Their recommendation? Mechanical
switches and a split layout.
&lt;/p&gt;
&lt;p&gt;
I'm really enjoying the &lt;a href="http://www.kinesis-ergo.com/contoured.htm"&gt;Kinesis Contoured keyboard&lt;/a&gt;. The wrist pain I
was experiencing has disappeared. I only wish I had a second one for
home as it is now uncomfortable to go back to using the MS Natural
Ergonomic - the rubber dome keys require a lot more force.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7502602554657928209?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7502602554657928209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7502602554657928209'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/shared-keyboard-madness.html' title='Shared Keyboard Madness'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3682579188563958626</id><published>2011-02-20T19:49:00.001-05:00</published><updated>2011-02-20T19:49:51.846-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>More Watson</title><content type='html'>&lt;p&gt;There is a lot of good information available online about &lt;a href="http://www-03.ibm.com/innovation/us/watson/"&gt;Watson&lt;/a&gt;. Many
of my friends have wondered about Watson's wagering algorithms so I
was happy to find this &lt;a href="http://ibmresearchnews.blogspot.com/2011/02/watsons-wagering-strategies.html"&gt;blogpost and video&lt;/a&gt; by IBM research. I find
Watson's avatar endearing, &lt;a href="http://www.youtube.com/watch?v=WIKM732oEek"&gt;this video&lt;/a&gt; on its creation was fun to watch. 
&lt;/p&gt;
&lt;p&gt;
Part of me wonders if (read hopes that) Watson will inspire a whole
new generation of &lt;a href="http://en.wikipedia.org/wiki/Artificial_intelligence"&gt;AI&lt;/a&gt; researchers.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3682579188563958626?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3682579188563958626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3682579188563958626'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/more-watson.html' title='More Watson'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5048352733654034866</id><published>2011-02-17T19:41:00.001-05:00</published><updated>2011-02-17T19:44:43.790-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Watson Wins!</title><content type='html'>&lt;p&gt;Well, &lt;a href="http://www-03.ibm.com/innovation/us/watson/"&gt;Watson&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=xm8iUjzgPTg#t=16m27s"&gt;won&lt;/a&gt; Jeopardy!. It's an amazing feat. I look forward to
seeing where IBM takes the technology.
&lt;/p&gt;
&lt;p&gt;
NOVA has made their episode on Watson's creation &lt;a href="http://video.pbs.org/video/1786674622"&gt;free to watch&lt;/a&gt;
online. IBM has posted a &lt;a href="http://www.youtube.com/watch?v=3G2H3DZ8rNc&amp;amp;feature=relmfu"&gt;YouTube video&lt;/a&gt; of a talk by &lt;a href="http://www-943.ibm.com/innovation/us/watson/research-team/dr-david-ferrucci.html"&gt;Dr. David Ferrucci&lt;/a&gt;
on the overarching &lt;a href="http://www.research.ibm.com/deepqa/deepqa.shtml"&gt;DeepQA project&lt;/a&gt;. &lt;a href="http://en.wikipedia.org/wiki/Ken_Jennings"&gt;Ken Jennings&lt;/a&gt;, one of the 
human contestants, gave an entertaining &lt;a href="http://live.washingtonpost.com/jeopardy-ken-jennings.html?hpid=talkbox1"&gt;live Q&amp;amp;A&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
I would love to write software for a system like Watson.  As a kid, I
was fascinated by fictional super-intelligent computers like &lt;a href="http://en.wikipedia.org/wiki/HAL9000"&gt;HAL&lt;/a&gt;,
&lt;a href="http://en.wikipedia.org/wiki/WOPR"&gt;WOPR&lt;/a&gt;, and &lt;a href="http://en.wikipedia.org/wiki/Master_Control_Program_(Tron)#Master_Control_Program"&gt;MCP&lt;/a&gt;. Over the past year, I've rediscovered a strong interest
in data mining and machine learning.  While I've helped develop server
and storage systems capable of hosting such applications, I have not
yet worked with these technologies directly. This is something 
I'm considering as part of my annual career planning. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5048352733654034866?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5048352733654034866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5048352733654034866'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/watson-wins.html' title='Watson Wins!'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6297810985430244399</id><published>2011-02-11T06:18:00.001-05:00</published><updated>2011-02-11T06:19:33.883-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Andrew Lo, Kill the Quants</title><content type='html'>&lt;p&gt;A &lt;a href="http://www.youtube.com/watch?v=DhX0PGG-baI"&gt;good talk&lt;/a&gt; by &lt;a href="http://web.mit.edu/alo/www/"&gt;Andrew Lo&lt;/a&gt;, Director of the MIT &lt;a href="http://mitsloan.mit.edu/lfe/"&gt;Laboratory of Financial Engineering&lt;/a&gt;, on the merit of blaming quantitative methods for the
subprime crisis.
&lt;/p&gt;
&lt;p&gt;
Lo main assertion is that,
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;"blaming quantitative methods for the financial crisis is like blaming
accounting and the real number system for accounting fraud"
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
Instead, he suggests blaming the people that used the methods
inappropriately rather than the methods themselves. From what I've
read of the subprime crisis, I agree.
&lt;/p&gt;
&lt;p&gt;
Lo partially demystifies the subprime crisis by using a simple example
to explain &lt;a href="http://en.wikipedia.org/wiki/Collateralized_debt_obligation"&gt;collateralized debt obligations&lt;/a&gt;. He demonstrates how
pooling loans and securitizing them into new bonds in multiple
traunches can produce both higher and lower quality investments. He
explains that it was securitization that allowed subprime loans to
find their way into low risk funds like pensions and money markets. He
also demonstrates how the method fails when the underlying loans are
highly correlated.
&lt;/p&gt;
&lt;p&gt;
Lo then discusses the crisis preconditions that &lt;a href="http://en.wikipedia.org/wiki/Charles_Perrow"&gt;Charles Perrow&lt;/a&gt;
puts forth in his book "&lt;a href="http://www.amazon.com/dp/0691004129"&gt;Normal Accidents&lt;/a&gt;",
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Complexity
&lt;/li&gt;
&lt;li&gt;
Tight Coupling
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
To this Lo adds an additional precondition,
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The lack of (frequent) negative feedback
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;
Lo asserts that under these conditions, human behavior naturally leads
to crises like the one in 2008. His points greatly reminded of those
made by &lt;a href="http://rick.bookstaber.com/"&gt;Richard Bookstaber&lt;/a&gt; in his book "&lt;a href="http://www.amazon.com/dp/0470393750/"&gt;A Demon of Our Own Design&lt;/a&gt;".  I
found Lo's argument compelling. Without frequent negative feedback,
conditions build to a point where proactively unwinding them becomes
impossible - the cost is too great to actively decide to incur. The
cost inevitably comes but without conscious action.
&lt;/p&gt;
&lt;p&gt;
In closing, a great talk worth watching. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6297810985430244399?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6297810985430244399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6297810985430244399'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/02/andrew-lo-kill-quants.html' title='Andrew Lo, Kill the Quants'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1007778099961924915</id><published>2011-01-15T18:36:00.001-05:00</published><updated>2011-01-15T18:36:45.517-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>More Keyboard Madness</title><content type='html'>&lt;p&gt;Last March, I &lt;a href="http://jcardente.blogspot.com/2010/03/keyboard-madness.html"&gt;posted&lt;/a&gt; about a growing keyboard obsession and decision
to buy a &lt;a href="http://www.google.com/search?q=filco+majestouch+tenkeyless"&gt;Filco Majestouch&lt;/a&gt; mechanical keyboard. Well, the madness
continues. I am now the proud owner of a &lt;a href="http://www.kinesis-ergo.com/contoured.htm"&gt;Kinesis Contoured Advantage&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The Filco is a high quality keyboard. The &lt;a href="http://www.cherrycorp.com/english/switches/key/mx.htm"&gt;Cherry Blue&lt;/a&gt; switches feel
great but are indeed noisy - not ideal for late night hacking at
home. Actually, even day time hacking was frowned upon by my
family. Luckily, I got a closed door office at work which allowed me
to use the Filco all day without driving others crazy.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, I started experiencing wrist pain and the evidence
pointed to using the Filco. The ergonomic people at work set me up
with a keyboard tray to promote better hand positioning. That helped
but I felt "trapped" in the "correct" position. Not only did I still
have wrist pain, I was even more uncomfortable.
&lt;/p&gt;
&lt;p&gt;
Frustrated, I switched back to using a &lt;a href="http://www.microsoft.com/hardware/mouseandkeyboard/productdetails.aspx?pid=043"&gt;Microsoft Natural&lt;/a&gt; keyboard. The
pain subsided but the rubber dome keys felt awful after typing on the
Cherry switches. The extra force required to press the keys was
noticeable and tiring.
&lt;/p&gt;
&lt;p&gt;
I considered pre-ordering a &lt;a href="http://www.trulyergonomic.com/"&gt;Truly Ergonomic&lt;/a&gt; keyboard but I didn't want
to be an early adopter. I'd rather wait until there are many
reviews.
&lt;/p&gt;
&lt;p&gt;
I mentioned in the March post that I've long wanted a Kinesis
Contoured Advantage keyboard. They look really cool but, more
importantly, have many positive reviews online. I decided that it
was finally time to try one.
&lt;/p&gt;
&lt;p&gt;
I've been using the Kinesis for a couple of weeks and really like it.
The Cherry brown switches feel as good as the Filco's blue switches
but without the noise. I really like the thumb keys and use my pinky
fingers much less. I remapped the CTRL and ALT keys to the same
positions on both thumb pads. Using &lt;a href="http://www.gnu.org/software/emacs/"&gt;Emacs&lt;/a&gt; and &lt;a href="http://orgmode.org/"&gt;org-mode&lt;/a&gt; is now easier
and faster. Most importantly, the wrist pain is fading.
&lt;/p&gt;
&lt;p&gt;
It didn't take as long to adjust to the Kinesis layout as I feared.
After a couple of hours, I was typing at my usual rate. Switching back
and forth between the Kinesis and regular keyboards does take some
adjustment but it appears to be getting easier.
&lt;/p&gt;
&lt;p&gt;
The Kinesis may well be my ideal keyboard. Now I'm wondering when I
should consider switching to a &lt;a href="http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard"&gt;Dvorak&lt;/a&gt; layout. A challenge for another
day.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1007778099961924915?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1007778099961924915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1007778099961924915'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/01/more-keyboard-madness.html' title='More Keyboard Madness'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6232300687429371898</id><published>2011-01-02T19:35:00.002-05:00</published><updated>2011-01-02T19:36:19.885-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><title type='text'>Where's Johnny?</title><content type='html'>&lt;p&gt;I've been neglecting this blog lately. Instead of posting, I've been
spending my rare spare time exploring multiple
interests. Specifically, &lt;a href="http://clojure.org/"&gt;clojure&lt;/a&gt;, &lt;a href="http://radar.oreilly.com/2010/06/what-is-data-science.html"&gt;data science&lt;/a&gt;, and &lt;a href="http://en.wikipedia.org/wiki/Quantitative_investing"&gt;quantitative investing&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I'm hoping to combine these interests and post about them in the near
future. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6232300687429371898?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6232300687429371898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6232300687429371898'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2011/01/wheres-johnny.html' title='Where&apos;s Johnny?'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-464043288994050635</id><published>2010-11-11T19:06:00.001-05:00</published><updated>2010-11-11T19:08:10.612-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='History'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Book Review: The Cuckoo's Egg</title><content type='html'>&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/The_Cuckoo's_Egg_(book)"&gt;The Cuckoo's Egg Tracking: a Spy Through the Maze of Computer Espionage&lt;/a&gt; by Cliff Stoll
&lt;/p&gt;
&lt;p&gt;
In 1986, Cliff Stoll's boss asked him to investigate a $0.75
accounting error for the use of their lab's computer. He quickly
discovered that a hacker had penetrated their computer system and was
attempting to do the same to the &lt;a href="http://en.wikipedia.org/wiki/MILNET"&gt;Milnet&lt;/a&gt; computers it was connected
to. The situation quickly cascaded into a multi-national search for a
malicious hacker-spy involving multiple "three letter" government
agencies. At the center of it all was Cliff, an astronomer turned
system administrator turned digital sleuth. In &lt;i&gt;The Cuckoo's Egg&lt;/i&gt;,
Cliff provides a detailed account of his adventure and eventual
success.
&lt;/p&gt;
&lt;p&gt;
I really enjoyed &lt;i&gt;The Cuckoo's Egg&lt;/i&gt;. Although I was vaguely aware of
the story, I only recently learned of the book. I was immediately
hooked and struggled to put it down.
&lt;/p&gt;
&lt;p&gt;
&lt;i&gt;The Cuckoo's Egg&lt;/i&gt; provides a detailed description of 1980's
computing, a subject for which I have an irrational fondness.  It's a
great reminder of how innocent a time that was and how far technology
has come.
&lt;/p&gt;
&lt;p&gt;
Timeless, though, were Cliff's creativity and persistence. I wish more
people today put as much effort into solving problems, even apparently
small ones. Cliff's story remains an inspiration.
&lt;/p&gt;
&lt;p&gt;
Cliffs political retrospection added an unexpected dimension to the
story. His initial mental model of government agents was comic
book-esque. During the pursuit, he gradually realized that they were
just normal people with similar values. Cliff's willingness to alter
his political views was also encouraging.
&lt;/p&gt;
&lt;p&gt;
PDF versions of the book are available &lt;a href="http://projects.autonomy.net.au/ai/chrome/site/resource/ebooks-security/cuckoo_egg.pdf"&gt;online&lt;/a&gt;. The book was also
summarized in the NOVA episode &lt;a href="http://www.youtube.com/watch?v=v1swbLfrP6g"&gt;&lt;i&gt;The KGB, The Computer, and Me&lt;/i&gt;&lt;/a&gt; albeit
without much of the detail that made the story interesting. 
&lt;/p&gt;
&lt;p&gt;
If you like computer history, cyber-security, and mystery stories then
you will likely enjoy &lt;i&gt;The Cuckoo's Egg&lt;/i&gt;.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-464043288994050635?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/464043288994050635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/464043288994050635'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/11/book-review-cuckoos-egg.html' title='Book Review: The Cuckoo&apos;s Egg'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1301007129030900303</id><published>2010-11-05T18:50:00.000-04:00</published><updated>2010-11-05T18:51:41.376-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='History'/><title type='text'>Book Review: Fermat's Enigma</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=uaOQPwAACAAJ"&gt;Fermat's Enigma&lt;/a&gt; by Simon Singh
&lt;/p&gt;
&lt;p&gt;
In 1637, &lt;a href="http://en.wikipedia.org/wiki/Pierre_de_Fermat"&gt;Pierre de Fermat&lt;/a&gt;, a renown amateur-but-genius mathematician,
was reading &lt;a href="http://en.wikipedia.org/wiki/Diophantus"&gt;Diophantus&lt;/a&gt;'s &lt;a href="http://en.wikipedia.org/wiki/Arithmetica"&gt;Arithmetica&lt;/a&gt; and wrote the following margin
note:
&lt;/p&gt;
&lt;p&gt;
I have discovered a truly marvelous proof that it is impossible to
separate a cube into two cubes, or a fourth power into two fourth
powers, or in general, any power higher than the second into two like
powers. This margin is too narrow to contain it.
&lt;/p&gt;
&lt;p&gt;
In other words, although there are many three integer solutions to
&lt;a href="http://en.wikipedia.org/wiki/Pythagorean_theorem"&gt;Pythagoras's Theorem&lt;/a&gt;,
&lt;/p&gt;



&lt;pre class="example"&gt;X^2+Y^2=Z^2
&lt;/pre&gt;



&lt;p&gt;
There are no three integer solutions for higher powers,
&lt;/p&gt;



&lt;pre class="example"&gt; X^3+Y^3=Z^3
 X^4+Y^4=Z^4
....
&lt;/pre&gt;



&lt;p&gt;
Unfortunately, no written record of Fermat's proof has ever been
found. Worse, Fermat had a reputation for pranking his fellow
mathematicians by claiming to have secretly solved impossible
problems. For 350 years, it was uncertain if a proof ever existed. But
that didn't stop a lot of people from trying, including some of the
best mathematicians in history.
&lt;/p&gt;
&lt;p&gt;
In this book, &lt;a href="http://www.simonsingh.net/"&gt;Simon Singh&lt;/a&gt; provides a thorough account of the many
efforts to prove &lt;a href="http://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem"&gt;Fermat's Last Theorem&lt;/a&gt;. Particular attention is given
to &lt;a href="http://en.wikipedia.org/wiki/Andrew_wiles"&gt;Andrew Wiles's&lt;/a&gt; successful solution in 1994, the result of a seven
year solitary effort that shocked the mathematics world.
&lt;/p&gt;
&lt;p&gt;
The book is structured very well. Singh expertly interweaves the
history and mathematics behind Fermat's conjecture in an easy to
understand and engaging manner. Singh describes the complicated
mathematics involved just enough to appreciate Wiles's solution
without going into too much detail. The narrative flows evenly and
holds the reader's attention well.
&lt;/p&gt;
&lt;p&gt;
Wiles's story is incredible. He was first fascinated by Fermat's Last
Theorem as an adolescent. After earning a PhD in mathematics, Wiles
found himself uniquely positioned to pursue a proof. He made the bold
decision to both work in secret and devote all of his time to
developing a proof. For seven years, Wiles worked night and day in
isolation until he finally succeeded. After a fatal flaw was found
during peer review, he spent an additional year fixing the proof.
Wiles's focus, dedication, and determination are truly inspiring. 
&lt;/p&gt;
&lt;p&gt;
&lt;i&gt;Fermat's Last Theorem&lt;/i&gt; is great book if your into math, history, and
solitary geniuses overcoming the odds.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1301007129030900303?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1301007129030900303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1301007129030900303'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/11/book-review-fermats-enigma.html' title='Book Review: Fermat&apos;s Enigma'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3478919154169319265</id><published>2010-09-26T20:10:00.004-04:00</published><updated>2010-09-29T22:04:20.032-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>Saving weblinks to org-mode from Safari</title><content type='html'>&lt;p&gt;Each day, I come across numerous web articles, blog posts, newsgroup
posts, etc, that appear interesting. Often, I discover them while
working on another task. To avoid distraction, I typically save their
links for later review. Sometimes I drag the links to my
desktop. Sometimes I bookmark them in my browser. Sometimes I send
them to myself via email. Sometimes, I post them to my delicious
account. It's time to admit that I need a better process.
&lt;/p&gt;
&lt;p&gt;
In a &lt;a href="http://jcardente.blogspot.com/2010/06/org-mode-hack-tasks-done-last-month.html"&gt;prior post&lt;/a&gt;, I mentioned that I use Emacs's &lt;a href="http://orgmode.org/"&gt;org-mode&lt;/a&gt; to organize
my notes and tasks. I recently setup org-mode's &lt;a href="http://orgmode.org/manual/Capture.html#Capture"&gt;Capture&lt;/a&gt; capability to
&lt;a href="http://zenhabits.net/tips-for-gtds-ubiquitous-capture/"&gt;easily record&lt;/a&gt; the deluge of thoughts that come at random throughout
the day. While reading the documentation, I discovered the solution to
my link saving woes, &lt;a href="http://orgmode.org/worg/org-contrib/org-protocol.php"&gt;&lt;code&gt;org-protocol&lt;/code&gt;&lt;/a&gt;. In particular, the ability to
capture links to an org file directly from a web browser as
demonstrated in this &lt;a href="http://vimeo.com/5662410"&gt;screencast&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Imitating the screencast on &lt;a href="http://www.apple.com/macosx/"&gt;OS X&lt;/a&gt; turned out to be harder than I
expected. So, I thought it worth wile to post my approach. 
&lt;/p&gt;
&lt;p&gt;
To begin, it's worthwhile to understand how &lt;code&gt;org-protocol&lt;/code&gt; captures
work.  &lt;code&gt;org-protocol&lt;/code&gt; is based on &lt;a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server"&gt;Emacs server&lt;/a&gt; which allows
applications to use Emacs for text editing. A common practice is to
have &lt;a href="http://en.wikipedia.org/wiki/Shell_(computing)"&gt;shells&lt;/a&gt; use an already running Emacs instance rather than starting
a new one. This is accomplished by a helper program, &lt;a href="http://www.gnu.org/software/emacs/manual/html_node/emacs/Invoking-emacsclient.html#Invoking-emacsclient"&gt;&lt;code&gt;emacsclient&lt;/code&gt;&lt;/a&gt;, that
communicates with the primary Emacs instance. For &lt;code&gt;org-protocol&lt;/code&gt;
captures, &lt;code&gt;emacsclient&lt;/code&gt; is launched with a specially formatted
argument.
&lt;/p&gt;



&lt;pre class="example"&gt;emacsclient org-protocol:/capture:/tname/http://foo.com
&lt;/pre&gt;



&lt;p&gt;
By &lt;a href="http://www.gnu.org/software/emacs/elisp/html_node/Advising-Functions.html#Advising-Functions"&gt;advising&lt;/a&gt; the &lt;code&gt;server-visit-files&lt;/code&gt; function, &lt;code&gt;org-protocol&lt;/code&gt; detects
such arguments and creates org-mode entries from them. A powerful
&lt;a href="http://orgmode.org/manual/Capture-templates.html#Capture-templates"&gt;template&lt;/a&gt; facility is provided to specify how the argument information
is transformed into an org entry. Multiple templates are supported and
selected by the argument's &lt;code&gt;tname&lt;/code&gt; field. The remainder of the
argument specifies the URL, in this case &lt;code&gt;http://foo.com&lt;/code&gt;, and an
optional note (not shown in the example). 
&lt;/p&gt;
&lt;p&gt;
Templates are specified by &lt;a href="http://en.wikipedia.org/wiki/Emacs_Lisp"&gt;elisp&lt;/a&gt; code like the following,
&lt;/p&gt;



&lt;pre class="src src-lisp"&gt;(setq org-capture-templates
      '((&lt;span style="color: #8a2151;"&gt;"tname"&lt;/span&gt; &lt;span style="color: #8a2151;"&gt;"Link"&lt;/span&gt; entry 
        (file+headline org-default-notes-file &lt;span style="color: #8a2151;"&gt;"Links to Read"&lt;/span&gt;)
        &lt;span style="color: #8a2151;"&gt;"* %a\n %?\n %i"&lt;/span&gt;)))
&lt;/pre&gt;



&lt;p&gt;
This template, called &lt;code&gt;tname&lt;/code&gt;, tells org to save the entry in the
default notes file under the header "Links to Read" with the url as a
sub-heading. This results in something like,
&lt;/p&gt;


&lt;pre class="example"&gt;
* Links to Read
** http://foo.com
&lt;/pre&gt;



&lt;p&gt;
See the &lt;a href="http://orgmode.org/manual/Template-expansion.html#Template-expansion"&gt;Emacs documentation&lt;/a&gt; for all of the template facility's
capabilities and options.
&lt;/p&gt;
&lt;p&gt;
In the screencast, two "tricks" are used to have FireFox call
&lt;code&gt;emacsclient&lt;/code&gt; with the argument needed to capture the present page.
The first is a &lt;a href="http://en.wikipedia.org/wiki/Bookmarklet"&gt;bookmarklet&lt;/a&gt; that creates the appropriate &lt;code&gt;emacsclient&lt;/code&gt;
argument,
&lt;/p&gt;



&lt;pre class="src src-javascript"&gt;javascript:location.href=&lt;span style="color: #8a2151;"&gt;'org-protocol://capture://tname/'&lt;/span&gt;+
      encodeURIComponent(location.href)+&lt;span style="color: #8a2151;"&gt;'/'&lt;/span&gt;+
      encodeURIComponent(document.title)+&lt;span style="color: #8a2151;"&gt;'/'&lt;/span&gt;+
      encodeURIComponent(window.getSelection())
&lt;/pre&gt;



&lt;p&gt;
The second trick takes advantage of the fact that the &lt;code&gt;emacsclient&lt;/code&gt;
argument is formatted as a &lt;a href="http://en.wikipedia.org/wiki/Uniform_Resource_Identifier"&gt;URI&lt;/a&gt;. The topmost component of a URI is
called the &lt;a href="http://en.wikipedia.org/wiki/URI_scheme"&gt;URI scheme&lt;/a&gt;. The standard scheme, &lt;code&gt;http&lt;/code&gt;, represents HTTP
hyperlinks and is handled directly by the browser. Many other URI
schemes exist and most browsers support launching separate programs,
sometimes called URI handlers, to process them.  In the screencast,
FireFox is configured to launch &lt;code&gt;emacsclient&lt;/code&gt; when links with the
&lt;code&gt;org-protocol&lt;/code&gt; scheme are "clicked".
&lt;/p&gt;
&lt;p&gt;
Duplicating this functionality with &lt;a href="http://www.apple.com/safari/"&gt;Safari&lt;/a&gt; on &lt;a href="http://www.apple.com/macosx/"&gt;OSX&lt;/a&gt; turned out to be
harder than expected. In an effort to make things easy, OSX provides
the &lt;a href="http://developer.apple.com/mac/library/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html#//apple_ref/doc/uid/TP30000999-CH201-TP1"&gt;Launch Services&lt;/a&gt; API to automate the registration of URI schemes
and their handlers. Unfortunately, a manual method &lt;a href="http://developer.apple.com/mac/library/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html#//apple_ref/doc/uid/TP30000999-CH202-SW20"&gt;isn't provided&lt;/a&gt; to
specify new URI schemes and handlers. This means Safari can't simply
be told to launch &lt;code&gt;emacsclient&lt;/code&gt; when &lt;code&gt;org-protocol&lt;/code&gt; links are
"clicked".
&lt;/p&gt;
&lt;p&gt;
While searching for a solution, The &lt;a href="http://orgmode.org/worg/index.php"&gt;worg&lt;/a&gt; website led me to the
&lt;a href="http://github.com/claviclaws/org-mac-protocol"&gt;&lt;code&gt;org-mac-protocol&lt;/code&gt;&lt;/a&gt; project. Although promising, &lt;code&gt;org-mac-protocol&lt;/code&gt; uses
AppleScripts executed via a drop down menu. Call me lazy but I really
like the bookmarklet approach. &lt;code&gt;org-mac-protocol&lt;/code&gt; also provides far
more functionality than I was interested in. I like to keep things
simple.
&lt;/p&gt;
&lt;p&gt;
A second look at the Launch Services documentation revealed that two
mechanisms are provided to register URI schemes and handlers. Using a
programmatic API, applications can, at execution time, &lt;a href="http://developer.apple.com/library/mac/#documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCTasks/LSCTasks.html%23//apple_ref/doc/uid/TP30000999-CH203-SW6"&gt;register themselves&lt;/a&gt; as the handler for new URI schemes. Alternatively,
applications &lt;a href="http://developer.apple.com/library/mac/#documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html%23//apple_ref/doc/uid/TP30000999-CH202-SW18"&gt;can include&lt;/a&gt; the URI scheme and handler information in
their &lt;a href="http://en.wikipedia.org/wiki/Bundle_(Mac_OS_X)"&gt;application bundle&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Property_list"&gt;property list&lt;/a&gt;. Of the two approaches, the
property list approach looked like the best to pursue.
&lt;/p&gt;
&lt;p&gt;
Property lists are used by OSX to store application and user
settings. They're essentially &lt;a href="http://en.wikipedia.org/wiki/Objective-c"&gt;Objective-C&lt;/a&gt; objects &lt;a href="http://en.wikipedia.org/wiki/Serialization"&gt;serialized&lt;/a&gt; to
XML. Every OSX application contains a default property list in its
application bundle that the &lt;a href="http://en.wikipedia.org/wiki/Finder_(software)"&gt;Finder&lt;/a&gt; reads after &lt;a href="http://developer.apple.com/mac/library/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html#//apple_ref/doc/uid/TP30000999-CH202-BABEJFCD"&gt;certain events&lt;/a&gt;. While
processing property lists, the Finder will register any URI schemes
and handlers it finds with Launch Services. With this approach, all
that is necessary to register a new URI scheme and handler is to edit
an XML text file.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, modifying &lt;code&gt;Emacs&lt;/code&gt;'s application bundle plist didn't
seem to be an option. I didn't see a way to specify the helper program
&lt;code&gt;emacsclient&lt;/code&gt; as the URI handler.
&lt;/p&gt;
&lt;p&gt;
I knew from prior experience that AppleScripts can be packaged as
application bundles. I reasoned that I could write an AppleScript to
launch &lt;code&gt;emacsclient&lt;/code&gt;, save it as an application bundle, and modify its
property list to register the script as a handler for &lt;code&gt;org-protocol&lt;/code&gt;
URIs. I suspected that this had been done before and a quick google
search led me to &lt;a href="http://stackoverflow.com/questions/2418910/osx-defining-a-new-url-handler-that-points-straight-at-a-python-script"&gt;this stackoverflow thread&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Using &lt;a href="http://www.apple.com/macosx/what-is-macosx/apps-and-utilities.html#scripteditor "&gt;AppleScript Editor&lt;/a&gt; and the stackoverflow thread as an example, I
wrote the following script and saved it as an application bundle
called &lt;code&gt;EmacsClientCapture.app&lt;/code&gt;.
&lt;/p&gt;



&lt;pre class="src src-applescript"&gt;on open location this_URL
   do shell script 
      &lt;span style="color: #8a2151;"&gt;"/Applications/Emacs.app/Contents/MacOS/bin/emacsclient "&lt;/span&gt; 
      &amp;amp; this_URL
end open location
&lt;/pre&gt;



&lt;p&gt;
Next, I edited the script's plist at the path,
&lt;/p&gt;



&lt;pre class="example"&gt;EmacsClientCapture.app/Contents/Info.plist
&lt;/pre&gt;



&lt;p&gt;
And added the following XML elements just before the final
&lt;code&gt;&amp;lt;/dict&amp;gt;&amp;lt;/plist&amp;gt;&lt;/code&gt; tags.
&lt;/p&gt;

&lt;pre class="src src-xml"&gt;&amp;lt;&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;CFBundleIdentifier&amp;lt;/&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;com.mycompany.AppleScript.EmacsClientCapture&amp;lt;/&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;CFBundleURLTypes&amp;lt;/&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;
&amp;lt;&lt;span style="color: #0000ff;"&gt;array&lt;/span&gt;&amp;gt;
  &amp;lt;&lt;span style="color: #0000ff;"&gt;dict&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;CFBundleURLName&amp;lt;/&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;EmacsClientCapture&amp;lt;/&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;CFBundleURLSchemes&amp;lt;/&lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&amp;gt;
    &amp;lt;&lt;span style="color: #0000ff;"&gt;array&lt;/span&gt;&amp;gt;
      &amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;org-protocol&amp;lt;/&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;
    &amp;lt;/&lt;span style="color: #0000ff;"&gt;array&lt;/span&gt;&amp;gt;
  &amp;lt;/&lt;span style="color: #0000ff;"&gt;dict&lt;/span&gt;&amp;gt;
&amp;lt;/&lt;span style="color: #0000ff;"&gt;array&lt;/span&gt;&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
I then moved the application bundle to the &lt;code&gt;/Applications&lt;/code&gt; directory.
This caused the Finder to read the property list and register
&lt;code&gt;EmacsClientCapture&lt;/code&gt; with Launch Services as the handler for the
&lt;code&gt;org-protocol&lt;/code&gt; URI scheme.
&lt;/p&gt;
&lt;p&gt;
I added the bookmarket described above to Safari and viola!  I can now
click on the bookmarklet and save a link to the current page in an
org-mode file. No more disorganization. Of course, it's now easier to
collect distractions but that is a problem for a future post.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3478919154169319265?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3478919154169319265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3478919154169319265'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/09/saving-weblinks-to-org-mode-from-safari.html' title='Saving weblinks to org-mode from Safari'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4547665443695122442</id><published>2010-09-21T18:54:00.001-04:00</published><updated>2010-09-21T18:56:03.423-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Writing'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: On Writing Well</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=R-85PhmkW5gC&amp;amp;printsec=frontcover&amp;amp;dq=on+writing+well&amp;amp;source=bl&amp;amp;ots=YpdPPApne1&amp;amp;sig=jI6VQR3Ka1Spbmn6ybovmxdLuUY&amp;amp;hl=en&amp;amp;ei=beWXTLfOHIOC8gbn8YWYDA&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;ct=result&amp;amp;resnum=4&amp;amp;ved=0CEYQ6AEwAw#v=onepage&amp;amp;q&amp;amp;f=false"&gt;On Writing Well&lt;/a&gt; by William Zinsser
&lt;/p&gt;
&lt;p&gt;
Clarity. Simplicity. Brevity. Humanity. Those are the four attributes
of good writing that Zinsser promotes and teaches in this classic book
on writing.
&lt;/p&gt;
&lt;p&gt;
The book is structured into four parts. Part one, Principles, covers
fundamental topics like simplicity, clutter, style, words, and
usage. In part two, Methods, Zinsser teaches how to structure a
coherent story. Part three, Forms, provides advice on writing
interviews, travel stories, technical articles, business
communications, and other types of writing. Part four, Attitudes,
discusses common feelings and decisions during the writing
process. Throughout the book, Zinsser uses examples to illustrate and
reinforce his points.
&lt;/p&gt;
&lt;p&gt;
I really liked this book and agree strongly with Zinsser's values. A
lot of modern writing is long-winded, complex, and
content-free. Simple and clear writing is not only more effective but
also more enjoyable. I've always tried to use a simple, direct writing
style. I'm eager to apply the book's advice to further improve my
writing.
&lt;/p&gt;
&lt;p&gt;
I was greatly encouraged by many of Zinsser's comments on the writing
process itself. For example, I consider myself a slow writer. I often
spend a lot of time revising my writing to get it "just right". Even
simple things like emails and blog posts seem to take an excessive
amount of time. I frequently feel insecure about this so I was glad to
read the following,
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;Writing is hard work. A clear sentence is no accident. Very few
sentences come out right the first time, or even the third time. 
Remember this in moments of despair. If you find that writing is 
hard, it's because it is hard.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Similarly, I was inspired by the following comment in the chapter
"Enjoyment, Fear, and Confidence",
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;Living is the trick. Writers who write interestingly tend to be men
and women who keep themselves interested. That's almost the whole
point of becoming a writer. I've used writing to give myself an
interesting life and a continuing education. If you write about
subjects you think you would enjoy knowing about, your enjoyment will
show in what you write. Learning is a tonic. 
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
I started this blog to learn by writing about my many interests. I
find that explaining a topic often leads to deeper insights. I hope
that writing and blogging will help me to continue learning and
leading an interesting life.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4547665443695122442?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4547665443695122442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4547665443695122442'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/09/book-review-on-writing-well.html' title='Book Review: On Writing Well'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1737924975212934701</id><published>2010-09-07T18:58:00.001-04:00</published><updated>2010-09-07T19:00:12.671-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>Brain fitness videos</title><content type='html'>&lt;p&gt;The &lt;a href="http://jcardente.blogspot.com/2010/07/thoughts-about-thinking.html"&gt;"Thoughts about thinking"&lt;/a&gt; post motivated me to improve my brain
fitness. To that end, I found the following three talks very
informative.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=IK1nMQq67VI"&gt;Authors@Google: Dr. John Medina&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=UyPrL0cmJRs"&gt;GoogleTechTalks: Think faster focus better and remember more, Rewiring our brain to stay younger&amp;hellip;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=-PA-buwI3q4"&gt;GoogleTechTalks: Nutrients for Better Mental Performance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I really appreciate Google posting internal guest lectures to YouTube
and GoogleVideo. I have watched many of the computer science talks and
learned a lot. It's nice to know that talks on many other valuable
topics are also available.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1737924975212934701?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1737924975212934701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1737924975212934701'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/09/brain-fitness-videos.html' title='Brain fitness videos'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8191683490471046599</id><published>2010-08-22T14:42:00.001-04:00</published><updated>2010-08-22T21:41:13.823-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>Doing less than your best</title><content type='html'>&lt;p&gt;I once learned a valuable lesson - never do well a task that you don't
want to do again.
&lt;/p&gt;
&lt;p&gt;
The situation started out innocently. A task for the project I was
working on needed to get done. I disliked the task but grudgingly did
it for the benefit of the team. I did my best to do the task well with
the expectation that I would be rewarded with more enjoyable
work. Instead, I was asked to &lt;i&gt;keep&lt;/i&gt; doing it. I protested
unsuccessfully and was soon miserable. I couldn't understand how
acting selflessly and doing a good job led to such "punishment".
&lt;/p&gt;
&lt;p&gt;
I discovered that when you do any job well the benefiters want you to
keep doing it. If they're confident you'll produce good results, they
would rather encourage you than find a replacement (who may not do as
well). I eventually realized that I shouldn't have done a good job in
the first place.
&lt;/p&gt;
&lt;p&gt;
Initially, I found this insight disturbing. As a child, I was taught to
&lt;i&gt;always&lt;/i&gt; do my best at &lt;i&gt;everything&lt;/i&gt;. As I grew up, I tried to do my
best at school, sports, hobbies, and part-time jobs. Gradually,
"always doing my best" became a core attribute of my
self-image. Therefore, the notion of consciously doing less than my
best just seemed wrong.
&lt;/p&gt;
&lt;p&gt;
Then I made a discovery - some of my role models consciously do or did
less than their best to avoid undesirable work.
&lt;/p&gt;
&lt;p&gt;
For example, the legendary physicist Richard Feynman discusses
"actively acting irresponsible" in &lt;a href="http://www.youtube.com/watch?v=Br_PJY-LZTw#t=9m0s"&gt;this BBC interview&lt;/a&gt;. He also
cautioned against &lt;a href="http://www.lettersofnote.com/2010/06/you-dont-understand-ordinary-people.html"&gt;administrative roles&lt;/a&gt; in this letter to Stephen
Wolfram. &lt;a href="http://calnewport.com/blog/2008/07/17/bonus-post-how-the-worlds-most-famous-computer-scientist-checks-e-mail-only-once-every-three-months/"&gt;Donald Knuth&lt;/a&gt; and &lt;a href="http://web.mac.com/nealstephenson/Neal_Stephensons_Site/Bad_Correspondent.html"&gt;Neal Stephenson&lt;/a&gt; are well known for being
"bad" at correspondence. On a somewhat related note, Paul Graham warns
in &lt;a href="http://www.paulgraham.com/top.html"&gt;this essay&lt;/a&gt; against distracting thoughts and activities. More
personally, some of my mentors have privately admitted to performing
badly at tasks that they don't want to do.
&lt;/p&gt;
&lt;p&gt;
From these examples I've formulated the following three guidelines:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="http://zenhabits.net/say-no/"&gt;Say "no"&lt;/a&gt; to any undesirable tasks.
&lt;/li&gt;
&lt;li&gt;
If unavoidable, only do an adequate job.
&lt;/li&gt;
&lt;li&gt;
Focus all remaining time on excelling at the work you most want to
do.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Following guidelines 1 and 2 will hopefully provide more time for
pursuing guideline 3. The ideal result is to be so highly valued for
doing work you enjoy that you won't get asked to do anything else -
the opportunity cost for distracting you will be too high.
&lt;/p&gt;
&lt;p&gt;
For myself, I expect a lot of time and practice will be required to
put these guidelines into consistent application. After all, a self-image
can be hard to change. Thankfully, I have role models to encourage me.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8191683490471046599?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8191683490471046599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8191683490471046599'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/08/doing-less-than-your-best.html' title='Doing less than your best'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1032380555922360579</id><published>2010-07-19T18:51:00.003-04:00</published><updated>2010-07-19T18:54:59.497-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>Thoughts about thinking</title><content type='html'>&lt;p&gt;Challenging work assignments, family matters, and home maintenance
projects have kept me very busy lately. Hence, the significant
drop-off in blog posts over the past couple of months. I'm hoping to
correct that soon.
&lt;/p&gt;
&lt;p&gt;
Not surprisingly, I've been thinking a lot about leisure time - in
particular its benefits for creative thinking. My reflections are guided
by two lectures on the topic.
&lt;/p&gt;
&lt;p&gt;
The first is a GoogleTechTalk by Dr. David M. Levy, &lt;a href="http://www.youtube.com/watch%3Fv=KHGcvj3JiGA"&gt;No Time to Think&lt;/a&gt;. In the talk, Dr. Levy asserts that deep contemplation not only
promotes creativity but also provides a sense of calmness and
satisfaction. He further argues that deep thinking cannot be forced
directly - instead we must make ourselves available to it by seeking
out silence, and sanctuary. Dr Levy observes that this requirement for
sanctuary conflicts with modern social pressures to multi-task, remain
in constant communication, and solve issues through repetitive searches
of existing information.
&lt;/p&gt;
&lt;p&gt;
The second is a lecture, &lt;a href="http://www.theamericanscholar.org/solitude-and-leadership/"&gt;Solitude and Leadership&lt;/a&gt;, by &lt;a href="http://en.wikipedia.org/wiki/William_Deresiewicz"&gt;William Deresiewicz&lt;/a&gt; at West Point in 2009. The talk is deeply insightful and
worth reading in its entirety but, for this post, can be fairly
summarized by the following three points:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Original thinking is a core attribute of leadership.
&lt;/li&gt;
&lt;li&gt;
Formulating original thoughts requires long periods of
concentration and distance from the thoughts of others.
&lt;/li&gt;
&lt;li&gt;
Quiet solitude is necessary to do both. 

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Like Dr. Levy, Mr. Deresiewicz laments social pressures to multi-task,
remain in constant communication, and rely on existing information
to solve problems. To quote,
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;Multitasking, in short, is not only not thinking, it impairs your
ability to think. Thinking means concentrating on one thing long
enough to develop an idea about it. Not learning other people’s ideas,
or memorizing a body of information, however much those may sometimes
be useful. Developing your own ideas. In short, thinking for
yourself. You simply cannot do that in bursts of 20 seconds at a time,
constantly interrupted by Facebook messages or Twitter tweets, or
fiddling with your iPod, or watching something on YouTube.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
and,
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;Here’s the other problem with Facebook and Twitter and even
The New York Times. When you expose yourself to those things,
especially in the constant way that people do now—older people as well
as younger people—you are continuously bombarding yourself with a
stream of other people’s thoughts. You are marinating yourself in the
conventional wisdom. In other people’s reality: for others, not for
yourself. You are creating a cacophony in which it is impossible to
hear your own voice, whether it’s yourself you’re thinking about or
anything else.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
Thinking originally about difficult problems is an activity that I
deeply enjoy and find satisfying. Like Dr. Levy and Mr. Deresiewicz, I
reached similar conclusions about distracting activities years ago and
have since guarded my time and attention vigorously. I've often been
teased for my resistance to social media. At times I've felt
self-conscious about this choice, even deficient or outdated. These
talks give me renewed confidence in my choices.
&lt;/p&gt;
&lt;p&gt;
The challenge, as indicated at the outset of this post, is finding the
time to think. A hard task given the pace of modern society and the
high-tech industry in particular.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1032380555922360579?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1032380555922360579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1032380555922360579'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/07/thoughts-about-thinking.html' title='Thoughts about thinking'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5292284057289602137</id><published>2010-06-23T20:11:00.003-04:00</published><updated>2010-07-10T06:38:31.533-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>Org-mode hack: tasks done last month</title><content type='html'>&lt;p&gt;I'm a big fan of Emacs's &lt;a href="http://orgmode.org/"&gt;org-mode&lt;/a&gt;. Over the past year, I've started
using it for everything - tracking tasks, taking notes, and drafting
all my reports, papers, and blog posts. Org-mode is the only
task-tracking software that I've used for more then a week.
&lt;/p&gt;
&lt;p&gt;
At work, I am required to produce a monthly status report. To automate
part of the process, I figured out a way to have org-mode produce a
list of the tasks completed during a specific month. Since I couldn't
find a similar example through a Google search, I thought I would post
my approach for the benefit of others (and as a reminder to myself!).
&lt;/p&gt;
&lt;p&gt;
Below is an example org file containing completed tasks that I'll use
to illustrate the approach. The &lt;a href="http://orgmode.org/org.html#Closing-items"&gt;tracking closed items&lt;/a&gt; feature has
been configured to add a time-stamp when each task is transitioned to
the &lt;code&gt;DONE&lt;/code&gt; state. The header specifies a category, &lt;code&gt;Foo&lt;/code&gt;, that org
will associate with all of the tasks in the file.
&lt;/p&gt;



&lt;pre class="src src-org"&gt;&lt;span style="color: #b12121;"&gt;#+Category: Foo&lt;/span&gt;

&lt;span style="color: #0000ff;"&gt;* &lt;/span&gt;&lt;span style="color: #218a21; font-weight: bold;"&gt;DONE&lt;/span&gt;&lt;span style="color: #0000ff;"&gt; Feed the dog&lt;/span&gt;
   &lt;span style="color: #bb8e8e;"&gt;CLOSED:&lt;/span&gt; &lt;span style="color: #7f007f; text-decoration: underline;"&gt;[2010-04-30 ]&lt;/span&gt;

&lt;span style="color: #0000ff;"&gt;* &lt;/span&gt;&lt;span style="color: #218a21; font-weight: bold;"&gt;DONE&lt;/span&gt;&lt;span style="color: #0000ff;"&gt; Mow the lawn&lt;/span&gt;
   &lt;span style="color: #bb8e8e;"&gt;CLOSED:&lt;/span&gt; &lt;span style="color: #7f007f; text-decoration: underline;"&gt;[2010-05-01 ]&lt;/span&gt;

&lt;span style="color: #0000ff;"&gt;* &lt;/span&gt;&lt;span style="color: #218a21; font-weight: bold;"&gt;DONE&lt;/span&gt;&lt;span style="color: #0000ff;"&gt; Take out the trash&lt;/span&gt;
   &lt;span style="color: #bb8e8e;"&gt;CLOSED:&lt;/span&gt; &lt;span style="color: #7f007f; text-decoration: underline;"&gt;[2010-05-20 ]&lt;/span&gt;

&lt;span style="color: #0000ff;"&gt;* &lt;/span&gt;&lt;span style="color: #218a21; font-weight: bold;"&gt;DONE&lt;/span&gt;&lt;span style="color: #0000ff;"&gt; Pay the bills&lt;/span&gt;
   &lt;span style="color: #bb8e8e;"&gt;CLOSED:&lt;/span&gt; &lt;span style="color: #7f007f; text-decoration: underline;"&gt;[2010-06-01 ]&lt;/span&gt;
&lt;/pre&gt;



&lt;p&gt;
First, configure org-mode's &lt;a href="http://orgmode.org/org.html#Agenda-Views"&gt;agenda&lt;/a&gt; feature and use the &lt;code&gt;C-c [&lt;/code&gt; command
to add the example file to the &lt;a href="http://orgmode.org/manual/Agenda-files.html#Agenda-files"&gt;agenda files list&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
At this point, a list of the tasks completed in May can be produced by
issuing the &lt;a href="http://orgmode.org/org.html#Matching-tags-and-properties"&gt;agenda tag matching command&lt;/a&gt;, &lt;code&gt;C-c a m&lt;/code&gt;, and giving it the
following match string:
&lt;/p&gt;
&lt;pre&gt;
CATEGORY="Foo"+TODO="DONE"+CLOSED&amp;gt;="[2010-05-01]"+CLOSED&amp;lt;="[2010-05-31]"
&lt;/pre&gt;
&lt;p&gt;
This should produce the following list (slightly reformatted to 
fit blog width):
&lt;/p&gt;

&lt;pre class="example"&gt;Headlines with TAGS match: CATEGORY="Foo"+TODO="DONE"\
+CLOSED&amp;gt;="[2010-05-01]"+CLOSED&amp;lt;="[2010-05-31]"
Press `C-u r' to search again with new search string
  Foo:        DONE Mow the lawn
  Foo:        DONE Take out the trash
&lt;/pre&gt;

&lt;p&gt;
Although this works, entering the search string is a cumbersome
task. A better solution would avoid this step. 
&lt;/p&gt;
&lt;p&gt;
Agenda provides a way to define &lt;a href="http://orgmode.org/manual/Custom-agenda-views.html#Custom-agenda-views"&gt;custom commands&lt;/a&gt; that can perform
searches using pre-defined match strings. The following elisp code
defines a custom command that performs the above tag search
automatically.
&lt;/p&gt;



&lt;pre class="src src-elisp"&gt;(setq org-agenda-custom-commands
  `((&lt;span style="color: #8a2151;"&gt;"F"&lt;/span&gt; &lt;span style="color: #8a2151;"&gt;"Closed Last Month"&lt;/span&gt; 
     tags (concat &lt;span style="color: #8a2151;"&gt;"CATEGORY=\"Foo\""&lt;/span&gt;
                  &lt;span style="color: #8a2151;"&gt;"+TODO=\"DONE\""&lt;/span&gt;
                  &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;gt;=\"[2010-05-01]\""&lt;/span&gt;
                  &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;lt;=\"[2010-05-30]\""&lt;/span&gt;)))
&lt;/pre&gt;



&lt;p&gt;
After eval-ing this command, typing &lt;code&gt;C-c a F&lt;/code&gt; will produce the same
list as above without having to enter the match string. This approach
is indeed better but uses a hard-coded match string. An even better
solution would generate the match string based on the current date.
&lt;/p&gt;
&lt;p&gt;
Although the call to &lt;code&gt;concat&lt;/code&gt; in the example above programatically
generates the match string, it does so only when the &lt;code&gt;setq&lt;/code&gt; is
evaluated. If the &lt;code&gt;setq&lt;/code&gt; is in an initialization file
(e.g. &lt;code&gt;~/.emacs&lt;/code&gt;) the match string will get generated based on the
date emacs was started and not the date on which the search is
performed. This could produce erroneous searches when using an Emacs
instance started before the turn of the month. In such cases, the
&lt;code&gt;setq&lt;/code&gt; could be manually re-evaluated to generate the correct match
string but an automatic solution would be best. 
&lt;/p&gt;
&lt;p&gt;
Unfortunately, org doesn't currently support providing a lambda
to generate the match string at search time. For instance, 
this example:
&lt;/p&gt;



&lt;pre class="src src-elisp"&gt;(setq org-agenda-custom-commands
  `((&lt;span style="color: #8a2151;"&gt;"F"&lt;/span&gt; &lt;span style="color: #8a2151;"&gt;"Closed Last Month"&lt;/span&gt; 
     tags
     (&lt;span style="color: #7f007f;"&gt;lambda&lt;/span&gt; ()   
       (concat &lt;span style="color: #8a2151;"&gt;"CATEGORY=\"Foo\""&lt;/span&gt;
               &lt;span style="color: #8a2151;"&gt;"+TODO=\"DONE\""&lt;/span&gt;
               &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;gt;=\"[2010-05-01]\""&lt;/span&gt;
               &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;lt;=\"[2010-05-30]\""&lt;/span&gt;)))))
&lt;/pre&gt;



&lt;p&gt;
produces the error message "Wrong type argument: stringp, &amp;hellip;".
Patching org-mode to support lambdas for match strings is an option
but I prefer to maintain the stock org-mode code. 
&lt;/p&gt;
&lt;p&gt;
Thanks to the near infinite hackability of emacs, it's possible to
extend the stock org mode functionality without modifying it
directly. The below elisp code defines two new interactive
functions that call into org-mode to perform a tag search
for a specific month. 
&lt;/p&gt;



&lt;pre class="src src-elisp"&gt;(&lt;span style="color: #7f007f;"&gt;require&lt;/span&gt; '&lt;span style="color: dark cyan;"&gt;calendar&lt;/span&gt;)

(&lt;span style="color: #7f007f;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;jtc-org-tasks-closed-in-month&lt;/span&gt; (&lt;span style="color: #218a21;"&gt;&amp;amp;optional&lt;/span&gt; month year match-string)
  &lt;span style="color: #8a2151;"&gt;"Produces an org agenda tags view list of the tasks completed 
in the specified month and year. Month parameter expects a number 
from 1 to 12. Year parameter expects a four digit number. Defaults 
to the current month when arguments are not provided. Additional search
criteria can be provided via the optional match-string argument "&lt;/span&gt;
  (interactive)
  (&lt;span style="color: #7f007f;"&gt;let*&lt;/span&gt; ((today (calendar-current-date))
         (for-month (or month (calendar-extract-month today)))
         (for-year  (or year  (calendar-extract-year today))))
    (org-tags-view nil 
          (concat
           match-string
           (format &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;gt;=\"[%d-%02d-01]\""&lt;/span&gt; 
                   for-year for-month)
           (format &lt;span style="color: #8a2151;"&gt;"+CLOSED&amp;lt;=\"[%d-%02d-%02d]\""&lt;/span&gt; 
                   for-year for-month 
                   (calendar-last-day-of-month for-month for-year))))))

(&lt;span style="color: #7f007f;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;jtc-foo-tasks-last-month&lt;/span&gt; ()
  &lt;span style="color: #8a2151;"&gt;"Produces an org agenda tags view list of all the tasks completed
last month with the Category Foo."&lt;/span&gt;
  (interactive)
  (&lt;span style="color: #7f007f;"&gt;let*&lt;/span&gt; ((today (calendar-current-date))
         (for-month (calendar-extract-month today))
         (for-year  (calendar-extract-year today)))
       (calendar-increment-month for-month for-year -1)
       (jtc-org-tasks-closed-in-month 
        for-month for-year &lt;span style="color: #8a2151;"&gt;"CATEGORY=\"Foo\"+TODO=\"DONE\""&lt;/span&gt;)))
&lt;/pre&gt;



&lt;p&gt;
The first function, &lt;code&gt;jtc-org-tasks-closed-in-month&lt;/code&gt;, generates an
appropriate query string and calls the internal org-mode agenda
function &lt;code&gt;org-tags-view&lt;/code&gt;. The function defaults to the current month
but takes optional arguments for the desired month and year. The
function also takes a &lt;code&gt;match-string&lt;/code&gt; argument that can be used to
provide additional match criteria.
&lt;/p&gt;
&lt;p&gt;
The second function, &lt;code&gt;jtc-foo-tasks-last-month&lt;/code&gt;, calculates the prior
month and calls &lt;code&gt;jtc-org-tasks-closed-in-month&lt;/code&gt; with an additional
match string to limit the list to &lt;code&gt;DONE&lt;/code&gt; tasks from the category
&lt;code&gt;Foo&lt;/code&gt;. Executing &lt;code&gt;jtc-foo-tasks-last-month&lt;/code&gt; interactively
automatically produces a list of the tasks closed in the prior month.
For my purposes, this is close enough to the ideal solution. Using the
optional &lt;code&gt;match-string&lt;/code&gt; argument, I can re-use this solution to search
for tasks completed in other categories or with specific tags.
&lt;/p&gt;
&lt;p&gt;
My typical work flow is to &lt;a href="http://orgmode.org/org.html#Archiving"&gt;archive&lt;/a&gt; the closed tasks after my status
report is written. Org-mode's agenda makes this an easy task. First I
mark all of the tasks for a bulk operation by typing &lt;code&gt;m&lt;/code&gt; on each. Then
I perform a bulk archive by typing the command &lt;code&gt;B $&lt;/code&gt;.  This will move
the closed tasks to an archive file, typically a file of the same name
with an added &lt;code&gt;_archive&lt;/code&gt; suffix.
&lt;/p&gt;
&lt;p&gt;
Org-mode is a great productivity tool. Combined with Emacs's
hackability, it's possible to create tools optimized for
your particular work flow. 
&lt;/p&gt;
&lt;p&gt;
&lt;b&gt;Addendum&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
I found that searching on &lt;code&gt;CLOSED&lt;/code&gt; date ranges didn't work in 
org-mode version 6.34a. The problem appears to be fixed in the
6.36c release so be sure to have the right version if you want to
replicate this method. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5292284057289602137?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5292284057289602137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5292284057289602137'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/06/org-mode-hack-tasks-done-last-month.html' title='Org-mode hack: tasks done last month'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6850968449500677684</id><published>2010-05-25T20:08:00.001-04:00</published><updated>2010-05-25T20:10:26.755-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: The Quants</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=TN832aGNmWAC&amp;amp;printsec=frontcover&amp;amp;dq=The+Quants&amp;amp;ei=gBTrS7qwJqeCkAS8yNmACA&amp;amp;cd=1#v=onepage&amp;amp;q&amp;amp;f=false"&gt;The Quants&lt;/a&gt; by Scott Patterson
&lt;/p&gt;
&lt;p&gt;
In &lt;i&gt;The Quants&lt;/i&gt;, Patterson provides an intriguing account of Wall
Street's most successful &lt;a href="http://en.wikipedia.org/wiki/Quantitative_analyst"&gt;quantitative analysts&lt;/a&gt; (aka quants) and the
role they played in the subprime crisis.
&lt;/p&gt;
&lt;p&gt;
The first few chapters introduce the main players and provide a brief
introduction to &lt;a href="http://en.wikipedia.org/wiki/Mathematical_finance"&gt;quantitative finance&lt;/a&gt;. Patterson begins by describing
how &lt;a href="http://www.wilmottwiki.com/wiki/index.php/Thorp,_Edward"&gt;Ed Thorp&lt;/a&gt; applied his mathematics background and experience
pioneering Black Jack card counting techniques to invent various
hedging techniques and start the first arbitrage hedge fund. From
there, Patterson lightly introduces other important concepts like
&lt;a href="http://en.wikipedia.org/wiki/Brownian_motion"&gt;Brownian Motion&lt;/a&gt;, &lt;a href="http://www.investopedia.com/terms/r/randomwalktheory.asp"&gt;Random Walk Theory&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Efficient-market_hypothesis"&gt;Efficient Market Hypothesis&lt;/a&gt;, and
&lt;a href="http://en.wikipedia.org/wiki/Statistical_arbitrage"&gt;statistical arbitrage&lt;/a&gt;. The introduction winds down with the &lt;a href="http://en.wikipedia.org/wiki/October_1987_stock_market_crash"&gt;October, 1987 market crash&lt;/a&gt; which is used as the context to introduce the "&lt;a href="http://en.wikipedia.org/wiki/Fat_tail#Fat_tails_and_risk_estimate_distortions"&gt;fat tail&lt;/a&gt;" contrary point of view personified by &lt;a href="http://en.wikipedia.org/wiki/Benoît_Mandelbrot"&gt;Benoit Mandelbrot&lt;/a&gt;, and
&lt;a href="http://en.wikipedia.org/wiki/Nassim_taleb"&gt;Nassim Nicholas Taleb&lt;/a&gt; - a not so subtle foreshadow.
&lt;/p&gt;
&lt;p&gt;
The "middle" of the book discusses the background, career, and
substantial success of primarily five high-profile quants: Pete
Muller, Ken Griffin, Cliff Asness, Boaz Weinstein, and Jim Simmons.
Other Wall Street personalities are also mentioned but to a lesser
extent. This part of the book more or less establishes that the
above quants are very smart, and very rich. 
&lt;/p&gt;
&lt;p&gt;
The last part of the book provides a blow-by-blow account of the
sub-prime crisis. All of the quants appeared to be caught off guard,
perplexed by the market's "irrational" behavior, and unshure of how to
adjust their models to prevent further losses. Throughout the ordeal,
many of the quants are forced to question the very foundations of
their mathematical models and prior success - was it all just luck?
&lt;/p&gt;
&lt;p&gt;
Over all, I thought this book was OK but felt it tried to cover too
much ground as a quantitative finance primer, homage to quants,
historical account of the sub-prime crisis, and financial
mystery-thriller. Since my interest lies more in the technical
details, I was a disappointed with those portions of the book and
uninterested in the dramatized historical account. Perhaps I simply
had the wrong expectations of the book. 
&lt;/p&gt;
&lt;p&gt;
It's also possible that my expectations were set artificially high by
Poundstone's excellent book &lt;a href="http://jcardente.blogspot.com/2009/10/book-review-fortunes-formula.html"&gt;Fortune's Formula&lt;/a&gt; which provides a
detailed historical and technical account of the events that gave rise
to the quantitative finance industry. If you're interested in this
topic, then I highly recommend Fortune's Formula. 
&lt;/p&gt;
&lt;p&gt;
While reviewing the book to write this review, one passage caught my
eye on page 250 regarding &lt;a href="http://papers.ssrn.com/sol3/papers.cfm?abstract_id=1015987"&gt;a study&lt;/a&gt; performed by MIT Professor Andrew Lo
and his student Amir Khandani:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;There was also the worry about what happened if high-frequency quant
funds, which had become a central cog of the market, helping transfer
risk at lightning speeds, were forced to shut down by extreme
volatility.  "Hedge funds can decide to withdraw liquidity at a
moment's notice," they wrote, "and while this may be benign if it
occurs rarely and randomly, a coordinated withdrawal of liquidity
among an entire sector of hedge funds could have a disastrous
consequences for the viability of the financial system if it occurs at
the wrong time and in the wrong sector."
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
There is &lt;a href="http://www.cnbc.com/id/37190208"&gt;some evidence&lt;/a&gt; indicating that the withdrawal of
high-frequency liquidity was a contributing factor to the &lt;a href="http://en.wikipedia.org/wiki/May_6,_2010_market_event"&gt;May 6, 2010 flash crash&lt;/a&gt;. I doubt the story of the quants is over just yet. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6850968449500677684?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6850968449500677684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6850968449500677684'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/05/book-review-quants.html' title='Book Review: The Quants'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6450166120010635580</id><published>2010-04-14T18:50:00.001-04:00</published><updated>2010-04-14T18:52:44.441-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Book Review: Daemon &amp; FreedomTM</title><content type='html'>&lt;p&gt;&lt;a href="http://thedaemon.com/daemonsynopsis.html"&gt;Daemon&lt;/a&gt; and &lt;a href="http://thedaemon.com/freedomtmsynopsis.html"&gt;FreedomTM&lt;/a&gt; by Daniel Suarez
&lt;/p&gt;
&lt;p&gt;
I haven't enjoyed a science fiction, techno-thriller this much since
reading &lt;a href="http://www.nealstephenson.com/"&gt;Neal Stephenson&lt;/a&gt;'s &lt;a href="http://books.google.com/books?id=PB4S6015DP0C&amp;amp;printsec=frontcover&amp;amp;dq=Stephenson+cryptonomicon&amp;amp;ei=s_WnS-qdFaXmygS0pdjTCA&amp;amp;cd=1#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Cryptonomicon&lt;/a&gt;. I liked Daemon so much that
I finished the sequel, FreedomTM, before I got the chance to write a
review (or do anything else for that matter).
&lt;/p&gt;
&lt;p&gt;
I tried a couple of times to summarize the basic plot without
revealing too much but failed. So I think I'll just say that if you're
into computers, AI, hacking, MMORPG's, augmented reality, sustainable
technologies, and overthrowing corporate social control then you'll
probably like these books. My only criticism is that they are a bit
too graphic in places for my taste (mostly violence but some sex).
&lt;/p&gt;
&lt;p&gt;
One of the most refreshing things about the book is that the author is
an IT specialist so the technology stuff isn't too bogus. In fact,
even the "far-fetched" technology in the book is actually &lt;a href="http://thedaemon.com/daemontech.html"&gt;just an exaggeration of the current state of the art&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Preview chapters are available online for both &lt;a href="http://thedaemon.com/daemonpreview.html"&gt;Daemon&lt;/a&gt; and &lt;a href="http://thedaemon.com/freedomtmpreview.html"&gt;FreedomTM&lt;/a&gt; if
you would like to read a sample before buying. I actually listened to
the audiobook version of both books via iTunes which worked out quite
well - the reader's voices enhanced the overall experience, especially
the Daemon's computer-generated, English-accented female voice.
&lt;/p&gt;
&lt;p&gt;
One of my favorite quotes from the book was:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;"Technology. It is the physical manifestation of the human will. It
began with simple tools. Then came the wheel, and on it goes to this
very day. Civilizations rise and fall based on technological
innovation. Bronze falls to iron. Iron falls to steel. Steel falls to
gunpowder. Gunpowder falls to circuitry."
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
I don't think there is any doubt that circuitry, more specifically
digital information, is becoming the dominant source of power. Why
destroy a nation when you can simply crash its infrastructure and
delete its data? Daemon and FreedomTM certainly drive this point home.
&lt;/p&gt;
&lt;p&gt;
The future suggested by Daemon and FreedomTM is both frightening and
exciting. Although a work of fiction primarily intended to entertain,
I think some valuable lessons and cautions can be drawn from the
story. Good stuff. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6450166120010635580?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6450166120010635580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6450166120010635580'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/04/book-review-daemon-freedomtm.html' title='Book Review: Daemon &amp; FreedomTM'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6202148206123903810</id><published>2010-03-29T18:11:00.002-04:00</published><updated>2010-03-29T18:19:16.794-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>StudyHack's Stretch Churn</title><content type='html'>&lt;p&gt;Although I am no longer a student, I really enjoy reading Cal
Newport's &lt;a href="http://calnewport.com/blog"&gt;StudyHacks blog&lt;/a&gt;. In particular, I like its focus on
achieving success through good &lt;a href="http://calnewport.com/blog/category/tips-time-management-scheduling-productivity/"&gt;time management&lt;/a&gt;, &lt;a href="http://calnewport.com/blog/2009/06/22/on-the-value-of-hard-focus/"&gt;hard focus&lt;/a&gt;, &lt;a href="http://calnewport.com/blog/2010/01/06/the-grandmaster-in-the-corner-office-what-the-study-of-chess-experts-teaches-us-about-building-a-remarkable-life/"&gt;deliberate practice&lt;/a&gt;, and &lt;a href="http://calnewport.com/blog/2008/02/01/the-steve-martin-method-a-master-comedians-advice-for-becoming-famous/"&gt;obtaining outstanding skill&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://calnewport.com/blog/2010/03/15/how-to-become-a-star-grad-student-james-mclurkin-and-the-power-of-stretch-churn/ "&gt;In this post&lt;/a&gt; on &lt;a href="http://people.csail.mit.edu/jamesm/"&gt;James McLurkin&lt;/a&gt;, Cal discusses an interesting concept
called Stretch Churn. Paraphrased from Cal's post:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;b&gt;Stretch Project&lt;/b&gt;: A project that requires a skill you don't have at
the outset. Importantly, a stretch project is hard enough to stretch
your ability but reasonable enough to be completed.
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;Stretch Churn&lt;/b&gt;: The number of stretch projects you complete per
unit time.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The premise is that the higher your stretch churn rate the more likely
you are to obtain the kind of skill required to be a leader in your
chosen field. As the interview with James demonstrates, highly
successful people are adept at maintaining a high stretch churn
rate. I suspect this is one of the underlying attributes of &lt;a href="http://jcardente.blogspot.com/2009/09/book-review-outliers.html"&gt;Outliers&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I think the stretch churn concept is an important insight because it
clarifies how to apply the deliberate practice concept in engineering
and research environments. Instead of working on a single problem over
a long period of time - a common approach in research - the stretch
churn concept suggests that it is better to work on a series of
related, hard-but-achievable projects. In a way, this strikes me as the
agile development model applied to becoming a domain expert.
&lt;/p&gt;
&lt;p&gt;
On a personal level, I found the stretch churn concept interesting for
two reasons. First, it explains why I highly value my advanced
development experience - the very nature of the work has allowed me to
maintain a high stretch churn rate for years. Second, it helped me
realize that if I want to become a real domain expert that I'll have
to more tightly focus my stretch projects so that they build upon each
other. It's a vector math problem - stretch projects in many different
directions result in little change when added together.
&lt;/p&gt;
&lt;p&gt;
I suspect the stretch churn concept will be a valuable addition to my
self-development toolbox.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6202148206123903810?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6202148206123903810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6202148206123903810'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/studyhacks-stretch-churn.html' title='StudyHack&apos;s Stretch Churn'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8663229827704736031</id><published>2010-03-21T10:49:00.002-04:00</published><updated>2010-03-21T11:01:28.676-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 9</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 9 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
A month ago (!), &lt;a href="http://jcardente.blogspot.com/2010/02/recovering-deleted-jpegs-from-fat-file.html"&gt;in part 8&lt;/a&gt;, we looked at the JPEG file format
specification to determine if there was sufficient determinism in the
on-disk layout to allow the recovery of deleted files through
analyzing the residual data in the file system. The answer was mixed:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;b&gt;GOOD&lt;/b&gt;: Uniquely valued markers, discoverable through data
inspection, identify the beginning and type of the segments that
constitute a JPEG file.
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;GOOD&lt;/b&gt;: the metadata segments have a pre-defined size
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;BAD&lt;/b&gt;: the length of the entropy encoded image data is, to the best
of my knowledge, unspecified in the &lt;code&gt;START-OF-SCAN&lt;/code&gt; segment
header. Instead, an &lt;code&gt;END-OF-IMAGE&lt;/code&gt; marker is used to identify
the end of the entropy encoded data. The theory is that this
is done to allow JPEG files to be written as the image is 
processed. 

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Essentially, this means that there is no way to determine through data
inspection the length or location of the clusters containing the encoded
image data. The only clue available is the &lt;code&gt;END-OF-IMAGE&lt;/code&gt; marker at
the end of the entropy encoded data. 
&lt;/p&gt;
&lt;p&gt;
One option is to discover and analyze latent directory entries in the
data area - doing so could provide valuable clues to the start and
length of erased JPEG files. The downsides to this approach are added
complexity (recovering deleted directory entries) and incompleteness
(directory entries for deleted JPEG files may not exist due to reuse).
&lt;/p&gt;
&lt;p&gt;
A simpler approach is to inspect each cluster in the data area to see
if it begins with a &lt;code&gt;START-OF-IMAGE&lt;/code&gt; marker or contains an
&lt;code&gt;END-OF-IMAGE&lt;/code&gt; marker. Any extent of clusters bounded by
&lt;code&gt;START-OF-IMAGE&lt;/code&gt; and &lt;code&gt;END-OF-IMAGE&lt;/code&gt; markers stands a good chance of
being the data for a contiguous JPEG file - the very kind of file
we've been trying to recover in this series. In this post, I'll
implement this simple method and test the results. Follow the "Read
more" think for the rest of the post.
&lt;/p&gt;



&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
I added the following code to the &lt;code&gt;fatrecover&lt;/code&gt; program being developed
alongside these posts to perform the marker scan (excerpted from a
larger procedure).
&lt;/p&gt;



&lt;pre class="src src-c"&gt;&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;JPEG_MARKER_COMMON&lt;/span&gt; (0xFF)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;JPEG_MARKER_SOI&lt;/span&gt;    (0xD8)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;JPEG_MARKER_EOI&lt;/span&gt;    (0xD9)

&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;firstCluster&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;lastCluster&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;clusterIndex&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;byteOffset&lt;/span&gt;;

&lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;lastMarkerType&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;            &lt;span style="color: #b7850a;"&gt;lastMarkerCluster&lt;/span&gt;;

printf(&lt;span style="color: #bb8e8e;"&gt;"Scanning clusters:\n"&lt;/span&gt;);
lastMarkerType    = 0;
lastMarkerCluster = 0;
&lt;span style="color: #7f007f;"&gt;for&lt;/span&gt;(clusterIndex = firstCluster; 
    clusterIndex &amp;lt; lastCluster; 
    clusterIndex++) {

  frClusterRead(pimageInfo, clusterIndex, 
                1, pimageInfo-&amp;gt;tmpBuff);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;N.B. - the following code does not cover the
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;case of markers spanning cluster boundaries.
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Not sure if this is a legal condition for
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;JPEG files. 
&lt;/span&gt;  &lt;span style="color: #7f007f;"&gt;for&lt;/span&gt; (byteOffset = 0; 
       byteOffset &amp;lt; (pimageInfo-&amp;gt;clusterSizeBytes-1);
       byteOffset++) {

    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;N.B. - SOI markers should be in the first two bytes of the
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;cluster. This constraint can be relaxed to defeat
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;attempts to hide images by adding a preamble.
&lt;/span&gt;    &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; ((byteOffset == 0) &amp;amp;&amp;amp;
        (JPEG_MARKER_COMMON == pimageInfo-&amp;gt;tmpBuff[byteOffset]) &amp;amp;&amp;amp;
        (JPEG_MARKER_SOI    == pimageInfo-&amp;gt;tmpBuff[byteOffset+1])) {
      printf(&lt;span style="color: #bb8e8e;"&gt;"SOI cluster: %#08x\n"&lt;/span&gt;,clusterIndex);

      lastMarkerType    = JPEG_MARKER_SOI;
      lastMarkerCluster = clusterIndex;
    }

    &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; ((JPEG_MARKER_COMMON == pimageInfo-&amp;gt;tmpBuff[byteOffset]) &amp;amp;&amp;amp;
        (JPEG_MARKER_EOI    == pimageInfo-&amp;gt;tmpBuff[byteOffset+1])) {
      printf(&lt;span style="color: #bb8e8e;"&gt;"EOI cluster: %#08x  offset: %#x\n"&lt;/span&gt;,
             clusterIndex, byteOffset);

      &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (JPEG_MARKER_SOI == lastMarkerType) {
        printf(&lt;span style="color: #bb8e8e;"&gt;"** CONTIG JPEG? start: %#08X  length: %d\n"&lt;/span&gt;, 
               lastMarkerCluster,
               (clusterIndex-lastMarkerCluster+1));
      } 

      lastMarkerType    = JPEG_MARKER_EOI;
      lastMarkerCluster = clusterIndex;
    }
  }
}
&lt;/pre&gt;



&lt;p&gt;
Processing the post-deletion test disk image from &lt;a href="http://jcardente.blogspot.com/2010/01/recovering-deleted-jpegs-from-fat-file.html"&gt;post 7&lt;/a&gt; with this new
code via the shell command &lt;code&gt;jpgscan&lt;/code&gt; results in:
&lt;/p&gt;



&lt;pre class="example"&gt;$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.6.2
BuildVersion:   10C540

$ wc -l fatrecover.c 
    1431 fatrecover.c

$ make all
gcc -g -Wall fatrecover.c -o fatrecover

$ ./fatrecover frtest2.dmg 

=== FAT RECOVER v0.0 ===

Opening file frtest2.dmg................OK
Reading boot sector.....................OK
Processing boot sector..................OK
Reading root dir........................OK

AT YOUR COMMAND:

&amp;gt;&amp;gt; ls

LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.H..D.         0            .fseventsd  (0x000006)
.H..D.         0              .Trashes  (0x000002)
.H...A      4096            ._.Trashes  (0x000003)
...V.A         0              FRTEST2.  (00000000)

&amp;gt;&amp;gt; fat

FAT TABLE ( KEY: .=free  B=bad  -=used X=last R=reserved)

00000000-0000001F: RXX-XXX.........................
00000020-0000003F: ................................
00000040-0000005F: ................................
00000060-0000007F: ................................
00000080-0000009F: ................................
000000A0-000000BF: ................................
[output omitted for brevity]

&amp;gt;&amp;gt; jpgscan
Scanning clusters:
SOI cluster: 0x000007
EOI cluster: 0x00000c  offset: 0x7f4
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X000007  length: 6&lt;/b&gt;
SOI cluster: 0x00000d
EOI cluster: 0x000013  offset: 0x564
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X00000D  length: 7&lt;/b&gt;
SOI cluster: 0x000014
EOI cluster: 0x00001c  offset: 0x6f4
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X000014  length: 9&lt;/b&gt;
SOI cluster: 0x00001d
EOI cluster: 0x000027  offset: 0x47c
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X00001D  length: 11&lt;/b&gt;
SOI cluster: 0x000028
EOI cluster: 0x000032  offset: 0x7e6
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X000028  length: 11&lt;/b&gt;
SOI cluster: 0x000033
EOI cluster: 0x00003e  offset: 0x195
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X000033  length: 12&lt;/b&gt;
SOI cluster: 0x00003f
EOI cluster: 0x00004a  offset: 0x37d
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X00003F  length: 12&lt;/b&gt;
SOI cluster: 0x00004b
EOI cluster: 0x00005c  offset: 0x27e
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X00004B  length: 18&lt;/b&gt;
SOI cluster: 0x00005d
EOI cluster: 0x000082  offset: 0x6ac
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X00005D  length: 38&lt;/b&gt;
SOI cluster: 0x000083
EOI cluster: 0x0000ac  offset: 0x552
&lt;b style="color:red;"&gt;** CONTIG JPEG? start: 0X000083  length: 42&lt;/b&gt;
&lt;/pre&gt;



&lt;p&gt;
In part 7, we used the &lt;code&gt;contig&lt;/code&gt; command to list the location and size
of the contiguous JPEG files before they were deleted. For convenience,
I copied the information from post 7 below:
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; contig

LAYOUT STATUS OF FILES IN DIR: ROOT

       NAME          1ST      LENGTH    CONTIGUOUS?
 ---------------  --------  --------  -------------
      4.2.05.jpg  0x000083       42     CONTIGUOUS
      4.2.01.jpg  0x00005d       38     CONTIGUOUS
      4.1.06.jpg  0x00004b       18     CONTIGUOUS
      4.1.01.jpg  0x00003f       12     CONTIGUOUS
      4.1.05.jpg  0x000033       12     CONTIGUOUS
      4.1.04.jpg  0x000028       11     CONTIGUOUS
      4.1.02.jpg  0x00001d       11     CONTIGUOUS
      4.1.08.jpg  0x000014        9     CONTIGUOUS
      4.1.07.jpg  0x00000d        7     CONTIGUOUS
      4.1.03.jpg  0x000007        6     CONTIGUOUS
      ._.Trashes  0x000003        2     CONTIGUOUS
&lt;/pre&gt;



&lt;p&gt;
Comparing the &lt;code&gt;contig&lt;/code&gt; and &lt;code&gt;jpgscan&lt;/code&gt; outputs confirms that the &lt;code&gt;CONTIG JPEG?&lt;/code&gt; lines accurately reflect the location and size of the
contiguous JPEG files before they were deleted - success, the simple
marker scan method worked!
&lt;/p&gt;
&lt;p&gt;
The contiguous files discovered by the scan can be recovered manually
using the &lt;code&gt;fatrecover&lt;/code&gt; utility's &lt;code&gt;extract&lt;/code&gt; command that was
implemented in part 7. With a little more effort, we can automate the
recovery of such extents into files for further analysis. This is
fairly straight forward and shouldn't require explanation.
&lt;/p&gt;
&lt;p&gt;
In the next post, we'll take a look at what happens when there are
non-contiguous files in the file system. After that, I'll try to fix
the long file name code from post 5 and wrap up this part of series. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8663229827704736031?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8663229827704736031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8663229827704736031'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/recovering-deleted-jpegs-from-fat-file.html' title='Recovering Deleted JPEGs from a FAT File System - Part 9'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3963402227904841695</id><published>2010-03-19T08:35:00.001-04:00</published><updated>2010-03-19T08:38:17.501-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>Wait a moment...</title><content type='html'>&lt;p&gt;The other day, I posted &lt;a href="http://jcardente.blogspot.com/2010/03/grokking-and-modern-programming.html"&gt;a lament&lt;/a&gt; about modern (popular) programming
being mostly a matter of connecting pre-existing components or
libraries with minimal work. In the post I stated that I didn't find
such work satisfying.
&lt;/p&gt;
&lt;p&gt;
The next day, however, I realized something - I've just spent nearly a
decade in advanced development roles &lt;i&gt;happily&lt;/i&gt; creating prototypes by
modifying and combining pre-existing software components with the
minimal possible work. Spot the inconsistency?
&lt;/p&gt;
&lt;p&gt;
This perplexed me - why did I react this way to &lt;a href="http://reprog.wordpress.com/2010/03/12/programming-books-part-3-programming-the-commodore-64/"&gt;Mike Taylor's post&lt;/a&gt;
when I have enjoyed such prototyping work so much? 
&lt;/p&gt;
&lt;p&gt;
After some thought, I concluded that my prototype work has involved
modifying complex systems. This has required first understanding
enough about each system's design and software to determine the
minimal changes required to implement the desired functionality. So,
although the eventual changes were relatively minor (100s to 10Ks
LOC), along the way I had to obtain a deep understanding to complete
the task. I think this is the material difference between the
prototyping work I've enjoyed and the kind of "library gluing" that I
dislike (and Mike of course!). If true, then there is no inconsistency
after all.
&lt;/p&gt;
&lt;p&gt;
Clearly the issue isn't black-and-white - the amount of work performed
doesn't represent the amount of understanding required. To some degree
this reminds me of the &lt;a href="http://jcardente.blogspot.com/2010/01/book-review-simplicity-cycle.html"&gt;Simplicity Cycle&lt;/a&gt; - after a certain point,
enough understanding is achieved to make the solution simpler, not
more complex. From this perspective, I suspect that the satisfaction
that I - and perhaps others - seek is the result of crossing that
complexity-understanding threshold - this may be the "grokking" point
that was easier to achieve in simpler times (e.g. 8bit programming).
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3963402227904841695?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3963402227904841695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3963402227904841695'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/wait-moment.html' title='Wait a moment...'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3233935426052803852</id><published>2010-03-16T18:47:00.001-04:00</published><updated>2010-03-16T18:51:43.886-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Grokking and Modern Programming</title><content type='html'>&lt;p&gt;For this blog, I try not to repost popular links from high traffic
aggregation sites - odds are you've already seen them. But &lt;a href="http://reprog.wordpress.com/about/"&gt;Mike Taylor&lt;/a&gt;'s comments in &lt;a href="http://reprog.wordpress.com/2010/03/12/programming-books-part-3-programming-the-commodore-64/"&gt;this post&lt;/a&gt; so resonated with me that I felt
compelled to discuss it despite the attention it has received.
&lt;/p&gt;
&lt;p&gt;
While reviewing a classic Commodore64 book, Mike segues into a
follow-up discussion to his &lt;a href="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/"&gt;What Ever Happened to Programming post&lt;/a&gt; 
in which he says:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;So I think this is part of what I was bemoaning in Whatever happened
…: the loss of the total control that we had over our computers back
when they were small enough that everything you needed to know would
fit inside your head.  It’s left me with a taste for grokking systems
deeply and intimately, and that tendency is probably not a good fit
for most modern programming, where you really don’t have time to go in
an learn, say, Hibernate or Rails in detail: you just have to have the
knack of skimming through a tutorial or two and picking up enough to
get the current job done, more or less.  I don’t mean to denigrate
that: it’s an important and valuable skill.  But it’s not one that
moves my soul as Deep Knowing does.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
I found this comment insightful as it made me realize that I have been
possibly struggling with the same thing.
&lt;/p&gt;
&lt;p&gt;
For as long as I can remember, I've felt compelled to Deeply
Know the systems that I work on. Starting with my own adolescent
experiments programming 8bit computers, I've enjoyed diving deep into
the machine to understand its fundamental operation and then using
that knowledge to grok whole-system behaviors. As Mike states in the
post, this was possible to do in the 8bit days due to the simple
machines and books providing all of the necessary information (my tome
was &lt;a href="http://www.scribd.com/doc/25457150/Your-Atari-Computer-A-Guide-to-Atari-400-800-Personal-Computers"&gt;Your Atari Computer&lt;/a&gt; which still sits on my bookshelf as a reminder
of those happy times).
&lt;/p&gt;
&lt;p&gt;
Unfortunately, two things have happened since then - machines have
gotten more complicated and having a deep understanding seems to be
less valued.
&lt;/p&gt;
&lt;p&gt;
The first point is obvious, computers have gotten more complicated on
every level - hardware architectures, operating systems, applications,
and networking. Although the fundamentals can be learned, obtaining
deep expertise in any of these areas requires specialization. 
&lt;/p&gt;
&lt;p&gt;
Having spent most of &lt;a href="http://www.linkedin.com/in/jcardente"&gt;my career&lt;/a&gt; working on large-scale, enterprise
computing and storage systems, I've experienced the leading edge of
this expanding complexity first hand.  &lt;a href="http://jcardente.blogspot.com/2009/11/lucky-career-start.html"&gt;In my first job&lt;/a&gt;, I think I did
pretty well at understanding large portions of that machine but it's
been a losing battle since then. The systems that I now work with are
now so numerous and complex that it's impossible to deeply understand
them all - but I try.
&lt;/p&gt;
&lt;p&gt;
Lately, I've been trying to do more hobby projects to have fun, and
expose myself to domains outside of work.  But in doing so I find that
I quickly run into the second point that Mike's &lt;a href="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/"&gt;What Ever Happened to Programming post&lt;/a&gt; captures well - the hero of modern programming is the
person that can stitch together libraries in the shortest amount of
time in the fewest lines of code. The greatest heroes are those that
can create and launch a startup in &lt;a href="http://thestartupbus.com/about/"&gt;less than 24 hours while on a bus&lt;/a&gt;. That's great but it's not for me - I just don't find this form of
programming satisfying.
&lt;/p&gt;
&lt;p&gt;
I've been overly nostalgic lately, hence recent posts on &lt;a href="http://jcardente.blogspot.com/2010/03/keyboard-madness.html"&gt;clicky keyboards&lt;/a&gt;, &lt;a href="http://jcardente.blogspot.com/2010/02/book-review-tinkertoy-computer.html"&gt;old computer books&lt;/a&gt;, and &lt;a href="http://jcardente.blogspot.com/2010/02/doctors-computer.html"&gt;old computer commercials&lt;/a&gt;. At first,
I thought a looming birthday was to blame but Mike's post has me
thinking that it's really a reaction to the shift in programming. The
kind of work I like to do doesn't seem to be where all the "action" (read
capital investment) is.
&lt;/p&gt;
&lt;p&gt;
It's unreasonable to expect the world to revert back to the way things
were. Therefore, there are two possible reactions:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Deal with it and change with the times
&lt;/li&gt;
&lt;li&gt;
Pick a niche were it is possible to "grok" the system and
specialize. This doesn't mean building everything from scratch but
rather picking a stable domain that allows an accumulated
understanding of as much of the system as I care to know.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditionally I've been &lt;a href="http://jcardente.blogspot.com/2009/06/being-generalist.html"&gt;a generalist&lt;/a&gt; but I must admit that option 2
seems much more attractive than 1. I guess this is something to
reflect on while planning out my future career.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3233935426052803852?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3233935426052803852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3233935426052803852'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/grokking-and-modern-programming.html' title='Grokking and Modern Programming'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6376711688581568776</id><published>2010-03-12T19:50:00.001-05:00</published><updated>2010-03-12T19:52:05.222-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Keyboard Madness</title><content type='html'>&lt;p&gt;Last year, I decided to finally become a competent touch-typist and
better &lt;a href="http://www.gnu.org/software/emacs/"&gt;Emacs&lt;/a&gt; user. An unforeseen result of both decisions is that I am
developing a mild keyboard obsession.
&lt;/p&gt;
&lt;p&gt;
For the past couple of years I have been using a &lt;a href="http://www.microsoft.com/hardware/mouseandkeyboard/productdetails.aspx?pid=043"&gt;Microsoft Natural Ergonomic&lt;/a&gt; keyboard which has been good but lately I've felt the urge
for a change - an irrational desire I'm sure.
&lt;/p&gt;
&lt;p&gt;
Somewhere along the way in considering a new keyboard, I thought it
would be cool to get a mechanical model. Probably due to nostalgia,
the sound of a "clicky" keyboard is just one of those aesthetic things
that makes me think of "real" programming and helps me enter a flow
state.
&lt;/p&gt;
&lt;p&gt;
Not knowing anything about mechanical keyboards, I thought it best to
do some research. Through the power of Google, I found:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://hothardware.com/cs/blogs/mrtg/archive/2009/03/09/mechanical-key-switch-keyboards-demystified.aspx"&gt;this blog post&lt;/a&gt; on mechanical keyboards with a video demonstration of
various models.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=nEDv5eWB9lU"&gt;this video&lt;/a&gt; summarizing the attributes of various mechanical
switches.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.youtube.com/watch?v=02y7rjYYHSE"&gt;many&lt;/a&gt;, &lt;a href="http://www.youtube.com/watch?v=b5CeNunbHto"&gt;many&lt;/a&gt; YouTube videos of people typing on various keyboards

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After reviewing this material, I considered four options:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.kinesis-ergo.com/advantage-features.htm"&gt;Kinesis Advantage Contoured&lt;/a&gt;: well known for its ergonomics and "cool factor". However, the high price (~$300) and unusual
layout made me hesitant.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://pckeyboards.stores.yahoo.net/customizer.html"&gt;Unicomp Customizer 104&lt;/a&gt;: based on the same &lt;a href="http://en.wikipedia.org/wiki/Buckling_spring"&gt;buckling spring&lt;/a&gt; technology as the renown &lt;a href="http://en.wikipedia.org/wiki/Model_M_Keyboard"&gt;IBM Model M&lt;/a&gt; and reasonably priced at $70. I decided against it mainly due to the greater key resistance.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.daskeyboard.com/"&gt;Das Keyboard&lt;/a&gt;: a popular keyboard amongst geeks that uses
&lt;a href="http://www.cherrycorp.com/english/switches/key/mx.htm"&gt;Cherry Blue switches&lt;/a&gt; (tactile, and clicky) and priced at $130. Unfortunately the glossy case reportedly attracts an abnormal amount 
of dust.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.elitekeyboards.com/products.php?sub=filco_keyboards,majestouch_87key&amp;amp;pid=fkbn87mceb"&gt;Filco Majestouch Tenkeyless&lt;/a&gt;: an imported keyboard available with
either Cherry Blue or Cherry Brown (tactile, no click) switches
available from elitekeyboards.com for ~$120.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, I went with the Filco after reading many positive reviews.
While I came close to buying the brown switches, I decided that I
really wanted that aesthetic "clicky" sound of the  blues.
&lt;/p&gt;
&lt;p&gt;
It's only been a couple of days but so far I really like the
Filco. The keys feel smooth and solid - my MacBookPro keyboard just
feels wimpy now. The switches are definitely clicky, this &lt;a href="http://www.youtube.com/watch?v=yRfMdA2oyko&amp;amp;feature=related"&gt;YouTube video&lt;/a&gt; provides a pretty good example. The only negative is that I miss
the ergonomic design of the Microsoft keyboard.
&lt;/p&gt;
&lt;p&gt;
I'm just using the Filco at home for now to avoid driving my cube
neighbors nuts with all the clicking but if things go well perhaps
I'll buy another Filco with the brown switches for work.
&lt;/p&gt;
&lt;p&gt;
Join the mechanical keyboard retro-revolution!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6376711688581568776?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6376711688581568776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6376711688581568776'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/keyboard-madness.html' title='Keyboard Madness'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2818504808140071580</id><published>2010-03-03T19:24:00.001-05:00</published><updated>2010-03-03T19:26:55.462-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Science'/><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><category scheme='http://www.blogger.com/atom/ns#' term='Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>The Kahn Academy</title><content type='html'>&lt;p&gt;About a month ago I discovered &lt;a href="http://www.khanacademy.org/"&gt;The Kahn Academy&lt;/a&gt;. Since then, I've
found myself spending spare moments &lt;a href="http://www.youtube.com/user/khanacademy?blend=1&amp;amp;ob=4"&gt;watching&lt;/a&gt; the video tutorials to
refresh my memory on a number of topics including &lt;a href="http://www.youtube.com/user/khanacademy#grid/user/9ECA8AEB409B3E4F"&gt;finance&lt;/a&gt;, &lt;a href="http://www.youtube.com/user/khanacademy#grid/user/CECDA315A8848B99"&gt;banking&lt;/a&gt;,
the &lt;a href="http://www.youtube.com/user/khanacademy#grid/user/945E4F0ED131E4D1"&gt;credit crisis&lt;/a&gt;, &lt;a href="http://www.youtube.com/user/khanacademy#grid/user/1328115D3D8A2566"&gt;statistics&lt;/a&gt;, &lt;a href="http://www.youtube.com/user/khanacademy#grid/user/FD0EB975BA0CC1E0"&gt;linear algebra&lt;/a&gt;, and &lt;a href="http://www.youtube.com/user/khanacademy?blend=1&amp;amp;ob=4#g/c/AD5B880806EBE0A4"&gt;physics&lt;/a&gt;. Although
short (~10 minutes), the tutorials are very well done and a great
resource.
&lt;/p&gt;
&lt;p&gt;
Evidently, the creator, Salman Kahn, started out making video
tutorials for a geographically distant niece. Unexpectedly, other
people around the world began watching the videos and sending positive
feedback. This motivated Kahn to turn the effort into a non-profit
venture dedicated to providing high-quality, free educational
materials. An inspiring story.
&lt;/p&gt;
&lt;p&gt;
Also impressive is Kahn's simple approach of drawing diagrams on a
"black board" in real-time - very similar to &lt;a href="http://www.thebackofthenapkin.com/"&gt;back-of-the-napkin&lt;/a&gt; visual
brainstorming. Like all good teachers, Kahn's deep understanding
allows him to discuss complex topics in a readily understandable
manner.
&lt;/p&gt;
&lt;p&gt;
If you've got a spare moment, I encourage you to check out a video.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2818504808140071580?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2818504808140071580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2818504808140071580'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/03/kahn-academy.html' title='The Kahn Academy'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6744531578029163842</id><published>2010-02-23T04:28:00.001-05:00</published><updated>2010-02-23T04:30:40.599-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>How the World Will Try to Stop You</title><content type='html'>&lt;p&gt;The other day, I watched a recorded lecture on Google Video by
University of Waterloo Economics Professor &lt;a href="http://economics.uwaterloo.ca/fac-Smith.html"&gt;Larry Smith&lt;/a&gt; entitled "&lt;a href="http://video.google.com/videoplay?docid=-485953455534289791&amp;amp;ei=j89-S_GZOsOBlgeEgq3XCg"&gt;How the World Will Try to Stop You and Your Idea&lt;/a&gt;". The lecture was given
to students interested in entrepreneurship with the goal of advising
them on how to overcome resistance.
&lt;/p&gt;
&lt;p&gt;
The points that I took away from the lecture were:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Entrepreneurs, especially young ones, are often given mixed
messages. They are encouraged to change the world but when they
try to are told that they won't be successful.
&lt;/li&gt;
&lt;li&gt;
The vast majority of people are busy beyond belief. While it may
seem like most are incapable of substantive thought, the fact of the
matter is that they are simply too busy to do so.
&lt;/li&gt;
&lt;li&gt;
Because they are too busy, most people rarely listen closely. This
means that their feedback is very superficial and shouldn't be taken
seriously.
&lt;/li&gt;
&lt;li&gt;
Common criticisms are "it's been tried before" and "it won't work".
It's important to counter these comments with probative questions
like "when was it tried?" and "why not?". A lack of response
indicates a baseless criticism. The rare factual response may
provide useful information to refine the idea.
&lt;/li&gt;
&lt;li&gt;
Young entrepreneurs are often told to get more experience, earn
their "spurs", wait their turn, and suffer a few failures before
starting a venture. This implies that entrepreneurs should get more
degrees, work in a big company for 15 years, and wait until their
forties to start a company. This is nonsense, history is full of
examples of young, inexperienced people changing the world.
&lt;/li&gt;
&lt;li&gt;
Instead of fighting resistance, avoid it. Don't tell anyone what you are
really up to. Keep your own counsel and only tell people what they need 
to know. &lt;i&gt;The best way to change the world is to sneak up on it.&lt;/i&gt;
&lt;/li&gt;
&lt;li&gt;
Ultimately, courage is needed to ignore negative
feedback. Unfortunately, there is no easy way to acquire it.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regrettably, one of the two cameras used to record the lecture had a
faulty audio connection. As a result, parts of the recording are not
understandable. It's a shame that the audio from the working camera
wasn't dubbed into the recording to fix the problem. Regardless, the
talk is still worth listening to for the audio that is understandable.
&lt;/p&gt;
&lt;p&gt;
I found many of Professor Smith's comments insightful and in-line with
my experiences. For example, I've spent the majority of my career in
advanced development and have heard the "it won't work" and "it's been
tried before" feedback almost daily. Professor Smith's talk helped me
realize that I now reflexively ask the "why" and "when" follow up
questions. As he indicated, the criticism is often either outdated or
baseless.
&lt;/p&gt;
&lt;p&gt;
I also often run into the too-busy-to-think dilemma and presently spend
the majority of my time supporting long-running campaigns to gradually
get people to recognize and embrace innovative opportunities. These
campaigns can be a lengthy process but the alternative is rash
decisions based on limited thought which I think causes more harm than
good.
&lt;/p&gt;
&lt;p&gt;
Although I don't like to admit it, I have allowed myself to fall
victim to the "need more experience" feedback more than once -
sometimes it was even self-generated after working alongside extremely
talented colleagues. The result, I now have multiple degrees and 14
years experience working in a large company. The good news is that I
am approaching my forties so perhaps "my time" is nearing! Humor
aside, I now recognize that focus and persistence are often more
important for success than experience. That said, I strongly believe
in continuous self-development and am always eager to obtain new
knowledge and experiences. I think it's a matter of keeping a healthy
balance between humility and hubris.
&lt;/p&gt;
&lt;p&gt;
In summary, I really enjoyed this lecture. Professor Smith is clearly
very thoughtful about many topics and passionate about helping
students - both were inspiring to watch. If you have the time, I
recommend seeing it for yourself.
&lt;/p&gt;
&lt;p&gt;
A few years ago, I had a similar experience with another of Professor
Smith's recorded lectures &lt;a href="http://video.google.com/videoplay?docid=-485953455534289791&amp;amp;ei=j89-S_GZOsOBlgeEgq3XCg&amp;amp;q=larry+smoth#docid=3136634457413762837"&gt;on the potential of expert systems&lt;/a&gt;. That
lecture made a significant impression on me and continues to factor
into my long-term career goals. I'm presently reading Professor
Smith's book on the topic, &lt;i&gt;&lt;a href="http://books.google.com/books?id=5-AIAAAACAAJ&amp;amp;dq=beyond+the+internet&amp;amp;ei=2D-AS4fLFKa8M7n98ekP&amp;amp;cd=2"&gt;Beyond the Internet: how expert systems will truly transform business&lt;/a&gt;&lt;/i&gt;, and look forward to writing its review
in a future post.
&lt;/p&gt;
&lt;p&gt;
In the immortal words of &lt;a href="http://jcardente.blogspot.com/2009/06/book-review-art-of-start.html"&gt;Guy Kawasaki&lt;/a&gt;, don't let the bozos &lt;a href="http://blog.guykawasaki.com/2006/01/the_art_of_inno.html#axzz0gLiGeJFm"&gt;grind you down&lt;/a&gt;!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6744531578029163842?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6744531578029163842'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6744531578029163842'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/02/how-world-will-try-to-stop-you.html' title='How the World Will Try to Stop You'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5724041832459973136</id><published>2010-02-18T08:46:00.004-05:00</published><updated>2010-02-18T08:55:16.405-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 8</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 8 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
In &lt;a href="http://jcardente.blogspot.com/2010/01/recovering-deleted-jpegs-from-fat-file.html"&gt;part 7&lt;/a&gt;, I demonstrated recovering deleted JPEG files through
knowing their pre-deletion location in a FAT file system. In the real
use-case of recovering accidentally deleted files, the locations are
unknown making this approach impossible.
&lt;/p&gt;
&lt;p&gt;
Recovering deleted files without knowing their location requires a
method to find them within the unerased data. In this post, I'll show
how the structure of a JPEG file can be used to do just that. Follow
the read more link for the full discussion.
&lt;/p&gt;


&lt;a name='more'&gt;&lt;/a&gt;

&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;JPEG File Structure&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
Generally speaking, files meant to be processed programmatically
employ some form of deterministic structure. &lt;a href="http://en.wikipedia.org/wiki/File_format#File_structure"&gt;Two common approaches&lt;/a&gt; are
to partition the file into well defined segments or use an embedded
catalog to record the file's contents (similar to a file system
directory).  Regardless of the approach, this deterministic structure
can be used to reconstitute a file from its residual data.
&lt;/p&gt;
&lt;p&gt;
In JPEG's case, the segmentation approach is used. The official JPEG
format is specified in Annex B of the ISO/IEC International Standard
&lt;a href="http://www.w3.org/Graphics/JPEG/itu-t81.pdf"&gt;10918-1&lt;/a&gt; - otherwise known as the &lt;a href="http://en.wikipedia.org/wiki/JPEG_Interchange_Format#JPEG_files"&gt;JPEG Interchange Format&lt;/a&gt; (JIF). The
specification defines:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
a variety of segment types to store metadata, compressed image
data, etc.
&lt;/li&gt;
&lt;li&gt;
the combinations of segment types that form valid JPEG files.
&lt;/li&gt;
&lt;li&gt;
unique two-byte markers used to demarcate segments and other key
aspects of a JPEG file's structure.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purposes of this discussion, the markers are particularly
important.
&lt;/p&gt;
&lt;p&gt;
Every two-byte marker consists of the value &lt;code&gt;0xFF&lt;/code&gt; followed by a
non-zero value representing the marker's type. In some cases, markers
may be proceeded by a series of &lt;code&gt;0xFF&lt;/code&gt; "fill bytes" to meet alignment
requirements - these fill bytes can be ignored. To avoid false markers
in segment payloads (e.g. compressed image data), all non-marker
related &lt;code&gt;0xFF&lt;/code&gt; values must be followed by &lt;code&gt;x0x00&lt;/code&gt; to escape them - the
null bytes should be ignored during processing. This simple marker
scheme allows a JPEG file to be parsed without having to interpret
each constituent segment.
&lt;/p&gt;
&lt;p&gt;
Apparently, the JIF format is rarely used in practice due to its
complexity. Instead, two simplified variants - &lt;a href="http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format"&gt;JFIF&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Exchangeable_image_file_format"&gt;EXIF&lt;/a&gt; - are
commonly used instead. Both JFIF and EXIF utilize JIF's built-in
extension mechanism and marker values.  This means that a program
that understands JIF markers can determine the structure of both JFIF
and EXIF files.
&lt;/p&gt;

&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;An Example&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
To further understand the structure of a JPEG file, let's examine one
of the test image files. Inspecting the first 128 bytes of the file
&lt;code&gt;4.1.01.jpg&lt;/code&gt; using &lt;code&gt;hexdump&lt;/code&gt; results in:
&lt;/p&gt;


&lt;pre&gt;
$ hexdump -n 128 -s 0 -C images/4.1.01.jpg
00000  &lt;b style="color:red;"&gt;ff d8&lt;/b&gt; &lt;b style="color:blue;"&gt;ff e0&lt;/b&gt; 00 10 4a 46  49 46 00 01 01 01 00 48  |......JFIF.....H|
00010  00 48 00 00 &lt;b style="color:red;"&gt;ff db&lt;/b&gt; 00 43  00 03 02 02 03 02 02 03  |.H.....C........|
00020  03 03 03 04 03 03 04 05  08 05 05 04 04 05 0a 07  |................|
00030  07 06 08 0c 0a 0c 0c 0b  0a 0b 0b 0d 0e 12 10 0d  |................|
00040  0e 11 0e 0b 0b 10 16 10  11 13 14 15 15 15 0c 0f  |................|
00050  17 18 16 14 18 12 14 15  14 &lt;b style="color:red;"&gt;ff db&lt;/b&gt; 00 43 01 03 04  |............C...|
00060  04 05 04 05 09 05 05 09  14 0d 0b 0d 14 14 14 14  |................|
00070  14 14 14 14 14 14 14 14  14 14 14 14 14 14 14 14  |................|
&lt;/pre&gt;

&lt;p&gt;
To make the output clearer, the markers have been bolded and color coded. 
&lt;/p&gt;
&lt;p&gt;
First, we see that the file starts off with the marker &lt;code&gt;0xFF_D8&lt;/code&gt;, this
is the Start-of-Image marker (&lt;code&gt;SOI&lt;/code&gt;) that must begin all JPEG
files. &lt;code&gt;SOI&lt;/code&gt; markers stand alone and do not have an associated
segment.
&lt;/p&gt;
&lt;p&gt;
Next comes the marker &lt;code&gt;0xFF_E0&lt;/code&gt; which identifies an &lt;code&gt;APP0&lt;/code&gt; application
specific segment. Application segments are JIF's built-in extension
mechanism to allow application specific information to be embedded
inside JPEG files. The JIF standard reserves 16 markers (&lt;code&gt;0xFF_E0&lt;/code&gt; to
&lt;code&gt;0xFF_EF&lt;/code&gt;) for application segments which are available for general
use - the JIF standard makes no attempt to assign application segments
to specific applications. By convention, JFIF files use an &lt;code&gt;APP0&lt;/code&gt;
segment while EXIF files use an &lt;code&gt;APP1&lt;/code&gt; segment. To guard against other
applications using the same application segments, both JFIF and EXIF
identify themselves by including the 'JFIF' or 'EXIF' ASCII string in
the 4th through 7th bytes of the segment (from the marker's first
byte). In this case, we see that the &lt;code&gt;APP0&lt;/code&gt; segment indeed contains
the string 'JFIF' in bytes 6 through 9.
&lt;/p&gt;
&lt;p&gt;
After the marker, each segment begins with a two-byte length parameter
(excluding the marker). The output above indicates that the &lt;code&gt;APP0&lt;/code&gt; is
&lt;code&gt;0x10&lt;/code&gt; bytes long and sure enough the next marker is found at offset
&lt;code&gt;0x14&lt;/code&gt;. This time the marker &lt;code&gt;0xFF_DB&lt;/code&gt; indicates the beginning of a
quantization table segment (&lt;code&gt;DQT&lt;/code&gt;) that is &lt;code&gt;0x43&lt;/code&gt; bytes long. This is
followed by another &lt;code&gt;DQT&lt;/code&gt; segment at offset &lt;code&gt;0x59&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
Similarly analyzing the remainder of the file reveals the following
markers and segments.
&lt;/p&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;
&lt;caption&gt;&lt;/caption&gt;
&lt;colgroup&gt;&lt;col align="right" /&gt;&lt;col align="right" /&gt;&lt;col align="center" /&gt;&lt;col align="right" /&gt;&lt;col align="right" /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th scope="col"&gt;OFFSET&lt;/th&gt;&lt;th scope="col"&gt;MARKER&lt;/th&gt;&lt;th scope="col"&gt;SEGMENT?&lt;/th&gt;&lt;th scope="col"&gt;LENGTH(B)&lt;/th&gt;&lt;th scope="col"&gt;DESCRIPTION&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0X0000&lt;/td&gt;&lt;td&gt;0XFF_D8 (&lt;code&gt;SOI&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;Start of image&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0X0002&lt;/td&gt;&lt;td&gt;0XFF_E0 (&lt;code&gt;APP0&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0X10&lt;/td&gt;&lt;td&gt;Application segment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0X0014&lt;/td&gt;&lt;td&gt;0XFF_DB (&lt;code&gt;DQT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0X43&lt;/td&gt;&lt;td&gt;Quantization table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0X0059&lt;/td&gt;&lt;td&gt;0XFF_DB (&lt;code&gt;DQT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0X43&lt;/td&gt;&lt;td&gt;Quantization table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x009E&lt;/td&gt;&lt;td&gt;0xFF_C0 (&lt;code&gt;SOF0&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0x11&lt;/td&gt;&lt;td&gt;Start of frame&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x00B1&lt;/td&gt;&lt;td&gt;0xFF_C4 (&lt;code&gt;DHT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0x1D&lt;/td&gt;&lt;td&gt;Huffman table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x00D0&lt;/td&gt;&lt;td&gt;0xFF_C4 (&lt;code&gt;DHT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0x3E&lt;/td&gt;&lt;td&gt;Huffman table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x0110&lt;/td&gt;&lt;td&gt;0xFF_C4 (&lt;code&gt;DHT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0x1B&lt;/td&gt;&lt;td&gt;Huffman table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x012D&lt;/td&gt;&lt;td&gt;0xFF_C4 (&lt;code&gt;DHT&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;0x37&lt;/td&gt;&lt;td&gt;Huffman table&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x0166&lt;/td&gt;&lt;td&gt;0xFF_DA (&lt;code&gt;SOS&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;td&gt;UNSPECIFIED&lt;/td&gt;&lt;td&gt;Start of scan&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x5B7D&lt;/td&gt;&lt;td&gt;0xFF_D9 (&lt;code&gt;EOI&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;End of image&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;br/&gt;
&lt;p&gt;
&lt;b&gt;Houston we have a problem&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
Notice in the table above that the &lt;code&gt;SOS&lt;/code&gt; segment has an unspecified
length. Based on my reading of the JIF specification and other
references, the length of the entropy coded image data in the &lt;code&gt;SOS&lt;/code&gt;
segment is not explicitly specified. Instead, it is terminated by
either an &lt;code&gt;EOI&lt;/code&gt; or other marker. I suspect this was done to allow
encoders to generate JPEG files as images are compressed - in this
case the length of the compressed data is unknown when the &lt;code&gt;SOS&lt;/code&gt; is
started.
&lt;/p&gt;
&lt;p&gt;
This presents a problem for recovering deleted JPEG files as it means
their structure isn't completely deterministic. In the next post, I'll
discuss the limitations this imposes and demonstrate how markers can
be used to recover contiguous deleted files.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5724041832459973136?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5724041832459973136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5724041832459973136'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/02/recovering-deleted-jpegs-from-fat-file.html' title='Recovering Deleted JPEGs from a FAT File System - Part 8'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8040527218636704572</id><published>2010-02-11T18:33:00.000-05:00</published><updated>2010-02-11T18:34:47.189-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>The Doctor's Computer</title><content type='html'>&lt;p&gt;Who knew, &lt;a href="http://en.wikipedia.org/wiki/Dr_Who"&gt;The Doctor&lt;/a&gt; prefers &lt;a href="http://en.wikipedia.org/wiki/Prime_Computer"&gt;Prime Computers&lt;/a&gt;. I love these.
&lt;/p&gt;


&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/iJeu3LCo-6A&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/iJeu3LCo-6A&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8040527218636704572?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8040527218636704572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8040527218636704572'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/02/doctors-computer.html' title='The Doctor&apos;s Computer'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-691783787236694130</id><published>2010-02-11T18:00:00.001-05:00</published><updated>2010-02-11T18:00:00.343-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Book Review: The Tinkertoy Computer</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=gDFvQgAACAAJ&amp;amp;dq=tinkertoy+computer&amp;amp;ei=vTtzS5mmIIe4ywTihsilBQ&amp;amp;cd=1"&gt;The Tinkertoy Computer&lt;/a&gt; by A.K. Dewdney.
&lt;/p&gt;
&lt;p&gt;
After reading &lt;a href="http://jcardente.blogspot.com/2009/12/book-review-planiverse.html"&gt;The Planiverse&lt;/a&gt;, I picked up a couple of Dewdney's other
books. Published in 1993, this book is a compilation of some of his
articles from the periodicals &lt;i&gt;&lt;a href="http://en.wikipedia.org/wiki/Scientific_American"&gt;Scientific American&lt;/a&gt;&lt;/i&gt; and &lt;i&gt;Algorithm&lt;/i&gt;.
&lt;/p&gt;
&lt;p&gt;
The book contains 23 chapters organized into four themes. Of them, the following
were my favorites:
&lt;/p&gt;
&lt;p&gt;
&lt;b&gt;Theme One: Matter Computes&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
Chapter 1: The Tinkertoy computer - an account of a
tinkertoy computer built by &lt;a href="http://en.wikipedia.org/wiki/Daniel_Hillis"&gt;Daniel Hillis&lt;/a&gt; and others at MIT designed
to play Tic-Tac-Toe.
&lt;/li&gt;
&lt;li&gt;
Chapter 2: The Rope-and-Pulley Wonder - methods for
building boolean logic blocks out of ropes and pulleys.
&lt;/li&gt;
&lt;li&gt;
Chapter 6: Dance of the Tur-mites - Turing machines and cellular
automata.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Theme Two: Matter Misbehaves&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
Chapter 8: Star Trek Dynamics - implementation details of a 1970s
era computer game.
&lt;/li&gt;
&lt;li&gt;
Chapter 9: Weather in a Jar - the &lt;a href="http://en.wikipedia.org/wiki/Lorenz_attractor"&gt;Lorenz attractor&lt;/a&gt; and simple
models demonstrating the behavior.
&lt;/li&gt;
&lt;li&gt;
Chapter 11: Designer Fractals - &lt;a href="http://en.wikipedia.org/wiki/Iterated_function"&gt;iterated function systems&lt;/a&gt; and their
use for creating fractal patterns.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Theme Three: Mathematics Matters&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
Chapter 13: Mathematical Morsels - a summary of mathematical puzzles
by Ross Honsberger.
&lt;/li&gt;
&lt;li&gt;
Chapter 14: Golygon City - an introduction to &lt;a href="http://en.wikipedia.org/wiki/Golygon"&gt;golygons&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
Chapter 15: Scanning the Cat - a simple algorithm for reconstructing
2-D images from 1-D shadows.
&lt;/li&gt;
&lt;li&gt;
Chapter 16: Rigid Thinking - &lt;a href="http://en.wikipedia.org/wiki/Structural_rigidity"&gt;rigidity theory&lt;/a&gt;, and flexible nonconvex
surfaces.
&lt;/li&gt;
&lt;li&gt;
Chapter 17: Automated Math - an algorithm for determining the rules
for numerical sequences.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Theme Four: Computers Create&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
Chapter 19: Chaos in A Major - the use of &lt;a href="http://en.wikipedia.org/wiki/Logistic_map"&gt;logistic maps&lt;/a&gt; to create
chaotic music.
&lt;/li&gt;
&lt;li&gt;
Chapter 23: Latticeworks by Hand - algorithms for creating lattices.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can tell, I more or less enjoyed the entire book. This wasn't
surprising given that I bought the book because it touched upon a number
of topics that I find very interesting: complexity theory, fractals,
math puzzles, physics simulations, geometric patterns, and algorithms.
&lt;/p&gt;
&lt;p&gt;
I was, however, pleasantly surprised by the nostalgia invoked by the
simple programs presented in the book. The short, simple BASIC
programs reminded me of the many, many hours I spent programming 8bit
computers during my adolescence. I still recall the feeling of awe
that resulted from seeing simple programs like these produce seemingly
"magical" results. Computing was much simpler then but no less
rewarding - hopefully short but powerful programs like these aren't
becoming a lost art.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-691783787236694130?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/691783787236694130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/691783787236694130'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/02/book-review-tinkertoy-computer.html' title='Book Review: The Tinkertoy Computer'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6646536597909510919</id><published>2010-02-06T06:29:00.003-05:00</published><updated>2010-02-06T06:52:41.951-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Learning'/><category scheme='http://www.blogger.com/atom/ns#' term='History'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Dynabook reflections</title><content type='html'>&lt;p&gt;I'm sure like many others, the recent iPad excitement has led me to
think more about &lt;a href="http://en.wikipedia.org/wiki/Alan_Kay"&gt;Alan Kay&lt;/a&gt;'s &lt;a href="http://en.wikipedia.org/wiki/Dynabook"&gt;Dynabook&lt;/a&gt; concept.
&lt;/p&gt;
&lt;p&gt;
Some think the Dynabook is just a highly portable computer such as a
conventional laptop or tablet computer. Kay, however, had a much
grander vision - the Dynabook was to be an easy to use and program
"instrument" that would allow "children of all ages" to learn experientially through simulation.
&lt;/p&gt;
&lt;p&gt;
Kay and Goldberg's 1977 article, &lt;a href="http://portal.acm.org/citation.cfm?id=1301186"&gt;Personal Dynamic Media&lt;/a&gt;, briefly
discusses the Dynabook as a learning device and their successful
results from letting children use the "Interim Dynabook" - otherwise
known as the &lt;a href="http://en.wikipedia.org/wiki/Xerox_Alto"&gt;Xerox PARC Alto&lt;/a&gt;. Kay explores this topic in greater depth
in his talk &lt;a href="http://video.google.com/videoplay?docid=-533537336174204822"&gt;Doing With Images Makes Symbols&lt;/a&gt;. &lt;a href="http://www.rheingold.com/"&gt;Howard Rheingold&lt;/a&gt;
discusses the Dynabook's history and potential as a "fantasy
amplifier" in greater depth in &lt;a href="http://www.rheingold.com/texts/tft/11.html"&gt;Chapter 11&lt;/a&gt; of his book &lt;i&gt;&lt;a href="http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&amp;amp;tid=3784"&gt;Tools for Thought&lt;/a&gt;&lt;/i&gt; (a great book, available for free online &lt;a href="http://www.rheingold.com/texts/tft/"&gt;here&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;
Kay conceived of the Dynabook in 1968 based on a number of influences
including:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Vannevar Bush's &lt;a href="http://en.wikipedia.org/wiki/Memex"&gt;Memex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
Ivan Sutherland's &lt;a href="http://en.wikipedia.org/wiki/Sketchpad"&gt;Sketchpad&lt;/a&gt; (&lt;a href="http://www.youtube.com/watch?v=mOZqRJzE8xg&amp;amp;feature=related"&gt;video&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;
Douglas Engelbart's &lt;a href="http://www.dougengelbart.org/pubs/augment-3906.html"&gt;Augmenting Human Intellect&lt;/a&gt; research and ground
breaking &lt;a href="http://en.wikipedia.org/wiki/NLS_(computer_system)"&gt;NLS&lt;/a&gt; project (&lt;a href="http://video.google.com/videoplay?docid=-533537336174204822#docid=-8734787622017763097s"&gt;video of the famous demo&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Seymour_Papert"&gt;Seymour Papert&lt;/a&gt;'s use of &lt;a href="http://en.wikipedia.org/wiki/Logo_(programming_language)"&gt;Logo&lt;/a&gt; to teach children mathematics (&lt;a href="http://www.youtube.com/watch?v=xMzojQFyMo0"&gt;video&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;
The learning theories of &lt;a href="http://en.wikipedia.org/wiki/Jerome_Bruner"&gt;Jerome Bruner&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Jean_Piaget"&gt;Jean Piaget&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://www.youtube.com/watch?v=tQg4LquY0uU#t=13m"&gt;In his talk&lt;/a&gt; at the &lt;a href="http://www.computerhistory.org/"&gt;Computer History Museum&lt;/a&gt;'s 40th Anniversary of the
Dynabook event, Kay does a great job of describing how these works
influenced his thinking. 
&lt;/p&gt;
&lt;p&gt;
Kay's pursuit of the Dynabook produced many significant innovations
like &lt;a href="http://en.wikipedia.org/wiki/Object-oriented_programming"&gt;Object Oriented Programming&lt;/a&gt; and the overlapping window &lt;a href="http://en.wikipedia.org/wiki/Graphical_user_interface"&gt;graphical user interface&lt;/a&gt;. It has also been the motivation behind his support of
efforts like the &lt;a href="http://wiki.laptop.org/go/One_Laptop_per_Child"&gt;One Laptop Per Child&lt;/a&gt; program, &lt;a href="http://www.squeak.org/"&gt;Squeak project&lt;/a&gt;, and
&lt;a href="http://wiki.laptop.org/go/Etoys"&gt;EToys&lt;/a&gt; learning environment.
&lt;/p&gt;
&lt;p&gt;
Modern technology now makes it possible to build portable computers
that strongly resemble the Dynabook's physical form - the iPad is a
great example. However, I think little progress has been made towards
creating software that realizes the fantasy amplifier vision.
&lt;/p&gt;
&lt;p&gt;
Kay is famous for saying that the &lt;a href="http://video.google.com/videoplay?docid=-2950949730059754521#"&gt;computer revolution hasn't happened yet&lt;/a&gt;. I tend to agree - current computing seems almost primitive
compared to the work cited above. I suspect the vast majority of
computers are used today as mechanistic productivity enhancers (office
apps), communicators of trivialities (tweets?), and sensory
overloading distractions (games, video, etc).
&lt;/p&gt;
&lt;p&gt;
The iPad will certainly be used for the same purposes - in fact Job's
specifically profiled these use-cases in his keynote speech. However I
think the iPad has the greatest potential in the education
space. Transforming textbooks into eBooks is only the first step - the
next is to add interactive learning aids like simulations for
exploratory experimentation. If (when?) this happens, the Dynabook
will finally have come to life.
&lt;/p&gt;
&lt;p&gt;
Perhaps it's time to download the &lt;a href="http://www.apple.com/ipad/sdk/"&gt;iPad SDK&lt;/a&gt;, watch &lt;a href="http://www.stanford.edu/class/cs193p/cgi-bin/drupal/"&gt;Stanford's iPhone Development class&lt;/a&gt; on iTunes, and lend a hand in making the revolution
happen.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6646536597909510919?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6646536597909510919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6646536597909510919'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/02/dynabook-reflections.html' title='Dynabook reflections'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3068132162643581303</id><published>2010-01-28T18:18:00.000-05:00</published><updated>2010-01-28T18:22:57.382-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 7</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 7 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
After a hiatus due to the holidays, personal matters, and &lt;a href="http://jcardente.blogspot.com/2010/01/new-year-new-role.html"&gt;work-stuff&lt;/a&gt;
I'm ready to continue the &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;FAT Recover&lt;/a&gt; project. In this post I'll
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
finally demonstrate the two principal assumptions that this project
is based on.
&lt;/li&gt;
&lt;li&gt;
actually recover deleted files using a manual approach.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow the "read more" link for the detailed discussion.
&lt;/p&gt;
&lt;p&gt;
But first a mea culpa - I've discovered that the code for long file
name support in &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_24.html"&gt;post 5&lt;/a&gt; is utterly broken. The code works for files
that only use a &lt;i&gt;single&lt;/i&gt; long file name entry but does not correctly
process file names spanning &lt;i&gt;multiple&lt;/i&gt; entries. For now, I'll side-step
the issue and post a fix at a later time. I'll also update &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_24.html"&gt;post 5&lt;/a&gt; to
warn future readers. Apologies for the error - that's the danger of
hacking in the &lt;a href="http://en.wiktionary.org/wiki/wee_hours"&gt;wee hours&lt;/a&gt; and minimal unit testing. 
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
By this point, it's likely apparent that this project is based on two
assumptions:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Deleting a file from a FAT file system often does not erase the
associated data.
&lt;/li&gt;
&lt;li&gt;
In simple cases - like a digital camera saving pictures on a fresh
memory card - files are stored contiguously in FAT file systems.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To test if these assumptions hold, let's create and analyze a new disk
image with multiple JPEG files.
&lt;/p&gt;
&lt;p&gt;
Expanding on the method from &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_14.html"&gt;post 2&lt;/a&gt;, the following shell script
creates a new disk image and copies to it ten images from the test
corpus.
&lt;/p&gt;



&lt;pre class="src src-bash"&gt;#!/bin/bash

IMAGENAME=frtest2
VOLNAME=FRTEST2
VOLSIZE='-megabytes 16'
MOUNTPATH=/Volumes/${VOLNAME}
TESTPATH=/Users/jcardent/projects/fatrecover2/test/images
TESTFILES=`ls -1Sr ${TESTPATH}/4*.jpg | head -10`

hdiutil create \
         -fs &lt;span style="color: #bb8e8e;"&gt;"MS-DOS FAT16"&lt;/span&gt; \
         ${VOLSIZE} \
         -layout NONE \
         -volname ${VOLNAME} ${IMAGENAME}

hdiutil attach ${IMAGENAME}.dmg

for file in $TESTFILES
do
    echo &lt;span style="color: #bb8e8e;"&gt;"Copying file "&lt;/span&gt; ${file}
    cp ${file} ${MOUNTPATH}
done  

hdiutil detach /Volumes/${VOLNAME}
&lt;/pre&gt;



&lt;p&gt;
Mounting the image and listing the root directory confirms that ten
image files were copied to the encapsulated FAT file system.
&lt;/p&gt;



&lt;pre class="example"&gt;$ hdiutil attach frtest2.dmg 
/dev/disk1                      /Volumes/FRTEST2

$ls -ao /Volumes/FRTEST2/
total 712
drwxrwxrwx  1 jcardent  16384 Jan 26 07:18 .
drwxrwxrwt@ 4 root        136 Jan 26 07:18 ..
drwxrwxrwx@ 1 jcardent   2048 Jan 26 07:18 .Trashes
-rwxrwxrwx  1 jcardent   4096 Jan 26 06:30 ._.Trashes
drwxrwxrwx  1 jcardent   2048 Jan 26 07:18 .fseventsd
-rwxrwxrwx  1 jcardent  23423 Jan 26 06:30 4.1.01.jpg
-rwxrwxrwx  1 jcardent  21630 Jan 26 06:30 4.1.02.jpg
-rwxrwxrwx  1 jcardent  12278 Jan 26 06:30 4.1.03.jpg
-rwxrwxrwx  1 jcardent  22504 Jan 26 06:30 4.1.04.jpg
-rwxrwxrwx  1 jcardent  22935 Jan 26 06:30 4.1.05.jpg
-rwxrwxrwx  1 jcardent  35456 Jan 26 06:30 4.1.06.jpg
-rwxrwxrwx  1 jcardent  13670 Jan 26 06:30 4.1.07.jpg
-rwxrwxrwx  1 jcardent  18166 Jan 26 06:30 4.1.08.jpg
-rwxrwxrwx  1 jcardent  77486 Jan 26 06:30 4.2.01.jpg
-rwxrwxrwx  1 jcardent  85332 Jan 26 06:30 4.2.05.jpg
&lt;/pre&gt;



&lt;p&gt;
Since the last post, I've made a number of enhancements to the
&lt;code&gt;fatrecover&lt;/code&gt; program that I am developing for this series.  One new
feature is a simple shell that allows interactively analyzing a disk
image.  Building and running the program results in:
&lt;/p&gt;



&lt;pre class="example"&gt;$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.6.2
BuildVersion:   10C540

$ wc -l fatrecover.c 
    1348 fatrecover.c

$ make all
gcc -g -Wall fatrecover.c -o fatrecover

$./fatrecover frtest2.dmg 

=== FAT RECOVER v0.0 ===

Opening file frtest2.dmg................OK
Reading boot sector.....................OK
Processing boot sector..................OK
Reading root dir........................OK

AT YOUR COMMAND:

&amp;gt;&amp;gt; help
      quit     exits program
      help     displays commands
   bsector     prints boot sector
   fsareas     prints size and location of fs areas
       fat     prints file allocation table
        ls     prints current directory
     chain     prints cluster chain
    contig     prints file continuity status
  checksum     prints cluster checksums
   extract     extracts clusters to file

&amp;gt;&amp;gt;
&lt;/pre&gt;



&lt;p&gt;
Listing the root directory within &lt;code&gt;fatrecover&lt;/code&gt; also shows the ten
copied files (and the volume name entry which is usually hidden).
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; ls

LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.....A     85332            4.2.05.jpg  (0x000083)
.....A     77486            4.2.01.jpg  (0x00005d)
.....A     35456            4.1.06.jpg  (0x00004b)
.....A     23423            4.1.01.jpg  (0x00003f)
.....A     22935            4.1.05.jpg  (0x000033)
.....A     22504            4.1.04.jpg  (0x000028)
.....A     21630            4.1.02.jpg  (0x00001d)
.....A     18166            4.1.08.jpg  (0x000014)
.....A     13670            4.1.07.jpg  (0x00000d)
.....A     12278            4.1.03.jpg  (0x000007)
.H..D.         0            .fseventsd  (0x000006)
.H..D.         0              .Trashes  (0x000002)
.H...A      4096            ._.Trashes  (0x000003)
...V.A         0              FRTEST2.  (00000000)
&lt;/pre&gt;



&lt;p&gt;
Printing the FAT table suggests a well-ordered layout of the JPEG
files in the data area.
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; fat

FAT TABLE ( KEY: .=free  B=bad  -=used X=last R=reserved)

00000000-0000001F: RXX-X..-----X------X--------X---
00000020-0000003F: -------X----------X-----------X-
00000040-0000005F: ----------X-----------------X---
00000060-0000007F: --------------------------------
00000080-0000009F: --X-----------------------------
000000A0-000000BF: ------------X...................
000000C0-000000DF: ................................
&lt;/pre&gt;



&lt;p&gt;
Inspecting the cluster chains of the first three files confirms their
contiguous layout.
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; chain 0x7

0x0007 -&amp;gt; 0x0008 -&amp;gt; 0x0009 -&amp;gt; 0x000a -&amp;gt; 0x000b -&amp;gt; 
0x000c -&amp;gt; EOC
(6 clusters, check 6, Contiguous)

&amp;gt;&amp;gt; chain 0xd

0x000d -&amp;gt; 0x000e -&amp;gt; 0x000f -&amp;gt; 0x0010 -&amp;gt; 0x0011 -&amp;gt; 
0x0012 -&amp;gt; 0x0013 -&amp;gt; EOC
(7 clusters, check 7, Contiguous)

&amp;gt;&amp;gt; chain 0x14

0x0014 -&amp;gt; 0x0015 -&amp;gt; 0x0016 -&amp;gt; 0x0017 -&amp;gt; 0x0018 -&amp;gt; 
0x0019 -&amp;gt; 0x001a -&amp;gt; 0x001b -&amp;gt; 0x001c -&amp;gt; EOC
(9 clusters, check 9, Contiguous)
&lt;/pre&gt;



&lt;p&gt;
For convenience, I implemented the &lt;code&gt;contig&lt;/code&gt; shell command which lists
each file and reports if it is stored contiguously in the file system.
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; contig

LAYOUT STATUS OF FILES IN DIR: ROOT

       NAME          1ST      LENGTH    CONTIGUOUS?
 ---------------  --------  --------  -------------
      4.2.05.jpg  0x000083       42     CONTIGUOUS
      4.2.01.jpg  0x00005d       38     CONTIGUOUS
      4.1.06.jpg  0x00004b       18     CONTIGUOUS
      4.1.01.jpg  0x00003f       12     CONTIGUOUS
      4.1.05.jpg  0x000033       12     CONTIGUOUS
      4.1.04.jpg  0x000028       11     CONTIGUOUS
      4.1.02.jpg  0x00001d       11     CONTIGUOUS
      4.1.08.jpg  0x000014        9     CONTIGUOUS
      4.1.07.jpg  0x00000d        7     CONTIGUOUS
      4.1.03.jpg  0x000007        6     CONTIGUOUS
      ._.Trashes  0x000003        2     CONTIGUOUS
&lt;/pre&gt;



&lt;p&gt;
The &lt;code&gt;contig&lt;/code&gt; command confirms that all of the copied files are indeed
stored contiguously. This evidence supports the second assumption -
contiguous storage - but what about the first assumption that file
data doesn't get erased after deletion?
&lt;/p&gt;
&lt;p&gt;
I've also enhanced &lt;code&gt;fatrecover&lt;/code&gt; with the ability to checksum a series
of clusters using the &lt;a href="http://www.isthe.com/chongo/tech/comp/fnv/index.html"&gt;FNV hash algorithm&lt;/a&gt;. Hashing the clusters for the
first three files yields:
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; checksum 0x7 6

0007-000A: 621102b1 5f618b21 d2855912 f5e1af11 
000B-000C: 5587dd40 8988110b 

&amp;gt;&amp;gt; checksum 0xd 7

000D-0010: e736c48d 2554ee35 e613f172 fe82cc2f 
0011-0013: de7bf92b 515eea02 acefd6c3 

&amp;gt;&amp;gt; checksum 0x14 9

0014-0017: 73e7cfea cdc2d024 5625892b 560cd9b1 
0018-001B: e5257e13 49fddb98 44610606 6011d7c7 
001C-001C: eadda707 
&lt;/pre&gt;



&lt;p&gt;
To validate these checksums, I also implemented a standalone FNV hash
utility to checksum the original image files in 2KB chunks (last chunk
padded with 0s).
&lt;/p&gt;



&lt;pre class="example"&gt;$ ./fnvtest 4.1.03.jpg

0000-0003: 621102b1 5f618b21 d2855912 f5e1af11 
0004-0007: 5587dd40 8988110b 

$ ./fnvtest 4.1.07.jpg 

0000-0003: e736c48d 2554ee35 e613f172 fe82cc2f 
0004-0007: de7bf92b 515eea02 acefd6c3 

$ ./fnvtest 4.1.08.jpg 

0000-0003: 73e7cfea cdc2d024 5625892b 560cd9b1 
0004-0007: e5257e13 49fddb98 44610606 6011d7c7 
0008-000B: eadda707 
&lt;/pre&gt;



&lt;p&gt;
They match! This indicates that we have indeed located the data for
these files in the disk image.
&lt;/p&gt;
&lt;p&gt;
Now let's mount the disk image and delete the JPEG files.
&lt;/p&gt;



&lt;pre class="example"&gt;$ hdiutil attach frtest2.dmg 
/dev/disk1                      /Volumes/FRTEST2

$ rm /Volumes/FRTEST2/4*.jpg

$ ls -ao /Volumes/FRTEST2/
total 48
drwxrwxrwx  1 jcardent  16384 Jan 28 06:59 .
drwxrwxrwt@ 4 root        136 Jan 28 06:59 ..
drwxrwxrwx@ 1 jcardent   2048 Jan 28 06:59 .Trashes
-rwxrwxrwx  1 jcardent   4096 Jan 26 06:30 ._.Trashes
drwxrwxrwx  1 jcardent   2048 Jan 28 06:59 .fseventsd

$ hdiutil detach /Volumes/FRTEST2/
"disk1" unmounted.
"disk1" ejected.
&lt;/pre&gt;



&lt;p&gt;
Re-examining the image in &lt;code&gt;fatrecover&lt;/code&gt; confirms that the files are no
longer listed in the root directory and that their associated clusters
have been marked as unused in the FAT table.
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; ls

LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.H..D.         0            .fseventsd  (0x000006)
.H..D.         0              .Trashes  (0x000002)
.H...A      4096            ._.Trashes  (0x000003)
...V.A         0              FRTEST2.  (00000000)

&amp;gt;&amp;gt; fat

FAT TABLE ( KEY: .=free  B=bad  -=used X=last R=reserved)

00000000-0000001F: RXX-XXX.........................
00000020-0000003F: ................................
00000040-0000005F: ................................
00000060-0000007F: ................................
00000080-0000009F: ................................
000000A0-000000BF: ................................
000000C0-000000DF: ................................
&lt;/pre&gt;



&lt;p&gt;
Checksumming the same clusters as before results in:
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; checksum 0x7 6

0007-000A: 621102b1 5f618b21 d2855912 f5e1af11 
000B-000C: 5587dd40 8988110b 

&amp;gt;&amp;gt; checksum 0xd 7

000D-0010: e736c48d 2554ee35 e613f172 fe82cc2f 
0011-0013: de7bf92b 515eea02 acefd6c3 

&amp;gt;&amp;gt; checksum 0x14 9

0014-0017: 73e7cfea cdc2d024 5625892b 560cd9b1 
0018-001B: e5257e13 49fddb98 44610606 6011d7c7 
001C-001C: eadda707 
&lt;/pre&gt;



&lt;p&gt;
The same checksums! Although the files were deleted the data remains
unchanged in the file system! This supports the first assumption that
deleting files does not erase their associated data.
&lt;/p&gt;
&lt;p&gt;
To recover the deleted files, all that is needed is to extract the
data from the disk image and write it to a new file. I added the
&lt;code&gt;extract&lt;/code&gt; command to do just that:
&lt;/p&gt;



&lt;pre class="example"&gt;&amp;gt;&amp;gt; extract 0x7 6 recover1.jpg
Extracting clusters:
0x07 0x08 0x09 0x0a 0x0b 0x0c 

&amp;gt;&amp;gt; extract 0xd 7 recover2.jpg
Extracting clusters:
0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 

&amp;gt;&amp;gt; extract 0x14 9 recover3.jpg
Extracting clusters:
0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 
&lt;/pre&gt;



&lt;p&gt;
The recovered files are slightly larger than the originals but this is
due to extracting the entire last cluster even though it was only
partially used.
&lt;/p&gt;



&lt;pre class="example"&gt;$ ls -ao images/4.1.0[378].jpg 
-rw-r--r--  1 jcardent  12278 Dec  4 21:16 images/4.1.03.jpg
-rw-r--r--  1 jcardent  13670 Dec  4 21:16 images/4.1.07.jpg
-rw-r--r--  1 jcardent  18166 Dec  4 21:16 images/4.1.08.jpg

$ ls -ao recover*.jpg
-rw-r--r--  1 jcardent  12288 Jan 28 10:50 recover1.jpg
-rw-r--r--  1 jcardent  14336 Jan 28 10:50 recover2.jpg
-rw-r--r--  1 jcardent  18432 Jan 28 10:50 recover3.jpg
&lt;/pre&gt;



&lt;p&gt;
Comparing the original and recovered files with &lt;a href="http://www.imagemagick.org/script/index.php"&gt;ImageMagik&lt;/a&gt;'s
&lt;a href="http://www.imagemagick.org/script/identify.php"&gt;&lt;code&gt;identify&lt;/code&gt;&lt;/a&gt;, and &lt;a href="http://www.imagemagick.org/script/compare.php"&gt;&lt;code&gt;compare&lt;/code&gt;&lt;/a&gt; utilities results in:
&lt;/p&gt;



&lt;pre class="example"&gt;$ identify images/4.1.0[378].jpg
images/4.1.03.jpg JPEG 256x256 256x256+0+0 8-bit DirectClass 12kb 
images/4.1.07.jpg[1] JPEG 256x256 256x256+0+0 8-bit DirectClass 13.3kb 
images/4.1.08.jpg[2] JPEG 256x256 256x256+0+0 8-bit DirectClass 17.7kb 

$ identify recover[123].jpg
recover1.jpg JPEG 256x256 256x256+0+0 8-bit DirectClass 12kb 
recover2.jpg[1] JPEG 256x256 256x256+0+0 8-bit DirectClass 14kb 
recover3.jpg[2] JPEG 256x256 256x256+0+0 8-bit DirectClass 18kb 

$ compare -metric RMSE images/4.1.03.jpg recover1.jpg null:
0 (0)

$ compare -metric RMSE images/4.1.07.jpg recover2.jpg null:
0 (0)

$ compare -metric RMSE images/4.1.08.jpg recover3.jpg null:
0 (0)
&lt;/pre&gt;



&lt;p&gt;
Success, the recovered files are identical to the originals! For
further confirmation, I opened these files with Apple's Preview
application and visually compared the recovered images to the
originals - they were indeed identical.
&lt;/p&gt;
&lt;p&gt;
It's important to note that this manual recovery process worked
because we knew where the files originally were in the file system. In
the real use-case, the location of the original files is unknown so a
method is needed to discover the beginning and ending of deleted
files. In the next post, I'll begin developing such a method.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3068132162643581303?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3068132162643581303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3068132162643581303'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/01/recovering-deleted-jpegs-from-fat-file.html' title='Recovering Deleted JPEGs from a FAT File System - Part 7'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-194722838752309844</id><published>2010-01-20T05:06:00.001-05:00</published><updated>2010-01-20T05:09:23.376-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>Book Review: The Simplicity Cycle</title><content type='html'>&lt;p&gt;A couple of weeks ago I came across a link to the book &lt;a href="http://www.lulu.com/content/877467"&gt;&lt;i&gt;The Simplicity Cycle&lt;/i&gt;&lt;/a&gt; by Dan Ward. It's available as a &lt;a href="http://www.lulu.com/product/download/the-simplicity-cycle/3162736"&gt;free download&lt;/a&gt; so I grabbed a
copy and finally got the chance to read it.
&lt;/p&gt;
&lt;p&gt;
The book begins with the argument that projects follow a curve in the
two-dimensional space formed by the qualities "goodness" and
"complexity". Initially, all projects start off at the origin with no
complexity or goodness. As they progress, complexity and goodness
increase together as the initial problems are understood and suitable
solutions created.
&lt;/p&gt;
&lt;p&gt;
The book's primary thesis is that eventually all projects reach an
inflection point where further increases in complexity yield &lt;i&gt;less&lt;/i&gt;
goodness, not more. To improve goodness beyond this point, the focus
must switch from increasing complexity to reducing it.  These
transitions from simple, to complex, and back to simple again form the
"simplicity cycle" which, when successful, results in "an elegant,
graceful, and streamlined solution" capable of representing
"tremendous complexity" while being "at once profound and
breathtakingly simple". Inspiring stuff.
&lt;/p&gt;
&lt;p&gt;
Not surprisingly, the author asserts that increasing complexity after
the inflection point results in an undesirable outcome. For software,
this could mean an application with too many features, options, or
ways to accomplish tasks. For decision making, this could mean having
too much information to draw an effective conclusion.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, the non-linear relationship between complexity
and goodness can go unrecognized - from page 26:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;One pitfall that designers, engineers, and academicians may fall prey
to is the belief that continuing to increase complexity &amp;hellip; will
continue to produce increases in goodness. It just isn't the case.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
In some cases, this mistaken belief can be due to a fascination with
complexity itself - here goodness doesn't mean utility to the consumer
but rather intellectual gratification to the producer. Also quoting
from page 26:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;This is genesis-gone-wild, cancerous growth. It is the product
of “over-learning” and smugness, where people fall in love with
complexity.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
In other cases, the complexity is mastered but without achieving the
deep insight needed to reduce the subject to its fundamental problems
and solutions - to my mind a kind of mechanistic expertise. Quoting
from page 30:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;It generally means we are over-thinking a problem or over-
engineering a solution. Think of it as achieving &lt;i&gt;“the complexity on the other side of understanding,”&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;hellip;
&lt;/p&gt;
&lt;p&gt;
Here we find the learned academician who everyone assumes is brilliant
because nobody can understand a word he says. In fact, his academics
may simply be complicated and have very limited goodness. All those
extra squiggles are not very useful, no matter how complex.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
The book admits the difficulty of knowing when the inflection point
has been reached. Guessing too late results in over complicatedness as
has been discussed. However, guessing too soon results in premature
optimization - an equally bad outcome as it fails to reach optimal
goodness. Unfortunately, the author asserts that a method for reliably
identifying the inflection point doesn't exist.
&lt;/p&gt;
&lt;p&gt;
The book makes the important point that the complexity phase cannot
simply be skipped. Quoting from on page 46:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;However, the simplicity in this region is built upon an essential
foundation of earlier complexity. We can’t just jump from simplistic
to simple, skipping the complex entirely. The initial increase in
complexity is as crucial to maximizing goodness as the later decrease
in complexity.
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
The book concludes with examples of good and bath paths through the
complexity vs. goodness space. I found these helpful for reflecting on
how the simplicity cycle relates to past experiences.
&lt;/p&gt;
&lt;p&gt;
This book was a pleasant surprise. Although short and free, I found it
thought provoking and insightful. In particular, the "simplicity
cycle" concept matches well with my experiences. When developing
software, I often find that after writing a fair amount of code common
patterns emerge that allow for a simpler design. Conversely, I've
witnessed first hand the disastrous outcome of projects that fail to
simplify after the inflection point. Although these experiences have
given me a healthy fear of complexity, this book provided a context
for that fear and a framework for managing it.
&lt;/p&gt;
&lt;p&gt;
My only criticism of the simplicity cycle concept is that it may
encourage the kind of over generalization that Joel Spolsky warns
against in his essay "&lt;a href="http://www.joelonsoftware.com/articles/fog0000000018.html"&gt;Don't Let Architecture Astronauts Scare You&lt;/a&gt;". 
Quoting from Joel's essay:
&lt;/p&gt;
&lt;blockquote&gt;

&lt;p&gt;When you go too far up, abstraction-wise, you run out of
oxygen. Sometimes smart thinkers just don't know when to stop, and
they create these absurd, all-encompassing, high-level pictures of the
universe that are all good and fine, but don't actually mean anything
at all.
&lt;/p&gt;
&lt;p&gt;
These are the people I call Architecture Astronauts. It's very hard to
get them to write code or design programs, because they won't stop
thinking about Architecture. They're astronauts because they are above
the oxygen level, I don't know how they're breathing. 
&lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;
The point I take away from this is that simplification is not the same
thing as abstraction - simplification remains focused and actionable
while abstraction becomes vague and unactionable. Unfortunately, I
think many mistake generalization for simplification but now that I'm
consciously aware of the difference I'll be less likely to fall into
the trap myself.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-194722838752309844?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/194722838752309844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/194722838752309844'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/01/book-review-simplicity-cycle.html' title='Book Review: The Simplicity Cycle'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4565495405606073710</id><published>2010-01-16T08:00:00.000-05:00</published><updated>2010-01-16T08:01:10.284-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><title type='text'>New Year, New Role</title><content type='html'>&lt;p&gt;With the New Year I took on a new position at work - same kind of role
leading an advanced development team but with a much expanded sphere
of responsibility. While exciting, this change plus some personal
matters has left less time for blogging.
&lt;/p&gt;
&lt;p&gt;
I expect to recover my blogging time this week and plan to finish up
the &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;FAT Recover series&lt;/a&gt; by the end of the month.
&lt;/p&gt;
&lt;p&gt;
The bright side to this interruption is that it helped me realize how
much I enjoy blogging. According to &lt;a href="http://www.google.com/analytics/"&gt;Google Analytics&lt;/a&gt;, this blog isn't
setting the web on fire but regardless the personal benefits have more
than justified the effort.
&lt;/p&gt;
&lt;p&gt;
I have a lot of plans for this blog in 2010 and I intend to set aside
the time required to remain active. That said, family and work take
higher priority so such interruptions are probably inevitable - who
knows what the year will bring.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4565495405606073710?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4565495405606073710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4565495405606073710'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/01/new-year-new-role.html' title='New Year, New Role'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8638813972962271044</id><published>2010-01-06T06:15:00.000-05:00</published><updated>2010-01-06T06:16:06.814-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>Book Review: Organizing Genius</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=p1m-qnHJdY4C&amp;amp;dq=organizing+genius&amp;amp;printsec=frontcover&amp;amp;source=bn&amp;amp;hl=en&amp;amp;ei=tNNDS_O0OMaOlAeK1-2hBw&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;ct=result&amp;amp;resnum=4&amp;amp;ved=0CBsQ6AEwAw#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Organizing Genius: The Secrets of Creative Collaboration &lt;/a&gt;
by Warren Bennis &amp;amp; Patricia Ward Biederman
&lt;/p&gt;
&lt;p&gt;
I first read this book a few of years ago and recently reviewed it in
preparation for a new role at work. As before, I found it to be an
extremely insightful resource on the attributes of great, innovative
teams.
&lt;/p&gt;
&lt;p&gt;
The majority of the book provides accounts of the following "great"
groups:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The Walt Disney Studio
&lt;/li&gt;
&lt;li&gt;
Xerox PARC
&lt;/li&gt;
&lt;li&gt;
The Apple Macintosh Team
&lt;/li&gt;
&lt;li&gt;
The 1992 Clinton Campaign Team
&lt;/li&gt;
&lt;li&gt;
Lockheed's Skunk Works Group
&lt;/li&gt;
&lt;li&gt;
Black Mountain College
&lt;/li&gt;
&lt;li&gt;
The Manhattan Project

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As stated in the introduction, the authors attempt to systematically
examine these groups in the hope of identifying their "collective
magic". In the final chapter they summarize their findings into the
following 15 points.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Greatness starts with superb people.
&lt;/li&gt;
&lt;li&gt;
Great Groups and great leaders create each other.
&lt;/li&gt;
&lt;li&gt;
Every Great Group has a strong leader.
&lt;/li&gt;
&lt;li&gt;
The leaders of Great Groups love talent and know where to find it.
&lt;/li&gt;
&lt;li&gt;
Great Groups are full of talented people who can work together.
&lt;/li&gt;
&lt;li&gt;
Great Groups think they are on a mission from God.
&lt;/li&gt;
&lt;li&gt;
Every Great Group is an island - but an island with a bridge to the mainland.
&lt;/li&gt;
&lt;li&gt;
Great Groups see themselves as underdogs.
&lt;/li&gt;
&lt;li&gt;
Great Groups always have an enemy.
&lt;/li&gt;
&lt;li&gt;
People in Great Groups have blinders on. 
&lt;/li&gt;
&lt;li&gt;
Great Groups are optimistic, not realistic.
&lt;/li&gt;
&lt;li&gt;
In Great Groups the right person has the right job.
&lt;/li&gt;
&lt;li&gt;
The Leaders of Great Groups give them what they need and free them
from the rest.
&lt;/li&gt;
&lt;li&gt;
Great Groups ship.
&lt;/li&gt;
&lt;li&gt;
Great work is its own reward.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Generally speaking I agree with all of these points. The closest I've
come to working in a Great Group was my &lt;a href="http://jcardente.blogspot.com/2009/11/lucky-career-start.html"&gt;first job&lt;/a&gt; building a big-iron
ccNUMA machine. That group and experience had many of the elements
listed above and a decade later I recognize how rare of an opportunity
that was. I suspect others from the team feel the same way based on
the reminiscing we do whenever any of us cross paths.
&lt;/p&gt;
&lt;p&gt;
Clearly the leaders of Great Groups are responsible for creating an
appropriate environment for the team to flourish. In my role as the
leader of an advanced development engineering team I've tried my best
to follow this guidance with I think moderate success. As my role and
responsibility expands I need to place greater focus on creating the
right environment - to that end this book will be a valuable resource.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8638813972962271044?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8638813972962271044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8638813972962271044'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2010/01/book-review-organizing-genius.html' title='Book Review: Organizing Genius'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2158993596563277477</id><published>2009-12-31T14:39:00.000-05:00</published><updated>2009-12-31T14:41:18.032-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 6</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 6 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
In this post we'll tackle the last major FAT file system data
structure - the file allocation table. Compared to the &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_24.html"&gt;previous post&lt;/a&gt;
on the root directory, this post is much less complex. Follow the
"read more" link for an overview of the file allocation table and the
code required to process it.
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
As mentioned in previous posts, a FAT16 file allocation table consists
of one 16 bit entry for each cluster in the data area. The value
stored in each entry indicates the associated cluster's allocation
state and, if needed, the next cluster containing data for the same
file system object.
&lt;/p&gt;
&lt;p&gt;
Although table entries exist for clusters &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, they are
reserved for the system's use. The first byte of entry &lt;code&gt;0&lt;/code&gt; is used to
store a copy of the &lt;code&gt;mediaType&lt;/code&gt; field from the common boot sector -
the entry's remaining bits are set to &lt;code&gt;1&lt;/code&gt;. Entry &lt;code&gt;1&lt;/code&gt; is sometimes used
to record if the volume was unmounted cleanly. Because of these
reservations, the data area cluster information beings with entry &lt;code&gt;2&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
Initially, table entries &lt;code&gt;2&lt;/code&gt; and higher are initialized to a value of
&lt;code&gt;0x0000&lt;/code&gt; indicating that the associated clusters are available for
use.  Bad clusters are signified by the value &lt;code&gt;0xFFF7&lt;/code&gt; while reserved
clusters have a value between &lt;code&gt;0xFFF0&lt;/code&gt; and &lt;code&gt;0xFFF6&lt;/code&gt; (inclusive).
&lt;/p&gt;
&lt;p&gt;
Table entries for allocated clusters contain either:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The number of the next cluster containing additional data for the
associated file system object (file or directory).
&lt;/li&gt;
&lt;li&gt;
A value between &lt;code&gt;0xFFF8&lt;/code&gt; and &lt;code&gt;0xFFFF&lt;/code&gt; (inclusive) indicating
that this is the last cluster containing data for the associated
file system object. Collectively, these values are known as
end-of-chain (&lt;code&gt;EOC&lt;/code&gt;) values.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, allocated table entries form a singly linked-list
specifying the location and order of the clusters containing a file
system object's data. To illustrate this, consider the following
diagram:
&lt;/p&gt;



&lt;pre class="example"&gt;
          ROOT                        FAT
          DIR                        TABLE
  +--------+------+            +-------+-------+
  | FNMAME | 1ST C|            | ENTRY | VALUE |
  +========+======+            +=======+=======+
  | FILEA  | 0x02 +-------+    | 0X00  | RESVD |
  |        |      |       |    |       |       |
  +--------+------+       |    +-------+-------+
  | FILEB  | 0x03 +-----+ |    | 0X01  | RESVD |
  |        |      |     | |    |       |       |
  +--------+------+     | |    +-------+-------+
  |        |      |     | +---&amp;gt;| 0X02  | EOC   |
  |        |      |     |      |       |       |
                        |      +-------+-------+
                        +-----&amp;gt;| 0X03  | 0X04  |
                               |       |       +----+
                               +-------+-------+    |
                               | 0X04  | 0X06  |&amp;lt;---+
                               |       |       +----+
                               +-------+-------+    |
                               | 0X05  | 0X00  |    |
                               |       |       |    |
                               +-------+-------+    |
                               | 0X06  | EOC   |&amp;lt;---+
                               |       |       |
                               +-------+-------+
                               |       |       |
                               |       |       |

&lt;/pre&gt;




&lt;p&gt;
The diagram depicts two examples, &lt;code&gt;FILEA&lt;/code&gt; and &lt;code&gt;FILEB&lt;/code&gt;, and their
associated entries in the root directory and file allocation table.
&lt;/p&gt;
&lt;p&gt;
&lt;code&gt;FILEA&lt;/code&gt;'s root directory entry states that cluster &lt;code&gt;0x02&lt;/code&gt; contains the
first portion of its data. To reflect this allocation, file allocation
table entry &lt;code&gt;0x02&lt;/code&gt; has a non-zero value. Furthermore, entry &lt;code&gt;0x02&lt;/code&gt;
contains an &lt;code&gt;EOC&lt;/code&gt; value indicating that no other clusters contain data
for &lt;code&gt;FILEA&lt;/code&gt; - all of &lt;code&gt;FILEA&lt;/code&gt;'s data fits within the single cluster.
&lt;/p&gt;
&lt;p&gt;
&lt;code&gt;FILEB&lt;/code&gt; is a longer file requiring three clusters to store its
data. Its root directory entry identifies cluster &lt;code&gt;0x03&lt;/code&gt; as the first
and, appropriately, file allocation table entry &lt;code&gt;0x03&lt;/code&gt; contains a
non-zero value. In this case, entry &lt;code&gt;0x03&lt;/code&gt; contains the value &lt;code&gt;0x04&lt;/code&gt;
indicating that &lt;code&gt;FILEB&lt;/code&gt;'s second cluster of data resides in cluster
&lt;code&gt;0x04&lt;/code&gt;. File allocation table entry &lt;code&gt;0x04&lt;/code&gt; in turn contains the value
of &lt;code&gt;0x06&lt;/code&gt; identifying cluster &lt;code&gt;0x06&lt;/code&gt; as the third holding &lt;code&gt;FILEB&lt;/code&gt;'s
data. Finally, file allocation table &lt;code&gt;0x06&lt;/code&gt; contains an &lt;code&gt;EOC&lt;/code&gt; value
indicating that it is the last cluster containing &lt;code&gt;FILEB&lt;/code&gt;'s data. Note
that cluster &lt;code&gt;0x05&lt;/code&gt;, although between clusters holding &lt;code&gt;FILEB&lt;/code&gt;'s data,
remains unallocated and is available for use.
&lt;/p&gt;
&lt;p&gt;
The example illustrates how the file allocation table is used to both
track cluster allocations and construct linked-lists describing the
location of file system object data. In FAT parlance, these linked
lists are called &lt;i&gt;cluster chains&lt;/i&gt;. 
&lt;/p&gt;
&lt;p&gt;
The file allocation table forms the heart of the file system - a fact
reflected by the file system's name, FAT. Before discussing the code
required to process the on-disk table, it's worth noting a couple of
things:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
The smallest allocation unit is a cluster - 2KB in our test image's
case. File system objects smaller than a cluster consume the whole
thing.  As a result, a large number of small files will result in
severe &lt;a href="http://en.wikipedia.org/wiki/Fragmentation_(computer)#Internal_fragmentation"&gt;internal fragmentation&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
The file allocation table only contains forward pointers. There are
are no back pointers to prior clusters or the directory entry for
the file system object the chain belongs to.
&lt;/li&gt;
&lt;li&gt;
The file allocation table alone describes the location of file
system object data - erasing the table essentially erases the file
system although the data remains unchanged.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recall from the &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file.html"&gt;first post&lt;/a&gt; that the third point was the basis for the
original project in 2004. File systems that erase files by only
clearing their directory and file allocation table entries leave the
data in place thus making it theoretically possible to recover them.
&lt;/p&gt;
&lt;p&gt;
Compared to the root directory, processing the file allocation table
is trivial. The following code fragment provides all of the required
constants, macros, and type definitions:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_FREESECTOR&lt;/span&gt;       (0x0000U)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_RESERVED_START&lt;/span&gt;   (0xfff0U)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_RESERVED_END&lt;/span&gt;     (0xfff6U)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_BADSECTOR&lt;/span&gt;        (0xfff7U)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_LASTSECTOR_START&lt;/span&gt; (0xfff8U)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_FAT16ENT_LASTSECTOR_END&lt;/span&gt;   (0xffffU)

&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_FAT16ENT_ISFREESECTOR&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  ((x) == FAT_FAT16ENT_FREESECTOR)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_FAT16ENT_ISBADSECTOR&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  ((x) == FAT_FAT16ENT_BADSECTOR)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_FAT16ENT_ISLASTSECTOR&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  ((x) &amp;gt;= FAT_FAT16ENT_LASTSECTOR_START)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_FAT16ENT_ISRESERVEDSECTOR&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;)\
  (((x) &amp;gt;= FAT_FAT16ENT_RESERVED_START) &amp;amp;&amp;amp; \
   ((x) &amp;lt;= FAT_FAT16ENT_RESERVED_END))

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #218a21;"&gt;fatFAT16Entry_t&lt;/span&gt;;
&lt;/pre&gt;




&lt;p&gt;
Reading a cluster entry requires calculating the associated sector
number and offset, loading the sector into memory, and extracting the
correct two bytes.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
fatTableOffset = clusterNum * &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatFAT16Entry_t); 
sectorNum      = (fatTableOffset / bytesPerSector)
                 + pimageInfo-&amp;gt;fatFirstSector;
sectorOffset   = fatTableOffset % bytesPerSector;

frSectorRead(pimageInfo, sectorNum,
             1, pSectorBuff);

memcpy(pfatEntry, (pSectorBuff + sectorOffset),
       &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatFAT16Entry_t));
&lt;/pre&gt;




&lt;p&gt;
Alternatively, a portion of the table loaded into a memory buffer can
be iterated over as an array.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
 &lt;span style="color: #218a21;"&gt;fatFAT16Entry_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;fatTable&lt;/span&gt;;
 &lt;span style="color: #218a21;"&gt;fatFAT16Entry_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;entry&lt;/span&gt;;

  fatTable = (&lt;span style="color: #218a21;"&gt;fatFAT16Entry_t&lt;/span&gt; *) pfatTableBuffer;
  &lt;span style="color: #7f007f;"&gt;for&lt;/span&gt;(entryIndex=0; entryIndex &amp;lt; numEntries; entryIndex++ ) {

    entry = fatTable[entryIndex];
    ....
  }
&lt;/pre&gt;




&lt;p&gt;
Using the code fragments above, I enhanced the program I am
(re)developing alongside these posts to process and printout the file
allocation table. Running it against the test disk image,
&lt;code&gt;frtest1.dmg&lt;/code&gt;, created in the &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_14.html"&gt;second post&lt;/a&gt; and used to produce the
examples in the prior two posts yielded:
&lt;/p&gt;



&lt;pre class="example"&gt;
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.6.2
BuildVersion:   10C540

$ wc -l fatrecover.c 
     829 fatrecover.c

$ make clean all
rm -r fatrecover fatrecover.dSYM 
gcc -g -Wall fatrecover.c -o fatrecover

$ ./fatrecover frtest1.dmg 

=== FAT RECOVER v0.0 ===

Opening file frtest1.dmg................OK
Reading boot sector.....................OK
Processing boot sector..................OK
Reading root dir........................OK

BOOT SECTOR:
          OEMName: (BSD  4.4)
         volumeID: 63260cf5
     Volume Label: FRTEST1    
           FSType: FAT16   
     Bytes/Sector: 512
  Sectors/Cluster: 4
 Reserved Sectors: 1
   Hidden Sectors: 0
 Sectors/Vol (2B): 32768
 Sectors/Vol (4B): 0
      Sectors/FAT: 32
        # of FATs: 2
# RootDir Entries: 512

MAJOR STRUCTURES:
      AREA    OFFSET      SIZE
----------  --------  --------
 BOOT+RESV  00000000  0x000001 (sectors)
       FAT  0x000001  0x000040 (sectors)
   ROOTDIR  0x000041  0x000020 (sectors)
      DATA  0x000061  0x001fe7 (clusters)

LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.....A     92889            4.2.04.jpg  (0x00000a)
.H..D.         0              .Trashes  (0x000002)
.H...A      4096            ._.Trashes  (0x000003)
...V.A         0              FRTEST1.  (00000000)

FAT TABLE ( KEY: .=free  B=bad  -=used X=last R=reserved)

00000000-0000001F: RXX-X.....----------------------
00000020-0000003F: -----------------------X........
00000040-0000005F: ................................
00000060-0000007F: ................................
&amp;lt;OUTPUT FOR EMPTY ENTRIES OMITTED&amp;gt;

CLUSTER CHAIN FOR ENTRY 0xa:
0x000a-&amp;gt;0x000b-&amp;gt;0x000c-&amp;gt;0x000d-&amp;gt;0x000e-&amp;gt;0x000f-&amp;gt;0x0010-&amp;gt;
0x0011-&amp;gt;0x0012-&amp;gt;0x0013-&amp;gt;0x0014-&amp;gt;0x0015-&amp;gt;0x0016-&amp;gt;0x0017-&amp;gt;
0x0018-&amp;gt;0x0019-&amp;gt;0x001a-&amp;gt;0x001b-&amp;gt;0x001c-&amp;gt;0x001d-&amp;gt;0x001e-&amp;gt;
0x001f-&amp;gt;0x0020-&amp;gt;0x0021-&amp;gt;0x0022-&amp;gt;0x0023-&amp;gt;0x0024-&amp;gt;0x0025-&amp;gt;
0x0026-&amp;gt;0x0027-&amp;gt;0x0028-&amp;gt;0x0029-&amp;gt;0x002a-&amp;gt;0x002b-&amp;gt;0x002c-&amp;gt;
0x002d-&amp;gt;0x002e-&amp;gt;0x002f-&amp;gt;0x0030-&amp;gt;0x0031-&amp;gt;0x0032-&amp;gt;0x0033-&amp;gt;
0x0034-&amp;gt;0x0035-&amp;gt;0x0036-&amp;gt;0x0037-&amp;gt;EOC (46 clusters)
&lt;/pre&gt;




&lt;p&gt;
As discussed previously, file allocation table entries &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; are
reserved by the system. The program output indeed shows that entry &lt;code&gt;0&lt;/code&gt;
contains a &lt;code&gt;RESERVED&lt;/code&gt; value but entry &lt;code&gt;1&lt;/code&gt; instead contains an &lt;code&gt;EOC&lt;/code&gt;
value. A little research&lt;sup&gt;&lt;a class="footref" name="fnr.1" href="#fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt; uncovered that &lt;code&gt;EOC&lt;/code&gt; values are used to both
prevent entry &lt;code&gt;1&lt;/code&gt; from being used and to encode the volume's last
unmount state - the output thus makes sense.
&lt;/p&gt;
&lt;p&gt;
Table entry &lt;code&gt;2&lt;/code&gt; is also allocated and the root directory listing tells
us that it belongs to the &lt;code&gt;.Trashes&lt;/code&gt; subdirectory. Furthermore, the
&lt;code&gt;EOC&lt;/code&gt; value indicates that the directory's contents fit within the
single sector.
&lt;/p&gt;
&lt;p&gt;
Entry &lt;code&gt;4&lt;/code&gt; is allocated to the &lt;code&gt;._.Trashes&lt;/code&gt; file which requires two
clusters to store its 4096 bytes of data. Presumably, entry &lt;code&gt;5&lt;/code&gt;
also belongs to this file and is the last in its cluster chain. 
&lt;/p&gt;
&lt;p&gt;
Finally, entries &lt;code&gt;0x0a&lt;/code&gt; to &lt;code&gt;0x37&lt;/code&gt; are all allocated to the test JPG
file &lt;code&gt;4.2.04.jpg&lt;/code&gt;. This is suggested by the root directory listing and
allocation table summary and confirmed by the additional output
showing the cluster chain starting at entry &lt;code&gt;0x0a&lt;/code&gt;. The cluster
chain's length, 46, provides a sanity check as it is the smallest
number of clusters required to store the file's &lt;code&gt;92889&lt;/code&gt; bytes of data
(&lt;code&gt;46 * 2KB -&amp;gt; 94208B&lt;/code&gt;).
&lt;/p&gt;
&lt;p&gt;
With that we now have all the code required to examine a FAT file
system disk image. With a little work, the code fragments provided
in this and prior posts can be used to create a program capable of
inspecting file system images of varying size and complexity. 
&lt;/p&gt;
&lt;p&gt;
In the next post, we'll regroup by using the functionality developed
thus far to inspect various file system images to determine the best
approach for recovering deleted JPG files. 
&lt;/p&gt;

&lt;div id="footnotes" style="font-size: 0.8em;"&gt;
&lt;h2 class="footnotes"&gt;Footnotes: &lt;/h2&gt;
&lt;div id="text-footnotes"&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.1" href="#fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;a href="http://en.wikipedia.org/wiki/File_Allocation_Table#File_Allocation_Table"&gt;http://en.wikipedia.org/wiki/File_Allocation_Table#File_Allocation_Table&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2158993596563277477?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2158993596563277477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2158993596563277477'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_31.html' title='Recovering Deleted JPEGs from a FAT File System - Part 6'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-67310223752676922</id><published>2009-12-29T06:45:00.001-05:00</published><updated>2009-12-29T06:47:16.746-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Book Review: The Planiverse</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=wIzwyzHSrL4C&amp;amp;printsec=frontcover&amp;amp;dq=planiverse&amp;amp;ei=H2M2S8i4H5SuyAS2qLjLAQ&amp;amp;cd=1#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;The Planiverse: Computer Contact with a Two Dimensional World&lt;/a&gt;
by &lt;a href="http://en.wikipedia.org/wiki/A._K._Dewdney"&gt;A.K. Dewdney&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
Written in 1984, this is a fictional story of a computer science
professor and his students making contact with a two-dimensional
universe - &lt;a href="http://en.wikipedia.org/wiki/Planiverse"&gt;The Planiverse&lt;/a&gt; - through a simulation program developed as
a class project. More specifically, a telepathic connection is
established with one of the universe's inhabitants called &lt;code&gt;YNDRD&lt;/code&gt;
otherwise known as "Yendred". The professor and students accompany
Yendred on a journey and in the process discover the workings of the
two-dimensional world.
&lt;/p&gt;
&lt;p&gt;
Essentially, the book can be decomposed into two concurrent streams:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The story of Yendred's journey across his world in search of a
mystic - Drabk - said to possess "knowledge of the beyond".

&lt;/li&gt;
&lt;li&gt;
A series of expositions on the scientific, technological, and social
aspects of Yendred's two-dimensional universe.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, Yendred's journey provides the context and segues for the
expositions. This structure reflects the book's derivation from two
prior publications - the monograph &lt;i&gt;Two-Dimensional Science and Technology&lt;/i&gt; and book &lt;i&gt;Symposium on Two-Dimensional Science and Technology&lt;/i&gt;. Material from these prior works formed the basis for the
expositions around which the fictional Yendred plot was wrapped to
create a coherent, entertaining, and intellectually stimulating story.
&lt;/p&gt;
&lt;p&gt;
Although I thought the Yendred story was only "OK", I really enjoyed
the expositions - clearly a lot of thought was given to how a two
dimensional world would work. For example, some of the scientific
exposition topics were:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Thermodynamics and the extremely low melting point that
two-dimensions lead to.
&lt;/li&gt;
&lt;li&gt;
The mechanics of two-dimensional turbulence.
&lt;/li&gt;
&lt;li&gt;
Electromagnetism and the lack of magnetic forces.
&lt;/li&gt;
&lt;li&gt;
The "Global" weather patterns of a two-dimensional "planet"
&lt;/li&gt;
&lt;li&gt;
Energy diminishing according to the inverse of distance (instead of
the inverse-square as in three dimensions).
&lt;/li&gt;
&lt;li&gt;
The biological structure of two-dimensional organisms.
&lt;/li&gt;
&lt;li&gt;
Two-dimensional chemistry and the limited number of possible
elements.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The technical expositions discussed the design of two-dimensional
objects such as:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Buildings that avoided obstructing surface travel.
&lt;/li&gt;
&lt;li&gt;
Doors that allowed privacy and structural integrity while remaining
passable.
&lt;/li&gt;
&lt;li&gt;
Functional steam engines, foundries, space rockets, and fishing boats.
&lt;/li&gt;
&lt;li&gt;
Balloons for travel and the shipment of freight.
&lt;/li&gt;
&lt;li&gt;
Gears capable of varying torque in the absence of axles. 
&lt;/li&gt;
&lt;li&gt;
Subterranean elevators.
&lt;/li&gt;
&lt;li&gt;
Electronic circuits suitable for constructing rudimentary computers.
&lt;/li&gt;
&lt;li&gt;
A wind powered generator for recharging batteries - the only viable
source of electricity as wires create impassable obstacles.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The social expositions discussed topics such as:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
How the world's inhabitants "passed" each other while traveling
(they walk over each other according to social conventions).
&lt;/li&gt;
&lt;li&gt;
How traveler's deal with surface events like a sports game (they wait
until it's done).
&lt;/li&gt;
&lt;li&gt;
Two dimensional warfare and its influence on politics.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, if you enjoy thinking differently about science, and
technology then I think you'll enjoy reading this book. The ability to
think about problems from different perspectives is an important and
powerful skill - I think reading books like &lt;i&gt;The Planiverse&lt;/i&gt; is one
way to help develop that skill.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-67310223752676922?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/67310223752676922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/67310223752676922'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/book-review-planiverse.html' title='Book Review: The Planiverse'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7764641655086489926</id><published>2009-12-24T18:33:00.012-05:00</published><updated>2010-01-28T18:16:53.546-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 5</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 5 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE: 2010/1/28&lt;/b&gt;: it's come to my attention that the long file name
support code described in this post is broken - it only works for
files that utilize a single LFN entry. For the time being I plan to
work around this bug for the FATRecover series but I do intend to post
a fix eventually. I'll be sure to add another update with a link
to the fix when it is available.
&lt;/p&gt;

&lt;p&gt;
The topic of this installment is the root directory and the code
required to process it. Although strictly speaking the file allocation
table is the next major on-disk area, I think covering the root
directory first will make the overall discussion clearer.
&lt;/p&gt;
&lt;p&gt;
Note that this is a watershed post as the "hack" level of the example
code increases dramatically. This was unfortunate but necessary to
accommodate certain FAT functionality while limiting the discussion's
length. That said, readers with sufficient skill should be able to 
use the example code to create more robust implementations.
&lt;/p&gt;
&lt;p&gt;
Follow the "read more" link for a detailed description of how to read
and interpret the root directory. 
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
As discussed in parts &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_15.html"&gt;3&lt;/a&gt; and &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_19.html"&gt;4&lt;/a&gt;, the root directory area is located
on-disk immediately after the FAT area and consumes a fixed amount of
pre-allocated space. The root directory area is essentially an array
of 32 byte entries with the following structure:
&lt;/p&gt;


&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTS_FNSZ&lt;/span&gt; (8)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTS_EXTSZ&lt;/span&gt; (3)

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; &lt;span style="color: #218a21;"&gt;fatDirectoryEntrySFN_s&lt;/span&gt; {     
  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;filename&lt;/span&gt;[FAT_DIRENTS_FNSZ];   &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;07-00
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;extension&lt;/span&gt;[FAT_DIRENTS_EXTSZ]; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;10-08
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;attributes&lt;/span&gt;;                   &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;11-11
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;reserved1&lt;/span&gt;;                    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;12-12
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;createTimeDsec&lt;/span&gt;;               &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;13-13
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;createTimeHMS&lt;/span&gt;;                &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;15-14
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;createTimeDay&lt;/span&gt;;                &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;17-16
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;accessedDay&lt;/span&gt;;                  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;19-18
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;firstClusterHigh&lt;/span&gt;;             &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;21-20
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;writeTimeHMS&lt;/span&gt;;                 &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;23-22
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;writeDay&lt;/span&gt;;                     &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;25-24
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;firstCluster&lt;/span&gt;;                 &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;26-27
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint32_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;fileSize&lt;/span&gt;;                     &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;31-28
&lt;/span&gt;} &lt;span style="color: #7f007f;"&gt;__attribute__&lt;/span&gt; ((packed)) &lt;span style="color: #218a21;"&gt;fatDirectoryEntrySFN_t&lt;/span&gt;;
&lt;/pre&gt;




&lt;p&gt;
The the first byte of the &lt;code&gt;filename&lt;/code&gt; field specifies an entry's
allocation status. Initially, all directory table entries are in the
&lt;code&gt;UNUSED&lt;/code&gt; state (&lt;code&gt;0x00&lt;/code&gt;).  Entries that are allocated and then later
deallocated (due to file system deletions) are moved to the &lt;code&gt;DELETED&lt;/code&gt;
state (&lt;code&gt;0xE5&lt;/code&gt;). The first byte of valid directory entries contains the
first file name character which must have a value other than &lt;code&gt;0x00&lt;/code&gt; or
&lt;code&gt;0xE5&lt;/code&gt; (if necessary the value &lt;code&gt;0x05&lt;/code&gt; is used to encode the character
value &lt;code&gt;0xE5&lt;/code&gt;).
&lt;/p&gt;
&lt;p&gt;
File systems are expected to allocate directory entries sequentially
starting with the first. As a result, partially allocated directory tables 
should consist of an  inter-mixture of allocated and
&lt;code&gt;DELETED&lt;/code&gt; entries followed by one or more &lt;code&gt;UNUSED&lt;/code&gt; entries. Directory
tables that have been fully utilized will consist of only allocated
and &lt;code&gt;DELETED&lt;/code&gt; entries. When allocating entries, file systems can
either re-use &lt;code&gt;DELETED&lt;/code&gt; entries or allocate the next &lt;code&gt;UNUSED&lt;/code&gt; entry
(if one exists) - the preference is implementation specific.
&lt;/p&gt;
&lt;p&gt;
The following code fragment provides &lt;code&gt;#defines&lt;/code&gt; and a macro that can
be used to determine a directory entry's allocation status.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTS_FNUNUSED&lt;/span&gt;  (0x00)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTS_FNDELETED&lt;/span&gt; (0xE5)

&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRENTS_FNVALID&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;pfatDE&lt;/span&gt;) \
  ((FAT_DIRENTS_FNUNUSED  != (pfatDE)-&amp;gt;filename[0]) &amp;amp;&amp;amp;  \
   (FAT_DIRENTS_FNDELETED != (pfatDE)-&amp;gt;filename[0]))
&lt;/pre&gt;




&lt;p&gt;
Valid directory entries contain the associated file system object's
name in &lt;a href="http://en.wikipedia.org/wiki/8.3"&gt;8.3 format&lt;/a&gt;. The &lt;code&gt;filename&lt;/code&gt; field contains the base file name
while the &lt;code&gt;extension&lt;/code&gt; field may contain an optional 3 character
extension. As with the boot sector's text fields, the &lt;code&gt;filename&lt;/code&gt; and
&lt;code&gt;extension&lt;/code&gt; fields are padded with spaces and lack null
termination.
&lt;/p&gt;
&lt;p&gt;
The &lt;code&gt;attributes&lt;/code&gt; field contains flags describing the entry's type and
characteristics. The following code fragment defines the various flags
and provides macros for testing them.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_READONLY&lt;/span&gt;     (0x01)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_HIDDEN&lt;/span&gt;       (0x02)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_SYSTEM&lt;/span&gt;       (0x04)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_VOLUMELABEL&lt;/span&gt;  (0x08)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_LFN&lt;/span&gt;          (0x0f)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_DIRECTORY&lt;/span&gt;    (0x10)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_ARCHIVE&lt;/span&gt;      (0x20)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRATT_MASK&lt;/span&gt;         (0x3f);

&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISREADONLY&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_READONLY) == FAT_DIRATT_READONLY)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISHIDDEN&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_HIDDEN) == FAT_DIRATT_HIDDEN)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISSYSTEM&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_SYSTEM) == FAT_DIRATT_SYSTEM)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISVOLUMELABEL&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_VOLUMELABEL) == FAT_DIRATT_VOLUMELABEL)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISLFN&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_LFN) == FAT_DIRATT_LFN)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISDIRECTORY&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_DIRECTORY) == FAT_DIRATT_DIRECTORY)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRATT_ISARCHIVE&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRATT_ARCHIVE) == FAT_DIRATT_ARCHIVE)
&lt;/pre&gt;




&lt;p&gt;
Most of the attributes should be self-explanatory. Later in the post
we'll discuss the &lt;code&gt;LFN&lt;/code&gt; attribute and its dire implications. 
&lt;/p&gt;
&lt;p&gt;
For this project, the only other fields of interest are &lt;code&gt;firstCluster&lt;/code&gt;
and &lt;code&gt;fileSize&lt;/code&gt;. The &lt;code&gt;firstCluster&lt;/code&gt; field specifies the data area
cluster containing the associated file system object's first chunk of
data - the next post on the file allocation table will discuss this in
greater depth. The &lt;code&gt;fileSize&lt;/code&gt; field indicates a file's size in bytes -
non-file entries should have a value of 0 in this field.
&lt;/p&gt;
&lt;p&gt;
For the program I am re-developing alongside these posts, I took the
following approach to processing the root directory:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;malloc&lt;/code&gt; a temporary buffer the same size as the root directory
area.
&lt;/li&gt;
&lt;li&gt;
Read the on-disk root directory area into the buffer.
&lt;/li&gt;
&lt;li&gt;
Iterate over the directory table entries in the buffer and create
an abstracted data structure for each valid entry.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;free&lt;/code&gt; the temporary buffer

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is a high-level code fragment that implements the above steps:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
  pRootDirBuff = malloc(rootdirSizeBytes);
  memset(pRootDirBuff, 0, rootdirSizeBytes);
  
  frSectorRead(pimageInfo, 
               pimageInfo-&amp;gt;rootdirFirstSector,
               pimageInfo-&amp;gt;rootdirSizeSectors,   
               pRootDirBuff);

  frDirProcessEntries(&amp;amp;(pimageInfo-&amp;gt;rootDir), 
                      pRootDirBuff,
                      rootdirSizeBytes);

  free(pRootDirBuff);
&lt;/pre&gt;




&lt;p&gt;
&lt;code&gt;pimageInfo&lt;/code&gt; is a housekeeping data structure my program uses to pass
around context information like the disk image's open file descriptor,
copies of the boot sector structures, and the information calculated
in part &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_19.html"&gt;4&lt;/a&gt;. &lt;code&gt;frSectorRead()&lt;/code&gt; is a utility routine that reads the
specified sector &lt;a href="http://en.wikipedia.org/wiki/Extent_(file_systems)"&gt;extent&lt;/a&gt; from the disk image file into the memory
buffer provided. All of the real action occurs in
&lt;code&gt;frDirProcessEntries()&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
My first implementation of &lt;code&gt;frDirProcessEntries()&lt;/code&gt; looked something
like the following:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;frDirProcessEntries&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;frDirEntry_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pParentDir&lt;/span&gt;, 
                        &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pdirBuff&lt;/span&gt;, 
                        &lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;sizeBuff&lt;/span&gt;)
{
&lt;span style="color: #218a21;"&gt;fatDirectoryEntrySFN_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;fatDirectoryTable&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numFatDirTableEntries&lt;/span&gt;;

fatDirectoryTable = (&lt;span style="color: #218a21;"&gt;fatDirectoryEntrySFN_t&lt;/span&gt; *) pdirBuff;
numFatDirTableEntries = sizeBuff / 
                        &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatDirectoryEntrySFN_t);

&lt;span style="color: #7f007f;"&gt;for&lt;/span&gt; (entryIndex = 0;
     entryIndex &amp;lt; numFatDirTableEntries;
     entryIndex++) {

  &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (!(FAT_DIRENTS_FNVALID(&amp;amp;(fatDirectoryTable[entryIndex])))) {
    &lt;span style="color: #7f007f;"&gt;continue&lt;/span&gt;;
  }
  entryCount++;

  pDirEntry = (&lt;span style="color: #218a21;"&gt;frDirEntry_t&lt;/span&gt; * )malloc(&lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(frDirEntry_t));
  memset(pDirEntry, 0, &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(frDirEntry_t));

  ptmp  = (&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *) &amp;amp;(pDirEntry-&amp;gt;filename);
  ptmp += 
      utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)fatDirectoryTable[entryIndex].filename,
                        ptmp,
                        (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTS_FNSZ,
                        1);

  &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRENTS_FNVALID(&amp;amp;(fatDirectoryTable[entryIndex]))) {
     *ptmp = &lt;span style="color: #bb8e8e;"&gt;'.'&lt;/span&gt;;
      ptmp++;
      ptmp += 
         utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)fatDirectoryTable[entryIndex].extension, 
                           ptmp,
                           (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTS_EXTSZ,
                           1);
  }
  *ptmp = &lt;span style="color: #bb8e8e;"&gt;'\0'&lt;/span&gt;;

  pDirEntry-&amp;gt;attributes   = fatDirectoryTable[entryIndex].attributes;
  pDirEntry-&amp;gt;fileSize     = fatDirectoryTable[entryIndex].fileSize;
  pDirEntry-&amp;gt;firstCluster = fatDirectoryTable[entryIndex].firstCluster;

  pDirEntry-&amp;gt;pParentDir    = pParentDir;
  pDirEntry-&amp;gt;pNextSibling  = pParentDir-&amp;gt;pDirEntries;
  pParentDir-&amp;gt;pDirEntries  = pDirEntry;
  pDirEntry = &lt;span style="color: #5e9d9f;"&gt;NULL&lt;/span&gt;;
}

&lt;span style="color: #7f007f;"&gt;return&lt;/span&gt; entryCount;
}
&lt;/pre&gt;




&lt;p&gt;
Basically, the routine:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Calculates the number of directory entries in the buffer provided.
&lt;/li&gt;
&lt;li&gt;
Casts it as an array of directory entries.
&lt;/li&gt;
&lt;li&gt;
Iterates over the entries via a &lt;code&gt;for&lt;/code&gt; loop.
&lt;/li&gt;
&lt;li&gt;
Checks the first byte of each entry to see if it is valid.
&lt;/li&gt;
&lt;li&gt;
Allocates an abstracted data for each valid entry.
&lt;/li&gt;
&lt;li&gt;
Copies the file name and other metadata to the abstract structure.
&lt;/li&gt;
&lt;li&gt;
Adds the abstract structure to the parent directory's linked list
of contents.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Technically speaking the &lt;code&gt;for&lt;/code&gt; loop should terminate when a
&lt;code&gt;FAT_DIRENTS_FNUNUSED&lt;/code&gt; entry is encountered but the code above just
skips over such entries and continues processing the table - this
shouldn't be a problem so I didn't bother to fix it (this is a hobby
endeavor after all).
&lt;/p&gt;
&lt;p&gt;
As mentioned, &lt;code&gt;frDirEntry_t&lt;/code&gt; is an abstracted data structure used to
represent directory entries. Its structure can be inferred from the
code above. The motivation for using a secondary structure is to avoid
re-processing the raw entries for each listing (the importance of this
will become clear later).
&lt;/p&gt;
&lt;p&gt;
&lt;code&gt;utilCopyNameChars()&lt;/code&gt; is a utility routine that copies characters from
the raw directory entry text fields to a C string. 
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;utilCopyNameChars&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;srcString&lt;/span&gt;, 
                      &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;destString&lt;/span&gt;, 
                      &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;maxChars&lt;/span&gt;, &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;srcSkip&lt;/span&gt;)
{
  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Copy characters from srcString to destString until 
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;a space character, null character, 0xff value, or 
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;maxChars is reached.
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;//&lt;/span&gt;&lt;span style="color: #b12121;"&gt;
&lt;/span&gt;  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Returns the number of characters copied
&lt;/span&gt;
  &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;psrcChar&lt;/span&gt;  = srcString;
  &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pdestChar&lt;/span&gt; = destString;
  &lt;span style="color: #218a21;"&gt;int&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;count&lt;/span&gt; = 0;

  &lt;span style="color: #7f007f;"&gt;while&lt;/span&gt; ((*psrcChar != &lt;span style="color: #bb8e8e;"&gt;' '&lt;/span&gt;)  &amp;amp;&amp;amp;
         (*psrcChar != &lt;span style="color: #bb8e8e;"&gt;'\0'&lt;/span&gt;)  &amp;amp;&amp;amp;
         (((&lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt;)*psrcChar) != 0xff)  &amp;amp;&amp;amp;
         (count &amp;lt; maxChars)) {

    *pdestChar = *psrcChar;
    psrcChar += srcSkip;
    pdestChar++;
    count++; 
  }

  &lt;span style="color: #7f007f;"&gt;return&lt;/span&gt; count;
}
&lt;/pre&gt;




&lt;p&gt;
The &lt;code&gt;while&lt;/code&gt; loop terminates upon reaching either a space character,
NULL character, or the value &lt;code&gt;0xff&lt;/code&gt; (which will be explained
later). The &lt;code&gt;skip&lt;/code&gt; argument supports a hack that is explained below.
&lt;/p&gt;
&lt;p&gt;
After adding a short routine to print the abstracted directory entry
structures (which I won't show), I got the following output from
running the improved &lt;code&gt;fatrecover&lt;/code&gt; program against the test image,
&lt;code&gt;frtest1.dmg&lt;/code&gt;, from the prior two posts.
&lt;/p&gt;



&lt;pre class="example"&gt;
LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.....A     92889            4204~1.JPG  (0x00000a)
RHSV..        -1                   A4.  (00000000)
.H..D.         0             FSEVEN~1.  (0x000008)
RHSV..        -1                   A..  (00000000)
.H..D.         0             TRASHE~1.  (0x000002)
RHSV..        -1                   A..  (00000000)
.H...A      4096               _~1.TRA  (0x000003)
RHSV..        -1                   A..  (00000000)
...V.A         0              FRTEST1.  (00000000)
&lt;/pre&gt;




&lt;p&gt;
YUCK! The funky file names, &lt;code&gt;-1&lt;/code&gt; sizes, and simultaneous setting of
the &lt;code&gt;RHSV&lt;/code&gt; flags indicated that I had run into the dreaded (by me)
&lt;a href="http://en.wikipedia.org/wiki/File_Allocation_Table#Long_file_names"&gt;long file name support&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
When I first did this project in 2004 I punted on handling long file
names. I considered doing the same for this re-implementation but the
idea didn't sit well with me - I have a hard time avoiding the same
problem twice. Besides, without long file name support I would have
had to modify the test disk image to clean up the output and update
the prior posts as needed. This felt too much like hiding dirty
laundry so I decided to suck it up and hack together long file name
support - this isn't going to be pretty.
&lt;/p&gt;
&lt;p&gt;
Long file names are supported by using multiple directory entries
with the following structure:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTL_FN1SZ&lt;/span&gt; (10)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTL_FN2SZ&lt;/span&gt; (12)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTL_FN3SZ&lt;/span&gt; (4)

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; &lt;span style="color: #218a21;"&gt;fatDirectoryEntryLFN_s&lt;/span&gt; {     
  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;sequenceNum&lt;/span&gt;;                  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;00-00
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;filename1&lt;/span&gt;[FAT_DIRENTL_FN1SZ]; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;10-01
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;attributes&lt;/span&gt;;                   &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;11-11
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;reserved&lt;/span&gt;;                     &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;12-12
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;checksum&lt;/span&gt;;                     &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;13-13
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;filename2&lt;/span&gt;[FAT_DIRENTL_FN2SZ]; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;25-14
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;reserved2&lt;/span&gt;[2];                 &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;27-26
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;   &lt;span style="color: #b7850a;"&gt;filename3&lt;/span&gt;[FAT_DIRENTL_FN3SZ]; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;31-28
&lt;/span&gt;}  &lt;span style="color: #7f007f;"&gt;__attribute__&lt;/span&gt; ((packed)) &lt;span style="color: #218a21;"&gt;fatDirectoryEntryLFN_t&lt;/span&gt;;
&lt;/pre&gt;




&lt;p&gt;
These long file name (LFN) structures are identified by the &lt;code&gt;RHSV&lt;/code&gt;
flags being simultaneously set and precede their associated &lt;code&gt;8.3&lt;/code&gt;
entry (SFN) in the directory table. The &lt;code&gt;8.3&lt;/code&gt; directory entry includes
a shortened version of the long file name and the object's associated
metadata (i.e. size, starting cluster, etc).
&lt;/p&gt;
&lt;p&gt;
Each LFN entry includes a sequence number indicating its order. The
entries are stored in highest to lowest order in the directory table
making it easier to determine their total number.  The last LFN entry
has a a sequence number of 1 bitwise &lt;code&gt;OR&lt;/code&gt;'d with the value &lt;code&gt;0x40&lt;/code&gt;.
Below is a macro that can be used to check for the &lt;code&gt;0x40&lt;/code&gt; marker:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTL_LASTSEQ&lt;/span&gt; (0x40)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRENTL_ISLASTSEQ&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;x&lt;/span&gt;) \
  (((x) &amp;amp; FAT_DIRENTL_LASTSEQ) == FAT_DIRENTL_LASTSEQ)
&lt;/pre&gt;




&lt;p&gt;
The following is a simple example of an LFN and SFN directory table sequence:
&lt;/p&gt;



&lt;pre class="example"&gt;
| ENTRY# | TYPE | SEQ# |
|--------+------+------|
|      1 | LFN  | 0x04 |
|      2 | LFN  | 0x03 |
|      3 | LFN  | 0x02 |
|      4 | LFN  | 0x41 |
|      5 | SFN  |   -- |
&lt;/pre&gt;




&lt;p&gt;
Each LFN entry has three fields for storing file name characters. Some
or all of an LFN entry's text fields may contain file name
characters. Those that do will have a non-null value in the first
byte. Below are macros that can be used to test each field for valid
contents:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_DIRENTL_FNUNUSED&lt;/span&gt;  (0x00)

&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRENTL_FN1VALID&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;pfatDEL&lt;/span&gt;) \
  (FAT_DIRENTL_FNUNUSED  != (pfatDEL)-&amp;gt;filename1[0])
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRENTL_FN2VALID&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;pfatDEL&lt;/span&gt;) \
  (FAT_DIRENTL_FNUNUSED  != (pfatDEL)-&amp;gt;filename2[0])
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;FAT_DIRENTL_FN3VALID&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;pfatDEL&lt;/span&gt;) \
  (FAT_DIRENTL_FNUNUSED  != (pfatDEL)-&amp;gt;filename3[0])
&lt;/pre&gt;




&lt;p&gt;
As with other FAT structures, these fields lack null termination so
they must be handled similarly to other on-disk text fields.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, unlike the other on-disk structures, the LFN entry text
fields use a &lt;a href="http://en.wikipedia.org/wiki/Utf-16"&gt;UTF-16&lt;/a&gt; UNICODE encoding. I prefer my tangents one at a
time and since I don't have much experience in dealing with UTF-16 in
C I decided to hack a shortcut (I warned you). Since I know my test
images will only use &lt;a href="http://en.wikipedia.org/wiki/Utf-8"&gt;UTF-8&lt;/a&gt; characters I decided to only copy the
low-order byte of the UTF-16 characters. I accomplished this by
calling &lt;code&gt;utilCopyNameChars()&lt;/code&gt; with a &lt;code&gt;skip&lt;/code&gt; argument value of 2 and a &lt;code&gt;maxChars&lt;/code&gt; argument value of the field length divided by 2. Not
my proudest coding moment but it worked.
&lt;/p&gt;
&lt;p&gt;
To handle LFN entries, I modified my original directory table
processing &lt;code&gt;for&lt;/code&gt; loop to include a sub &lt;code&gt;while&lt;/code&gt; loop to iterate over
the LFN entries.  The code fragment below shows the modified loop
along with comments documenting the (many) assumptions made (the
remainder of &lt;code&gt;frDirProcessEntries()&lt;/code&gt; remained the same):
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
fatDirectoryTable = (&lt;span style="color: #218a21;"&gt;fatDirectoryEntrySFN_t&lt;/span&gt; *) pdirBuff;
numFatDirTableEntries = sizeBuff / 
                        &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatDirectoryEntrySFN_t);

&lt;span style="color: #7f007f;"&gt;for&lt;/span&gt; (entryIndex = 0;
     entryIndex &amp;lt; numFatDirTableEntries;
     entryIndex++) {

  &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (!(FAT_DIRENTS_FNVALID(&amp;amp;(fatDirectoryTable[entryIndex])))) {
    &lt;span style="color: #7f007f;"&gt;continue&lt;/span&gt;;
  }
  entryCount++;

  pDirEntry = (&lt;span style="color: #218a21;"&gt;frDirEntry_t&lt;/span&gt; * )malloc(&lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(frDirEntry_t));
  memset(pDirEntry, 0, &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(frDirEntry_t));

  ptmp  = (&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *) &amp;amp;(pDirEntry-&amp;gt;filename);

  &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRATT_ISLFN(fatDirectoryTable[entryIndex].attributes)) {
    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;N.B. - Hack alert. Rudimentary support for long file names.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that LFN entries are valid (no checksum checking).
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that last LFN marked with end of sequence flag
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that file names are in UTF-16.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that all LFN entries exist and are in proper order.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that LFN and SFN entries are contiguous.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Assumes that file name is less than 256 characters long.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//&lt;/span&gt;&lt;span style="color: #b12121;"&gt;
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Iterates over LFN entries, copies filename to pDirEntry.
&lt;/span&gt;    &lt;span style="color: #b12121;"&gt;//        &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Leaves entryIndex at the associated SFN.
&lt;/span&gt;
    &lt;span style="color: #218a21;"&gt;fatDirectoryEntryLFN_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pfatDirEntLFN&lt;/span&gt; = &lt;span style="color: #5e9d9f;"&gt;NULL&lt;/span&gt;;
    &lt;span style="color: #7f007f;"&gt;do&lt;/span&gt; {
      pfatDirEntLFN = 
        (&lt;span style="color: #218a21;"&gt;fatDirectoryEntryLFN_t&lt;/span&gt; *)&amp;amp;(fatDirectoryTable[entryIndex]);

      assert(FAT_DIRATT_ISLFN(fatDirectoryTable[entryIndex].attributes));
      assert(entryIndex &amp;lt; numFatDirTableEntries);

      &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRENTL_FN1VALID(pfatDirEntLFN)) {      
        ptmp += 
               utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pfatDirEntLFN-&amp;gt;filename1,
                            ptmp,
                            (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTL_FN1SZ/2,
                            2);
      }

      &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRENTL_FN2VALID(pfatDirEntLFN)) {      
        ptmp += 
              utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pfatDirEntLFN-&amp;gt;filename2,
                            ptmp,
                            (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTL_FN2SZ/2,
                            2);
      }

      &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRENTL_FN3VALID(pfatDirEntLFN)) {      
        ptmp += 
              utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pfatDirEntLFN-&amp;gt;filename3,
                            ptmp,
                            (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTL_FN3SZ/2,
                            2);
      }

      entryIndex++;

    } &lt;span style="color: #7f007f;"&gt;while&lt;/span&gt; (!(FAT_DIRENTL_ISLASTSEQ(pfatDirEntLFN-&amp;gt;sequenceNum)));
  } &lt;span style="color: #7f007f;"&gt;else&lt;/span&gt; {

    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;copy file name and extension&lt;/span&gt;   
    ptmp += 
       utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)fatDirectoryTable[entryIndex].filename,
                  ptmp,
                  (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTS_FNSZ,
                  1);

    &lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (FAT_DIRENTS_FNVALID(&amp;amp;(fatDirectoryTable[entryIndex]))) {
      *ptmp = &lt;span style="color: #bb8e8e;"&gt;'.'&lt;/span&gt;;
      ptmp++;
      ptmp += 
        utilCopyNameChars((&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)fatDirectoryTable[entryIndex].extension,
                          ptmp,
                          (&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;) FAT_DIRENTS_EXTSZ,
                          1);
    }
  }
  *ptmp = &lt;span style="color: #bb8e8e;"&gt;'\0'&lt;/span&gt;;

  pDirEntry-&amp;gt;attributes   = fatDirectoryTable[entryIndex].attributes;
  pDirEntry-&amp;gt;fileSize     = fatDirectoryTable[entryIndex].fileSize;
  pDirEntry-&amp;gt;firstCluster = fatDirectoryTable[entryIndex].firstCluster;

  pDirEntry-&amp;gt;pParentDir    = pParentDir;
  pDirEntry-&amp;gt;pNextSibling  = pParentDir-&amp;gt;pDirEntries;
  pParentDir-&amp;gt;pDirEntries  = pDirEntry;
  pDirEntry = &lt;span style="color: #5e9d9f;"&gt;NULL&lt;/span&gt;;
}
&lt;/pre&gt;




&lt;p&gt;
With this LFN hack in place, re-running the program against the test
image produced:
&lt;/p&gt;



&lt;pre class="example"&gt;
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.6.2
BuildVersion:   10C540

$ wc -l fatrecover.c 
     632 fatrecover.c

$ make clean all
rm -r fatrecover fatrecover.dSYM 
gcc -g -Wall fatrecover.c -o fatrecover

$ fatrecover frtest1.dmg 

=== FAT RECOVER v0.0 ===

Opening file frtest1.dmg................OK
Reading boot sector.....................OK
Processing boot sector..................OK
Reading root dir........................OK

&amp;lt;BOOT SECTOR INFO REMOVED FOR BREVITY&amp;gt;

LISTING DIR: ROOT
 ATTR     SIZE            NAME          1ST CLUSTER
------  --------  --------------------  -----------
.....A     92889            4.2.04.jpg  (0x00000a)
.H..D.         0            .fseventsd  (0x000008)
.H..D.         0              .Trashes  (0x000002)
.H...A      4096            ._.Trashes  (0x000003)
...V.A         0              FRTEST1.  (00000000)
&lt;/pre&gt;




&lt;p&gt;
Ah, much better. The first entry is the JPG image file which is 92889
bytes long and begins in cluster &lt;code&gt;0xa&lt;/code&gt;. The &lt;code&gt;.fseventsd&lt;/code&gt; and
&lt;code&gt;.Trashes&lt;/code&gt; entries are artifacts from mounting the disk image on an
OSX system - they can be ignored. The last entry (which actually
appears first in the on-disk table) is a volume label as indicated by
the &lt;code&gt;V&lt;/code&gt; attribute flag.
&lt;/p&gt;
&lt;p&gt;
As a sanity check, mounting the disk image and listing the root directory
from OSX produces:
&lt;/p&gt;



&lt;pre class="example"&gt;
$ hdiutil attach frtest1.dmg 
/dev/disk1                      /Volumes/FRTEST1

$ls -ao /Volumes/FRTEST1/
total 232
drwxrwxrwx  1 jcardent  16384 Dec 24 10:52 .
drwxrwxrwt@ 4 root        136 Dec 24 10:52 ..
drwxrwxrwx@ 1 jcardent   2048 Dec 24 10:52 .Trashes
-rwxrwxrwx  1 jcardent   4096 Dec 14 05:28 ._.Trashes
drwxrwxrwx  1 jcardent   2048 Dec 24 10:52 .fseventsd
-rwxrwxrwx  1 jcardent  92889 Dec 14 05:28 4.2.04.jpg
&lt;/pre&gt;




&lt;p&gt;
Other than being in opposite orders the two listings match. Woot!
&lt;/p&gt;
&lt;p&gt;
Before declaring total victory, there is one major shortcoming with
the LFN hack shown above - it requires an entire directory table to be
read into an in-memory buffer. My original intent was to process
directory tables one sector or cluster at a time to minimize the
memory footprint. However, the hack above can't handle LFN entries
spanning a sector or cluster boundary as this would involve separate
calls to &lt;code&gt;frDirProcessEntries()&lt;/code&gt;. I thought of a couple of ways to
achieve both goals but decided that they would unnecessarily
complicate the discussion as we'll only be dealing with small disk
images in this series. If you plan to use this code to handle
arbitrarily large file systems you will probably want to consider
solving this problem.
&lt;/p&gt;
&lt;p&gt;
In the next post we'll tackle the last file system area, the
file allocation table.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7764641655086489926?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7764641655086489926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7764641655086489926'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_24.html' title='Recovering Deleted JPEGs from a FAT File System - Part 5'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2736550539739745974</id><published>2009-12-21T18:17:00.001-05:00</published><updated>2009-12-21T18:20:10.517-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Oh Christmas Tree, Oh Christmas Tree</title><content type='html'>&lt;p&gt;The other day my son, age four, noticed that a string of lights were
out on our Christmas tree. Last year, we switched to the new LED
lights so I wasn't sure how long they should last or how to fix them.
&lt;/p&gt;
&lt;p&gt;
I assumed that, like incandescent tree lights, a single failing LED
would prevent the others from lighting. Unfortunately, we discarded
the original box and therefore did not have any replacement bulbs
(assuming that they came with some).
&lt;/p&gt;
&lt;p&gt;
I picked up a new set of lights but apparently all "white" LEDs are
not exactly the same - the new set had a bluer tint that didn't match
the remaining sets on the tree. Oh bother, suddenly this became a
"project". With the beauty of our tree at stake failure was &lt;i&gt;not&lt;/i&gt; an
option.
&lt;/p&gt;
&lt;p&gt;
A Google search led me to this &lt;a href="http://www.ciphersbyritter.com/RADELECT/LITES/LEDLITES.HTM"&gt;extremely informative web page&lt;/a&gt;. It
turns out these LED lights use a much simpler circuit than I first
imagined. No rectifiers, no smoothing capacitors - just 30 LEDs in
series with a current limiting resistor. That explains the blinking
that I sometimes find irritating.
&lt;/p&gt;
&lt;p&gt;
My plan was to replace the faulting LED with one from the new
set. First, however, I had to find it.  I used a 9V battery and
resistor to build a test rig, removed each LED, and tested
it. Eventually, I found the broken one and of course it was the first
one in the string that had the current limiting resistor soldered to
it. So much for a quick replacement.
&lt;/p&gt;
&lt;p&gt;
After a quick trip to the workshop to solder the resistor onto the new
LED, we once again had a working set of really-white lights - except
for one bluish one. Oh well, good enough is good enough. 
&lt;/p&gt;
&lt;p&gt;
An unexpected bonus from having one bluish LED is that my son now
considers it a game to "find" the different one - it doesn't move but
that doesn't appear to matter to a four year old :-).
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2736550539739745974?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2736550539739745974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2736550539739745974'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/oh-christmas-tree-oh-christmas-tree.html' title='Oh Christmas Tree, Oh Christmas Tree'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-451943603879796067</id><published>2009-12-19T06:12:00.002-05:00</published><updated>2009-12-19T06:24:29.131-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 4</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 4 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
Now that we've created a test data set and covered the high-level
structure of a FAT file system, it's time to get our hands dirty with
some code. &lt;i&gt;Enough talk, more action!!!&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
Follow the "read more" link for a detailed description of how to read
and interpret a FAT file system boot sector.
&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;
The following C structure defines the first 36 bytes of the FAT boot
sector which is common across all three variants - FAT12, FAT16, and
FAT32.
&lt;/p&gt;


&lt;pre class="src src-c"&gt;
&lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;COMMON BOOT SECTOR
&lt;/span&gt;&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_COMBS_OEMSZ&lt;/span&gt; (8)

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; {
  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;jmpInstruction&lt;/span&gt;[3];            &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;02-00
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;oemName&lt;/span&gt;[FAT_COMBS_OEMSZ];     &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;10-03
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;bytesPerSector&lt;/span&gt;;               &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;12-11
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;sectorsPerCluster&lt;/span&gt;;            &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;13-13
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;reservedSectors&lt;/span&gt;;              &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;15-14
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;numberOfFATs&lt;/span&gt;;                 &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;16-16
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numberOfRootDirEntries&lt;/span&gt;;       &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;18-17
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numberOfSectorsShort&lt;/span&gt;;         &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;20-19
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;mediaType&lt;/span&gt;;                    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;21-21
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;sectorsPerFAT&lt;/span&gt;;                &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;23-22
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;sectorsPerTrack&lt;/span&gt;;              &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;25-24
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numberOfHeads&lt;/span&gt;;                &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;27-26
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint32_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numberHiddenSectors&lt;/span&gt;;          &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;31-28
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint32_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;numberOfSectorsLong&lt;/span&gt;;          &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;35-32
&lt;/span&gt;} &lt;span style="color: #7f007f;"&gt;__attribute__&lt;/span&gt; ((packed))  &lt;span style="color: #218a21;"&gt;fatBootSectorCommon_t&lt;/span&gt;;
&lt;/pre&gt;




&lt;p&gt;
The &lt;code&gt;packed&lt;/code&gt; &lt;a href="http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html"&gt;attribute&lt;/a&gt; instructs the compiler to place the structure
members adjacent to each other in memory. Without this, the compiler
might insert padding between structure members to optimize their
alignment. In this case such padding would cause a mismatch between
the in-memory and on-disk structure layouts making reading the boot
sector more cumbersome.
&lt;/p&gt;
&lt;p&gt;
As might be expected, the common part of the boot sector provides the
information needed to understand the file system's on-disk
structure. Particular fields of interest are:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;oemName&lt;/code&gt; ASCII description of the OS used to format the file
system. OS dependent, no null termination.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bytesPerSector&lt;/code&gt;: sector size in bytes - &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_15.html"&gt;see part 3&lt;/a&gt; for a brief
explanation.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sectorsPerCluster&lt;/code&gt;: data area cluster size in sectors - &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_15.html"&gt;see part 3&lt;/a&gt;
for a brief explanation.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reservedSectors&lt;/code&gt;: number of sectors consumed by the boot sector and
any additional reserved space.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;numberOfFATs&lt;/code&gt;: multiple copies of the file allocation table may be
kept to guard against disk errors, this field indicates the number
of copies in the FAT area.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;numberOfRootDirEntries&lt;/code&gt;: size of the root directory area in terms
of the maximum number of directory entries. Recall that in both
FAT12 and FAT16 space is reserved after the FAT area to store the
root directory's contents. The size of a directory entry, 32 bytes,
is needed to compute the size of the root directory area from this
parameter.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;numberOfSectorsShort&lt;/code&gt;: total number of sectors in the volume,
only valid if the count is less than 64K. Otherwise the value of
this field will be 0 and &lt;code&gt;numberOfSectorsLong&lt;/code&gt; will contain the
correct value.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sectorsPerFAT&lt;/code&gt;: size in sectors of a single copy of the file
allocation table in the FAT area.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;numberOfSectorsLong&lt;/code&gt;: total number of sectors in the volume,
only valid if the count is greater than or equal to 64K. Otherwise
the value of this field will be 0 and &lt;code&gt;numberOfSectorsShort&lt;/code&gt; will
contain the correct value.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remainder of a FAT12 or FAT16 boot sector has the following
format:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;FAT12 &amp;amp; FAT16 EXTENDED BOOT SECTOR
&lt;/span&gt;&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_EXTBS_START&lt;/span&gt; (36)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_EXTBS_VLSZ&lt;/span&gt;  (11)
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;FAT_EXTBS_FSSZ&lt;/span&gt;  (8)

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; {
  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;driveNumber&lt;/span&gt;;                    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;36-36
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;reserved&lt;/span&gt;;                       &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;37-37
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;bootSignature&lt;/span&gt;;                  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;38-38
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint32_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;volumeID&lt;/span&gt;;                       &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;42-39
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;volumeLabel&lt;/span&gt;[FAT_EXTBS_VLSZ];    &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;53-43
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;filesystemType&lt;/span&gt;[FAT_EXTBS_FSSZ]; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;61-54
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint8_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;reserved2&lt;/span&gt;[448];                 &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;509-62
&lt;/span&gt;  &lt;span style="color: #218a21;"&gt;uint16_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;signature&lt;/span&gt;;                      &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;511-510
&lt;/span&gt;} &lt;span style="color: #7f007f;"&gt;__attribute__&lt;/span&gt; ((packed))  &lt;span style="color: #218a21;"&gt;fatBootSectorExt1216_t&lt;/span&gt;;
&lt;/pre&gt;




&lt;p&gt;
Much of the extended boot sector is reserved for the possible
inclusion of boot code. For this project, only the following extended
boot sector fields are of marginal interest:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;volumeID&lt;/code&gt;: numerical ID - value dependent on the OS/tool used to
create the file system.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volumeLabel&lt;/code&gt;: ASCII name for the file system, used by some OSs for
the mount-point name. No null termination.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fileSystemType&lt;/code&gt;: ASCII description of file system type. No standard
values, examples include "FAT", "FAT12", and "FAT16". No null
termination.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following C code fragment demonstrates the use of these structures
to read the common and extended boot sectors from an image file opened
with the descriptor &lt;code&gt;fdImage&lt;/code&gt;.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #218a21;"&gt;fatBootSectorCommon_t&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;bootSectorCommon&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;fatBootSectorExt1216_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;bootSectorExt1216&lt;/span&gt;;

fseek(fdImage, 0, SEEK_SET);
fread(&amp;amp;(bootSectorCommon), 
      &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatBootSectorCommon_t), 1, fdImage);

fseek(fdImage, FAT_EXTBS_START, SEEK_SET);
fread(&amp;amp;(bootSectorExt1216), 
      &lt;span style="color: #7f007f;"&gt;sizeof&lt;/span&gt;(fatBootSectorExt1216_t), 1, fdImage); 
&lt;/pre&gt;




&lt;p&gt;
Thanks to the use of the &lt;code&gt;packed&lt;/code&gt; attribute, the on-disk structures
can be read directly into their in-memory counterparts and accessed
using the standard C structure idioms. 
&lt;/p&gt;
&lt;p&gt;
The following code fragment shows how to use the boot sector
information to calculate the location and size of the major file
system areas.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;ALIGN&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;size&lt;/span&gt;, &lt;span style="color: #b7850a;"&gt;alignment&lt;/span&gt;) \
 (((size) + (alignment-1)) / (alignment))

&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;volSizeSectors&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;fatFirstSector&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;fatSizeSectors&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;rootdirFirstSector&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;rootdirSizeSectors&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;clustersFirstSector&lt;/span&gt;;
&lt;span style="color: #218a21;"&gt;size_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;clustersSizeClusters&lt;/span&gt;;

&lt;span style="color: #7f007f;"&gt;if&lt;/span&gt; (bootSectorCommon.numberOfSectorsShort &amp;gt; 0) {
  volSizeSectors = 
    bootSectorCommon.numberOfSectorsShort;
  
} &lt;span style="color: #7f007f;"&gt;else&lt;/span&gt; {
  volSizeSectors = 
    bootSectorCommon.numberOfSectorsLong;
}


fatFirstSector = 
  bootSectorCommon.reservedSectors;

fatSizeSectors = 
  bootSectorCommon.sectorsPerFAT * 
  bootSectorCommon.numberOfFATs;

  
rootdirFirstSector = 
  fatFirstSector + 
  fatSizeSectors;

rootdirSizeSectors = 
  ALIGN((bootSectorCommon.numberOfRootDirEntries * 32,
         bootSectorCommon.bytesPerSector);


clustersFirstSector = 
  rootdirFirstSector + 
  rootdirSizeSectors;

clustersSizeClusters = 
  (volSizeSectors - clustersFirstSector) /
  bootSectorCommon.sectorsPerCluster;
&lt;/pre&gt;




&lt;p&gt;
As mentioned above, calculating the size of the root directory area
requires knowing the size of a directory entry. We'll discuss the root
directory in a future post - for now it is sufficient to know that
each entry is 32 bytes.
&lt;/p&gt;
&lt;p&gt;
Printing the boot sector's contents to a console is a fairly simply
matter with the only complication being that the ASCII information
should first be copied to null-terminated strings.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;oemName&lt;/span&gt;[FAT_COMBS_OEMSZ+1];
&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;volumeLabel&lt;/span&gt;[FAT_EXTBS_VLSZ+1];
&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;filesystemType&lt;/span&gt;[FAT_EXTBS_FSSZ+1];

strncpy(oemName,        
        (&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pBootSectorCommon-&amp;gt;oemName, 
        FAT_COMBS_OEMSZ);
strncpy(volumeLabel,    
        (&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pBootSectorExt1216-&amp;gt;volumeLabel, 
        FAT_EXTBS_VLSZ);
strncpy(filesystemType, 
        (&lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *)pBootSectorExt1216-&amp;gt;filesystemType, 
        FAT_EXTBS_FSSZ);

oemName[FAT_COMBS_OEMSZ]       = 0;
volumeLabel[FAT_EXTBS_VLSZ]    = 0;
filesystemType[FAT_EXTBS_FSSZ] = 0;
&lt;/pre&gt;




&lt;p&gt;
Voila! This should be enough information to write a simple program
capable of reading a FAT file system boot sector, locating the major
areas, and printing the information to a console. 
&lt;/p&gt;
&lt;p&gt;
As an example, below is the output of the program I am (re)developing
alongside these posts from which the code fragments above were
taken. The output was generated by running the program against the
test disk image created in &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_14.html"&gt;part 2&lt;/a&gt; of this &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;series&lt;/a&gt;. Recall that my
development platform is an Apple MacBookPro running OS X Snow Leopard
(10.6.2).
&lt;/p&gt;



&lt;pre class="example"&gt;
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.6.2
BuildVersion:   10C540

$ wc -l fatrecover.c 
     306 fatrecover.c

$ make all
gcc -g -Wall fatrecover.c -o fatrecover

$ ./fatrecover frtest1.dmg 

=== FAT RECOVER v0.0 ===

Opening file frtest1.dmg................OK
Reading boot sector.....................OK
Processing boot sector..................OK

BOOT SECTOR:
          OEMName: (BSD  4.4)
         volumeID: 63260cf5
     Volume Label: FRTEST1    
           FSType: FAT16   
     Bytes/Sector: 512
  Sectors/Cluster: 4
 Reserved Sectors: 1
   Hidden Sectors: 0
 Sectors/Vol (2B): 32768
 Sectors/Vol (4B): 0
      Sectors/FAT: 32
        # of FATs: 2
  RootDir Entries: 512

MAJOR STRUCTURES:
      AREA    OFFSET      SIZE
----------  --------  --------
 BOOT+RESV  00000000  0x000001 (sectors)
       FAT  0x000001  0x000040 (sectors)
   ROOTDIR  0x000041  0x000020 (sectors)
      DATA  0x000061  0x001fe7 (clusters)
&lt;/pre&gt;




&lt;p&gt;
Note that the structure offsets are in units of sectors.
&lt;/p&gt;
&lt;p&gt;
From the output we can see that the:
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
volume name is "FRTEST1", matches the name specified when the disk
image was created.
&lt;/li&gt;
&lt;li&gt;
sector and cluster sizes are 512 and 2048 bytes respectively.
&lt;/li&gt;
&lt;li&gt;
FAT area starts at sector 1 and is 64 sectors long (2 * 32).
&lt;/li&gt;
&lt;li&gt;
root directory can contain up to 512 entries.
&lt;/li&gt;
&lt;li&gt;
data area contains 0x1fe7 or 8167 clusters.
&lt;/li&gt;
&lt;li&gt;
volume contains a total of 32768 sectors - 16MB as expected.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To sanity check the output, let's validate the size of the data
area.
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;
&lt;col align="left"&gt;&lt;/col&gt;&lt;col align="right"&gt;&lt;/col&gt;&lt;col align="left"&gt;&lt;/col&gt;
&lt;tr&gt;&lt;td&gt;Total Sectors&lt;/td&gt;&lt;td&gt;32768&lt;/td&gt;&lt;td&gt;0x8000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Less Boot Sector&lt;/td&gt;&lt;td&gt;32767&lt;/td&gt;&lt;td&gt;0x7FFF&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Less FATs&lt;/td&gt;&lt;td&gt;32703&lt;/td&gt;&lt;td&gt;0x7FBF&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Less RootDir&lt;/td&gt;&lt;td&gt;32671&lt;/td&gt;&lt;td&gt;0x7F9F&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Divided by 4&lt;/td&gt;&lt;td&gt;8167&lt;/td&gt;&lt;td&gt;0x1FE7&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;(remaining sectors)&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;
The check looks good. The remaining three sectors further validate
the calculations. Only the boot sector consumed an odd number of
sectors - one - which implies that there should be three or less
unused sectors in the data area (since the cluster size is four
sectors).
&lt;/p&gt;
&lt;p&gt;
As a second sanity check, let's make sure the file allocation table's
are an appropriate size. Since FAT entries are 16 bits (2 bytes), we
know that each FAT contains &lt;code&gt;(32*512)/2 = 8192&lt;/code&gt; entries which is
enough to represent the 8167 clusters in the data area.
&lt;/p&gt;
&lt;p&gt;
The sanity checks appear to pass - it looks like the above code
works. In the next post we'll skip ahead to processing the root
directory before tacking the FAT.
&lt;/p&gt;
&lt;p&gt;
&lt;b&gt;Addendum&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
One important detail omitted from the main-line discussion above is
that all FAT file system data structures are stored on-disk in &lt;a href="http://en.wikipedia.org/wiki/Little_endian"&gt;little endian order&lt;/a&gt;. As I am using a little endian machine (Intel Core 2 Duo)
the example code above uses the value of all multi-byte fields
directly. A big endian machine, however, would have to convert all of
the multi-byte field values from little to big endian before using
them.
&lt;/p&gt;
&lt;p&gt;
When I originally did this project in 2004, I used ah PowerPC Apple
PowerBook which had a big endian architecture. As a result, my
original program had to do the endian conversion. Below are the macros
I wrote to do the necessary byte swapping.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #b12121;"&gt;//  &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Macros to byte swap to change endianess
&lt;/span&gt;&lt;span style="color: #b12121;"&gt;//&lt;/span&gt;&lt;span style="color: #b12121;"&gt;
&lt;/span&gt;&lt;span style="color: #d96fd5;"&gt;#if&lt;/span&gt; BYTE_ORDER == BIG_ENDIAN


&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost16&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;)                          \
&lt;span style="color: #7f007f;"&gt;do&lt;/span&gt; {                                            \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;buff&lt;/span&gt; = (&lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *) _x; \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;tmp&lt;/span&gt;;                          \
    tmp     = buff[1];                          \
    buff[1] = buff[0];                          \
    buff[0] = tmp;                              \
   } &lt;span style="color: #7f007f;"&gt;while&lt;/span&gt;(0)


&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost24&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;)                          \
&lt;span style="color: #7f007f;"&gt;do&lt;/span&gt; {                                            \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;buff&lt;/span&gt; = (&lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *) _x; \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;tmp&lt;/span&gt;;                          \
    tmp     = buff[2];                          \
    buff[2] = buff[0];                          \
    buff[0] = tmp;                              \
   } &lt;span style="color: #7f007f;"&gt;while&lt;/span&gt;(0)


&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost32&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;)                          \
&lt;span style="color: #7f007f;"&gt;do&lt;/span&gt; {                                            \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;buff&lt;/span&gt; = (&lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; *) _x; \
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;tmp&lt;/span&gt;;                          \
    tmp     = buff[0];                          \
    buff[0] = buff[3];                          \
    buff[3] = tmp;                              \
    tmp     = buff[1];                          \
    buff[1] = buff[2];                          \
    buff[2] = tmp;
   } &lt;span style="color: #7f007f;"&gt;while&lt;/span&gt;(0)

&lt;span style="color: #d96fd5;"&gt;#else&lt;/span&gt;

&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost16&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;) 
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost24&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;) 
&lt;span style="color: #d96fd5;"&gt;#define&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;LEToHost32&lt;/span&gt;(&lt;span style="color: #b7850a;"&gt;_x&lt;/span&gt;) 

&lt;span style="color: #d96fd5;"&gt;#endif&lt;/span&gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-451943603879796067?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/451943603879796067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/451943603879796067'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_19.html' title='Recovering Deleted JPEGs from a FAT File System - Part 4'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-979578721949492108</id><published>2009-12-15T18:39:00.004-05:00</published><updated>2009-12-15T18:46:57.700-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>An Incredible Library</title><content type='html'>&lt;p&gt;I have never been more jealous in my life than I am of &lt;a href="http://www.wired.com/techbiz/people/magazine/16-10/ff_walker?currentPage=all"&gt;Jay Walker's personal library&lt;/a&gt;.
&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.wired.com/images/article/magazine/1610/ff_walker_f.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 315px; height: 247px;" src="http://www.wired.com/images/article/magazine/1610/ff_walker_f.jpg" border="0" alt="" /&gt;&lt;/a&gt;

&lt;p&gt;
If I had copious amounts of money, this is what I would do with it -
books, interesting gadgets, and lots of wood. I don't know if I would
ever leave the house :-).
&lt;/p&gt;
&lt;p&gt;
Even just working in such an aesthetic environment would be wonderful.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-979578721949492108?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/979578721949492108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/979578721949492108'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/incredible-library.html' title='An Incredible Library'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3197762112437609632</id><published>2009-12-15T06:17:00.002-05:00</published><updated>2009-12-15T06:19:01.415-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 3</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 3 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
The &lt;a href="http://en.wikipedia.org/wiki/FAT_file_system"&gt;FAT file system&lt;/a&gt; was invented in 1980 by &lt;a href="http://en.wikipedia.org/wiki/Tim_Paterson"&gt;Tim Paterson&lt;/a&gt; while
developing &lt;a href="http://en.wikipedia.org/wiki/86-DOS"&gt;86-DOS&lt;/a&gt;, the precursor of &lt;a href="http://en.wikipedia.org/wiki/MS-DOS"&gt;MS-DOS&lt;/a&gt;. Since its creation, FAT
has gone through a number of evolutions to accommodate growing disk
sizes, and provide more capabilities. Although FAT is nearly 30 years
old, it remains in common use due to its ubiquitous OS support and
simplicity - both important factors for embedded consumer devices like
digital cameras.
&lt;/p&gt;
&lt;p&gt;
FAT's history is &lt;a href="http://en.wikipedia.org/wiki/FAT_file_system#History"&gt;complex&lt;/a&gt;, a retelling could consume an entire series
of posts itself. For this project, it is sufficient to know that there
are three main variants - FAT12, FAT16, and FAT32 - each successively
supporting larger maximum disk sizes. Generally speaking, the on-disk
format of all three variants is very similar, so much so that code
developed to grok one format can, with reasonable effort, be hacked to
grok another&lt;sup&gt;&lt;a class="footref" name="fnr.1" href="#fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt;. In this project, we'll be working with a FAT16 
file system which &lt;code&gt;hdiutil&lt;/code&gt; can be explicitly requested to create by
using the argument &lt;code&gt;-fs "MS-DOS FAT16"&lt;/code&gt;&lt;sup&gt;&lt;a class="footref" name="fnr.2" href="#fn.2"&gt;2&lt;/a&gt;&lt;/sup&gt;.
&lt;/p&gt;
&lt;p&gt;
The following diagram depicts the high-level, on-disk structure of a
FAT16 file system (not drawn to scale).
&lt;/p&gt;


&lt;pre class="example"&gt;
+---------+-------+--------+-------+---------------------+
|   BOOT  | RESV  |   FAT  | ROOT  |  DATA ............. |
|  SECTOR | (OPT) |        | DIR   |  AREA ............. |
+---------+-------+--------+-------+---------------------+
0                                                        N
&lt;/pre&gt;




&lt;p&gt;
In computer storage, capacity is divided into fixed sized units called
&lt;a href="http://en.wikipedia.org/wiki/Disk_sector"&gt;sectors&lt;/a&gt; - typically 512 bytes long&lt;sup&gt;&lt;a class="footref" name="fnr.3" href="#fn.3"&gt;3&lt;/a&gt;&lt;/sup&gt;. The sector is the
fundamental unit for addressing storage and smallest amount of data
that can be transferred to/from a disk drive. While older disk drives
used an awkward addressing scheme based on &lt;a href="http://en.wikipedia.org/wiki/Cylinder-head-sector"&gt;physical geometry&lt;/a&gt;, newer
drives use a &lt;a href="http://en.wikipedia.org/wiki/Logical_block_addressing"&gt;linear series of sector addresses&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The first sector (offset 0) of a FAT16 file system contains the &lt;code&gt;BOOT SECTOR&lt;/code&gt; which provides critical information about the file system's
organization. Optionally, a number of sectors may be reserved after
the boot sector (&lt;code&gt;RESV&lt;/code&gt;).
&lt;/p&gt;
&lt;p&gt;
The next major data structure is the File Allocation Table (&lt;code&gt;FAT&lt;/code&gt;)
from which the file system gets its name. The FAT is essentially an
array of 16bit elements &lt;sup&gt;&lt;a class="footref" name="fnr.4" href="#fn.4"&gt;4&lt;/a&gt;&lt;/sup&gt; each representing a fixed size portion
of the &lt;code&gt;DATA AREA&lt;/code&gt;. To minimize the FAT's relative size, each element
represents a cluster&lt;sup&gt;&lt;a class="footref" name="fnr.5" href="#fn.5"&gt;5&lt;/a&gt;&lt;/sup&gt;, a power-of-two multiple number of
sectors. In addition to tracking the allocation status of each &lt;code&gt;DATA AREA&lt;/code&gt; cluster, FAT elements are also used to store pointers forming
linked-lists describing the on-disk location of files and
sub-directories spanning multiple clusters.
&lt;/p&gt;
&lt;p&gt;
After the FAT comes the root directory area (&lt;code&gt;ROOT DIR&lt;/code&gt;) used to hold
the information describing the files and sub-directories at the top of
the file system &lt;a href="http://en.wikipedia.org/wiki/Namespace_(computer_science)"&gt;namespace&lt;/a&gt; tree. In FAT12 and FAT16, the root directory
area is a fixed size which limits the numbers of files and
sub-directories that can be stored in it.
&lt;/p&gt;
&lt;p&gt;
The remainder of the disk capacity (&lt;code&gt;DATA AREA&lt;/code&gt;) is essentially a &lt;a href="http://en.wikipedia.org/wiki/Dynamic_memory_allocation"&gt;heap&lt;/a&gt;
allocated as needed to store file and sub-directory data. As mentioned
previously, use of the &lt;code&gt;DATA AREA&lt;/code&gt; is managed by the &lt;code&gt;FAT&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
In the next few posts, I'll describe each of these major areas in
greater detail and present the code needed to interpret them. By the
end, we should essentially have a read-only FAT file system
implementation that will serve as the basis for the remainder of the
project.
&lt;/p&gt;

&lt;div id="footnotes" style="font-size: 0.8em"&gt;
&lt;h3 class="footnotes"&gt;Footnotes: &lt;/h3&gt;
&lt;div id="text-footnotes"&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.1" href="#fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; Here I am ignoring many, many subtle differences and
complications. A thorough discussion of the technical details for all
of FAT's variants is outside the scope of this series. The curious
reader is recommended to read the bountiful information available via
a Google search. Brian Carrier's book, &lt;a href="http://books.google.com/books?id=z5hwQgAACAAJ&amp;amp;dq=file+system+forensic+analysis&amp;amp;ei=BxwmS_-qE4b-zAS04Z30Cg&amp;amp;cd=2"&gt;File System Forensic Analysis&lt;/a&gt;,
is another valuable resource (which sadly wasn't available when I
first did this project in 2004).
&lt;/p&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.2" href="#fnr.2"&gt;2&lt;/a&gt;&lt;/sup&gt; Note that FAT16 file systems have a minimum supported size -
usually 16MB but some tools may allow smaller sizes.
&lt;/p&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.3" href="#fnr.3"&gt;3&lt;/a&gt;&lt;/sup&gt; Some enterprise level disk drives use a 520 byte sector - the
additional 8 bytes are often used to store error checking and recovery
information for &lt;a href="http://en.wikipedia.org/wiki/RAID"&gt;RAID&lt;/a&gt; systems.
&lt;/p&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.4" href="#fnr.4"&gt;4&lt;/a&gt;&lt;/sup&gt; The size of the FAT elements is one of the principal
differences between the file system variants. FAT12 uses 12bit entries
while FAT32 uses 32bit entries. The FAT element size is one of the
factors that determines each variant's maximum supported disk size.
&lt;/p&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.5" href="#fnr.5"&gt;5&lt;/a&gt;&lt;/sup&gt; Other file systems may use the term block instead of cluster.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3197762112437609632?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3197762112437609632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3197762112437609632'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_15.html' title='Recovering Deleted JPEGs from a FAT File System - Part 3'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-720695667550418624</id><published>2009-12-14T05:39:00.002-05:00</published><updated>2009-12-14T05:44:19.023-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 2</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 2 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
Whenever I develop an analysis program I first create a dataset to
test and experiment against. &lt;a href="http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file.html"&gt;In part 1&lt;/a&gt;, I mentioned that I used my
digital camera to create a test data set for the original project
in 2004. For this series of posts, I thought it would be better to use
a data set reproducible by others. Two things are required to
accomplish this goal:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
A collection of commonly available test images
&lt;/li&gt;
&lt;li&gt;
A repeatable method for creating &lt;a href="http://en.wikipedia.org/wiki/Disk_image"&gt;disk images&lt;/a&gt; of &lt;a href="http://en.wikipedia.org/wiki/FAT_file_system"&gt;FAT file systems&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Google search for test images led me to this &lt;a href="http://en.wikipedia.org/wiki/Standard_test_image"&gt;wikipedia page&lt;/a&gt; from
which I selected the University of Southern California &lt;a href="http://sipi.usc.edu/database/index.html"&gt;Signal &amp;amp; Image Processing Institute's data set&lt;/a&gt; - specifically the miscellaneous
corpus. This collection consists of 44 &lt;code&gt;TIFF&lt;/code&gt; images of various
resolutions ranging in size from 64KB to 1MB (17MB in total). Since
&lt;code&gt;JPEG&lt;/code&gt; images are needed for this project, I used the following
&lt;a href="http://www.imagemagick.org/script/index.php"&gt;ImageMagick&lt;/a&gt; command to do the conversion.
&lt;/p&gt;



&lt;pre class="example"&gt;
mogrify -format jpg -quality 90 *.tiff
&lt;/pre&gt;




&lt;p&gt;
After the conversion, the file sizes ranged from 4.8KB to 350KB (3.4MB
in total) - a conveniently sized collection for this project.
&lt;/p&gt;
&lt;p&gt;
In the 2004 project, I used the UNIX &lt;code&gt;&lt;a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/dd.1.html"&gt;dd&lt;/a&gt;&lt;/code&gt; command to make a &lt;a href="http://en.wikipedia.org/wiki/Disk_image"&gt;disk image&lt;/a&gt;
of my camera's memory card. However for this project, I wanted a way
to create disk images without requiring a physical device. One option
considered was to use Linux &lt;a href="http://en.wikipedia.org/wiki/Loop_device"&gt;loop devices&lt;/a&gt; but I wanted to use my
machine-of-choice, an Apple MacBookPro, for this project - an
artificial but important constraint since this is for fun.
&lt;/p&gt;
&lt;p&gt;
After some research, I discovered that Apple's &lt;code&gt;&lt;a href="http://developer.apple.com/Mac/library/documentation/Darwin/Reference/ManPages/man1/hdiutil.1.html"&gt;hdiutil&lt;/a&gt;&lt;/code&gt; utility can
create a suitable image using the following command:
&lt;/p&gt;



&lt;pre class="example"&gt;
hdiutil create \
        -fs "MS-DOS" \
        -megabytes &amp;lt;SIZE&amp;gt; \
        -layout NONE \
        -volname &amp;lt;VOLNAME&amp;gt; &amp;lt;IMAGENAME&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
Executing this command creates the file &lt;code&gt;&amp;lt;IMAGENAME&amp;gt;.dmg&lt;/code&gt; that is an
image of a FAT file system called &lt;code&gt;&amp;lt;VOLNAME&amp;gt;&lt;/code&gt; with a total capacity of
&lt;code&gt;&amp;lt;SIZE&amp;gt;&lt;/code&gt; megabytes. The remaining argument, &lt;code&gt;-layout NONE&lt;/code&gt;, prevents a
&lt;a href="http://en.wikipedia.org/wiki/Partition_table"&gt;partition table&lt;/a&gt; from being added to the image - an unnecessary
complication for this project.
&lt;/p&gt;
&lt;p&gt;
The following example illustrates the OS X terminal commands needed to
create a 16MB disk image with a single &lt;code&gt;JPEG&lt;/code&gt; file on it&lt;sup&gt;&lt;a class="footref" name="fnr.1" href="#fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt;
&lt;/p&gt;



&lt;pre class="example"&gt;
$ hdiutil create \
          -fs "MS-DOS" \
          -megabytes 16 \
          -layout NONE \
          -volname FRTEST1 frtest1
..................................................
created: /&amp;lt;PATH&amp;gt;/frtest1.dmg

$ hdiutil attach frtest1.dmg 
/dev/disk1             /Volumes/FRTEST1

$ cp images/4.2.04.jpg //Volumes/FRTEST1//

$ hdiutil detach //Volumes/FRTEST1//
&lt;/pre&gt;




&lt;p&gt;
Using the selected image corpus and above &lt;code&gt;hdituil&lt;/code&gt; terminal commands,
a wide variety of test disk images can be created programmatically via
shell scripts - just what is needed for this project.
&lt;/p&gt;
&lt;p&gt;
In the next post, we'll start down the path of &lt;a href="http://en.wikipedia.org/wiki/Grok"&gt;grokking&lt;/a&gt; a FAT file
system layout.
&lt;/p&gt;

&lt;div id="footnotes" style="font-size:0.8em"&gt;
&lt;h3 class="footnotes"&gt;Footnotes: &lt;/h3&gt;
&lt;div id="text-footnotes"&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.1" href="#fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; Minor changes were made to the example output to remove machine
specific information (i.e. paths) and fit it within the limited size
of the post area.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-720695667550418624?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/720695667550418624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/720695667550418624'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file_14.html' title='Recovering Deleted JPEGs from a FAT File System - Part 2'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-699683040420716463</id><published>2009-12-11T06:28:00.007-05:00</published><updated>2009-12-14T05:16:53.806-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='FATrecover'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Recovering Deleted JPEGs from a FAT File System - Part 1</title><content type='html'>&lt;p&gt;&lt;i&gt;Part 1 in &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;a series of posts&lt;/a&gt; on recovering deleted JPEG files from a FAT file system.&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
In 2004, my in-laws returned from a vacation and had all of the
pictures accidentally deleted from their digital camera. When I heard
the news my first thought was that the data may still be there!
&lt;/p&gt;
&lt;p&gt;
Often, deleting a file only erases its associated file system metadata
(i.e. name, owner, etc) - the file's data is left unchanged. As a
result, a deleted file can sometimes be recovered by finding the
unchanged data fragments and recombining them in the correct order. I
reasoned that if their camera took this metadata-only approach then
there was a good chance that some of the pictures could be recovered.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, my in-laws lived in another state so I couldn't examine
their camera's memory card right away. I suggested that they remove the
card from the camera and bring it with them on their next visit so
that I could analyze it then.
&lt;/p&gt;
&lt;p&gt;
My initial plan was to try one of the many file recovery tools that
already existed. However, I soon began to wonder how hard it would be
to write a program to recover the pictures myself. Finding the
challenge exciting, I immediately began spending all of my spare time
coding up a recovery program. 
&lt;/p&gt;
&lt;p&gt;
After one week, and a lot of caffeine, I had a C program capable of
recovering deleted JPEG files from a FAT file system - the typical
file system used by digital cameras. Using my own camera as a test
bed, the program could reliably recover pictures after performing an
"erase all" operation. Cool!
&lt;/p&gt;
&lt;p&gt;
Unfortunately, the story didn't end as well for my in-laws. When I
finally received their memory card I found that all of the file system
data blocks had the value &lt;code&gt;0xFF&lt;/code&gt; - a clear sign that the entire FLASH
memory had been erased&lt;sup&gt;&lt;a class="footref" name="fnr.1" href="#fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt;. The data was gone.
&lt;/p&gt;
&lt;p&gt;
Despite the unfortunate ending, this project was one of my favorite
spare-time hacks. So much so in fact that I thought it would be fun to
recreate it as a series of blog posts. Over the next few weeks I plan
to write a series of posts under the label &lt;a href="http://jcardente.blogspot.com/search/label/FATrecover"&gt;"FATrecover"&lt;/a&gt; describing how
to develop such a program. By the end of the series, hopefully anyone
with sufficient coding experience will be able to write their own
FAT file system recovery program. 
&lt;/p&gt;

&lt;div id="footnotes" style="font-size:0.8em"&gt;
&lt;h3 class="footnotes"&gt;Footnotes: &lt;/h3&gt;
&lt;div id="text-footnotes"&gt;
&lt;p class="footnote"&gt;&lt;sup&gt;&lt;a class="footnum" name="fn.1" href="#fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; FLASH memory, being a form of EEPROM, cannot be re-written
directly. Instead, the FLASH memory cells must first be returned to an
unwritten state - an operation typically called an "erase". After being
erased, the affected memory cells have a value of &lt;code&gt;0xFF&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-699683040420716463?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/699683040420716463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/699683040420716463'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/recovering-deleted-jpegs-from-fat-file.html' title='Recovering Deleted JPEGs from a FAT File System - Part 1'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-910262931291832313</id><published>2009-12-01T05:46:00.001-05:00</published><updated>2009-12-01T05:48:10.933-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>Behind the Code: Jim Gray</title><content type='html'>&lt;p&gt;One of my engineering role models is the late &lt;a href="http://en.wikipedia.org/wiki/Jim_Gray_(computer_scientist)"&gt;Jim Gray&lt;/a&gt;. I happened
across a &lt;a href="http://channel9.msdn.com/shows/Behind+The+Code/Conversation-with-scientist-engineer-and-database-legend-Jim-Gray/"&gt;Microsoft Channel9 interview&lt;/a&gt; with him and decided to watch
it.
&lt;/p&gt;
&lt;p&gt;
I most appreciated Jim's comments on the following three points:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The importance of first principles
&lt;/li&gt;
&lt;li&gt;
The "risk" that research presents to established products
&lt;/li&gt;
&lt;li&gt;
The role writing played in his getting recognition for what was
actually joint work with others

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the most influential papers that I've read was Jim's (and
Shenoy's) &lt;a href="ftp://ftp.research.microsoft.com/pub/tr/tr-99-100.pdf"&gt;Rules of Thumb in Data Engineering&lt;/a&gt;. This relatively short
paper is packed with insights derived from simple trend data and the
application of first principles. Since that time I've tried to rely on
first-principles while researching new technologies and communicating
the results. I've found this approach to be both highly effective and
differentiating as, unfortunately, the practice doesn't seem to be as
common as it should be.
&lt;/p&gt;
&lt;p&gt;
Although my current position doesn't compare to the one Jim held at
Microsoft, I do run a small research team inside of a large company.
As a result, I found his comments on this topic interesting. In
particular, I was pleased to hear that I have come to a point-of-view
in common with Jim's - research innovations represent increased risk
to an established line of business that is trying to minimize
risk. It's a classic example of the "change vs. control" conflict that
Kotter describes in his seminal paper, &lt;a href="http://harvardbusiness.org/product/what-leaders-really-do-hbr-classic/an/R0111F-PDF-ENG"&gt;What Leaders Really Do&lt;/a&gt;. In
realizing this, I have come to the conclusion that it is my group's
responsibility to reduce the risk of research innovations to a level
that allows the main-line managers to predict with confidence the
cost/time/effort required to bring the new product/feature to
market. As a result, creating new technologies is not sufficient - my
team must also make those technologies consumable to an existing line
of business. This has been perhaps one of the most important
realizations of my career and it was encouraging to hear Jim make
similar comments.
&lt;/p&gt;
&lt;p&gt;
Towards the end of the interview, Jim made it a point to say that much
of "his" work was actually done in collaboration with others. However,
much of the credit has been given to Jim because he took the
additional effort to document the work in the form of papers,
articles, books, etc. This statement matches my own observations of
authors/presenters earning outsized credit and highlights the
importance of communicating effectively in regards to career growth.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-910262931291832313?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/910262931291832313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/910262931291832313'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/12/behind-code-jim-gray.html' title='Behind the Code: Jim Gray'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4074751563071420449</id><published>2009-11-30T19:04:00.002-05:00</published><updated>2009-11-30T19:08:30.501-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Science'/><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>400 Years of the Telescope</title><content type='html'>&lt;p&gt;If you're interested in astronomy then you might enjoy this
PBS documentary, &lt;a href="http://www.400years.org/en/"&gt;400 Years of the Telescope&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
This beautifully filmed show summarizes the history of the telescope
beginning with Galileo's first experiments with looking glasses up to
the Hubble and radio astronomy. Along the way it (briefly) describes
various recent technologies like adaptive optics and techniques for
building very large mirrors.
&lt;/p&gt;
&lt;p&gt;
While not a deeply technical resource, it is a nice way to spend an
hour. In my case, I watched this on my iPhone while waiting for my son
at a doctor's appointment and it was much better than reading
out-of-date magazines - well worth the $1.99 cost.
&lt;/p&gt;
&lt;p&gt;
Watching this made me want to work in an observatory someday, maybe
I've seen the movie &lt;a href="http://www.imdb.com/title/tt0118884/"&gt;Contact&lt;/a&gt; too many times (if that is even
possible) :-).
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4074751563071420449?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4074751563071420449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4074751563071420449'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/400-years-of-telescope.html' title='400 Years of the Telescope'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3166201723234465883</id><published>2009-11-29T18:16:00.000-05:00</published><updated>2009-11-29T18:17:58.930-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><title type='text'>N is a Number</title><content type='html'>&lt;p&gt;Earlier this year I read the book "&lt;a href="http://www.nd.edu/~networks/Linked/index.html"&gt;Linked: The New Science of Networks&lt;/a&gt;" which prominently featured the mathematician &lt;a href="http://en.wikipedia.org/wiki/Paul_Erdős"&gt;Paul Erdős&lt;/a&gt;. The book mentioned a documentary about Erdős entitled "&lt;a href="http://www.imdb.com/title/tt0125425/"&gt;N is a Number&lt;/a&gt;" that I was curious to see. Thanks to Netflix, I finally got the chance last weekend. 
&lt;/p&gt;
&lt;p&gt;
Most importantly, this movie is about Erdős "the person" and not his
mathematics. While the film discusses his mathematical activities and
accomplishments, it makes no material attempt to explain them.
&lt;/p&gt;
&lt;p&gt;
With that focus in mind, I thought this was a nice documentary.  The
footage of Erdős was very entertaining and I suspect conveyed an
accurate sense of his personality. I found the interviews of his
various acquaintances equally entertaining - especially the elderly
English women who recalled Erdős as a young man. Such focus on Erdős'
human qualities really helped cast him as an "exceptional person"
instead of an "un-relatable genius".
&lt;/p&gt;
&lt;p&gt;
As is often the case with people of substance, Erdős suffered hardship
and had a number of significant life experiences - especially during
WWII. Perhaps this explains his generosity and thoughtfulness of
others that was recounted a number of times in the documentary.
&lt;/p&gt;
&lt;p&gt;
I was most surprised to learn of Erdős' nomadic lifestyle - for most
of his life he had no permanent home or job. Instead, he followed his
interests around the world attending conferences and staying with
(very) supportive friends. If an acquaintance on a different continent
came up with an interesting problem, Erdős would hop on a plane with
all of his worldly possessions in two small suitcases and often only a
small amount of money in his pockets. I found his nomadic and
minimalistic lifestyle both impressive and foreign as I can't imagine
being without a "home" to return to. I wonder if this is an insight
into the personal sacrifice needed to achieve greatness beyond having
raw-talent alone.
&lt;/p&gt;
&lt;p&gt;
Clearly Erdős was a genius which I already understood from reading
other references before watching this film. The depth and breadth of
his work was unique and will likely be unmatched for a long time to
come. But, putting his mathematical achievements aside, Erdős still
seemed like an impressive, inspirational, and great man - definitely a
role model worth reflecting on.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3166201723234465883?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3166201723234465883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3166201723234465883'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/n-is-number.html' title='N is a Number'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-495548023839094833</id><published>2009-11-24T15:48:00.002-05:00</published><updated>2009-11-24T19:23:49.782-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>Bug Hunt Wrap-up</title><content type='html'>&lt;p&gt;To wrap up my &lt;a href="http://jcardente.blogspot.com/search/label/BugHunts"&gt;"Bug Hunts"&lt;/a&gt; series, I thought it worthwhile to quickly
reflect on what made these particular bugs so special.
&lt;/p&gt;
&lt;p&gt;
In the series I posted about five bugs:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://jcardente.blogspot.com/2009/09/case-of-errant-dma.html"&gt;The Case of the Errant DMA&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://jcardente.blogspot.com/2009/09/case-of-replayed-queue.html"&gt;The Case of the Replayed Queue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://jcardente.blogspot.com/2009/09/case-of-bloated-firmware.html"&gt;The Case of the Bloated Firmware&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://jcardente.blogspot.com/2009/11/case-of-deadlocked-queues.html"&gt;The Case of the Deadlocked Queues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://jcardente.blogspot.com/2009/11/case-of-corrupted-cache-line.html"&gt;The Case of the Corrupted Cache Line&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since these five represent only a small subset of all the bugs that I
have diagnosed during my career I have to ask myself - what makes
these bugs so special?
&lt;/p&gt;
&lt;p&gt;
To me, the common thread among them is that their resolution involved
a leap of thought based on skill, imagination, concentration, and an
intuitive understanding of the associated technologies. In each case
the answer was not self-evident from the information available and
became clear only after deep thought produced an incredibly rewarding
"ah-ha" moment. The short-term elation from these "ah-ha" moments are
one reason why these bugs hold a special place in my memories.
&lt;/p&gt;
&lt;p&gt;
However, I think the deeper answer to my question is that these bugs
were milestone events that, to varying degrees, demonstrated and
validated the skills that I had worked hard to acquire. The
longer-term feeling of "mastery" and confidence produced by these
moments were the reward required to make my past efforts satisfying
and motivate further developing my skills. 
&lt;/p&gt;
&lt;p&gt;
In many ways, this conclusion correlates well with Malcom Gladwell's
comments on satisfying work in his book &lt;a href="http://jcardente.blogspot.com/search/label/BookReview"&gt;Outliers&lt;/a&gt;. From page 49:
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Those three things - autonomy, complexity, and a connection between
effort and reward - are, most people agree, the three qualities that
work has to have if it is to be satisfying.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
The lesson that I draw from this reflection is that it is important to
periodically seek out validating experiences to demonstrate acquired
skill, obtain satisfaction, and refuel a continuous self-development
effort.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-495548023839094833?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/495548023839094833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/495548023839094833'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/bug-hunt-wrap-up.html' title='Bug Hunt Wrap-up'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2226960600121635066</id><published>2009-11-21T05:50:00.002-05:00</published><updated>2009-11-21T05:54:34.552-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>The Case of the Corrupted Cache Line</title><content type='html'>&lt;p&gt;To culminate (for now) my "&lt;a href="http://jcardente.blogspot.com/search/label/BugHunts"&gt;Bug Hunts&lt;/a&gt;" series of posts I will attempt to
describe the hardest problem that I have solved to date. This was a
very complicated problem which I found challenging to describe without
going into excessive detail but I think the discussion below does an
adequate job.
&lt;/p&gt;
&lt;p&gt;
In &lt;a href="http://jcardente.blogspot.com/2009/11/lucky-career-start.html"&gt;prior&lt;/a&gt; &lt;a href="http://jcardente.blogspot.com/2009/11/case-of-deadlocked-queues.html"&gt;posts&lt;/a&gt;, I described the &lt;a href="http://en.wikipedia.org/wiki/Ccnuma#Cache_coherent_NUMA_.28ccNUMA.29"&gt;ccNUMA&lt;/a&gt; system that I worked on during
my first industry job. After the (relative) success of the first
system, a second generation system was developed that consisted of
numerous incremental improvements - faster processors, better memory
controllers, faster interconnect ASICs, a few coherency protocol
optimizations, etc. In general, the pre-production testing went
smoothly and the system started shipping with the fullest of
confidence.
&lt;/p&gt;
&lt;p&gt;
Shortly after going GA, however, a coherency protocol problem appeared
in the field. The problem only seemed to occur on the largest (eight
node) second generation systems and had a long mean time between
failure even under very heavy workloads. After ruling out the obvious
suspects (those coherency protocol optimizations I mentioned), we
instrumented an eight node system in the lab and started the bug hunt.
&lt;/p&gt;
&lt;p&gt;
To describe the failure, I need to further explain the &lt;a href="http://en.wikipedia.org/wiki/Scalable_Coherent_Interface"&gt;SCI&lt;/a&gt; coherency
protocol used in these systems. Like other &lt;a href="http://en.wikipedia.org/wiki/Distributed_shared_memory"&gt;distributed shared memory&lt;/a&gt;
protocols, the SCI protocol segmented the system's memory into fixed
sized chunks called "lines" (64 bytes in SCI's case). Being a ccNUMA
machine, the lines were distributed across all of the nodes in the
system. For each line of memory in the system there was a single
"home-node" that contained the associated physical memory and served
as the coordination point for all accesses to it. Mapping tables
initialized at boot time identified the home-node for each line of
memory which remained constant while the machine was
running. "Remote-nodes" could access the lines from home-nodes and
cache them via the SCI protocol and interconnect.
&lt;/p&gt;
&lt;p&gt;
One big challenge in using the SCI protocol was that it assumed an
architecture where the memory and processors were located on separate
nodes. In this model, home-nodes were passive and simply responded to
protocol requests from remote nodes - the protocol didn't provide a
means for home-nodes to obtain access to their own lines cached
elsewhere in the system. Unfortunately, our system used a different
architecture with each node containing both processors and memory
which put it at odds with the standard SCI model. As a result, a
workaround was needed in our system to allow home-nodes to obtain
access to their own memory lines.
&lt;/p&gt;
&lt;p&gt;
The chosen workaround was to essentially allow home-nodes to
temporarily adopt multiple personalities. In order to re-gain access
to a local memory line cached elsewhere in the system, a home-node
would:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Temporarily create a fictitious remote node persona
&lt;/li&gt;
&lt;li&gt;
Participate in the coherency protocol as a remote node to obtain
the desired access rights
&lt;/li&gt;
&lt;li&gt;
Act as a remote-node performing an eviction to remove the
fictitious-remote-persona from the coherency sharing-chain (SCI
used a distributed linked list to identify the remote-nodes with
cached copies of each line).

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While this dual-personality solution worked, the need to
simultaneously track both the home and fake-remote-persona protocol
states significantly increased the complexity of our implementation
(foreshadow alert!).
&lt;/p&gt;
&lt;p&gt;
Another important aspect of the system was the way that the per-line
access-rights information was maintained. The Coherency ASIC was
responsible for maintaining the SCI state (which indicated the access
rights) for each line of local memory and remote memory cached in the
L3 cache. Because of the amount of main-memory (4GB per node) and L3
cache (512MB per node), the SCI state information was stored in an
external DRAM attached to the Coherency ASIC. Storing the SCI
information in this way presented a problem in that it would have
taken too long to query it for each transaction on the processor bus
(which was necessary to maintain coherency). To get around this
problem, an SRAM attached to the FSB ASIC was used to cache the
access-rights information for recently accessed lines (both local and
remote). Using the SRAM, the FSB ASIC could quickly determine if a
processor bus operation could be allowed to continue (node had
sufficient access) or needed to be postponed (node didn't have
sufficient access). Although the SRAM allowed the majority of
processor bus operations to complete un-delayed, it burdened the
Coherency ASIC with the additional task of keeping the access-rights
cached in SRAM consistent with the SCI tags (another foreshadow
alert!).
&lt;/p&gt;
&lt;p&gt;
With those additional explanations out of the way&amp;hellip;
&lt;/p&gt;
&lt;p&gt;
Our initial investigation into the failure revealed that the primary
symptom was an inconsistency between the SCI and SRAM states for a
local memory line. Specifically:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The SCI state indicated that the line was not cached anywhere else
in the system - the local node had &lt;b&gt;full&lt;/b&gt; access rights to the line.
&lt;/li&gt;
&lt;li&gt;
The SRAM state indicated that the line was cached in a modified
state elsewhere in the system - the local node had &lt;b&gt;no&lt;/b&gt; access
rights to the line.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh bother. This inconsistency was obviously incorrect and the ASICs
were designed to generate a panic when it occurred to prevent data
corruption (which access-rights information was correct??).
&lt;/p&gt;
&lt;p&gt;
At first, we couldn't think of how such an inconsistency could
occur. After much effort we managed to get a synthetic workload
running in the lab that could induce the failure approximately once
per day. However, because of the system's size (eight nodes), large
number of busses-of-interest (3 per node, 24 total), and a limitation
of only having two logic analyzers it was &lt;b&gt;extremely&lt;/b&gt; difficult to get
a clear picture of the failure. At best, all we could get (if we were
lucky) was a trace of a couple of bus operations being performed
against the failing line. It was clear that we stood little chance of
capturing a definitive, complete trace of the failure - it was time to
put on our thinking caps.
&lt;/p&gt;
&lt;p&gt;
As luck would have it, this failure emerged at the end of a financial
quarter and there was a &lt;b&gt;substantial&lt;/b&gt; order being prepared for
shipment. Management was unwilling to ship the order until the problem
was resolved which meant there was a large amount of revenue at
risk. All of sudden this problem got upper-management's full
attention - not good.
&lt;/p&gt;
&lt;p&gt;
Given the problem's complexity, and the substantial amount of
executive attention my mentor (the MicroKid) and I isolated ourselves
in his office and began brainstorming how the state inconsistency
could occur. After a while, we identified a couple of areas in the
microcode that could lead to such a problem but neither of us could
think of a sequence of events to manifest it. After a lot of joint
discussion we decided to brainstorm separately to parallelize the
effort.
&lt;/p&gt;
&lt;p&gt;
For three days I sat in my cube listening to music, staring at trace
fragments, and assembling in my mind all the possible interactions
that could cause the state inconsistency we were observing. I don't
think I have ever concentrated on a single thing as hard for that long
at any other time in life. Finally, everything "clicked" and I knew
how the state inconsistency was occurring.
&lt;/p&gt;
&lt;p&gt;
The failure involved a complex interaction between three nodes with one
being the associated memory-line's home node. The following is a (simplified!)
description of the failure:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
The initial condition was that the line was cached in a modified
state in a remote node (Node1) and the home-node was attempting to
get read access. To accomplish this task, the home-node created its
fake-remote-persona and performed the necessary operations to
obtain a read-copy of the line. Upon getting a copy of the line, the
SRAM access rights were set to "read-only" and the local processor's
request was completed.
&lt;/li&gt;
&lt;li&gt;
In order to remove the fake-remote-persona from the SCI sharing
chain, the home-node began an eviction operation to get rid of the
now unneeded second personality by sending a request to Node1. 
&lt;/li&gt;
&lt;li&gt;
Just as the home-node began performing the eviction operation for
its fake-remote-persona, Node1 also began an operation to evict the
line from its L3 cache. According to the SCI protocol, such
eviction races were resolved by allowing the "older" remote node
(Node1) to proceed ahead of the "younger" remote node (which in
this case was the home-node's fake-persona).
&lt;/li&gt;
&lt;li&gt;
While the eviction race condition between the home-node and Node1 was being
resolved, a third node (Node2) started an operation to obtain read
access to the same line which completed successfully.
&lt;/li&gt;
&lt;li&gt;
Upon completing the eviction operation with Node1, the home-node
detected that Node2 had obtained read-access to the line (Step 4)
which meant that it now had to perform an eviction operation with
Node2 in order to finish removing the fake-remote-persona from the
SCI sharing chain. This required the home-node to downgrade the
SRAM access rights to "no-access" to guard against potential SCI
operations that could invalidate the local copy. The home-node then
resumed its eviction effort by informing Node2 that it was leaving
the sharing chain.
&lt;/li&gt;
&lt;li&gt;
Node2 received the home-node's fake-remote-persona's eviction
request and sent a response.
&lt;/li&gt;
&lt;li&gt;
Immediately after it sent the response in step 6, Node2 began an
operation to evict the line from its L3 cache.
&lt;/li&gt;
&lt;li&gt;
Due to an extremely unlikely sequence of events, Node2's eviction
request from step 7 got received and processed by the home-node
while Node2's response from step 6 was still in-fight. In order for
this to have happened, a request had to bypass a response in the
interconnect which was highly improbable due to the priority given
to sending responses over requests.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the end of this sequence, the SCI state was correct in indicating
that the line was not cached anywhere else in the system. The problem
was that the microcode didn't explicitly check for the
request-bypassing-response order of events that occurred in step 8. As
a result, the microcode failed to update the SRAM information with the
correct access rights thus leading to the problem. Over a decade
later, I still get a brain cramp from thinking about this sequence of
events.
&lt;/p&gt;
&lt;p&gt;
Once the microcode bug was identified it took less than five minutes
to fix. Luckily, I figured out and fixed the problem on the Friday
afternoon before the end of the quarter so the big order sitting on
the docks was able to ship, the company got to recognize the revenue
in the quarter, and the account's sales team got a hefty
commission. For my efforts, my management compensated me with, wait
for it, a cup of coffee. Seriously, after "saving" millions in revenue
I got a $2.00 cup of coffee from my Director. Oh well, luckily I
wasn't in the role for the money :-). 
&lt;/p&gt;
&lt;p&gt;
And that, to date, is the hardest problem that I have ever solved. 
&lt;/p&gt;
&lt;p&gt;
The following diagram summarizes the failure sequence described
above. Note that to simplify the diagram I avoided using the actual SCI
commands and states involved as their obscure nature would have
required too much explanation. Recall that each lab trace contained,
at-best, only one or two of the included operations.  To solve the
problem, I more or less had to imagine all of the possible
interactions that could be occurring and then identify this sequence
as a failing case. 
&lt;/p&gt;


&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jlo6CPTvh4c/SwfGbFd0G9I/AAAAAAAAAA0/c2c4A5lS9cU/s1600/microcodeBug.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 263px; height: 400px;" src="http://2.bp.blogspot.com/_jlo6CPTvh4c/SwfGbFd0G9I/AAAAAAAAAA0/c2c4A5lS9cU/s400/microcodeBug.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5406508046307564498" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-2226960600121635066?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2226960600121635066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/2226960600121635066'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/case-of-corrupted-cache-line.html' title='The Case of the Corrupted Cache Line'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jlo6CPTvh4c/SwfGbFd0G9I/AAAAAAAAAA0/c2c4A5lS9cU/s72-c/microcodeBug.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1258563109453688168</id><published>2009-11-12T06:06:00.002-05:00</published><updated>2009-12-04T06:01:18.928-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>The Case of the Deadlocked Queues</title><content type='html'>&lt;p&gt;&lt;i&gt;Updated on 12.4.2009 with a diagram.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jcardente.blogspot.com/2009/11/lucky-career-start.html"&gt;In a previous post&lt;/a&gt; I described my first industry job where I got to
work on the coherency subsystem for an enterprise class ccNUMA server.
For my &lt;a href="http://jcardente.blogspot.com/search/label/BugHunts"&gt;"Bug Hunts" series of posts&lt;/a&gt; I thought I would describe a complex
problem that I root-caused during pre-production testing. 
&lt;/p&gt;
&lt;p&gt;
During the alpha silicon testing, the systems started &lt;a href="http://catb.org/jargon/html/H/hang.html"&gt;hanging&lt;/a&gt; under
heavy load. Our initial investigations discovered that operations on
the processor (FSB) and inter-ASIC (F2C&lt;sub&gt;Bus&lt;/sub&gt;) busses appeared to get
stuck waiting for various pregrant signals to activate. The situation
looked bad, it appeared that we had a multi-ASIC deadlock on our
hands.
&lt;/p&gt;
&lt;p&gt;
In simple terms, the coherency subsystem on each node had two
responsibilities:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
accept requests from the &lt;b&gt;local&lt;/b&gt; processors, send requests to other
nodes to perform the necessary coherency &amp;amp; memory operations on the
&lt;b&gt;remote&lt;/b&gt; processor busses, and return responses to the &lt;b&gt;local&lt;/b&gt;
processors.

&lt;/li&gt;
&lt;li&gt;
accept requests from other nodes on behalf of &lt;b&gt;remote&lt;/b&gt; processors,
perform the necessary coherency &amp;amp; memory operations on the &lt;b&gt;local&lt;/b&gt;
processor bus, and return responses to the other nodes for the
&lt;b&gt;remote&lt;/b&gt; processors.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To guarantee forward progress, the coherency subsystem had one golden
rule - &lt;i&gt;requests shall never block responses&lt;/i&gt;. At the transport layer
this rule was enforced through the use of two independent &lt;a href="http://en.wikipedia.org/wiki/Virtual_circuit"&gt;virtual channels&lt;/a&gt; with priority given to the response channel.  Within the
ASICs, the golden rule was maintained by carefully designed scheduling
and priority logic. Theoretically the golden rule should have
prevented deadlocks from occurring - so much for theory.
&lt;/p&gt;
&lt;p&gt;
Since this was a real system, our view was limited to the activity
observed on the busses - we had no visibility into what was happening
inside the ASICs. To get around the visibility problem, I worked
closely with a friend on the verification team to create a script that
reproduced, in simulation, the activity observed in the lab. Through
these simulation tests I gained an insight into what was happening
inside the ASICs. In the meantime, I consulted with the ASIC designers
to better understand some of the unexpected behaviors discovered
during the investigation.
&lt;/p&gt;
&lt;p&gt;
I can still remember well the meeting when everything suddenly
"clicked" in my head. We had brought together both ASIC teams (which
were geographically distributed) to discuss the problem and attempt an
explanation. To focus the conversation, I asked a series of very
specific "what if" questions that finally led to an "ah ha" moment.
&lt;/p&gt;
&lt;p&gt;
As with most complex failures, a number of smaller problems combined
to cause the deadlock. The diagram included in &lt;a href="http://jcardente.blogspot.com/2009/11/lucky-career-start.html"&gt;my prior&lt;/a&gt; post may
provide more context for the explanation that follows.
&lt;/p&gt;
&lt;p&gt;
The first problem was that the FSB ASIC violated the golden rule by
strictly ordering, in a single queue, requests from remote processors
and responses to local processors. During the investigation it was
discovered that this design change was made to resolve a race
condition between evicting modified data from the L3 cache
(accomplished via a remote processor request) and installing the
replacing data into the cache (accomplished via a local processor
response). Oops.
&lt;/p&gt;
&lt;p&gt;
The second problem was much more subtle, there was no ordering rule
between local and remote responses! The microsequencer inside the
Coherency ASIC was responsible for processing all coherency requests,
both from this and other nodes, and generating the associated
responses. The microsequencer treated all out-bound responses equally
and used a common queue to buffer them for transmission. This single
out-bound response queue essentially created a cycle in the path of
queues between the Coherency and FSB ASICs - a response blocked at the
head of this queue would prevent all other microsequencer responses from
making forward progress.
&lt;/p&gt;
&lt;p&gt;
The third problem was that the microsequencer could issue more
requests than it had buffers to queue out-bound responses. This meant
that the microsequencer relied completely on out-bound responses making
forward progress for it to be able to receive responses for the
requests it issued.
&lt;/p&gt;
&lt;p&gt;
Together, these three problems resulted in a deadlock when:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
The coherency microsequencer sent a burst of local processor
responses to the FSB ASIC. These actions freed coherency engine
resources but consumed FSB ASIC resources preventing it from
accepting more responses.

&lt;/li&gt;
&lt;li&gt;
The coherency engine's response queue got full with an FSB ASIC
bound local processor response at the head of the queue. At this
point, the resource consumption described in (1) prevented the
response at the head of the queue from proceeding.

&lt;/li&gt;
&lt;li&gt;
Shortly thereafter, the FSB ASIC completed a number of Coherency
ASIC requests and sent responses to the microsequencer. Because the
microsequencer's out-bound response queue was block by (2), it couldn't
send the remote responses generated by the FSB ASIC responses. This
condition prevented the microsequencer from accepting more responses
and caused the FSB ASIC's single request/response queue to stall.

&lt;/li&gt;
&lt;li&gt;
The local processor responses already in the FSB ASIC queue from
(1) were unable to make progress due to the stall described in
(3). As a result, the FSB ASIC could not accept more responses to
alleviate the stall described in (2).

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result, neither ASIC could make progress until the other could - a
classic deadlock. 
&lt;/p&gt;
&lt;p&gt;
In reality, the actual deadlock scenario was much more complicated
than this - it required a complex sequence of events, specific event
timings, and an interplay with non-coherent inter-node accesses (i.e.
accesses to another node's PCI space). However, the simplified
explanation above captures the salient points - in particular the
need for an additional ordering rule between local and remote
responses.
&lt;/p&gt;
&lt;p&gt;
Once the problem was understood, the fix was relatively easy. Luckily
we had programmatic control over the depth of various ASIC queues that
allowed us to configure the system to avoid the deadlock condition
without having to re-spin silicon - and then there was much rejoicing.
&lt;/p&gt;
&lt;p&gt;
Although the deadlock had been solved the story didn't end there for
me. I was so fascinated by this problem that I spent a significant
amount of time over the next few months writing a program capable of
analyzing a system of queues to discover such deadlocks. 10K lines of
perl code later, I had a program that took as input a queuing model
expressed in a domain specific language that I designed for the
task. The model language was rich enough to represent such things as
different kinds of tokens, tokens spawning new tokens, global token
limits, conditional routing rules for tokens, and the sharing of
limited resources. The program used this information to find patterns
of tokens in queues that resulted in deadlock conditions. Although I
didn't know it at the time, I essentially used a constraint
propagation method to reduce the search space to a reasonable
size. Using the program, I was able to "predict" the deadlock
condition found in the lab and validate the configuration changes made
to avoid it. Although the tool never got used again, the sense of
personal accomplishment from developing it made the effort worthwhile.
&lt;/p&gt;
&lt;p&gt;
My history with this deadlock finally came to an end when, for the
third generation system, I redesigned the coherency engine to fix this
and other troublesome bugs. The opportunity to fix this problem myself
really brought a fulfilling sense of closure to the experience.
&lt;/p&gt;
&lt;p&gt;
This deadlock bug is one of my favorite bug hunts of all time. It was
a complex problem that required a deep understanding of the system, a
holistic view of its behavior, and the ability to see how the many
micro-behaviors interacted to cause the deadlock. Furthermore, it led
to the simulation and ASIC re-design activities which were equally
rewarding experiences.
&lt;/p&gt;
&lt;p&gt;
This, however, wasn't the hardest bug that I ever root caused. That will
be the topic of the next &lt;a href="http://jcardente.blogspot.com/search/label/BugHunts"&gt;Bug Hunt&lt;/a&gt; post once I figure out how to describe
it. 
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;To clarify this post I decided to add a simple diagram of the deadlock
condition. The actual failure was much more complex but hopefully this
diagram captures the salient points from the description above - the impact of
the shared queue for requests+responses and the need to route local and remote
responses differently.&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqtKknU1I/AAAAAAAAABE/HykhpDY6yfo/s1600-h/deadlock.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 181px; height: 400px;" src="http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqtKknU1I/AAAAAAAAABE/HykhpDY6yfo/s400/deadlock.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5411333013938590546" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1258563109453688168?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1258563109453688168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1258563109453688168'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/case-of-deadlocked-queues.html' title='The Case of the Deadlocked Queues'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqtKknU1I/AAAAAAAAABE/HykhpDY6yfo/s72-c/deadlock.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1877103314128246992</id><published>2009-11-06T19:06:00.004-05:00</published><updated>2009-12-04T05:54:21.834-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>A Lucky Career Start</title><content type='html'>&lt;p&gt;&lt;i&gt;Updated on 12.4.2009 with a better diagram of the system's architecure&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;I have two more bugs in mind for my "&lt;a href="http://jcardente.blogspot.com/search/label/BugHunts"&gt;bug hunt&lt;/a&gt;" series but before I can
post about them I need to provide a little background information
about my first real industry job.
&lt;/p&gt;
&lt;p&gt;
After graduate school, I had the rare opportunity to help build a
large-scale, enterprise-class &lt;a href="http://en.wikipedia.org/wiki/CcNUMA"&gt;ccNUMA&lt;/a&gt; server. At the time, 1996, ccNUMA
architectures were leading-edge technology and ours was one of the
first to utilize the Intel platform. 
&lt;/p&gt;
&lt;p&gt;
The system's architecture was based on "building block" nodes each
containing four PentiumPro processors, 4GB of main memory, and 12 PCI
slots. Up to eight nodes could be connected together using a
high-speed interconnect to create a system with 32 processors, 32GB of
main memory, and 96 PCI slots. The system ran a proprietary version of
UNIX carefully tuned to work efficiently on its specific
architecture. At its maximum size the system consumed two 19 inch
racks - big iron indeed.
&lt;/p&gt;
&lt;p&gt;
The high-speed interconnect was based on the &lt;a href="http://en.wikipedia.org/wiki/Scalable_Coherent_Interconnect"&gt;IEEE Scalable Coherent Interconnect&lt;/a&gt; (SCI) standard and responsible for creating a single,
cache coherent memory space out of the combined node
resources. Through the interconnect, any processor could access the
memory on any of the nodes - albeit at variable latency based on their
relative locations in the system. In addition to the processor caches,
each node contained a large capacity "L3" cache to store data fetched
from other nodes to avoid, as much as possible, the significantly
greater "remote" access latency. The SCI interconnect inter-operated
with the processor and "L3" caches to ensure that accesses to cached
data were handled correctly. Together, these capabilities are what
made the system a cache-coherent non-uniform memory (ccNUMA)
architecture. SCI was a ring based protocol and our system used two
counter rotating rings to halve the average node-to-node latency,
double the bandwidth, and provide some measure of redundancy (not
fault-tolerance, just reboot level redundancy if a ring failed).
&lt;/p&gt;
&lt;p&gt;
The ccNUMA subsystem consisted of four custom ASICs, two designed
in-house and the others jointly developed with a partner company. One of
those ASICs was dedicated to maintaining the SCI coherency protocol
and used a &lt;a href="http://en.wikipedia.org/wiki/Microsequencer"&gt;microsequencer&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Microcode"&gt;microcode&lt;/a&gt; combination to provide a
programmable, high-performance coherency engine. The microsequencer 
utilized a &lt;a href="http://en.wikipedia.org/wiki/VLIW"&gt;VLIW&lt;/a&gt;-like instruction set to maximize parallel operation and 
minimize the &lt;a href="http://en.wikipedia.org/wiki/Control_store"&gt;control store's&lt;/a&gt; size. 
&lt;/p&gt;
&lt;p&gt;
I joined the project just after its start and my initial
responsibilities were to become an SCI expert, be able to program the
coherency microsequencer, and optimize the SCI coherency protocol
implementation to match our machine's specific architecture. After
completing those tasks ahead of schedule, I went on to do a wide
variety of things including writing verification scripts for the
coherency ASIC, implementing the BIOS responsible for initializing the
SCI subsystem, serving as a systems engineer for the coherency
subsystem, root-causing complicated bugs in all four ASICs, and
writing a variety of programs to automatically identify failure root
causes from simulation, emulation, and production logs. In the
subsequent two follow on projects, my responsibilities continued to
grow and culminated in an architect role for the third generation
system.
&lt;/p&gt;
&lt;p&gt;
As a computer-obsessed young engineer just starting a career this job
was a dream come true. On a daily basis, I had direct interaction with
ASICs, logic board design, exotic computer architectures, complicated
caching protocols, low-level coding, and advanced operating
systems. Stuff that I had only read about in text books was now
literally at my finger tips to be studied, understood, and played
with.
&lt;/p&gt;
&lt;p&gt;
It was the people that I got to work with, though, that really made
this job special. Everyone on the project shared an all-hands-on-deck,
add-value-where-you-can, succeed-at-all-costs attitude that created an
invigorating environment to work in. My principal mentor was a
&lt;a href="http://www.nytimes.com/1981/08/23/books/the-hardy-boys-and-the-microkids-make-a-computer.html"&gt;MicroKid&lt;/a&gt; from &lt;a href="http://en.wikipedia.org/wiki/The_Soul_of_a_New_Machine"&gt;The Soul of a New Machine&lt;/a&gt; from whom I learned a great,
great deal (and still do!). Thanks to his guidance and support I got
to "touch" nearly every part of the system and felt encouraged to
continuously take on new challenges. To this day I still recall the
feeling of endless possibilities and excitement of knowing that my
skills were improving daily.
&lt;/p&gt;
&lt;p&gt;
Truly a lucky way to start my career.
&lt;/p&gt;


&lt;p&gt;
&lt;b&gt;UPDATE&lt;/b&gt;
&lt;/p&gt;
&lt;p&gt;
To clarify this post, I decided to add a simplified diagram of the
system's architecture. The actual architecture was more complicated,
for example some of the ASICs depicted were actually multiple devices,
but the diagram should sufficiently convey the overall design.
&lt;/p&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqKm8BioI/AAAAAAAAAA8/hwg-hrVqWfI/s1600-h/ccnuma.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 186px; height: 400px;" src="http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqKm8BioI/AAAAAAAAAA8/hwg-hrVqWfI/s400/ccnuma.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5411332420257548930" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1877103314128246992?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1877103314128246992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1877103314128246992'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/11/lucky-career-start.html' title='A Lucky Career Start'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jlo6CPTvh4c/SxjqKm8BioI/AAAAAAAAAA8/hwg-hrVqWfI/s72-c/ccnuma.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8337800651428861017</id><published>2009-10-28T19:18:00.002-04:00</published><updated>2009-10-28T20:10:37.554-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>C Bitfields and HW Registers</title><content type='html'>&lt;p&gt;When I need a quick low-level programming "fix", I browse the archives
at &lt;a href="http://www.pagetable.com/"&gt;PageTable.com&lt;/a&gt;. Last week I read the post on &lt;a href="http://www.pagetable.com/?p=250"&gt;Readable and Maintainable Bitfields in C&lt;/a&gt; which argued the merits of using bitfields
over bitmasks+macros. Although I agree with the post's points I think
it omitted one important detail - the danger of using bitfields with
hardware registers.
&lt;/p&gt;
&lt;p&gt;
Hardware registers can be mapped into a processor's memory space and
accessed with standard memory read/write instructions. Therefore the
temptation is to define a bitfield type representing a register's
structure and set a pointer to its base memory address. For example,
assuming register &lt;code&gt;bar&lt;/code&gt; is four bytes wide, has five bit-fields, and
is located at memory address &lt;code&gt;0xBAADF00D&lt;/code&gt; one might be tempted to do
the following:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; {
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f1&lt;/span&gt;:8;
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f2&lt;/span&gt;:4;
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f3&lt;/span&gt;:8;
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f4&lt;/span&gt;:4;
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f5&lt;/span&gt;:8;
  } &lt;span style="color: #b7850a;"&gt;registerBar_t&lt;/span&gt;;


&lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set pointer to register's memory address
&lt;/span&gt;&lt;span style="color: #218a21;"&gt;registerBar_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pBar&lt;/span&gt; = 0xBAADF00D;

&lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Use pointer to access register
&lt;/span&gt;pBar-&amp;gt;f1 = 0xFE;
&lt;/pre&gt;




&lt;p&gt;
One problem with this approach is that many registers are designed to
be accessed only at their full size - all accesses must be aligned to
the register's base address and read/write the whole
thing. Unfortunately, the setting of &lt;code&gt;f1&lt;/code&gt; in the above example may
produce a partial register write that can lead to unexpected and
unintended results.
&lt;/p&gt;
&lt;p&gt;
Another challenge when dealing with registers is that, unlike main
memory, register accesses can have side effects. Even register reads
can cause the hardware to initiate action or clear information. Consider
the following example:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
...
tmpf1 = pBar-&amp;gt;f1;
tmpf2 = pBar-&amp;gt;f2;
...
&lt;/pre&gt;




&lt;p&gt;
If reads of register &lt;code&gt;bar&lt;/code&gt; are destructive causing its contents to be
cleared then the read of &lt;code&gt;f1&lt;/code&gt; may clear the contents of &lt;code&gt;f2&lt;/code&gt; before it
is read by the subsequent pointer dereference. The resulting loss of
information could cause the driver, hardware, or both to behave
unexpectedly.
&lt;/p&gt;
&lt;p&gt;
Alternatively, if reads of register &lt;code&gt;bar&lt;/code&gt; cause the hardware to
initiate action then spurious activity may occur if the &lt;code&gt;f1&lt;/code&gt; and &lt;code&gt;f2&lt;/code&gt;
accesses are done separately.
&lt;/p&gt;
&lt;p&gt;
Because of these granularity and side effect issues, I was advised
early in my career to avoid using bitfields with hardware
registers. This is, I think, an important point that is captured in
the PageTable.com post's comments but not in the post itself which is
an unfortunate omission.
&lt;/p&gt;
&lt;p&gt;
After reading the PageTable.com post, I realized that I always took
this advice on faith and never looked at the instructions generated by
bitfield accesses. So I decided to do a quick experiment, below is
a short program that accesses a bitfield with fields of varying size and
alignment.
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #d96fd5;"&gt;#include&lt;/span&gt; &lt;span style="color: #bb8e8e;"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span style="color: #7f007f;"&gt;typedef&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;union&lt;/span&gt; {
  &lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; {
    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f1&lt;/span&gt;:8; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Bits 07:00
&lt;/span&gt;    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f2&lt;/span&gt;:4; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Bits 11:08
&lt;/span&gt;    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f3&lt;/span&gt;:8; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Bits 19:12
&lt;/span&gt;    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f4&lt;/span&gt;:4; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Bits 23:20
&lt;/span&gt;    &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;f5&lt;/span&gt;:8; &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Bits 31:24
&lt;/span&gt;  };
  &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;raw&lt;/span&gt;;
} &lt;span style="color: #b7850a;"&gt;bitfield_t&lt;/span&gt;;


&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;main&lt;/span&gt;()
{
  &lt;span style="color: #218a21;"&gt;bitfield_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;bitfield&lt;/span&gt;;
  &lt;span style="color: #218a21;"&gt;unsigned&lt;/span&gt; &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;tmp&lt;/span&gt;;

  bitfield.raw = 0x0;

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set bit field f1
&lt;/span&gt;  bitfield.f1 = 0xEF;
  tmp = bitfield.f1;
  printf(&lt;span style="color: #bb8e8e;"&gt;" After f1: F1(0x%02x) RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp,
         bitfield.raw);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set bit field f2
&lt;/span&gt;  bitfield.f2 = 0xE;
  tmp = bitfield.f2;
  printf(&lt;span style="color: #bb8e8e;"&gt;" After f2: F2(0x%02x) RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp,
         bitfield.raw);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set bit field f3
&lt;/span&gt;  bitfield.f3 = 0xDB;
  tmp = bitfield.f3;
  printf(&lt;span style="color: #bb8e8e;"&gt;" After f3: F3(0x%02x) RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp,
         bitfield.raw);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set bit field f4
&lt;/span&gt;  bitfield.f4 = 0xA;
  tmp = bitfield.f4;
  printf(&lt;span style="color: #bb8e8e;"&gt;" After f4: F4(0x%02x) RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp,
         bitfield.raw);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set bit field f5
&lt;/span&gt;  bitfield.f5 = 0xDE;
  tmp = bitfield.f5;
  printf(&lt;span style="color: #bb8e8e;"&gt;" After f5: F5(0x%02x) RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp,
         bitfield.raw);

  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set with raw
&lt;/span&gt;  bitfield.raw = 0xDECAFBAD;
  tmp = bitfield.raw;
  printf(&lt;span style="color: #bb8e8e;"&gt;"After raw: RAW(0x%08x)\n"&lt;/span&gt;, 
         tmp);

  &lt;span style="color: #7f007f;"&gt;return&lt;/span&gt; 0;
}
&lt;/pre&gt;




&lt;p&gt;
Compiling and running this program on an Ubuntu system results in the
expected output.
&lt;/p&gt;



&lt;pre class="example"&gt;
jcardent@ubuntu:~/tmp$ gcc -g -o foo foo.c 
jcardent@ubuntu:~/tmp$ ./foo 
 After f1: F1(0xef) RAW(0x000000ef)
 After f2: F2(0x0e) RAW(0x00000eef)
 After f3: F3(0xdb) RAW(0x000dbeef)
 After f4: F4(0x0a) RAW(0x00adbeef)
 After f5: F5(0xde) RAW(0xdeadbeef)
After raw: RAW(0xdecafbad)
&lt;/pre&gt;




&lt;p&gt;
Running the command
&lt;/p&gt;



&lt;pre class="example"&gt;
jcardent@ubuntu:~/tmp$ objdump -d -S foo 
&lt;/pre&gt;




&lt;p&gt;
reveals the instructions generated to access the bit-fields. Looking at 
the &lt;code&gt;f1&lt;/code&gt; write and read sequence shows:
&lt;/p&gt;



&lt;pre class="example"&gt;
  // Set bit field f1
  bitfield.f1 = 0xEF;
 80483dc:       c6 45 f8 ef        movb   $0xef,-0x8(%ebp)
  tmp = bitfield.f1;
 80483e0:       0f b6 45 f8        movzbl -0x8(%ebp),%eax
 80483e4:       0f b6 c0           movzbl %al,%eax
 80483e7:       89 45 f4           mov    %eax,-0xc(%ebp)
&lt;/pre&gt;




&lt;p&gt;
The first thing to note from this disassembly fragment is that
&lt;code&gt;bitfield&lt;/code&gt; is located on the stack eight bytes below &lt;code&gt;%ebp&lt;/code&gt;. Likewise,
&lt;code&gt;tmp&lt;/code&gt; is located at offset 0xC.
&lt;/p&gt;
&lt;p&gt;
From this example it's clear that the write to &lt;code&gt;f1&lt;/code&gt; uses a single byte
move instruction. If &lt;code&gt;bitfield&lt;/code&gt; had been mapped to a hardware
register, this would have resulted in an aligned but too short write
access that could have produced unintended behavior.
&lt;/p&gt;
&lt;p&gt;
The read of &lt;code&gt;f1&lt;/code&gt; is less clear until the &lt;code&gt;movzbl&lt;/code&gt; instruction is
understood to be a move from a single byte to a word, four bytes in
this case. So here again, if &lt;code&gt;bitfield&lt;/code&gt; had been mapped to a register
the single-byte access may have resulted in unintended behavior like
dataloss (top three bytes cleared) or spurious action (if subsequent
reads are done to other fields for the same operation).
&lt;/p&gt;
&lt;p&gt;
Looking at the &lt;code&gt;f2&lt;/code&gt; write and read sequence shows:
&lt;/p&gt;



&lt;pre class="example"&gt;
 // Set bit field f2
  bitfield.f2 = 0xE;
 8048404:       0f b6 45 f9        movzbl -0x7(%ebp),%eax
 8048408:       83 e0 f0           and    $0xfffffff0,%eax
 804840b:       83 c8 0e           or     $0xe,%eax
 804840e:       88 45 f9           mov    %al,-0x7(%ebp)
  tmp = bitfield.f2;
 8048411:       0f b6 45 f9        movzbl -0x7(%ebp),%eax
 8048415:       83 e0 0f           and    $0xf,%eax
 8048418:       0f b6 c0           movzbl %al,%eax
 804841b:       89 45 f4           mov    %eax,-0xc(%ebp)
&lt;/pre&gt;




&lt;p&gt;
In this case, setting the four bit-wide field &lt;code&gt;f2&lt;/code&gt; results in a
byte-wide read-modify-write sequence aligned with the second byte of
&lt;code&gt;bitfield&lt;/code&gt;, evidenced by the offset of 0x7 instead of 0x8. Similarly,
reading &lt;code&gt;f2&lt;/code&gt; results in a byte-wide read aligned with the second byte
of &lt;code&gt;bitfield&lt;/code&gt;. Both accesses are too short and misaligned.
&lt;/p&gt;
&lt;p&gt;
Since &lt;code&gt;f3&lt;/code&gt; spans bytes 2 and 3 of &lt;code&gt;bitfield&lt;/code&gt;, its access sequence
results in aligned, four byte-wide &lt;code&gt;mov&lt;/code&gt; instructions. 
&lt;/p&gt;



&lt;pre class="example"&gt;
  bitfield.f3 = 0xDB;
 8048438:       8b 45 f8           mov    -0x8(%ebp),%eax
 804843b:       25 ff 0f f0 ff     and    $0xfff00fff,%eax
 8048440:       0d 00 b0 0d 00     or     $0xdb000,%eax
 8048445:       89 45 f8           mov    %eax,-0x8(%ebp)
  tmp = bitfield.f3;
 8048448:       8b 45 f8           mov    -0x8(%ebp),%eax
 804844b:       c1 e8 0c           shr    $0xc,%eax
 804844e:       80 e4 ff           and    $0xff,%ah
 8048451:       0f b6 c0           movzbl %al,%eax
 8048454:       89 45 f4           mov    %eax,-0xc(%ebp)
&lt;/pre&gt;




&lt;p&gt;
Although the accesses themselves are well-formed, unintended behaviors
can still result if &lt;code&gt;f3&lt;/code&gt; is only one of multiple fields that must be
set for a single operation.
&lt;/p&gt;
&lt;p&gt;
Since the structure of &lt;code&gt;bitfield&lt;/code&gt; is symmetrical, the accesses to
fields &lt;code&gt;f4&lt;/code&gt; and &lt;code&gt;f5&lt;/code&gt; produce instructions similar to those for &lt;code&gt;f2&lt;/code&gt;
and &lt;code&gt;f1&lt;/code&gt; respectively albeit with different offsets.
&lt;/p&gt;
&lt;p&gt;
Finally, the accesses to &lt;code&gt;raw&lt;/code&gt; produce aligned, full-width
instructions as expected.
&lt;/p&gt;



&lt;pre class="example"&gt;
  // Set with raw
  bitfield.raw = 0xDECAFBAD;
 80484cd:       c7 45 f8 ad fb ca de movl   $0xdecafbad,-0x8(%ebp)
  tmp = bitfield.raw;
 80484d4:       8b 45 f8           mov    -0x8(%ebp),%eax
 80484d7:       89 45 f4           mov    %eax,-0xc(%ebp)
&lt;/pre&gt;




&lt;p&gt;
This last example illustrates a tempting workaround for "safely" using
bitfields to manage register accesses. Consider:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span style="color: #218a21;"&gt;registerBar_t&lt;/span&gt; *&lt;span style="color: #b7850a;"&gt;pBar&lt;/span&gt; = 0xBAADF00D;
&lt;span style="color: #218a21;"&gt;registerBar_t&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;tmpBar&lt;/span&gt;;

&lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;Set field f1 to 0xff
&lt;/span&gt;tmpBar.raw = pBar-&amp;gt;raw;
tmpBar.f1  = 0xff;
pBar-&amp;gt;raw  = tmpBar.raw;
&lt;/pre&gt;




&lt;p&gt;
While this approach works, it suffers the risk of an uninformed future
maintainer "optimizing out" the temporary variable and just using the
bitfield method directly. In this regard, it may be more maintainable
to use bitmasks and macros for register accesses.
&lt;/p&gt;
&lt;p&gt;
Of course, problems can arise regardless of the method used if
"uninformed" developers are allowed to change the code. The only
prevention here is to make sure there is suitable training and
disciplined code reviews.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8337800651428861017?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8337800651428861017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8337800651428861017'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/c-bitfields-and-hw-registers.html' title='C Bitfields and HW Registers'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1514501588766309139</id><published>2009-10-22T05:52:00.001-04:00</published><updated>2009-10-22T05:56:12.729-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: Coders At Work</title><content type='html'>&lt;p&gt;&lt;a href="http://www.codersatwork.com/"&gt;Coders At Work: Reflections on the Craft of Programming&lt;/a&gt; by Peter Seibel
&lt;/p&gt;
&lt;p&gt;
I really enjoyed this book. Peter Seibel did an outstanding job in
selecting a group of interviewees that are both legendary and
represent a wide range of programming domains. In addition, Peter's
questions were similar enough to allow comparisons between responses
but maintained a conversational flow that prevented the interviews
from feeling formulaic. Ehud Lamm's quote from the font cover perhaps
says it best, "reading this book may be the next best thing to
chatting with these illustrious programmers in person".
&lt;/p&gt;
&lt;p&gt;
I read the interviews out of order based on my interests. All were
good but my favorite ones were (in no particular order):
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/donald-knuth.html"&gt;Donald Knuth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/peter-norvig.html"&gt;Peter Norvig&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/dan-ingalls.html"&gt;Dan Ingalls&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/guy-steele.html"&gt;Guy Steele&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/brendan-eich.html"&gt;Brendan Eich&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/bernie-cosell.html"&gt;Bernie Cosell&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/simon-peyton-jones.html"&gt;Simon Peyton Jones&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/l-peter-deutsch.html"&gt;L Peter Deutsch&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/jamie-zawinski.html"&gt;Jamie Zawinski&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.codersatwork.com/brad-fitzpatrick.html"&gt;Brad Fitzpatrick&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One striking observation was how many of the interviewees fit into
Malcolm Gladwell's &lt;a href="http://jcardente.blogspot.com/2009/09/book-review-outliers.html"&gt;definition of an Outlier&lt;/a&gt;. Nearly all of them had
opportunities to get a significant amount of programming experience at
a young age and serendipitously found jobs that provided the right
environment for them to succeed.
&lt;/p&gt;
&lt;p&gt;
I also found interesting the "organic" approach most of the
interviewees took to programming. None seemed to rely on formalized
best practices but instead used their intuition to tailor their
approach based on the task at hand. To me this suggests that you just
can't codify great programming which matches my experiences with the
great programmers that I have known.
&lt;/p&gt;
&lt;p&gt;
Finally, it's notable how many of the people interviewed either
starting out hacking Lisp or were heavily influenced by it. Of course
this may be due to selection bias given that the author, Peter Seibel,
is a a Lisp hacker himself and wrote the outstanding book &lt;a href="http://gigamonkeys.com/book/"&gt;Practical Common Lisp&lt;/a&gt;. As a Lisp fan, I don't mind the potential bias; it's a
feature, not a bug!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1514501588766309139?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1514501588766309139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1514501588766309139'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/book-review-coders-at-work.html' title='Book Review: Coders At Work'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7421784870001498907</id><published>2009-10-14T06:54:00.002-04:00</published><updated>2009-10-14T07:40:48.146-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><category scheme='http://www.blogger.com/atom/ns#' term='Innovation'/><title type='text'>Favorite Innovation Resources</title><content type='html'>&lt;p&gt;My company's annual innovation conference is today and I'll be
participating in a panel of innovators. As a result, innovation is on
my mind so I thought I would post my favorite innovation
resources. Unfortunately, this is only a quick list of links as I
don't have time to discuss in detail what I like about each
resource. Perhaps this will be a theme for a future series of posts.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Innovation&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.theinnovatorssolution.com/"&gt;The Innovator's Solution&lt;/a&gt;: sequel to The Innovator's Dilemma, I
prefer this book as it discusses many secondary factors related to
disruption and how to avoid it. Key points to me were transitions
between tightly-integrated and modular architectures and avoiding 
products that are incremental innovations for incumbents.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://books.google.com/books?id=-u7KxJb8f9kC&amp;amp;dq=drucker+innovation+and+entrepreneurship&amp;amp;printsec=frontcover&amp;amp;source=bl&amp;amp;ots=8V96WF8wky&amp;amp;sig=i6g9J5lyRSPh7uoUrfXQseOy5MI&amp;amp;hl=en&amp;amp;ei=bJzVSpXQDY_KlAf4kLidCQ&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;ct=result&amp;amp;resnum=3&amp;amp;ved=0CBoQ6AEwAg#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Drucker's Innovation and Entrepreneurship&lt;/a&gt;: simply a great book that
should be required reading for every innovator, entrepreneur, and
executive manager. Based on my years of experience as an innovator
within a large company I found Drucker's insights and advice to be
relevant, pragmatic, and actionable. Given its publication date, it
appears to me that this book served as the foundation for the
"popular" innovation books of the 90's and beyond. Reading Drucker's
book helped to provide more context for those later works. This book
is well worth the time to read it.

&lt;/li&gt;
&lt;li&gt;
The Doblin Group's &lt;a href="http://www.doblingroup.com/ideas/TenTypesOverview.html"&gt;Ten Types of Innovation&lt;/a&gt;: a great cheat-sheet
summarizing the various forms of innovation. I have this hanging on
my office wall at work.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://harvardbusiness.org/product/skate-to-where-the-money-will-be/an/R0110D-PDF-ENG"&gt;Skate to Where the Money Will Be&lt;/a&gt;: perhaps one of the most important
papers for any innovator to read, especially those involved in long
term strategic planning for an established company.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://harvardbusiness.org/product/creating-new-market-space/an/99105-PDF-ENG"&gt;Creating New Market Space&lt;/a&gt;: a great HBS article on systematic ways of
identifying new market opportunities.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://faculty.washington.edu/castlej/Articles of interest/What is Strategy - Porter HBR.pdf"&gt;What is a Strategy&lt;/a&gt;: to some degree innovating is deeply tied to
defining a strategy. As a result, it's important to understand what a
strategy is and Porter is a leading thinker in this area.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://oreilly.com/catalog/9780596527051"&gt;The Myths of Innovation&lt;/a&gt;: a thought provoking book that dispels some
of the myths surrounding innovation. 

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/Serious-Play-Companies-Simulate-Innovate/dp/0875848141"&gt;Serious Play&lt;/a&gt;: a decent book that makes the important point that
sometimes a model or prototype is needed to serve as the catalyst
and focal point for exploratory innovation. I've spent most of my
engineering career building prototypes so I found this book to be a
thoughtful resource.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/Power-Product-Platforms-Marc-Meyer/dp/0684825805"&gt;The Power of Product Platforms&lt;/a&gt;: written by one of my MBA professors,
this book introduces the concept of using modular architectures to
build derivative products to serve different market segments. A
powerful concept in my experience.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.nytimes.com/2008/02/03/business/03unbox.html?_r=4&amp;amp;oref=slogin&amp;amp;ref=technology&amp;amp;pagewanted=all&amp;amp;oref=slogin&amp;amp;oref=slogin"&gt;Eureka! It Really Takes Years of Hard Work&lt;/a&gt;: a nice New York Times
article discussing the fact that innovation is often the result of a
lot of hard work and not an instantaneous eureka moment.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Execution&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://books.google.com/books?id=yJXHUDSaJgsC&amp;amp;dq=crossing+the+chasm&amp;amp;printsec=frontcover&amp;amp;source=bn&amp;amp;hl=en&amp;amp;ei=DKDVSsusKdPVlAeL8-WcCQ&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;ct=result&amp;amp;resnum=8&amp;amp;ved=0CCEQ6AEwBw#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Crossing the Chasm&lt;/a&gt;: Geoffrey Moore's classic book about making the
transition from the early to mainstream markets. A lot of great
advice for innovators.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.dealingwithdarwin.com/"&gt;Dealing with Darwin&lt;/a&gt;: another Moore classic, this one discusses how
to structure an organization to maintain a pipeline of innovations
and sustain corporate growth. A good read for anyone involved in 
maintaining an established business.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.guykawasaki.com/books/art-of-the-start.shtml"&gt;That Art of the Start&lt;/a&gt;: a great and inspiring book on starting a new
venture. I particularly appreciated the no nonsense advice and
strong focus on fundamentals. My bias towards the latter has always
made me feel like a "pseudo-MBA" so Guy's advice increased my
confidence in my own opinions in this area.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Creative Problem Solving&lt;/b&gt;
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/gp/product/1580087736/ref=pd_lpo_k2_dp_sr_1?pf_rd_p=486539851&amp;amp;pf_rd_s=lpo-top-stripe-1&amp;amp;pf_rd_t=201&amp;amp;pf_rd_i=0898154081&amp;amp;pf_rd_m=ATVPDKIKX0DER&amp;amp;pf_rd_r=0MNYP54CDQZYXSRXBHHB"&gt;ThinkerToys&lt;/a&gt;: a great resource for creative thinking techniques.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://thebackofthenapkin.com/"&gt;Back of the Napkin&lt;/a&gt;: sometimes a whiteboard or pen+paper are the most
powerful tools for communicating ideas and innovations. This book
provides great tips on visual thinking and communication.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/Mind-Map-Book-Thinking-Potential/dp/0452273226"&gt;The Mind Map Book&lt;/a&gt;: I have found mind-mapping to be a very effective
and powerful technique for creative thinking. Written by the one of
the original proponents of mind-mapping, this is a good resource for
learning how to create and use mind-maps.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/How-Think-Like-Einstein-Discover/dp/1570715858"&gt;How to think Like Einstein&lt;/a&gt;: despite the cheeky title this book
contains useful tips on creative thinking.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.amazon.com/How-Think-Like-Einstein-Discover/dp/1570715858"&gt;How to Think Like Leonardo Da Vinci&lt;/a&gt;: same as the above, cheeky title
but good advice.


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I mentioned, this is just a short list of the resources that I've
found most useful. In future posts I may discuss these and other
resources in more detail.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7421784870001498907?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7421784870001498907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7421784870001498907'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/favorite-innovation-resources.html' title='Favorite Innovation Resources'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1951039923401375601</id><published>2009-10-09T05:41:00.001-04:00</published><updated>2009-10-09T05:43:40.158-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Investing'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: Fortune's Formula</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=9YDlaUfmhkgC&amp;amp;printsec=frontcover&amp;amp;dq=fortunes+formula&amp;amp;ei=96O0Su_AA6SCywTTqZX3Dg#v=onepage&amp;amp;q=fortunes formula&amp;amp;f=false"&gt;Fortune's Formula: The Untold Story of the Scientific Betting System that Beat the Casinos and Wall Street&lt;/a&gt; by William Poundstone
&lt;/p&gt;
&lt;p&gt;
&lt;i&gt;[Read in August of 2009 but was delayed in writing the review.]&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
I absolutely &lt;i&gt;loved&lt;/i&gt; this book, in fact I more or less devoured it as it involved
three topics that I find very interesting:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Computing history
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Information_theory"&gt;Information Theory&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
Investing &amp;amp; &lt;a href="http://en.wikipedia.org/wiki/Quantitative_finance"&gt;Quantitative Finance&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How can a book involving these three topics be anything but good! 
&lt;/p&gt;
&lt;p&gt;
OK, enough gushing, the book is essentially about the following three
people:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Claude_Shannon"&gt;Claude Shannon&lt;/a&gt;: Bell Labs super-genius most famous for creating the
field of Information Theory but who also invented the use of boolean
binary logic in digital computers, and made material contributions
to the fields of Mendelian genetics, cryptography, and computational
logistics.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/John_Larry_Kelly,_Jr"&gt;John L. Kelley&lt;/a&gt;: Bell Labs scientist famous amongst geeks for
programming an IBM 704 to sing the song &lt;i&gt;Daisy Bell&lt;/i&gt; which inspired
HAL-9000's song at the end of the movie &lt;i&gt;2001: A Space   Odyssey&lt;/i&gt;. Kelly is also famous for using Shannon's Information
Theory to create the &lt;a href="http://en.wikipedia.org/wiki/Kelly_criterion"&gt;Kelly Criterion&lt;/a&gt; method for optimal betting or
investing given inside information.

&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Edward_O._Thorp"&gt;Edward O. Thorpe&lt;/a&gt;: M.I.T. professor famous for writing the book &lt;i&gt;Beat   the Dealer&lt;/i&gt; which proved &lt;a href="http://en.wikipedia.org/wiki/Card_counting"&gt;card counting&lt;/a&gt; could be used to win at Black
Jack. Thorpe is also famous for pioneering the use of computers in
hedge fund investing (a.k.a quantitative finance). Thorpe's fund,
&lt;a href="http://en.wikipedia.org/wiki/Princeton_Newport_Partners"&gt;Princeton Newport Partners&lt;/a&gt;, achieved an impressive annualized return
of 15.1% over its 19 year duration.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The story more or less goes thusly:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Shannon invents Information Theory
&lt;/li&gt;
&lt;li&gt;
With Shannon's help Kelley uses Information Theory to create the
Kelly Criterion for determining an optimal betting strategy given
inside information.
&lt;/li&gt;
&lt;li&gt;
After meeting at M.I.T., Shannon and Thorpe research ways of using
wearable computers to win at roulette. Thorpe goes on to use the
Kelly Criteria to first make optimal bets while card counting and
later make profitable investments for his hedge fund.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The book focuses on how the interaction between these three men gave
rise to much wealth. Along the way Poundstone discusses related topics
such as &lt;a href="http://en.wikipedia.org/wiki/Efficient_market_theory"&gt;Efficient Market Theory&lt;/a&gt;, the &lt;a href="http://en.wikipedia.org/wiki/Random_walk_hypothesis"&gt;Random Walk Hypothesis&lt;/a&gt;, and
&lt;a href="http://en.wikipedia.org/wiki/Statistical_arbitrage"&gt;statistical arbitrage&lt;/a&gt;. Poundstone includes just enough mathematics to
make the story interesting to technical readers without alienating
other audiences.
&lt;/p&gt;
&lt;p&gt;
It must be noted that the book offers a much richer story including
the influences of organized crime at various stages of history.
&lt;/p&gt;
&lt;p&gt;
All in all, a great book that I highly recommend to anyone interested
in these topics.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1951039923401375601?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1951039923401375601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1951039923401375601'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/book-review-fortunes-formula.html' title='Book Review: Fortune&apos;s Formula'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3900109173562239151</id><published>2009-10-09T05:38:00.001-04:00</published><updated>2009-10-09T05:39:39.549-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>Book Review: Labyrinths of Reason</title><content type='html'>&lt;P&gt;&lt;i&gt;[Read in August of 2009 but was delayed in writing the review.]&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="#Labyrinths==of==Reason:==paradox,==puzzles,==and==the==frailty==of==knowledge"&gt;Labyrinths of Reason: paradox, puzzles, and the frailty of knowledge&lt;/a&gt;
by William Poundstone.
&lt;/p&gt;
&lt;p&gt;
Ouch, my head hurts. How do we &lt;i&gt;really&lt;/i&gt; know we're not just
brains-in-vats living in a simulated reality? 
&lt;/p&gt;
&lt;p&gt;
This is more or less the central theme of the book which guides the
reader through various thought experiments designed to probe the
depths of meaning, understanding, and knowing. Along the way Poundstone
discusses various topics such as: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Deductive_reasoning"&gt;Deductive&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Inductive_reasoning"&gt;inductive&lt;/a&gt; reasoning
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Counterfactual_conditional"&gt;Counterfactuals&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Henri_Poincaré"&gt;Poincare&lt;/a&gt;'s universe doubling riddle
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Complex_systems"&gt;Complexity Theory&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Satisfiability"&gt;Satisfiability&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://en.wikipedia.org/wiki/Computational_complexity_theory"&gt;Computability Theory&lt;/a&gt; and &lt;a href="#NP-Completeness"&gt;NP-Completeness&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could go on but doing so would still do the book an injustice. It's
a great book that covers a lot of interesting topics. Well worth
reading if you are interested in these kinds of things but be prepared
to get a few headaches from excessive thinking. I'm planning to
re-read it perhaps a year from now to try to better absorb the
material.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3900109173562239151?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3900109173562239151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3900109173562239151'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/book-review-labyrinths-of-reason.html' title='Book Review: Labyrinths of Reason'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7115007350247626716</id><published>2009-10-09T05:06:00.000-04:00</published><updated>2009-10-09T05:07:48.785-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='History'/><title type='text'>Book Review: Prisoner's Dilemma</title><content type='html'>&lt;p&gt;&lt;i&gt;[Read in August of 2009 but was delayed in writing the review.]&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://books.google.com/books?id=Zf804exGloQC&amp;amp;q=prisoner%27s+dilemma&amp;amp;dq=prisoner%27s+dilemma"&gt;Prisoner's Dilemma: John Von Neumann, Game Theory, and the Puzzle of the Bomb&lt;/a&gt; by William Poundstone.
&lt;/p&gt;
&lt;p&gt;
A book ostensibly about &lt;a href="http://en.wikipedia.org/wiki/Game_theory"&gt;Game Theory&lt;/a&gt;, I felt that it was more of a montage of:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Game Theory
&lt;/li&gt;
&lt;li&gt;
A biography of &lt;a href="http://en.wikipedia.org/wiki/Von_Neumann"&gt;John Von Neumann&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
A history of the &lt;a href="http://en.wikipedia.org/wiki/RAND_Corporation"&gt;RAND Corporation&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Cold_War"&gt;cold war&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Game Theory content was fascinating and informative covering
various kinds of games, strategies, and complications stemming from
repeated games. Good stuff.
&lt;/p&gt;
&lt;p&gt;
I found the Von Neumann biography captivating as it provided a deeper
insight into his personal nature than I had read before. Clearly he
was an incredibly intelligent man but he also clearly had a number of
personal challenges. While this material makes Von Neumann appear more
"human", it's not particularly pleasant to read about. Also, I thought
the account of his death from cancer was too detailed and grim.
&lt;/p&gt;
&lt;p&gt;
Personally, I have no interest in the history of the RAND corporation
or the cold war so I found these parts of the book difficult to get
through. But others may find this material more interesting. 
&lt;/p&gt;
&lt;p&gt;
All in all, not a bad book if you're interested in these topics but I
wouldn't recommend this for "feel good" reading.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7115007350247626716?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7115007350247626716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7115007350247626716'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/10/book-review-prisoners-dilemma.html' title='Book Review: Prisoner&apos;s Dilemma'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4979402606515247570</id><published>2009-09-27T16:28:00.002-04:00</published><updated>2009-09-27T16:36:55.800-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: Complexity</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=sSgzHayrDBsC&amp;amp;printsec=frontcover&amp;amp;dq=complexity:+a+guided+tour&amp;amp;ei=-MGKSrinIoO0zASVhryFDg#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Complexity: A Guided Tour&lt;/a&gt; by Melanie Mitchell
&lt;/p&gt;
&lt;p&gt;
&lt;i&gt;[Read in July of 2009 but was delayed in writing the review.]&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;
I came across this book during a bookstore trip and decided to
purchase it as it included topics related to a couple of &lt;a href="http://jcardente.blogspot.com/2009/07/book-review-recursive-universe.html"&gt;recent&lt;/a&gt; &lt;a href="http://jcardente.blogspot.com/2009/06/new-kind-of-science.html"&gt;posts&lt;/a&gt; and
previously read book, &lt;a href="http://books.google.com/books?id=QTHsGNY4wcwC&amp;amp;printsec=frontcover&amp;amp;dq=linked#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Linked: The New Science of Networks&lt;/a&gt;. I'm glad I
did as it made me realize that many of the things that I am interested
in fall under the domain of &lt;a href="http://en.wikipedia.org/wiki/Complex_systems"&gt;Complex Systems&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The book is structured as five parts and nineteen chapters as
summarized below:
&lt;/p&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;
&lt;col align="left"&gt;&lt;/col&gt;&lt;col align="right"&gt;&lt;/col&gt;&lt;col align="left"&gt;&lt;/col&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;PART&lt;/th&gt;&lt;th&gt;CHAPTER&lt;/th&gt;&lt;th&gt;THEME&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;BACKGROUND AND HISTORY&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;What is Complexity?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Dynamics, Chaos, and Prediction&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Information&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Computation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Evolution&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;Genetics, Simplified&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;Defining and Measuring Complexity&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;LIFE AND EVOLUTION IN COMPUTERS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;Self-Reproducing Computer Programs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;Genetic Algorithms&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;COMPUTATION WRIT LARGE&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Cellular Automata, Life, and the Universe&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Computing with Particles&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;Information Processing in Living Systems&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;How to Make Analogies (if You Are a Computer)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;Prospects of Computer Modeling&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;NETWORK THINKING&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;The Science of Networks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;Applying Network Science to Real-World Networks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;The Mystery of Scaling&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;Evolution, Complexified&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;CONCLUSION&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;The Past and Future of the Sciences of Complexity&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;
Part 1, nearly a third of the entire book, provides a thorough
introduction to Complex Systems which are defined by the author
as having the following properties:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Complex collective behavior (i.e. the whole is greater than the sum
of the parts)
&lt;/li&gt;
&lt;li&gt;
Signaling and information processing
&lt;/li&gt;
&lt;li&gt;
Adaptation to environmental changes

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example complex systems cited were insect colonies, the brain, the
immune system, economies, and the world wide web.
&lt;/p&gt;
&lt;p&gt;
Chapter 2 was an enjoyable introduction to dynamical systems and chaos
theory which I had only a cursory awareness of. The simple "rabbit
population" example using &lt;a href="http://en.wikipedia.org/wiki/Logistic_map"&gt;logistic maps&lt;/a&gt; was a great aid in
understanding the concepts of &lt;a href="http://en.wikipedia.org/wiki/Attractor#Types_of_attractors"&gt;fixed/periodic/strange attractors&lt;/a&gt;, and a
&lt;a href="http://en.wikipedia.org/w/index.php?title=Sensitive_dependence_on_initial_conditions&amp;amp;redirect=no"&gt;sensitive dependence on initial conditions&lt;/a&gt; (aka. the Butterfly
Effect). New to me were the concepts of the &lt;a href="http://en.wikipedia.org/wiki/Period-doubling_bifurcation"&gt;period doubling route to chaos&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Feigenbaum_constants"&gt;Feigenbaum's Constant&lt;/a&gt; (which is just plain odd).
&lt;/p&gt;
&lt;p&gt;
Chapters 3 and 4 covered concepts related to information and
computation including: entropy, Maxwell's Demon, statistical
mechanics, Shannon's information theory, Hilbert's problems, Godel's
incompletness theorem, and Turing machines. Overall the treatment was
good and a sufficient introduction for readers unfamiliar with
these concepts.
&lt;/p&gt;
&lt;p&gt;
Chapters 5 and 6 discussed the topics of evolution and genetics. I
found the discussion on evolution interesting as I don't recall
learning about the feud between the early Darwinians, who believed in
continuous small variations, and Mendelians, who believed in discrete
large variations. I also don't recall learning about the &lt;a href="http://en.wikipedia.org/wiki/Modern_synthesis"&gt;Modern Synthesis&lt;/a&gt; or the continued challenges in the form of &lt;a href="http://en.wikipedia.org/wiki/Punctuated_equilibrium"&gt;punctuated equilibrium&lt;/a&gt;. Interesting stuff. 
&lt;/p&gt;
&lt;p&gt;
Chapter 7 discussed various ways of defining and measuring complexity
as: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
size
&lt;/li&gt;
&lt;li&gt;
entropy
&lt;/li&gt;
&lt;li&gt;
algorithmic information content
&lt;/li&gt;
&lt;li&gt;
logical depth
&lt;/li&gt;
&lt;li&gt;
thermodynamic depth
&lt;/li&gt;
&lt;li&gt;
computatoinal capacity
&lt;/li&gt;
&lt;li&gt;
statistical complexity
&lt;/li&gt;
&lt;li&gt;
fractal dimension
&lt;/li&gt;
&lt;li&gt;
degree of hierarchy

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While no single measure was identified as &lt;i&gt;the&lt;/i&gt; measure, all were
interesting to consider.
&lt;/p&gt;
&lt;p&gt;
Chapters 8 to 10 discussed cellular automata, genetic algorithms, and
artificial life. While the treatment was good, it felt light-weight after 
having just read &lt;a href="http://jcardente.blogspot.com/2009/07/book-review-recursive-universe.html"&gt;The Recursive Universe&lt;/a&gt;. That said, Chapter 10 did
provide a nice overview of Wolfram's work on cellular automata that
augmented &lt;a href="http://jcardente.blogspot.com/2009/06/new-kind-of-science.html"&gt;the prior material I had read on this subject&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Chapter 11 discussed the use of cellular automata for majority
classification tasks. The specific task discussed was the
determination of the dominant color in a one-dimensional vector of
white and black pixels. The authors used genetic algorithms to evolve
a set of CA rules that reliably performed this task by creating
diagonal vectors that carried local majority-voting decisions which
eventually intersected to produce a majority-vote for the entire
original vector. The authors seemed unable to at first understand how
the evolved CA worked but I thought it obvious as clearly any solution
must involve the horizontal communication of local-majority votes in
order to reach a global decision. It seems obvious (to me) that such
horizontal communication would manifest as diagonal vectors when the
CA evolutions are graphed vertically. I thought the use of particle
equations to explain the phenomina was overkill but still interesting.
&lt;/p&gt;
&lt;p&gt;
I enjoyed the author's account in Chapter 13 of her work on the
&lt;a href="http://en.wikipedia.org/wiki/Copycat_(software)"&gt;CopyCat program&lt;/a&gt; which was designed to process analogies. The problem
appears hard and the program was quite clever. I must admit I am
extermely jealous that she got to work so closely with &lt;a href="http://en.wikipedia.org/wiki/Douglas_Hofstadter"&gt;Douglas Hofstadter&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Chapters 15 to 16 discussed real-world network theory which included
topics such as small-world phenomina, scale-free networks, clustering,
preferential association, and network resiliance. Overall the
treatment was good but mostly a review after having just read
(actually listened to) Barabasi's book &lt;a href="http://books.google.com/books?id=QTHsGNY4wcwC&amp;amp;printsec=frontcover&amp;amp;dq=linked#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Linked&lt;/a&gt;, which I highly 
recommend reading.
&lt;/p&gt;
&lt;p&gt;
Chapters 17 and 18 discussed network theory in the contexts of
metabolic scaling and evolution which was very interesting.  Some of
this material was a review of &lt;a href="http://en.wikipedia.org/wiki/Stuart_Kauffman"&gt;Stuart Kauffman&lt;/a&gt;'s work documented in his
book &lt;a href="http://books.google.com/books?id=lZcSpRJz0dgC&amp;amp;printsec=frontcover&amp;amp;dq=origins+of+order&amp;amp;ei=-KG0SuWLIJW-zATylbjsDg#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Origins of Order&lt;/a&gt;. I previously read another of Kauffman's books,
&lt;a href="http://books.google.com/books?id=o-Owb5IDkSQC&amp;amp;printsec=frontcover&amp;amp;dq=at+home+in+the+universe&amp;amp;ei=J6K0SojoAaP8yATd-vTlDg#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;At Home in the Universe&lt;/a&gt;, which touched on some of the same topics but
perhaps I'll add Origins to the list of potential future readings.
&lt;/p&gt;
&lt;p&gt;
Chapter 19 concluded the book with an overview of the state of
complexity science and the need for a unifying theory. While the 
field has accomplished many great things, it appears that a lot
of difficult work remains which is perhaps why this is such an 
appealing topic. 
&lt;/p&gt;
&lt;p&gt;
In summary, Complexity is a great book that covers a lot of
interesting topics. On a personal level, I am thankful to have
read this book because, as I mentioned, it helped me realize
that complexity science is the meta-topic that unifies many of 
my long term interests and therefore serves a guide for future
research.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4979402606515247570?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4979402606515247570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4979402606515247570'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/book-review-complexity.html' title='Book Review: Complexity'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6481858213819568421</id><published>2009-09-24T18:24:00.002-04:00</published><updated>2009-09-24T18:27:23.512-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><title type='text'>The Case of the Bloated Firmware</title><content type='html'>&lt;p&gt;A few years ago, a co-worker of mine was working on an embedded
micro-controller for a proprietary enterprise class motherboard.  I
don't recall the exact details but my recollection is that the
micro-controller was responsible for monitoring a group of sensors and
reporting any anomalies.
&lt;/p&gt;
&lt;p&gt;
My friend used a commercial C compiler to develop the code for the
micro-controller which compiled fine but resulted in a binary that was
too large for the device's EPROM. He tried a number of things to
reduce the binary's size but was unable to get the code to fit. During
a serendipitous hallway conversation he asked if I could take a look
at the problem and we returned to his cube on a mission to shrink the
binary.
&lt;/p&gt;
&lt;p&gt;
First, he walked me through the source code to explain the overall
purpose and design. At the C level the code looked fine so we next
took a look at the disassembled binary and found our first clue - one
of the &lt;code&gt;for&lt;/code&gt; loops seemed to generate an excessive amount of
instructions. Strange.
&lt;/p&gt;
&lt;p&gt;
Again, the exact details escape me but the code in question looked
roughly as follows:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span class="linenr"&gt; 1:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value1&lt;/span&gt;[NUM_SENSORS];
&lt;span class="linenr"&gt; 2:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value2&lt;/span&gt;[NUM_SENSORS];
&lt;span class="linenr"&gt; 3:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value3&lt;/span&gt;[NUM_SENSORS];
&lt;span class="linenr"&gt; 4:  &lt;/span&gt;
&lt;span class="linenr"&gt; 5:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;for&lt;/span&gt;(sensor_num= 0; sensor_num &amp;lt; NUM_SENSORS; sensor_num++) {
&lt;span class="linenr"&gt; 6:  &lt;/span&gt;
&lt;span class="linenr"&gt; 7:  &lt;/span&gt;  value1[sensor_num] = readSensorValue1(sensor_num);
&lt;span class="linenr"&gt; 8:  &lt;/span&gt;  value2[sensor_num] = readSensorValue2(sensor_num);
&lt;span class="linenr"&gt; 9:  &lt;/span&gt;  value3[sensor_num] = readSensorValue3(sensor_num);
&lt;span class="linenr"&gt;10:  &lt;/span&gt;}
&lt;/pre&gt;




&lt;p&gt;
What we observed was that each of the array references resulted in a
lot of instructions to compute the specified element's memory address.
Since the loop body included three references, it generated a &lt;i&gt;lot&lt;/i&gt; of
instructions. Matters were made worse by the fact that there were
multiple loops of this kind in the code. We found our culprit.
&lt;/p&gt;
&lt;p&gt;
To work around the problem, I suggested that he re-write the code
as follows:
&lt;/p&gt;



&lt;pre class="src src-c"&gt;
&lt;span class="linenr"&gt; 1:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;struct&lt;/span&gt; {
&lt;span class="linenr"&gt; 2:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value1&lt;/span&gt;;
&lt;span class="linenr"&gt; 3:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value2&lt;/span&gt;;
&lt;span class="linenr"&gt; 4:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;value3&lt;/span&gt;;
&lt;span class="linenr"&gt; 5:  &lt;/span&gt;} &lt;span style="color: #b7850a;"&gt;sensors&lt;/span&gt;[NUM_SENSORS];
&lt;span class="linenr"&gt; 6:  &lt;/span&gt;
&lt;span class="linenr"&gt; 7:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;for&lt;/span&gt;(sensor_num= 0; sensor_num &amp;lt; NUM_SENSORS; sensor_num++) {
&lt;span class="linenr"&gt; 8:  &lt;/span&gt;
&lt;span class="linenr"&gt; 9:  &lt;/span&gt;  sensors[sensor_num].value1 = readSensorValue1(sensor_num);
&lt;span class="linenr"&gt;10:  &lt;/span&gt;  sensors[sensor_num].value2 = readSensorValue2(sensor_num);
&lt;span class="linenr"&gt;11:  &lt;/span&gt;  sensors[sensor_num].value3 = readSensorValue3(sensor_num);
&lt;span class="linenr"&gt;12:  &lt;/span&gt;}
&lt;/pre&gt;




&lt;p&gt;
We changed the loops, recompiled the code, and presto chango
the binary was small enough to fit!
&lt;/p&gt;
&lt;p&gt;
A quick look at the disassembly confirmed my hypothesis that writing
the code as proposed would result in only a single array element
address calculation in the loop body - the address of each structure
member was just an offset from the array element's starting address.
The array element address calculations were still inefficient but we
had reduced their number enough for the binary to fit within the
device's EPROM. Success!
&lt;/p&gt;
&lt;p&gt;
Overall, this wasn't a particularly difficult problem and didn't take
that much time to resolve. However, the necessity to imagine how the
compiler would respond to restructuring the code made this a fun, and
memorable "bug" to work on. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6481858213819568421?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6481858213819568421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6481858213819568421'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/case-of-bloated-firmware.html' title='The Case of the Bloated Firmware'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3025169406692940215</id><published>2009-09-24T06:08:00.001-04:00</published><updated>2009-09-24T06:09:56.931-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><title type='text'>Dan Weinreb Talk at Google</title><content type='html'>&lt;p&gt;In August, &lt;a href="http://en.wikipedia.org/wiki/Dan_Weinreb"&gt;Dan Weinreb&lt;/a&gt; gave &lt;a href="http://www.youtube.com/watch?v=xquJvmHF3S8"&gt;an interesting talk&lt;/a&gt; at Google on &lt;a href="http://en.wikipedia.org/wiki/ITA_Software"&gt;ITA Software&lt;/a&gt;'s high-performance transaction system for airline ticket
searching and pricing. ITA's software serves as the foundation for
more well known travel pricing services like Orbitz who is an ITA
customer. The problem ITA attempts to solve is extremely complex as
explained in this &lt;a href="http://www.demarcken.org/carl/papers/ITA-software-travel-complexity/ITA-software-travel-complexity.pdf"&gt;slideument&lt;/a&gt; by Carl de Marken.
&lt;/p&gt;
&lt;p&gt;
Amongst the Lisp community, ITA is famous for the signficant
use of Lisp in their system, hiring well-known Lisp hackers, and
using &lt;a href="http://www.itasoftware.com/careers/hiringpuzzles.html?catid=114"&gt;programming puzzles&lt;/a&gt; to filter job applicants. 
&lt;/p&gt;
&lt;p&gt;
Given my deep interest in all things Lisp (programming, lisp-machines,
A.I., etc), &lt;a href="http://danweinreb.org/blog/"&gt;Dan's blog&lt;/a&gt; is one of my favorite websites and someday I
hope to meet him at a Boston Lisp Users meeting (if I can ever make it
to one again).
&lt;/p&gt;
&lt;p&gt;
Nice talk and worth watching if you're interested in this kind of 
thing.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3025169406692940215?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3025169406692940215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3025169406692940215'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/dan-weinreb-talk-at-google.html' title='Dan Weinreb Talk at Google'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7496023407443918881</id><published>2009-09-23T06:22:00.001-04:00</published><updated>2009-09-23T19:01:07.091-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><title type='text'>The Case of the Replayed Queue</title><content type='html'>&lt;p&gt;A number of years ago, I wrote a Linux device driver for a host bus
adapter (HBA). The interface to the HBA consisted of two &lt;a href="http://en.wikipedia.org/wiki/Circular_buffer"&gt;circular buffers&lt;/a&gt;, one to send information to the HBA (TxQ) and another to
receive information from the HBA (RxQ). In this particular project,
the HBAs were being used in a non-standard way to implement a
bi-directional communication link between two Linux systems. As a
result, the driver sent both commands and responses via the TxQ and
received both commands and responses via the RxQ. In both cases, the
driver communicated with the HBA using fixed-size data structures
called IO control blocks (IOCBs).
&lt;/p&gt;
&lt;p&gt;
Another aspect of this non-traditional use of the HBAs was that a
proprietary &lt;a href="http://en.wikipedia.org/wiki/Upper_layer_protocol"&gt;upper layer protocol&lt;/a&gt; was used instead of the standard
protocol used with the HBA. In this case, the protocol command packets
did not fit within a single IOCB. As a result, two IOCBs were needed
to send and receive command packets between the driver and HBA. These
IOCB pairs were required to be adjacent in the circular buffers.
&lt;/p&gt;
&lt;p&gt;
As the project reached the beta-testing phase, a perplexing driver bug
began manifesting during QA testing. The failure was extremely rare
happening only once a day while under heavy load. Unfortunately, the
unit-test I had created to test the driver ex-situ continued to run
without error so the problem was only occurring in-situ in QA'a full
system configuration and under their heavy load. Oh bother.
&lt;/p&gt;
&lt;p&gt;
Faced with a difficult bug and a nearing deadline I got straight to
work on figuring out the problem. To start, I reviewed the QA logs to
determine a pattern to the failures. The evidence suggested that the
driver was re-processing protocol packets that had already been
handled. To me this indicated a potential problem with the RxQ
circular buffer but a code-review didn't reveal any obvious bugs.
&lt;/p&gt;
&lt;p&gt;
I proceeded on to debugging the driver in-situ. Unfortunately, for
reasons that I can't recall, I had little success using a kernel
debugger. As a result, I initially tried to use print statements but
the verbose system messages generated by the failure caused the
information to immediately scroll off the screen. I tried sending
debug information out a serial port but this slowed the system down
enough to significantly increase the time between failures beyond
practicality. Hmph.
&lt;/p&gt;
&lt;p&gt;
After a week of frustration and an increasing pressure from the team
to resolve the bug, I decided to take a fresh view of the problem.
First, I considered what could cause the driver to replay the RxQ and
quickly concluded that erroneously putting the tail-pointer past the
head-pointer could cause the problem. A quick review of the head-tail
pointer management code didn't reveal any mistakes. Hmph.
&lt;/p&gt;
&lt;p&gt;
Next, I considered how the head-tail pointer code could be &lt;i&gt;tricked&lt;/i&gt;
into putting the tail pointer beyond the head. I reasoned that one way
this could happen was if the driver thought the contents of the
circular buffer were &lt;i&gt;longer&lt;/i&gt; than its actual size. If this were the
case, then when the driver updated the tail pointer it would put it
past the head which would erroneously make the circular appear full
when the driver re-examined it. But what could make the driver think
that the buffer contents were longer than they actually were?
&lt;/p&gt;
&lt;p&gt;
After some deep pondering the problem hit me, it must those multi-IOCB
commands! My thought was that if for some reason the second of the two
IOCBs wasn't posted to the queue when the driver processed it and if
the driver didn't check for this condition, that the driver would
adjust the tail pointer as if the second IOCB was present which would
put it after the head pointer. The timing window for this seemed to be
impossibly small but the failure was taking a day to manifest under
heavy load so it had to be a rare sequence of events. If there is one
thing that I have learned from my years working on large-scale
parallel computer systems it's that highly improbably events occur far
more frequently than one suspects.
&lt;/p&gt;
&lt;p&gt;
I checked the code and confirmed that I had failed to account for
incomplete multi-IOCBs. I added the necessary guards to the code, gave a new
binary to QA, and crossed my fingers. A few days later success was
declared and from then on the driver performed without error. 
&lt;/p&gt;
&lt;p&gt;
I never figured out why the HBA was delayed in posting the second half of
the protocol command IOCBs to the RxQ. But you can be sure that I'll
never forget to check for circular buffer fragments again!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7496023407443918881?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7496023407443918881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7496023407443918881'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/case-of-replayed-queue.html' title='The Case of the Replayed Queue'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3911159307534138240</id><published>2009-09-19T04:04:00.000-04:00</published><updated>2009-09-19T04:05:33.125-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><title type='text'>The Case of the Errant DMA</title><content type='html'>&lt;p&gt;The C++ puzzle that &lt;a href="http://jcardente.blogspot.com/2009/09/fun-c-problem.html"&gt;I posted about the other day&lt;/a&gt; got me thinking of
various bugs that I've helped root-cause in the past. I thought it
would be fun to post about some of the most memorable ones as a series
of posts tagged as BugHunts.
&lt;/p&gt;
&lt;p&gt;
About a year ago, a programmer that I was mentoring ran into a problem
with errant DMAs in a ATA device driver that he was writing for a
proprietary operating system (mostly as a training exercise). Said
programmer did his initial development in a virtualized environment
(i.e. virtual machine running on a hypervisor) and the device driver
worked just fine. However, when run on a real platform the second and
subsequent DMA operations would access "random" memory locations
instead of those specified in the supplied scatter-gather lists.
&lt;/p&gt;
&lt;p&gt;
After confirming that the basic scatter-gather code was working
properly, the developer asked if I could help identify the problem. I
started off by asking him to walk me through how the DMA operations
were supposed to occur. Next, we walked through the code to ensure
that it matched his description. Not seeing any obvious bugs, we then
proceeded on to running the code, breaking into the debugger, and
inspecting the scatter-gather lists to make sure that they contained
the correct source/destination addresses. We observed the first DMA
operation, which always worked, and the subsequent operations, which
always failed, but saw no differences between the two. Humph.
&lt;/p&gt;
&lt;p&gt;
At this point, I suggested that we focus on the fact that the first
operation &lt;i&gt;always&lt;/i&gt; succeeded but the subsequent operations &lt;i&gt;always&lt;/i&gt;
failed. I asserted that there must be a difference between how the
first and subsequent operations were set up. So again we inspected the
code and watched the execution in the debugger but didn't see any
differences. Double humph.
&lt;/p&gt;
&lt;p&gt;
After some thought, I then hypothesized that perhaps the first DMA
operation was unexpectedly altering settings that were setup during
the device driver's initialization. Since these setup operations were
performed outside of the DMA setup code, we wouldn't observe any
execution differences between the first and subsequent operations. So
again we walked through the DMA operation but this time considered the
additional configuration settings that they depended on. Bingo.
&lt;/p&gt;
&lt;p&gt;
The chipset in question used a memory resident scatter-gather
list. Since only a single DMA operation was outstanding at any time
(per ATA channel) the developer pre-allocated the memory region during
the device driver's initialization and wrote the region's base address
to the appropriate chipset register. Since the same memory region was
re-used to hold the scatter-gather list for each operation, the
developer didn't re-write the base address to the register for each
operation.
&lt;/p&gt;
&lt;p&gt;
It turned out that on the &lt;i&gt;virtualized&lt;/i&gt; platform the emulated ATA
chipset left this register unchanged between operations, hence the
device driver worked fine. However, on the &lt;i&gt;real&lt;/i&gt; platform the chipset
modified the register to point to the active scatter-gather element as
the DMA operation was performed. As a result, the first DMA operation
completed OK while all subsequent DMA operations walked off the end of
the scatter-gather table and misdirected the DMA accesses based on
the random data present in memory above the scatter-gather table. Once
the developer added the code to re-initialize the scatter-gather base
address register for each operation everything worked fine.
&lt;/p&gt;
&lt;p&gt;
After the bug was fixed, the developer stated that he found this
behavior surprising. I explained that having worked on designing ASICs
in the past, I wasn't at all surprised that the hardware reused the
register to hold the address of each scatter-gather element as the
operation progressed; all's fair in ASIC design when it means saving
some gates!
&lt;/p&gt;
&lt;p&gt;
He then wondered how he could find such bugs in the future without
having similar low-level hardware experience. I told him that the
important lesson from the exercise was that once the possible errors
have been ruled out you need to suspend disbelief and begin
considering the &lt;i&gt;impossible&lt;/i&gt; errors. Often our understanding of
complicated systems is incomplete or flawed, therefore things we
assume to be impossible may in fact be possible and therefore must be
considered. I explained that this is a skill that must be hard-won
over time through solving progressively harder bugs. I encouraged him
to seek out hard bugs, spend time trying to resolve them on his own,
but if stuck to utilize the already hard-won experiences of his
mentors to accelerate his own skill development. This is the advice
that has worked for me during my career and I am confident that the
developer in question will do just fine.
&lt;/p&gt;
&lt;p&gt;
Anyway, this particular bug was fun because it required thinking about
how the system affected the program rather than how the program
affected the system. These kinds of puzzles are always fun&amp;hellip; after
you've figured out the problem that is.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3911159307534138240?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3911159307534138240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3911159307534138240'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/case-of-errant-dma.html' title='The Case of the Errant DMA'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3937531285894730897</id><published>2009-09-16T11:37:00.008-04:00</published><updated>2009-09-19T04:04:09.338-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BugHunts'/><title type='text'>Fun C++ Problem</title><content type='html'>&lt;p&gt;Today a co-worker of mine ran into an interesting C++ inheritance
problem that perplexed the usual lunch crowd. Some might call this a
character flaw, but when presented with puzzles like this I feel
&lt;i&gt;compelled&lt;/i&gt; to figure them out. So, when I got back to my desk I
reduced the problem to the following minimal program and got to work
on figuring out the root-cause.
&lt;/p&gt;



&lt;pre class="src src-c++"&gt;
&lt;span class="linenr"&gt; 1:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;class&lt;/span&gt; &lt;span style="color: #218a21;"&gt;Superclass&lt;/span&gt;
&lt;span class="linenr"&gt; 2:  &lt;/span&gt;{
&lt;span class="linenr"&gt; 3:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;public&lt;/span&gt;:
&lt;span class="linenr"&gt; 4:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;void&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;);
&lt;span class="linenr"&gt; 5:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;void&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;oneParameter&lt;/span&gt;,
&lt;span class="linenr"&gt; 6:  &lt;/span&gt;            &lt;span style="color: #218a21;"&gt;int&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;twoParameter&lt;/span&gt;);
&lt;span class="linenr"&gt; 7:  &lt;/span&gt;};
&lt;span class="linenr"&gt; 8:  &lt;/span&gt;
&lt;span class="linenr"&gt; 9:  &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;class&lt;/span&gt; &lt;span style="color: #218a21;"&gt;Subclass&lt;/span&gt; : &lt;span style="color: #7f007f;"&gt;public&lt;/span&gt; &lt;span style="color: #218a21;"&gt;Superclass&lt;/span&gt;
&lt;span class="linenr"&gt;10:  &lt;/span&gt;{
&lt;span class="linenr"&gt;11:  &lt;/span&gt;  &lt;span style="color: #218a21;"&gt;void&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;);
&lt;span class="linenr"&gt;12:  &lt;/span&gt;  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;inherits foo(int, int)&lt;/span&gt;
&lt;span class="linenr"&gt;13:  &lt;/span&gt;};
&lt;span class="linenr"&gt;14:  &lt;/span&gt;
&lt;span class="linenr"&gt;15:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;void&lt;/span&gt; &lt;span style="color: #5e9d9f;"&gt;Superclass&lt;/span&gt;::&lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;)
&lt;span class="linenr"&gt;16:  &lt;/span&gt;{}
&lt;span class="linenr"&gt;17:  &lt;/span&gt;
&lt;span class="linenr"&gt;18:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;void&lt;/span&gt; &lt;span style="color: #5e9d9f;"&gt;Superclass&lt;/span&gt;::&lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;oneParameter&lt;/span&gt;,
&lt;span class="linenr"&gt;19:  &lt;/span&gt;                     &lt;span style="color: #218a21;"&gt;int&lt;/span&gt;  &lt;span style="color: #b7850a;"&gt;twoParameter&lt;/span&gt;)
&lt;span class="linenr"&gt;20:  &lt;/span&gt;{}
&lt;span class="linenr"&gt;21:  &lt;/span&gt;
&lt;span class="linenr"&gt;22:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;void&lt;/span&gt; &lt;span style="color: #5e9d9f;"&gt;Subclass&lt;/span&gt;::&lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;)
&lt;span class="linenr"&gt;23:  &lt;/span&gt;{
&lt;span class="linenr"&gt;24:  &lt;/span&gt;  foo(aParameter, 2);
&lt;span class="linenr"&gt;25:  &lt;/span&gt;}
&lt;span class="linenr"&gt;26:  &lt;/span&gt;
&lt;span class="linenr"&gt;27:  &lt;/span&gt;&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;main&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;argc&lt;/span&gt;, &lt;span style="color: #218a21;"&gt;char&lt;/span&gt; * &lt;span style="color: #b7850a;"&gt;argv&lt;/span&gt;[])
&lt;span class="linenr"&gt;28:  &lt;/span&gt;{
&lt;span class="linenr"&gt;29:  &lt;/span&gt;  &lt;span style="color: #7f007f;"&gt;return&lt;/span&gt; (0);
&lt;span class="linenr"&gt;30:  &lt;/span&gt;}
&lt;/pre&gt;




&lt;p&gt;
In words, there is a super-class, &lt;code&gt;Superclass&lt;/code&gt;, with an overloaded
method, &lt;code&gt;foo()&lt;/code&gt;, that has two variants - &lt;code&gt;foo(int)&lt;/code&gt; and
&lt;code&gt;foo(int,int)&lt;/code&gt;. In addition, there is a sub-class, &lt;code&gt;Subclass&lt;/code&gt;, derived
from &lt;code&gt;Superclass&lt;/code&gt; that redefines the &lt;code&gt;foo(int)&lt;/code&gt; variant but inherits
&lt;code&gt;Superclass&lt;/code&gt;'s &lt;code&gt;foo(int,int)&lt;/code&gt; method.
&lt;/p&gt;
&lt;p&gt;
Straight forward, right? Nope. 
&lt;/p&gt;



&lt;pre class="example"&gt;
$g++ borked.cpp 
borked.cpp: In member function ‘void Subclass::foo(int)’:
borked.cpp:26: error: no matching function for call to ‘Subclass::foo(int&amp;amp;, int)’
borked.cpp:24: note: candidates are: void Subclass::foo(int)
&lt;/pre&gt;




&lt;p&gt;
For some reason the compiler isn't able to resolve the call to the
inherited &lt;code&gt;foo(int,int)&lt;/code&gt; when called from &lt;code&gt;Subclass&lt;/code&gt;'s &lt;code&gt;foo(int)&lt;/code&gt;
method. How strange.
&lt;/p&gt;
&lt;p&gt;
Commenting out the offending call on line 24 allows the program to
compile and dumping the symbol table yields the expected results:
&lt;/p&gt;



&lt;pre class="example"&gt;
$nm a.out
0000000100000ea4 s  stub helpers
0000000100001048 D _NXArgc
0000000100001050 D _NXArgv
0000000100000e60 T __ZN10Superclass3fooEi
0000000100000e6e T __ZN10Superclass3fooEii
0000000100000e7e T __ZN8Subclass3fooEi
                 U ___gxx_personality_v0
0000000100001060 D ___progname
0000000100000000 A __mh_execute_header
0000000100001058 D _environ
                 U _exit
0000000100000e8b T _main
0000000100001020 s _pvars
                 U dyld_stub_binder
0000000100000e24 T start
&lt;/pre&gt;




&lt;p&gt;
All the methods are present and have unique signatures. Recompiling
with the &lt;code&gt;-fdump-class-hierachy&lt;/code&gt; option also doesn't produce any
suprises - there is no vtable magic going on.
&lt;/p&gt;



&lt;pre class="example"&gt;
$g++ -fdump-class-hierarchy borked.cpp
$cat borked.cpp.002t.class 
Class Superclass
   size=1 align=1
   base size=0 base align=1
Superclass (0x1407c18c0) 0 empty

Class Subclass
   size=1 align=1
   base size=1 base align=1
Subclass (0x1407c1930) 0 empty
  Superclass (0x1407c19a0) 0 empty
&lt;/pre&gt;




&lt;p&gt;
As a final piece of the puzzle, fully qualifying the call to 
&lt;code&gt;foo(int,int)&lt;/code&gt; as follows resolves the problem.
&lt;/p&gt;



&lt;pre class="src src-c++"&gt;
&lt;span style="color: #218a21;"&gt;void&lt;/span&gt; &lt;span style="color: #5e9d9f;"&gt;Subclass&lt;/span&gt;::&lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;)
{
  &lt;span style="color: #5e9d9f;"&gt;Superclass&lt;/span&gt;::foo(aParameter, 2);
}
&lt;/pre&gt;




&lt;p&gt;
Now, I am no C++ guru but I do have a fair amount of experience with
it - mostly from writing system level code for a proprietary RTOS that
was written in C++. In fact, I wrote a loadable module framework for
that RTOS that included a runtime dynamic linker capable of handling
C++ intermediate object files. So, I'd like to think I have a decent
knowledge of the C++ object model and yet this problem made little
sense to me.
&lt;/p&gt;
&lt;p&gt;
As is often the case, I didn't know the anwser to the problem but I
did know enough to determine the right keywords to Google for the
answer. The solution is to add a &lt;code&gt;using&lt;/code&gt; declaration to the Sublcass's
definition.
&lt;/p&gt;



&lt;pre class="src src-c++"&gt;
&lt;span style="color: #7f007f;"&gt;class&lt;/span&gt; &lt;span style="color: #218a21;"&gt;Subclass&lt;/span&gt; : &lt;span style="color: #7f007f;"&gt;public&lt;/span&gt; &lt;span style="color: #218a21;"&gt;Superclass&lt;/span&gt;
{
  &lt;span style="color: #7f007f;"&gt;using&lt;/span&gt; &lt;span style="color: #5e9d9f;"&gt;Superclass&lt;/span&gt;::&lt;span style="color: #218a21;"&gt;foo&lt;/span&gt;;

  &lt;span style="color: #218a21;"&gt;void&lt;/span&gt;  &lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt;(&lt;span style="color: #218a21;"&gt;int&lt;/span&gt; &lt;span style="color: #b7850a;"&gt;aParameter&lt;/span&gt;);
  &lt;span style="color: #b12121;"&gt;// &lt;/span&gt;&lt;span style="color: #b12121;"&gt;inherits foo(int, int)
&lt;/span&gt;};
&lt;/pre&gt;




&lt;p&gt;
As I now understand the situation, when a set of overloaded methods span 
the base and derived classes, the &lt;code&gt;using&lt;/code&gt; declaration is required to 
introduce the base class's methods into the scope of the derived class.
Without the &lt;code&gt;using&lt;/code&gt; declaration, the compiler can't resolve the reference
to the base class's method without fully qualifying it. Good to know!
&lt;/p&gt;
&lt;p&gt;
I had a lot of fun figuring this out despite the fact that it took 20
minutes of time out of an already busy day. But, sometimes you just have
to let the inner-geek exercise itself!
&lt;/p&gt;
&lt;p&gt;
As an aside, I recommend Stanley Lippman's book &lt;a href="http://www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1253115126&amp;amp;sr=8-1"&gt;Inside the C++ Object Model&lt;/a&gt; to anyone interested in learning the inner workings of the C++
object model.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3937531285894730897?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3937531285894730897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3937531285894730897'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/fun-c-problem.html' title='Fun C++ Problem'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5965513958182972998</id><published>2009-09-15T18:53:00.002-04:00</published><updated>2009-09-15T18:55:40.951-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Investing'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><title type='text'>Nice Finance Video Collection</title><content type='html'>&lt;p&gt;While web-surfing I stumbled across a &lt;a href="http://www.youtube.com/user/bionicturtledotcom#play/uploads"&gt;nice collection of YouTube videos&lt;/a&gt; by &lt;a href="http://www.bionicturtle.com/"&gt;Bionic Turtle&lt;/a&gt; on statistics, finance, and quantitative
finance.
&lt;/p&gt;
&lt;p&gt;
The videos tend to be short (under ten minutes) which makes them
convenient to watch. Also, the author effectively uses Excel to work
through examples and highlight the key points.
&lt;/p&gt;
&lt;p&gt;
So far, I've only watched a handful of these videos but I plan to work
my way through them gradually with a priority towards the topics
relevant for re-balancing and evaluating the performance of my
portfolio.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5965513958182972998?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5965513958182972998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5965513958182972998'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/nice-finance-video-collection.html' title='Nice Finance Video Collection'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8605128996095170048</id><published>2009-09-09T13:34:00.001-04:00</published><updated>2009-09-09T13:36:00.054-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Investing'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><title type='text'>An Algorithmic Trading Primer</title><content type='html'>&lt;p&gt;While I was getting my MBA I developed a strong interest in
quantitative finance. I doubt I'll ever pursue a career in this field
but I continue to enjoy reading about it in my spare time as part of a
larger, general interest in applied mathematics (more about that in a
future post).
&lt;/p&gt;
&lt;p&gt;
Needless to say, I was eager to read the paper &lt;a href="http://www.iinews.com/site/pdfs/JOT_Summer2009_Palmer.pdf"&gt;Algorithmic Trading: A Primer&lt;/a&gt; published in the summer 2009 issue of &lt;a href="http://www.iijournals.com/toc/jot/current"&gt;The Journal of Trading&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Unfortunately, I apparently misinterpreted the title as instead of
discussing the algorithms themselves, the article covered the
high-level points that clients of algorithmic trading firms should be
aware of. To this end the article did cover some important points
including:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
The desire (nay need) for algorithms to increase trader productivity
&lt;/li&gt;
&lt;li&gt;
The importance of Regulation NMS and why it favors electronic
trading (which I must admit was new to me).
&lt;/li&gt;
&lt;li&gt;
The basic framework of algorithmic trading systems such as
performance benchmarks, assumptions, etc.
&lt;/li&gt;
&lt;li&gt;
The rationale for and limitations of using log-normal models for
price fluctuations.
&lt;/li&gt;
&lt;li&gt;
The importance of avoiding trades that signal the market and may
therefore invalidate the algorithm's model.
&lt;/li&gt;
&lt;li&gt;
The importance of using the right algorithms at the right time to 
achieve the intended goals. 

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, although the article didn't cover the topics I had initially hoped
for I did find it a useful read anyway. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8605128996095170048?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8605128996095170048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8605128996095170048'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/algorithmic-trading-primer.html' title='An Algorithmic Trading Primer'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7836580102855100507</id><published>2009-09-05T07:46:00.000-04:00</published><updated>2009-09-05T07:47:16.217-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: Outliers</title><content type='html'>&lt;p&gt;&lt;a href="http://books.google.com/books?id=VFDgOwAACAAJ&amp;amp;dq=outliers"&gt;Outliers&lt;/a&gt; by Malcolm Gladwell
&lt;/p&gt;
&lt;p&gt;
An interesting book asserting that rather than just latent talent,
an individual's success is do to:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;b&gt;timing&lt;/b&gt;: some people are born at just the right time to take
advantage of historical watershed moments (e.g. the PC revolution)
or age-based selections (e.g. birthday cut-offs for joining sports
teams, schools, etc).
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;practice opportunities&lt;/b&gt;: Gladwell asserts that the "universal"
requirement for achieving expertise is 10,000 hours of
practice. Gladwell further asserts that successful individuals have
often been given abnormal opportunities to obtain that amount of
practice in a short period of time and at an early age.
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;environment&lt;/b&gt;: the amount of nurture and support that a person
receives from his family and community has a significant effect on
their success. This includes parents teaching their children how to 
be assertive, and actively guide their own lives.
&lt;/li&gt;
&lt;li&gt;
&lt;b&gt;culture&lt;/b&gt;: cultural values and behaviors can have a profound effect
on an individual's level of success.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To some degree, Outliers reminded me of other recent readings:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
In the &lt;a href="http://books.google.com/books?id=gWW4SkJjM08C&amp;amp;printsec=frontcover&amp;amp;dq=the+black+swan#v=onepage&amp;amp;q=&amp;amp;f=false"&gt;Black Swan&lt;/a&gt;, Taleb asserted that individual success in
"scalable" fields was due primarily to luck and a rich-get-richer
effect. In Outliers I think Gladwell provides a compelling
explanation for that phenomenon in his focus on lucky opportunities
for obtaining expertise.
&lt;/li&gt;
&lt;li&gt;
The &lt;a href="http://calnewport.com/blog/"&gt;StudyHacks blog&lt;/a&gt; ran &lt;a href="http://calnewport.com/blog/2009/06/03/the-pyramid-method-a-simple-strategy-for-becoming-exceptionally-good"&gt;a post&lt;/a&gt; on how one person successfully
entered the music industry by selecting a single nightclub to
perform at and then focused all their energy on becoming the best
performer at that venue. To me this resonated strongly with Gladwell's 
assertion that opportunities for obtaining 10,000 hours of practice are
one of the keys to success.

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, there appears to be supporting opinions for some of Gladwell's
assertions. Personally, I suspect that he is on the right track.
&lt;/p&gt;
&lt;p&gt;
I enjoyed the first half of the book but found the second halves
multi-generational discussion weaker as I suspect a "lucky event" can
be found in everyone's family history if you go back a few
generations. 
&lt;/p&gt;
&lt;p&gt;
Being a computer geek, I enjoyed the brief biographies on both Bill
Joy and Bill Gates. I had no idea that Gates had so much programming
experience before going to college. By all appearances both Bills had
rare opportunities to become expert programmers at just the right age
to ride the PC technology wave.
&lt;/p&gt;
&lt;p&gt;
Some of the excerpts that I dog-earned were:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Page 11: &amp;hellip; the values of the world we inhabit and the people that
we surround ourselves with have a profound effect on who we are.
&lt;/li&gt;
&lt;li&gt;
Page 42: (regarding the assertion that 10,000 hours of practice are
required to become an expert at any particular skill) &amp;hellip; ten
thousand hours is an enormous amount of time. It's all but
impossible to reach that number all by yourself by the time you're a
young adult. &amp;hellip; In fact most people can only reach that number only
if they get into some kind of special program &amp;hellip; or if they get
some kind of extraordinary opportunity that gives them the chance to
put in those hours.
&lt;/li&gt;
&lt;li&gt;
Page 149: Those three things - autonomy, complexity, and a
connection between effort and reward - are, most people agree, the
three qualities that work has to have if it is to be satisfying. 
&lt;/li&gt;
&lt;li&gt;
Page 267: &amp;hellip; success follows a predictable course. It is not the
brightest who succeed. &amp;hellip; Nor is success simply the sum of
decisions and efforts we make on our own behalf. It is, rather, a
gift. Outliers are those who have been given opportunities - and who
have had the strength and presence of mind to seize them. 

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon reflection, I reduced (perhaps too far?) the book's message down
to the following three points:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Pick a meaningful skill and practice it as much as possible.
&lt;/li&gt;
&lt;li&gt;
Maximize your exposure to opportunities that require the chosen
skill to be successful and provide more practice.
&lt;/li&gt;
&lt;li&gt;
Aggressively pursue the opportunities that you come across to
extract the most value from them.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Over all a good book, and worth the time to read it.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7836580102855100507?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7836580102855100507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7836580102855100507'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/09/book-review-outliers.html' title='Book Review: Outliers'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5265427674925153213</id><published>2009-08-29T18:19:00.001-04:00</published><updated>2009-08-29T18:21:47.551-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><title type='text'>The Netflix Culture</title><content type='html'>&lt;p&gt;
The other day a co-worker sent me a link to &lt;a href="http://www.slideshare.net/reed2001/culture-1798664"&gt;a presentation&lt;/a&gt; from
Netflix that describes their corporate culture. Well worth the twenty
minutes required to read the slides.
&lt;/p&gt;
&lt;p&gt;
I'm sure like many others, I found the described culture very
attractive. I thought the desired values were so compelling that I
printed out slide 19 and hung it up in my office as a reminder of my
self-development goals. I think anyone in a creative profession,
technical or otherwise, wants to work with "stunning" colleagues as a
means for improving one's own skill. Too often I've seen loyalty
appreciated more than performance so I liked the "adequate performance
gets a generous severance package" rule. The guidelines designed to
increase employee automaticity, decrease complexity, and minimize
process strike me as recipes for success, provided that you have the
suitable talent. Finally, I really liked the notion of always paying
at the top-market rate. Frankly, the practice of reactively adjusting
compensation to market rate when an employee tries to resign never
made sense to me; why not pro-actively pay appropriately to avoid the
employee from feeling unappreciated or being tempted by external
opportunities?
&lt;/p&gt;
&lt;p&gt;
Congratulations to Netflix for creating such a unique and outstanding
corporate culture. It will be interesting to see if this culture
persists as the company grows and the original staff moves on to other
opportunities.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5265427674925153213?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5265427674925153213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5265427674925153213'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/08/netflix-culture.html' title='The Netflix Culture'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-6652536539273676171</id><published>2009-08-25T08:45:00.006-04:00</published><updated>2009-08-26T20:05:33.655-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Investing'/><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><category scheme='http://www.blogger.com/atom/ns#' term='Finance'/><title type='text'>Crooked Crazy Eddie's</title><content type='html'>&lt;p&gt;
One of my iconic memories from the 80's is the &lt;a href="http://www.youtube.com/watch?v=ivhRPiQueaE"&gt;commercials for Crazy Eddie's&lt;/a&gt;, a NY-NJ-CN area &lt;a href="http://en.wikipedia.org/wiki/Crazy_Eddie"&gt;consumer electronics store&lt;/a&gt; that claimed to offer "insane" prices. Since I grew up in RI, I had no direct experience with Crazy Eddie's and never knew what became of the company.&lt;/p&gt;

&lt;p&gt;My ignorance came to an end upon listening to an interview of the company's former CFO in &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?i=58697783&amp;id=290783428"&gt;episode 82&lt;/a&gt; of &lt;a href="http://www.npr.org/blogs/money/"&gt;NPR's Planet Money&lt;/a&gt; podcast. Evidently, Crazy Eddie's was crooked!&lt;/p&gt;

&lt;p&gt;The corruption began with skimming sales and tax revenues but progressed to full-fledged money laundering as the company's size grew and the amount of unreported money became considerable. As the CFO became more financially savy, he hit upon the clever and even more profitable idea of:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;taking the company public&lt;/li&gt;
&lt;li&gt;gradually bringing the skimmed monies back onto the books to artificially inflate revenues and profits which led Wall Street to overestimate the company's future growth and over-value the stock&lt;/li&gt;
&lt;li&gt;selling the owner's majority equity position at the inflated market prices yielding over $100M of capital gains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
What a story! In the end greed tore the family owned business apart which resulted in someone informing the authorities out of revenge thus bringing the whole operation to an end. 
&lt;/p&gt;

&lt;p&gt;The podcast is very entertaining if you're interested in this kind of thing as the CFO seems to be proud of his cleverness despite its illegality. As it happens, he is now a consultant and instructor to various government agencies on the topic of corporate fraud. So much for the notion that crime doesn't pay.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-6652536539273676171?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6652536539273676171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/6652536539273676171'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/08/crooked-crazy-eddies.html' title='Crooked Crazy Eddie&apos;s'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3863916105133952874</id><published>2009-08-17T15:02:00.001-04:00</published><updated>2009-08-17T15:15:15.848-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Struggling with Book Reviews</title><content type='html'>&lt;p&gt;When I started this blog, I thought it would be a good idea to write
book reviews as I finished readings. I've discovered two problems with
this goal:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
It takes a long time to write a worthwhile book review.
&lt;/li&gt;
&lt;li&gt;
Due to the voracity of my reading appetite, I have been unable to
delay starting a new book until I have finished writing a review
for the previous book.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result, I am now behind in writing two reviews and close to
finishing a third book. So much for posting book reviews anywhere as
frequently as &lt;a href="http://eli.thegreenplace.net/category/book-reviews/"&gt;Eli Bendersky&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I can think of five solutions:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Write shorter reviews. 
&lt;/li&gt;
&lt;li&gt;
Force myself to write reviews before starting a new book.
&lt;/li&gt;
&lt;li&gt;
Only write reviews for selected books. 
&lt;/li&gt;
&lt;li&gt;
Don't worry about it and write reviews when the time permits.
&lt;/li&gt;
&lt;li&gt;
Stop writing reviews. 

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of these, only options 2 and 3 are acceptable. Part of my rational for
writing reviews is to obtain a deeper understanding of the material
through extended contemplation, review, and summarization. This
motivation rules out options 1 and 5. Option 4 is more or less a
formalization of the current situation which I already know isn't
working; I'll just keep on reading and never get around to writing the
reviews. This, again, leaves options 2 and 3. 
&lt;/p&gt;
&lt;p&gt;
So, going forward I will attempt a hybrid approach of:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Writing reviews before starting new books.
&lt;/li&gt;
&lt;li&gt;
Only writing in-depth reviews of selected books which deserve the
most reflection.

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hopefully this new approach will lead towards fulfilling my goal of
getting more out of the books that I read. 
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3863916105133952874?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3863916105133952874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3863916105133952874'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/08/struggling-with-book-reviews.html' title='Struggling with Book Reviews'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-7735415601549623251</id><published>2009-08-05T16:22:00.002-04:00</published><updated>2009-08-05T16:29:32.636-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Cool Commodore Hack</title><content type='html'>&lt;p&gt;Although I enjoy all of the content on the &lt;a href="http://www.pagetable.com/"&gt;PageTable blog&lt;/a&gt;, I like the
&lt;a href="http://www.pagetable.com/?cat=8"&gt;archeology&lt;/a&gt; stuff the most. &lt;a href="http://www.pagetable.com/?p=273"&gt;This post&lt;/a&gt; about the hack used to do
directory listings on the Commodore is a great example.
&lt;/p&gt;
&lt;p&gt;
Essentially, the Commodore's disk drive acted as a co-processor
handling all of the mass storage management and file system
operations. The Commodore's kernel and BASIC shell used a file
oriented API (i.e. open, close, read, write) to communicate with the
floppy drive. Commands like &lt;code&gt;list-directory&lt;/code&gt; were performed by an
&lt;code&gt;open()&lt;/code&gt; call to a special address and file name and reading the
results from the open "file" until an EOF was received.
&lt;/p&gt;
&lt;p&gt;
For reasons that are unclear from the post, it seems that the
Commodore's kernel and BASIC shell were unable to natively perform the
&lt;code&gt;list-directory&lt;/code&gt; operation. As a result, a hack was needed to avoid
users from having to write a BASIC program every time they wanted to
list a directory.
&lt;/p&gt;
&lt;p&gt;
The solution was for the floppy drive to return the directory listing
in the format of a BASIC program. Thus, a &lt;code&gt;list-directory&lt;/code&gt; operation
consisted of:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
opening the special file
&lt;/li&gt;
&lt;li&gt;
reading the returned fake program into memory as a program
&lt;/li&gt;
&lt;li&gt;
listing the fake program to display the directory contents

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What a clever hack!
&lt;/p&gt;
&lt;p&gt;
If I understood the post correctly, the returned fake program simply
used the structural description of a BASIC program but wasn't a valid
program itself. In other words you couldn't execute the program
returned by the &lt;code&gt;list-directory&lt;/code&gt; operation. Too bad because it would
have been really cool if the floppy drive returned a lambda function!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-7735415601549623251?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7735415601549623251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/7735415601549623251'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/08/cool-commodore-hack.html' title='Cool Commodore Hack'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4960626250459862571</id><published>2009-07-29T05:56:00.002-04:00</published><updated>2009-07-29T07:47:55.665-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Writing'/><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>Aesthetic environments</title><content type='html'>&lt;p&gt;One of the things that I've discovered about myself is that I have a
sensitivity to my work environment. I find that I am most creative,
and productive when I am in an aesthetically pleasing environment. This
applies not only to my physical environment but also to my computing
environment which is largely why I prefer to use OSX over other equally
capable computing environments such as Linux. 
&lt;/p&gt;
&lt;p&gt;
A by product of this sensitivity is that I have a deep interest in the
work environments of others as a means for further refining my own
environment. I'm especially interested in the work spaces and habits
of people that I admire.
&lt;/p&gt;
&lt;p&gt;
In a general sense, I admire writers as I have a growing interest in
doing more writing myself. So it was with pleasure that I came across 
&lt;a href="http://whereiwrite.org/"&gt;whereiwrite.org&lt;/a&gt;, an effort to collect photographs of the work spaces of
various science fiction and fantasy authors.
&lt;/p&gt;
&lt;p&gt;
I also enjoy programming (which is really writing for machines instead
of people) and therefore I am also drawn to images of aesthetically
pleasing offices such as:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.cartridgesave.co.uk/news/25-of-the-best-home-office-mac-setups/"&gt;25 of the Best Home and Office Mac Setups&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://successfulsoftware.net/2009/07/27/where-i-program/"&gt;Where I program&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://images.businessweek.com/ss/08/07/0723_home_offices/11.htm"&gt;BusinessWeek: Readers Show Off Their Home Offices&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of my greatest idols is the famous computer scientist Donald
Knuth.  As a result I enjoyed this &lt;a href="http://www.stanfordalumni.org/news/magazine/2006/mayjun/images/features/knuth_now.jpg"&gt;picture of his home office&lt;/a&gt; from a
Standford Alumni Magazine &lt;a href="http://www.stanfordalumni.org/news/magazine/2006/mayjun/features/knuth.html"&gt;interview&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Unfortunately my current work environment is a standard corporate
cube-farm.  I often consider sending our facilities people a copy of
the book &lt;a href="http://books.google.com/books?id=eA9PAAAAMAAJ&amp;amp;q=peopleware&amp;amp;dq=peopleware"&gt;PeopleWare&lt;/a&gt; but I suspect its suggestions would be deemed
"cost inefficient". My home environment is still a work-in-progress
and unfortunately I don't get to spend much time working in it. It's a
life goal of mine to find a way to earn a living in an environment of
my choosing. So far, I'm not banking on my blogging skills!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4960626250459862571?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4960626250459862571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4960626250459862571'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/asthetic-environments.html' title='Aesthetic environments'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-5923819565243285154</id><published>2009-07-28T18:51:00.002-04:00</published><updated>2009-07-28T18:56:34.137-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><title type='text'>Library, uploaded</title><content type='html'>&lt;p&gt;After weeks of gradual effort I've finally completed entering my
personal library into a &lt;a href="http://www.librarything.com"&gt;LibraryThing&lt;/a&gt; account. If interested, you
can view the contents of my library &lt;a href="http://www.librarything.com/catalog/jcardente/yourlibrary"&gt;here&lt;/a&gt;. This is just the dead-tree
stuff and doesn't include the many PDF books that I have collected 
over the years. Perhaps that will be the topic of a future post.
&lt;/p&gt;
&lt;p&gt;
Until now I have used an OSX desktop application, &lt;a href="http://www.delicious-monster.com/"&gt;Delicious Library&lt;/a&gt;,
to catalog my books.  It's a nice program that works fairly well but
the eye-candy UI clearly takes priority over the needs of a serious
book collector. LibraryThing, on the other hand, better suits my goals
of:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Cataloging all my books, including the older ones that don't
have an ISBN number
&lt;/li&gt;
&lt;li&gt;
Providing online access to my library making it easier to 
recall references and send links to potentially interested
colleagues
&lt;/li&gt;
&lt;li&gt;
Getting book recommendations based on the contents of my 
existing library

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some friends of mine use GoodReads for similar purposes. I spent
a little time comparing LibraryThing to GoodReads and it seems that
the material differences between the two are:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
GoodReads is free for an unlimited number of books, LibraryThing is
free up to 200 books and requires a paid-for account for larger
libraries.
&lt;/li&gt;
&lt;li&gt;
GoodReads prioritizes social networking over cataloging. LibraryThing
prioritizes cataloging over social networking. 

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think both services are good and provide much value to avid readers.
&lt;/p&gt;
&lt;p&gt;
While I was entering my catalog, I also tagged each book to make it
easier to browse and track my library. One nice feature of
LibraryThing is that it provides RSS links for each tag so that
interested parties can track additions to my library for a specific
tag. Now I don't have to nag friends whenever I buy a new book, they
can just subscribe to my feeds!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-5923819565243285154?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5923819565243285154'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/5923819565243285154'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/library-uploaded.html' title='Library, uploaded'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8597903144012017691</id><published>2009-07-16T20:10:00.002-04:00</published><updated>2009-07-17T07:02:02.104-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GeekFun'/><title type='text'>Colossus: The Forbin Project</title><content type='html'>&lt;p&gt;While web surfing some time ago I came across a reference to a movie
that I hadn't seen before, &lt;a href="http://www.imdb.com/title/tt0064177/"&gt;Colossus: The Forbin Project&lt;/a&gt;. I finally got
a chance to watch it this weekend and it was fun.
&lt;/p&gt;
&lt;p&gt;
Filmed in 1970, I understand this to be the first movie with the
primary theme of a super-computer being given complete control of the
nation's defenses, achieving sentience, and overtaking mankind for its
own good. Given the period in which this was filmed, I was expecting
much "cheeze" and nonsensical computer technology. Instead, it
turned out to be a pretty good treatment of the concept based on sound
principals. Of course there was some cheeze but this can only be
expected from a forty year old movie.
&lt;/p&gt;
&lt;p&gt;
The primary lesson to be drawn from the movie - don't build a super
complicated mission-critical system, go straight into production, and
then make it impossible to disable it. Advice that continues to hold
value forty years later.
&lt;/p&gt;
&lt;p&gt;
Worth watching if you're into this kind of movie.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8597903144012017691?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8597903144012017691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8597903144012017691'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/colossus-forbin-project.html' title='Colossus: The Forbin Project'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3124456339004711256</id><published>2009-07-15T17:06:00.002-04:00</published><updated>2009-07-15T17:11:00.004-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Meta'/><title type='text'>First &amp; Latest Computers</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jlo6CPTvh4c/Sl5FsQ8cFNI/AAAAAAAAAAM/DNXYKjt5H38/s1600-h/first_latest.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_jlo6CPTvh4c/Sl5FsQ8cFNI/AAAAAAAAAAM/DNXYKjt5H38/s320/first_latest.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5358797233382495442" /&gt;&lt;/a&gt;

&lt;p&gt;On the right is my first computer, a &lt;a href="http://en.wikipedia.org/wiki/Timex_Sinclair_1000"&gt;Timex Sinclair 1000&lt;/a&gt;. On the left is my latest computer, a &lt;a href="http://en.wikipedia.org/wiki/Dell_Inspiron_Mini_Series"&gt;Dell Mini9&lt;/a&gt; hacked to run OS X. Putting these two machines side-by-side really puts into context how much computing technology has progressed over the past 27 years (1982 to 2009). Unfortunately, it also serves as evidence that I am getting old!
&lt;/p&gt;
&lt;p&gt;
Joking aside, I bought the Dell more or less for the fun of hacking it
to run OS X. My expectations for using it as an actual computer were
low. Although I still greatly prefer using my MacBook, I have to say
that the Mini has proved far more useful than I initially hoped. OS X
runs really well, the screen size and resolution is good, and the keyboard
isn't terrible. The Mini's small size, and no moving parts makes it a
great machine for taking everywhere which has saved me from boredom
on a number of occasions. For light work such as web surfing, email,
blogging, and play-programming the Mini is quite productive. 
&lt;/p&gt;
&lt;p&gt;
I look forward to posting 27 years from now with a similarly sized
computer from that time.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3124456339004711256?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3124456339004711256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3124456339004711256'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/first-latest-computers.html' title='First &amp; Latest Computers'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jlo6CPTvh4c/Sl5FsQ8cFNI/AAAAAAAAAAM/DNXYKjt5H38/s72-c/first_latest.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-4443980200856649203</id><published>2009-07-15T05:28:00.002-04:00</published><updated>2009-07-15T18:56:26.649-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><title type='text'>Is an Entrepreneurial Bubble Next?</title><content type='html'>&lt;p&gt;Will the next bubble be the collapse of small businesses started
during an entrepreneurial gold rush?
&lt;/p&gt;
&lt;p&gt;
This thought has been brewing in my mind for a while now but it was
brought to the fore by this Jack and Suzy Welch &lt;a href="http://www.businessweek.com/magazine/content/09_28/b4139000705635.htm"&gt;article&lt;/a&gt; in the July
13,2009 issue of BusinessWeek Magazine. The particular quotes that
caught my eye were:
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;
Look, this recession has really shocked people&amp;hellip;. The result? Many
people have come to the conclusion that they don't want to work for
"the man" anymore. They want to work for themselves or someone they know and trust.
&lt;/p&gt;
&lt;p&gt;
To be someone else's employee, people are telling us, is to be at
someone else's whim.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
If anything, starting a business puts you at the whim of more people
rather than less. Investors, customers, employees, suppliers, service
providers, and others all place demands on business owners that must
be addressed. Abandonment by any of these groups could be disastrous.
&lt;/p&gt;
&lt;p&gt;
Also starting or working for a small business involves a substantial
amount of risk and provides little insulation from economic
downturns. In fact, I suspect that the current downturn has had the
greatest impact on small businesses.
&lt;/p&gt;
&lt;p&gt;
Jay Goltz's New York Times blog post, &lt;a href="http://boss.blogs.nytimes.com/2009/07/08/the-dark-side-of-entrepreneurship/"&gt;The Dark Side of Entrepreneurship&lt;/a&gt;, captures both of these issues well.
&lt;/p&gt;
&lt;p&gt;
To be clear, I am a proponent of entrepreneurship and think that
starting a business can be liberating. But to be successful I don't
think liberation can be the only goal and I fear that many people are
going into small business without the right expectations. 
&lt;/p&gt;
&lt;p&gt;
If that is true then what will happen if many of those businesses
begin to fail in a couple of years? Do they represent a new form of
systemic risk to the economy?
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-4443980200856649203?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4443980200856649203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/4443980200856649203'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/is-entrepreneurial-bubble-next.html' title='Is an Entrepreneurial Bubble Next?'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-8674466990359423156</id><published>2009-07-12T19:46:00.005-04:00</published><updated>2009-07-13T12:51:54.008-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='History'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>Von Neumann's Universe</title><content type='html'>&lt;p&gt;In 2005, &lt;a href="http://en.wikipedia.org/wiki/George_Dyson_(science_historian)"&gt;George Dyson&lt;/a&gt; gave a talk at the O'Reilly Media Emerging Technology Conference on John Von Neumann's work at the &lt;a href="http://en.wikipedia.org/wiki/Institute_for_advanced_study"&gt;Institute for Advanced Study&lt;/a&gt; on building early computers. This talk has been a favorite of mine since it was posted to the &lt;a href="http://itc.conversationsnetwork.org/"&gt;IT Conversations website&lt;/a&gt;. It is informative, entertaining, and well worth &lt;a href="http://itc.conversationsnetwork.org/shows/detail454.html#"&gt;listening to&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Related to the Recursive Universe post, much of Dyson's talk focuses on Von Neumann's work on cellular automata in collaboration with &lt;a href="http://en.wikipedia.org/wiki/Stanislaw_Ulam"&gt;Stanislaw Ulam&lt;/a&gt;. If you liked that post then you will likely enjoy listening to Dyson's talk. The associated slides are available &lt;a href="http://conferences.oreillynet.com/presentations/et2005/Dyson.pdf"&gt;here&lt;/a&gt; (warning, they are 111MB).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-8674466990359423156?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8674466990359423156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/8674466990359423156'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/von-neumanns-universe.html' title='Von Neumann&apos;s Universe'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1086902631645611174</id><published>2009-07-12T07:09:00.004-04:00</published><updated>2009-07-12T19:40:07.637-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>Book Review: The Recursive Universe</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://g-ecx.images-amazon.com/images/G/01/ciu/b8/91/7d06228348a06952b39b0110.L._AA240_.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 240px; height: 240px;" src="http://g-ecx.images-amazon.com/images/G/01/ciu/b8/91/7d06228348a06952b39b0110.L._AA240_.jpg" border="0" alt="" /&gt;&lt;/a&gt;
&lt;p&gt;&lt;a href="http://books.google.com/books?id=A2VQAAAAMAAJ&amp;amp;q=the+recursive+universe&amp;amp;dq=the+recursive+universe"&gt;The Recursive Universe&lt;/a&gt;
by William Poundstone
&lt;/p&gt;&lt;p&gt;What a delightful book! I was made aware of it by Cosma Shalizi's commentary that I linked to in the &lt;a href="http://jcardente.blogspot.com/2009/06/new-kind-of-science.html"&gt;A New Kind of Science post&lt;/a&gt;. It sounded interesting so I bought a used copy. I'm happy I did so!
&lt;/p&gt;&lt;p&gt;The book takes the novel approach of using Conway's Game of Life to discuss complexity theory, specifically how simple rules can manifest into complex systems. Particular emphasis is placed on Von Neumann's interest in self-reproducing machines and cellular automata.
&lt;/p&gt;&lt;p&gt;The book is structured in chapter pairs with the first discussing a particular aspect of complexity theory and the second showing how Conway's Life can provide deeper insight. A brief summary of the chapter pairs are:&lt;/p&gt;&lt;p&gt;
&lt;/p&gt;&lt;col align="left"&gt;&lt;col align="left"&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;&lt;tbody&gt;&lt;/tbody&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Chapters&lt;/th&gt;&lt;th&gt;Theme&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1 &amp;amp; 2&lt;/td&gt;&lt;td&gt;introduction to complexity, reductionism, information theory, and the Game of Life&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3 &amp;amp; 4&lt;/td&gt;&lt;td&gt;entropy, Szilard's thesis on information and entropy, and the limits of empirical knowledge.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5 &amp;amp; 6&lt;/td&gt;&lt;td&gt;information, structure, meaning, and unlimited growth&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7 &amp;amp; 8&lt;/td&gt;&lt;td&gt;recursion, the challenges of predicting complex systems using recursive calculations, and self-reproduction.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9 &amp;amp; 10&lt;/td&gt;&lt;td&gt;the evolution of the cosmos and random Life initial states&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11 &amp;amp; 13&lt;/td&gt;&lt;td&gt;self-reproduction, the information theory of life, and Life computers.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;tbody&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Before reading this book I had a cursory knowledge of the Game of Life but I was unaware of the sophisticated constructs possible. The Life computers presented in Chapter 12 are fascinating and I can't imagine how much time was required to design them.&lt;/p&gt;
&lt;p&gt;I was also unaware of how much attention Life received when it was first created; it seems that many of the bright minds of the time spent considerable time playing the game. I found the historical aspects of this interesting as well but then I have a fondness for computing of this era.&lt;/p&gt;
&lt;p&gt;While discussing this book with others I was surprised to find out that an acquaintance of mine was on &lt;a href="http://en.wikipedia.org/wiki/Bill_Gosper"&gt;Gosper's&lt;/a&gt; team at MIT that discovered the first &lt;a href="http://en.wikipedia.org/wiki/Gun_(cellular_automaton)"&gt;Glider Gun&lt;/a&gt;. I always enjoy it when I find a personal connection like this to a period of history before "my time".&lt;/p&gt;
&lt;p&gt;While looking for additional materials to augment the book I found the following useful references:&lt;/p&gt;
&lt;p&gt;&lt;ul&gt;&lt;li&gt;Two excerpts (&lt;a href="http://www.youtube.com/watch?v=FdMzngWchDk"&gt;part 1&lt;/a&gt; and &lt;a href="http://www.youtube.com/watch?v=k2IZ1qsx4CM&amp;amp;feature=related"&gt;part 2&lt;/a&gt;) of a BBC documentary featuring
interviews with Conway.
&lt;/li&gt;
&lt;li&gt;A Game of Life &lt;a href="http://pentadecathlon.com/lifeNews/index.php"&gt;news website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://golly.sourceforge.net/"&gt;Golly&lt;/a&gt;, a feature rich cross platform Life implementation
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Hashlife"&gt;Hashlife&lt;/a&gt;, an algorithm devised by Gosper that uses quadtrees to
efficiently represent deterministic Life constructs.&lt;/li&gt;&lt;/ul&gt;
&lt;/p&gt;&lt;p&gt;All in all an entertaining book that is also very thought provoking. I only wish I had copious amounts of spare time to play the Game of Life myself!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1086902631645611174?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1086902631645611174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1086902631645611174'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/07/book-review-recursive-universe.html' title='Book Review: The Recursive Universe'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-1724316843431010272</id><published>2009-06-22T16:39:00.003-04:00</published><updated>2009-07-13T13:00:11.327-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Business'/><category scheme='http://www.blogger.com/atom/ns#' term='BookReview'/><title type='text'>Book Review: The Art of the Start</title><content type='html'>&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://ecx.images-amazon.com/images/I/4121XMD3A5L._SL500_SX85_.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 85px; height: 137px;" src="http://ecx.images-amazon.com/images/I/4121XMD3A5L._SL500_SX85_.jpg" border="0" alt="" /&gt;&lt;/a&gt;
&lt;a href="http://books.google.com/books?id=NO0LAAAACAAJ&amp;amp;dq=The+Art+of+the+Start"&gt;The Art of The Start&lt;/a&gt;&lt;br/&gt;by Guy Kawasaki&lt;/p&gt;
&lt;p&gt;A great and inspiring book. I particularly appreciated the no nonsense advice and strong focus on fundamentals. My bias towards the latter has always made me feel like a "pseudo-MBA" so Guy's advice increased my confidence in holding similar opinions.&lt;/p&gt;
&lt;p&gt;I particularly enjoyed the material on bootstrapping. I have always found exciting the kind of action-based, goal-oriented bootstrapping that Guy describes which likely explains why I have spent the majority of my career in small, advanced development teams building prototypes of new technologies and products.&lt;/p&gt;&lt;div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In summary, a great book that I highly recommend to anyone involved in starting new ventures, internal or external.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;
&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-1724316843431010272?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1724316843431010272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/1724316843431010272'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/06/book-review-art-of-start.html' title='Book Review: The Art of the Start'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-809664433090816169</id><published>2009-06-15T19:45:00.001-04:00</published><updated>2009-07-13T13:01:27.802-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LifeHacks'/><title type='text'>Daily routines...</title><content type='html'>&lt;p&gt;By way of &lt;a href="http://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt;, I discovered this delightful website, &lt;a href="http://dailyroutines.typepad.com/daily_routines/"&gt;Daily Routines&lt;/a&gt;, that documents the daily routines of various people, many famous. Simply fascinating to read. For instance who knew that Winston Churchill &lt;a href="http://dailyroutines.typepad.com/daily_routines/2009/02/winston-churchill.html"&gt;didn't get out of bed until 11:00AM&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;A daily routine is something that I've always desired but found difficult to maintain. Perhaps this website will inspire me to finally settle on a daily routine of my own.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-809664433090816169?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/809664433090816169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/809664433090816169'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/06/daily-routines.html' title='Daily routines...'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-3771874741498821505</id><published>2009-06-03T16:40:00.001-04:00</published><updated>2009-07-13T13:02:27.704-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Computer Science'/><title type='text'>A New Kind of Science</title><content type='html'>&lt;p&gt;I must admit that before all of the recent &lt;a href="http://www.wolframalpha.com/"&gt;WolframAlpha&lt;/a&gt; excitement I didn't know much about Stephen Wolfram. By all accounts he is an impressive person so I decided to &lt;a href="http://www.youtube.com/watch?v=_eC14GonZnU"&gt;watch a talk&lt;/a&gt; he gave on his book &lt;a href="http://www.wolframscience.com/"&gt;A New Kind of Science&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;In general, I find the kinds of &lt;a href="http://en.wikipedia.org/wiki/Cellular_automata"&gt;cellular automata&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Computability_theory_(computer_science)"&gt;computability theory&lt;/a&gt; that Wolfram discusses to be very interesting. However, I'm in no position to judge the quality or validity of his conclusions.  To that end a quick google search uncovered &lt;a href="http://www.cscs.umich.edu/~crshalizi/reviews/wolfram/"&gt;this commentary&lt;/a&gt; by Cosma Shalizi which appears to question at least the originality of Wolfram's work. It sounds like an unfortunate situation for all involved.&lt;/p&gt;
&lt;p&gt;Someday I may pick up a copy of &lt;span class="Apple-style-span" style="font-style: italic;"&gt;A New Kind of Science&lt;/span&gt; but in the meantime I may read some of Wolfram's papers as the topic does interest me. Based on the commentary linked above, I ordered a copy of &lt;a href="http://books.google.com/books?id=A2VQAAAAMAAJ&amp;amp;q=the+recursive+universe&amp;amp;dq=the+recursive+universe&amp;amp;ei=veMmSuCpBoKszgSAoeT0Cg&amp;amp;pgis=1"&gt;The Recursive Universe&lt;/a&gt; and look forward to reading it.  &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/424507293146612898-3771874741498821505?l=jcardente.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3771874741498821505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/424507293146612898/posts/default/3771874741498821505'/><link rel='alternate' type='text/html' href='http://jcardente.blogspot.com/2009/06/new-kind-of-science.html' title='A New Kind of Science'/><author><name>JCardente</name><uri>http://www.blogger.com/profile/06778436097141373565</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-424507293146612898.post-2975820847541184815</id><published>2009-06-03T10:12:00.001-04:00</published><updated>2009-07-13T13:04:04.783-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Investing'/><title type='text'>Analysis of Cramer's stock advice..</title><content type='html'>&lt;p&gt;I was listening to episode #48 of the &lt;a href="http://www.npr.org/rss/podcast/podcast_detail.php?siteId=94411890"&gt;PlanetMoney podcast&lt;/a&gt; on the way into work this morning and was surprised to hear that my MBA M&amp;amp;A Professor, Emery Trahan, together with another NEU professor did a study on Cramer’s investment advice. They found that during the period from mid-2005 to 2007 f
