<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hacking at 0300 &#187; Danny</title>
	<atom:link href="http://bililite.com/blog/author/danny/feed/" rel="self" type="application/rss+xml" />
	<link>http://bililite.com/blog</link>
	<description>Thoughts on web design and programming from a very occasional volunteer webmaster</description>
	<lastBuildDate>Fri, 23 Jul 2010 09:00:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Learning Scheme: Euler Problem 3</title>
		<link>http://bililite.com/blog/2010/07/13/learning-scheme-euler-problem-3/</link>
		<comments>http://bililite.com/blog/2010/07/13/learning-scheme-euler-problem-3/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 02:46:30 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Scheme]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1216</guid>
		<description><![CDATA[Been a long time since I last posted; real life has a way of getting in the way. The past few months read like the back cover of A Series of Unfortunate Events: if you don't want to hear about a thwarted move, a house fire, 2 graduations, negotiations for a new job and a [...]]]></description>
			<content:encoded><![CDATA[<p>Been a long time since I last posted; real life has a way of getting in the way. The past few months read like the back cover of <a href="http://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&#038;field-keywords=A+Series+of+Unfortunate+Events&#038;x=0&#038;y=0&#038;tag=bililitecom-20">A Series of Unfortunate Events</a>: if you don't want to hear about a thwarted move, a house fire, 2 graduations, negotiations for a new job and a carrot salad with lots of garlic, then you should put this down and live someone else's life.</p>
<p>But I'm not complaining; everything looks like it's working out well.</p>
<p>Going back to Scheme, the next <a href="http://projecteuler.net/index.php?section=problems">Project Euler</a> problem is:</p>
<blockquote>The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?</blockquote>
<p>And straightforwardly:</p>
<pre><code class="language-lisp">(define gpf ; greatest prime factor
  (lambda (n try) ; n is the number to find the factor of; try is the lowest number to try
    (cond
      ((> try (sqrt n)) n) ; no need to try a factor higher than the square root; if we get here, n is prime
      ((divides try n) (gpf (/ n try) try)) ; divide out the current trial factor and try again
      (else (gpf n (+ try 1))))))
(define euler3
  (lambda (n) (gpf n 2)))</code></pre>
<p><code class="language-lisp">(euler3 600851475143)</code> gives us the answer 6857 pretty quickly.</p>
<p>But this is getting boring; I'm not using the interesting parts of Scheme. This sort of problem cries out for a prime number generator, and that cries out for <a href="http://community.schemewiki.org/?call-with-current-continuation"><code class="language-lisp">call-with-current-continuation</code></a>, and <em>that</em> cries out for some more playing and learning. I'll see what I can do.</p>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/07/13/learning-scheme-euler-problem-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery UI 1.8</title>
		<link>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/</link>
		<comments>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/#comments</comments>
		<pubDate>Thu, 25 Mar 2010 20:58:58 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1187</guid>
		<description><![CDATA[Well, they released jQuery UI 1.8 two days ago, and now everything is broken. I understand why the changes were made, but it sure is inconvenient. Plugins and posts updated so far: timepickr widget tutorial extending widgets flexcal googlesearch and ajaxpopup textpopup and hebrewkeyboard And on top of that, chili stopped working with jQuery 1.4 [...]]]></description>
			<content:encoded><![CDATA[<p>Well, they released jQuery UI 1.8 two days ago, and now everything is broken. I understand why the changes were made, but it sure is inconvenient. Plugins and posts updated so far:</p>
<ul>
<li><a href="/blog/2009/07/09/updating-timepickr">timepickr</a></li>
<li><a href="/blog/understanding-jquery-ui-widgets-a-tutorial/">widget tutorial</a></li>
<li><a href="/blog/extending-jquery-ui-widgets/">extending widgets</a></li>
<li><a href="/blog/2009/04/03/new-jquery-widget-flexcal/">flexcal</a></li>
<li><a href="/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/">googlesearch and ajaxpopup</a></li>
<li><a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/">textpopup and hebrewkeyboard</a></li>
</ul>
<p>And on top of that, <a href="http://noteslog.com/chili/">chili</a> stopped working with <a href="http://jquery14.com/day-01/jquery-14">jQuery 1.4</a> and I haven't fixed it yet. <a href="http://noteslog.com/contact/">Andrea Ercolino</a> was nice enough to send me an updated version, but I haven't gotten around to installing it yet.</p>
<p>Update: got chili to work!</p>
<p>So much for using my spare time to <a href="/blog/2010/03/14/learning-scheme/">learn Scheme</a>!</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>A real pain scale</title>
		<link>http://bililite.com/blog/2010/03/25/a-real-pain-scale/</link>
		<comments>http://bililite.com/blog/2010/03/25/a-real-pain-scale/#comments</comments>
		<pubDate>Thu, 25 Mar 2010 20:48:55 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Medical Informatics]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1184</guid>
		<description><![CDATA[I usually don't blog about medical stuff here, but Allie Brosh's comments about the Wong-Baker pain scale are perfectly on-target. The Joint Commission accredits health care organizations and comes up with intrusive, well-intended but largely pointless standards that the rest of us have to uphold. A sort of "unfounded mandate." One of these is the [...]]]></description>
			<content:encoded><![CDATA[<p>I usually don't blog about medical stuff here, but <a href="http://hyperboleandahalf.blogspot.com/">Allie Brosh</a>'s <a href="http://hyperboleandahalf.blogspot.com/2010/02/boyfriend-doesnt-have-ebola-probably.html">comments</a> about the <a href="http://www.sjbhealth.org/documents/Pain%20Scale.pdf">Wong-Baker pain scale</a> <a href="http://hyperboleandahalf.blogspot.com/2010/02/boyfriend-doesnt-have-ebola-probably.html">are perfectly on-target</a>. The <a href="http://www.jointcommission.org/">Joint Commission</a> accredits health care organizations and comes up with intrusive, well-intended but largely pointless standards that the rest of us have to uphold. A sort of "unfounded mandate." One of these is the requirement that we document pain levels in every patient on a scale of one to ten. For pediatric patients, we have this "FACES" scale that starts from a big smile and doesn't even get to a frown until number six. I like Allie's much better.</p>
<p>And her comment about not eating red food when you're sick is absolutely right. Never give your kid red popsicles when they've got a stomach bug; you'll just rush into the ER worried that they're throwing up blood.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/25/a-real-pain-scale/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Learning Scheme, Euler problem 2</title>
		<link>http://bililite.com/blog/2010/03/16/learning-scheme-euler-problem-2/</link>
		<comments>http://bililite.com/blog/2010/03/16/learning-scheme-euler-problem-2/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 23:47:06 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Scheme]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1177</guid>
		<description><![CDATA[The second project Euler problem is: Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... Find the sum of all the even-valued terms in the sequence [...]]]></description>
			<content:encoded><![CDATA[<p>The second project Euler problem is:

<blockquote>Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

Find the sum of all the even-valued terms in the sequence which do not exceed four million.</blockquote>

</p><span id="more-1177"></span>
<p>Brute force Fibonacci generator:</p>
<pre><code class="language-lisp">(define fibo
  (lambda (n)
    (cond
      ((= n 1)
       1)
      ((= n 2)
       2)
      (else (+ (fibo (- n 1)) (fibo (- n 2)))))))</code></pre>
<p>But that takes exponential time, and the problem requires generating all of the numbers anyway, so we can use something like:</p>
<pre><code class="language-lisp">(define fibo-sum
  (lambda (a b lim)
    (let* ((c (+ a b)))
      (cond
        ((< lim c) 0)
        (else (+ c (fibo-sum b c lim)))))))</code></pre>
<p> to get the sum of all the fibonacci numbers less than lim (use <code class="language-lisp">(fibo-sum 1 2 4000000)</code>). I could add a test for even numbers to solve the problem, but more elegantly we notice that the pattern of the fibonacci numbers has to be odd-odd-even-odd-odd-even, so we have:</p>
<pre><code class="language-lisp">(define euler2
  (lambda (a b lim)
    (let* ((c (+ a b))
           (d (+ b c))
           (e (+ c d)))
      (cond
        ((< lim e) 0)
        (else (+ e (euler2 d e lim)))))))
(euler2 1 0 4000000)</code></pre>
<p>We start the fibonacci sequence with 1 0 so that the 2 is part of our sum.  And we get the correct answer, 4613732, imperceptibly fast.</p>
<p>One bug that took me forever to find was a mistyped line:</p>
<pre><code class="language-lisp">(let* ((c (+ a b))
  (d (+ b c))
   (e (+ d e)))
</code></pre>
<p>Where <code>e</code> was defined in terms of <code>d</code> and <code>e</code>, so it used the global value of <code>e</code>. If it hadn't been defined, it would have given me an error immediately, but unfortunately, DrScheme has it pre-defined as <code class="language-lisp">#i2.718281828459045</code> so my code went happily along giving me the wrong answers.</p>

]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/16/learning-scheme-euler-problem-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Learning Scheme</title>
		<link>http://bililite.com/blog/2010/03/14/learning-scheme/</link>
		<comments>http://bililite.com/blog/2010/03/14/learning-scheme/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 09:14:24 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Scheme]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1168</guid>
		<description><![CDATA[OK, so my new mini project is to learn Scheme. It can't be too hard, right? I mean Javascript is just Lisp in C's clothing, and I'm good at Javascript. I've installed PLT-Scheme and I figure the best way to learn it is solving Project Euler problems until I get bored. Problem 1: If we [...]]]></description>
			<content:encoded><![CDATA[<p>OK, so my new mini project is to learn <a href="http://download.plt-scheme.org/doc/360/html/t-y-scheme/t-y-scheme.html">Scheme</a>. It can't be too hard, right? I mean <a href="http://javascript.crockford.com/javascript.html">Javascript is just Lisp in C's clothing</a>, and I'm good at Javascript. I've installed <a href="http://www.plt-scheme.org/">PLT-Scheme</a> and I figure the best way to learn it is solving <a href="http://projecteuler.net/">Project Euler</a> problems until I get bored.</p>
<span id="more-1168"></span><p>Problem 1:
<blockquote>If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.</blockquote></p>
<p>Translating Javascript to Scheme means <code class="language-javascript">function(x, y) {foo;bar;}</code> becomes <code class="language-lisp">(lambda (x y) (foo) (bar))</code> and <code class="language-javascript">var x = y;</code> becomes <code class="language-lisp">(define x y)</code> and realizing that all loops have to be made into tail-recursive function calls gives us this pretty easily:</p>
<pre><code class="language-lisp">(define divides
  (lambda (a b)
    (= 0 (remainder b a))))

(define euler1
  (lambda (n)
    (cond
      ((= n 1) 
        0)
      ((or (divides 5 n) (divides 3 n))
        (+ n (euler1 (- n 1))))
      (else (euler1 (- n 1))))))

(euler1 999)</code></pre>
<p>And the correct answer is 233168. Not bad for my first Scheme program! (Certainly more interesting than <code class="language-lisp">(display "hello, world")</code>).</p>
<p>Addendum: I just looked at the forum at Project Euler and I see that there's a much better solution; just add the series 3+6+9....999, which is simply 3*(1+2+3+...333) or 3* 333 * (333+1)/2, to the series
5+10+15+...995, or 5 * 199 * (199+1)/2 and subtract the series 15+30+45... which is counted twice. No loops! Less interesting for learning to program, though.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/14/learning-scheme/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Quicken, the Dead</title>
		<link>http://bililite.com/blog/2010/03/12/the-quicken-the-dead/</link>
		<comments>http://bililite.com/blog/2010/03/12/the-quicken-the-dead/#comments</comments>
		<pubDate>Fri, 12 Mar 2010 10:35:40 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1162</guid>
		<description><![CDATA[I've been using Quicken for years, since Quicken 97 on Window then Quicken 5 on the Mac and then Quicken 2001 when I got my iMac. The problems is that Intuit doesn't upgrade Quicken, it replaces it each year with a version that has more frilly bells and whistles and less real utility (especially on [...]]]></description>
			<content:encoded><![CDATA[<p>I've been using Quicken for years, since Quicken 97 on Window then Quicken 5 on the Mac and then Quicken 2001 when I got my iMac. The problems is that Intuit doesn't upgrade Quicken, it <a href="http://quicken.intuit.com/support/articles/buying-and-billing/discontinuation/3874.html">replaces it</a> each year with a version that has more frilly bells and whistles and <a href="http://financialsoft.about.com/gi/rating/reviews.htm?bvv=&#038;bvid=4903LeO000X4&#038;u=/od/reviewsfinancesoftware/fr/Quicken_New_2.htm&#038;t=%20Quicken%202010&#160;Review">less real utility</a> (<a href="http://quicken.intuit.com/personal-finance-software/mac-personal-financial-software.jsp">especially on the Mac</a>; just look at the "Using a prior version of Quicken Mac?"). And the datafile is often different from the old file, so stuff gets lost with each "upgrade".</p>

<p>So I've kept my current Mac (a <a href="http://www.lowendmac.com/ppc/blue-white-power-mac-g3.html">blue &#038; white G3</a>, the <a href="http://www.advergence.com/newspage/1999/19990218_g3mickey.shtml">"Mickey Mouse" one</a>) just to be able to run Quicken 2001 under Classic. I can't upgrade to an Intel Mac, since Classic won't work. And I want to balance my checkbook while talking to my wife, not only downstairs in my <a href="http://www.randsinrepose.com/archives/2006/07/10/a_nerd_in_a_cave.html">basement office</a>. So I wanted something I could run on my dual-boot (XP and Ubuntu) laptop.</p>
<p>I've been looking at alternatives to Quicken for 5+ years, but with 20 years of data I was too scared. Until now. I've been using <a href="http://gnucash.org">gnuCash</a> for just a week now, but it seems to everything I need it to, including importing all my data, even from multiple investment accounts, from Quicken's exported QIF file. I haven't done any complicated investment things, so I don't know how well that side of it works.</p>
<h4>Strong points</h4>
<ul><li>It Just Works. Does what I expect it to do, in the way I expect it to. Of course, I'm a geek who understands double-entry bookkeeping, so that may not be surprising.</li>
<li>I can control-S and save the file; I always felt out of control with Quicken since you couldn't force it to save.</li>
<li>Open source. I will probably never have the time to tinker, but it's nice knowing that I could</li>
</ul>
<h4>Weak points</h4>
<ul><li>Keyboard shortcuts. I miss being able to assign my own keystrokes; I hate going back to the trackpad to do anything. But see Open Source above; I can probably fix that.</li>
<li>I'm going to have to learn <a href="http://download.plt-scheme.org/doc/360/html/t-y-scheme/t-y-scheme.html">Scheme</a> to create custom reports. Not sure if that's a bug or a feature.</li>
</ul>
<p>The biggest advantage is the feeling of freedom of not being tied to Intuit anymore. I'll see how it works out, but I'm not going back.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/12/the-quicken-the-dead/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Jewish History from a Unique Perspective</title>
		<link>http://bililite.com/blog/2010/02/19/jewish-history-from-a-unique-perspective/</link>
		<comments>http://bililite.com/blog/2010/02/19/jewish-history-from-a-unique-perspective/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 18:29:39 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Judaism]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1159</guid>
		<description><![CDATA[My children's Jewish history teacher, Rabbi Yonason Goldson has been teaching at Block Yeshiva High School for many years and presents Jewish history with a mix of traditional and scholarly sources, always from a Torah-true perspective and always looking at the broad sweep of history and the Jews' role in it. He has now published [...]]]></description>
			<content:encoded><![CDATA[<p>My children's Jewish history teacher, <a href="http://torahideals.com/">Rabbi Yonason Goldson</a> has been teaching at <a href="http://blockyeshiva-stl.org/">Block Yeshiva High School</a> for many years and presents Jewish history with a mix of traditional and scholarly sources, always from a Torah-true perspective and always looking at the broad sweep of history and the Jews' role in it. He has now published his notes (which I have always found fascinating) in book form, as
<a href="http://www.judaicapress.com/product_info.php?products_id=731"><em>Dawn to Destiny: Exploring Jewish History and its Hidden Wisdom</em></a>. I'm looking forward to reading it.</p>
<p>From the publisher's summary:</p>
<blockquote>A comprehensive overview of Jewish History from Creation through the redaction of the Talmud, illuminating the intricacies and complexities of Torah tradition and philosophy according to the sages and classical commentaries, spanning the length and breadth of Jewish experience to resolve many of history’s most perplexing episodes, offering profound insights and showing their relevance to life in the modern world.  An invaluable resource for scholars and laymen.  A priceless tool for education and outreach.</blockquote>
<blockquote>How did the sin of Adam transform mankind and the world?  How were the prophecies of Noah fulfilled through the rise of the Greek Empire?  How did the builders of the Tower of Babel believe they could wage war against G-d?  Why did there have to be three patriarchs?  What was King David's crime regarding his involvement with BasSheva?  Why did some Jews oppose the construction of the Second Temple?  How can we trust the transmission of Torah if our scholars engaged in such fierce disagreements?  These and many other questions are answered in this unique volume.</blockquote>

]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/02/19/jewish-history-from-a-unique-perspective/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Engauge Digitizer Tutorial</title>
		<link>http://bililite.com/blog/2010/01/14/engauge-digitizer-tutorial/</link>
		<comments>http://bililite.com/blog/2010/01/14/engauge-digitizer-tutorial/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 21:58:49 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Medical Informatics]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1152</guid>
		<description><![CDATA[A simple walkthrough to use the Engauge Digitizer to pull values off a "printed" graph There's a far more complete manual that comes with the download, but these are the steps I used to generate the data for the webservices graphs. It assumes you've got a black and white image of the graph, with continuous [...]]]></description>
			<content:encoded><![CDATA[<h3>A simple walkthrough to use the <a href="http://digitizer.sourceforge.net/">Engauge Digitizer</a> to pull values off a "printed" graph</h3>
<p>There's a far more complete manual that comes with the download, but these are the steps I used to generate the data for the
<a href="/webservices">webservices</a> graphs. It assumes you've got a black and white image of the graph, with continuous lines for the
data and orthogonal gridlines, and linear axes.</p><span id="more-1152"></span>
<p>We'll use the bilirubin risk levels from the <a href="http://aappolicy.aappublications.org/cgi/content/full/pediatrics;114/1/297">AAP bilirubin guidelines</a>:</p>
<a href="/images/bili.jpg"><img style="display: block; height: 20em" src="/images/bili.jpg" /></a>
<ol>
<li><h4>Open the digitizer</h4>
<p>You can't use <code>Open with...</code> from the context menu because the program expects filenames as part of the options;
you can read the manual to see how to modify the context menu. Click <code>Import</code>
on the toolbar:  <img src="/images/digitizer-import.png" alt="Import icon" /> and select your image file:</p>
<a href="/images/graph1.png"><img style="display: block; height: 20em" src="/images/graph1.png" alt="graph imported"/></a>
<p>Save your work (it's a <code>.dig</code> file) often. You don't need me to remind you.</p>
</li>
<li><h4>Set the axes</h4>
<p>Click <code>Axis Point</code> in the toolbar: <img src="/images/digitizer-axis.png" alt="Axis icon" /> and click on the origin
(lower left corner) of the graph and enter the x,y values for the origin in the units of the graph (not pixels!). Usually that's (0,0)
but can be anything. Click on the maximum of the y-axis (upper left corner), then the maximum  of the x-axis (lower right corner).</p>
<li><h4>Remove the grid</h4>
<p>Select <code>Grid Removal...</code> from the <code>Settings</code> menu. Select "Remove thin lines parallel to the axes" and play with
the "Minimum line thickness" until the gridlines are gone from the preview on the bottom but the real curves are still there.</p>
<a href="/images/removegrid.png"><img style="display: block" src="/images/removegrid.png" alt="remove grid window" /></a>
<p>Now the grid is gone and digitizing the curves will be easy:</p>
<a href="/images/gridremoved.png"><img style="display: block; height: 20em" src="/images/gridremoved.png" alt="graph without grid"/></a>
</li>
<li><h4>Create curves</h4>
<p>Select <code>Curves...</code> from the <code>Settings</code> menu. Create as many curves as you need (we have three). Give them descriptive
names if you like:</p>
<a href="/images/curves.png"><img style="display: block;" src="/images/curves.png" alt="curve window"/></a>
</li>
<li><h4>Digitize each curve</h4>
<p>Click <code>Segment Fill</code> in the toolbar: <img src="/images/digitizer-segment.png" alt="Segment icon" />. For each curve, select it
from the curve menu: <img src="/images/digitizer-curvemenu.png" alt="Axis icon" /> and click the points on the graph for that curve until
it is covered with points. There's no undo command. If a wrong point is included, use the <code>Select</code> tool:
<img src="/images/digitizer-select.png" alt="Select icon" /> to select the bad points and use the <code>Delete</code> key. To add individual points,
use the <code>Curve Point</code> tool: <img src="/images/digitizer-point.png" alt="Curve Point icon" />. In a few clicks, you have the fully
digitized graph:</p>
<a href="/images/curvesdigitized.png"><img style="display: block;height: 20em;" src="/images/curvesdigitized.png" alt="curve window"/></a>
</li>
<li><h4>Set up the export</h4>
<p>If you exported the data now, you'd have lots of points; probably far more than is justified from the original graph. Decide which x-axis
values should be exported (the program will interpolate appropriately if the digitized points don't land exactly). Select
<code>Grid Display...</code> from the <code>Settings</code> menu:</p>
<a href="/images/gridmesh.png"><img style="display: block;" src="/images/gridmesh.png" alt="grid window"/></a>
<p>The values to be specified are related by <code>count = (stop - start)/step</code> so you only need to specify three; select the one you
<em>don't</em> want to specify from the <code>Disable</code> dropdown menu. Make sure that the start and stop values are actually in the curve;
the program will happily extrapolate off the curve but it's probably not scientifically legitimate. Ignore the y axis grid lines.</p>
<p>Select <code>Export Setup...</code> from the <code>Settings</code> menu. In the <code>Points Selection</code> section, select
<code>Intepolate Y's at gridline X's</code>.</p>
<a href="/images/exportsettings.png"><img style="display: block;" src="/images/exportsettings.png" alt="grid window"/></a>
</li>
<li><h4>Export</h4>
<p>Click the <code>Export</code> icon in the toolbar: <img src="/images/digitizer-export.png" alt="Export icon" />. Pick a name and you now have a
<a href="http://en.wikipedia.org/wiki/Comma-separated_values">CSV</a> file with your data. Simple, no?</p>
</li>
</ol>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/01/14/engauge-digitizer-tutorial/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Digitizing graphs</title>
		<link>http://bililite.com/blog/2010/01/10/digitizing-graphs/</link>
		<comments>http://bililite.com/blog/2010/01/10/digitizing-graphs/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 02:55:04 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Medical Informatics]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1138</guid>
		<description><![CDATA[I wanted to add Down Syndrome growth charts to the bililite.com webservices, but as far as I can tell, the charts are available only as images in the AAP's guidelines (and the original paper; subscription only). The often-cited growthcharts.com has charts, and Greg Richards was generous enough to share his data with me. However, some [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to add <a href="http://en.wikipedia.org/wiki/Down_syndrome">Down Syndrome</a> growth charts to the <a href="/webservices/">bililite.com webservices</a>, but as far as I can tell, the charts are available only as images in the <a href="http://aappolicy.aappublications.org/cgi/content/full/pediatrics;107/2/442"><abbr title="American Academy of Pediatrics">AAP</abbr>'s guidelines</a> (and the <a href="http://pediatrics.aappublications.org/cgi/reprint/81/1/102">original paper</a>; subscription only). The often-cited <a href="http://growthcharts.com/charts/DS/charts.htm">growthcharts.com</a> has charts, and Greg Richards was generous enough to share his data with me. However, some of the data are from a different study, and he got his data from the original charts the old-fashioned way: with pencil, ruler, and a blown-up copy of the paper. Nothing wrong with that; that's how I got my numbers for the <a href="/webservices/bili">bilirubin chart</a>, but I wanted all my charts to match the AAP's.</p>
<p>So how to get the numbers off the graph? I emailed the lead author of the original paper, but haven't gotten any answer. I can pull the graphs as gif's from the PDF of the paper (thanks to <a href="http://www.openoffice.org/">OpenOffice.org</a> and <a href="http://extensions.services.openoffice.org/project/pdfimport">Sun's PDF importer</a>; Adobe's reader seems to get more limited with each upgrade). I was afraid I would have to digitize the graph by hand; I read the cool article on <a href="http://sudokugrab.blogspot.com/2009/07/how-does-it-all-work.html">Sudoku recognition</a> and figured I could learn about <a href="http://en.wikipedia.org/wiki/Hough_transform">Hough transforms</a> to get the graph, and <a href="http://en.wikipedia.org/wiki/Discrete_Fourier_transform#Multidimensional_DFT">2-D Fourier transforms</a> to remove the gridlines, then <a href="http://en.wikipedia.org/wiki/Blob_detection">blob detection</a> to find the lines. Turning pixels into measurements would be the trivial last step. Sounds like fun, if I had an infinite amount of free time.</p>
<p>Luckily, I found <a href="http://digitizer.sourceforge.net/">Engauge Digitizer</a>. With almost no time reading the manual, I had it removing gridlines, digitizing the curves on the graph, and exporting values at x-values that I selected into CSV files. It was close to easy. Not quite automated, but with only 4 graphs to digitize, I was done in half an hour. Highly recommended. With my remaining free time, I'll write a quick tutorial so I don't forget what I did.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/01/10/digitizing-graphs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Parsing the HTTP Accept: header</title>
		<link>http://bililite.com/blog/2010/01/06/parsing-the-http-accept-header/</link>
		<comments>http://bililite.com/blog/2010/01/06/parsing-the-http-accept-header/#comments</comments>
		<pubDate>Thu, 07 Jan 2010 03:03:13 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1126</guid>
		<description><![CDATA[I wanted the webservices to be as RESTful as possible, so they should use the Accept: header rather than file name extensions to determine the type. As I noted before, this won't work in general, because most browsers don't send the correct Accept: headers. Still, that's no reason not to use the header if no [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted the <a href="/blog/2009/12/31/bililite-com-webservices/">webservices</a> to be as <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">RESTful</a> as possible, so they should use the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"><code>Accept:</code> header</a> rather than file name extensions to determine the type.<span id="more-1126"></span> As I noted before, this won't work in general, because <a href="http://www.newmediacampaigns.com/page/browser-rest-http-accept-headers">most browsers don't send the correct <code>Accept:</code> headers</a>. Still, that's no reason not to use the header if no extension is sent.</p>
<p>Parsing the header is straightforward; the mime-types are separated by commas (note that the top-level separation is the comma, not the semicolon) and within each mime-type description the parameters are separated by semicolons. The first field is the mime-type itself (with possible wild card '*'), the second (if present) is the quality factor, "q={number from 0 to 1}" which describes how much the requestor wants that particular mime-type, and the remainder of the fields are other parameters.</p>
<p>As the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">specification</a> says, parameters in the <code>Accept:</code> header are rare, and my programs don't use them, so we will ignore them. That means that <code>Accept: text/html; q=0.8; level=2, text/html; q=0.2; level=1</code>
is interpreted the same as <code>Accept: text/html; q=0.2</code>; not exactly the same as the spec but easier to code.</p>
<p>The q parameter technically should be weighted by how much the <em>host</em> wants to send that type. Thus, <code>Accept: image/jpeg; q=1, image/gif; q=0.2</code> means "Send me a <code>png</code>; I'll accept
a <code>jpeg</code>, but I'll be only 20% as happy." If the host feels that the <code>gif</code> is 10 times better than the <code>jpeg</code>, it should rank the <code>gif</code> as 10*0.2 = 2 and the <code>jpeg</code> as 1*1 = 1, and send the <code>gif</code>. My code doesn't have such strong feelings and ignores such niceties.</p>
<p>A q parameter of 0 should be interpreted as "never send me this type" but my code simply ignores it and, if there are no other mime-types in the <code>Accept:</code> header, sends the default. If it's the default type that has the q=0, then the requestor loses.</p>
<p>One subtle trick from <a href="http://github.com/recess/recess/blob/master/recess/recess/http/AcceptsList.class.php">Kris Jordan's Recess</a>: wild card ('text/*' and '*/*') types should rank <em>below</em> more specific types with the same q parameter. We subtract 0.0001 from the stated q to push them lower; the specification says the value cannot have more than 3 decimal places.</p>
<pre><code class="language-php">function parseAccept ($accept){
	$mimetypes = array( // associate types with file extensions
		'*/*' =&gt; 'html',
		'text/*' =&gt; 'txt',
		'text/plain' =&gt; 'txt',
		'text/html' =&gt; 'html',
		'text/csv' =&gt; 'csv',
		'text/javascript' =&gt; 'js',
		'image/*' =&gt; 'png',
		'image/png' =&gt; 'png',
		'image/gif' =&gt; 'gif',
		'image/jpeg' =&gt; 'jpg',
		'application/*' =&gt; 'js',
		'application/json' =&gt; 'js',
		'application/xml' =&gt; 'xml'
	);

	$types = array();
	foreach (explode(',', $accept) as $mediaRange){
		@list ($type, $qparam) = preg_split ('/\s*;\s*/', $mediaRange); // the q parameter must be the first one according to the RFC
		$q = substr ($qparam, 0, 2) == 'q=' ? floatval(substr($qparam,2)) : 1;
		if ($q &lt;= 0) continue;
		if (substr($type, -1) == '*') $q -= 0.0001;
		if (@$type[0] == '*') $q -= 0.0001;
		$types[$type] = $q;
	}
	arsort ($types); // sort from highest to lowest q value
	foreach ($types as $type =&gt; $q){
		if (isset ($mimetypes[$type])) return $mimetypes[$type];
	}
	return 'html';
}</code></pre>
<p>The same sort of code would work for <code>Accept-Encoding:</code> and the other Accept-type headers.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/01/06/parsing-the-http-accept-header/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
