<?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-5113647</id><updated>2012-01-26T22:57:35.876-08:00</updated><category term='GRE'/><category term='node.js'/><category term='moments'/><category term='javascript'/><category term='fenwick tree'/><category term='storage'/><category term='environment'/><category term='scaling'/><category term='algorithms'/><category term='http'/><category term='svd'/><category term='firefox'/><category term='thecruftoftheworld'/><category term='filesystems'/><category term='segment tree'/><category term='python'/><category term='attitude'/><category term='dyslexia'/><category term='rant'/><category term='mongrel2'/><category term='weather'/><category term='insightful'/><category term='vanilla'/><category term='idea'/><category term='p2p'/><category term='xmpp'/><category term='technical'/><category term='jabber'/><category term='tool'/><category term='asynchronous I/O'/><category term='vacation'/><category term='php'/><category term='patterns'/><category term='security'/><category term='programming'/><category term='tutorial'/><category term='random'/><category term='mumbai'/><category term='humour'/><category term='oop'/><category term='jquery'/><category term='movie'/><category term='disgusting'/><category term='recipe'/><category term='data structures'/><category term='sqloptimization'/><category term='kerala'/><category term='food'/><category term='poetry'/><category term='fun'/><category term='duckduckgo'/><category term='tree'/><category term='health'/><title type='text'>Some thoughts on your (and my) thoughts.</title><subtitle type='html'>Just something whacky to get you thinking. At least it got me pulling apart my hair, and now, I'm half bald.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default?start-index=101&amp;max-results=100'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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>186</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5113647.post-6120347157132480283</id><published>2012-01-24T10:20:00.000-08:00</published><updated>2012-01-24T10:20:59.665-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query Optimization - Part-9 (Covering Indexes)</title><content type='html'>To speed up query performance, use a &lt;a href="https://en.wikipedia.org/wiki/Index_(database)#Covering_index"&gt;Covering Index&lt;/a&gt; that covers (indexes) not just columns that will be used in the WHERE clause, but also columns that will be used in the projection list of the SELECT query.&lt;br /&gt;&lt;br /&gt;See this &lt;a href="http://www.youtube.com/watch?v=AVNjqgf7zNw"&gt;video&lt;/a&gt; for a more detailed explanation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6120347157132480283?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6120347157132480283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6120347157132480283&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6120347157132480283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6120347157132480283'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/sql-query-optimization-part-9-covering.html' title='SQL Query Optimization - Part-9 (Covering Indexes)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-275979984453917073</id><published>2012-01-22T22:44:00.000-08:00</published><updated>2012-01-22T22:44:08.077-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>Converting Regular Expressions to a DFA</title><content type='html'>Instead of writing anything on this most researched topic, I'm just posting some links here.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=dlH2pIndNrU"&gt;http://www.youtube.com/watch?v=dlH2pIndNrU&lt;/a&gt;&lt;br /&gt;&lt;a href="http://projectsseminar.blogspot.com/2011/06/regular-expression-to-dfa.html"&gt;http://projectsseminar.blogspot.com/2011/06/regular-expression-to-dfa.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Regular_expression#Implementations_and_running_times"&gt;http://en.wikipedia.org/wiki/Regular_expression#Implementations_and_running_times&lt;/a&gt;&lt;br /&gt;&lt;a href="http://swtch.com/%7Ersc/regexp/regexp1.html"&gt;http://swtch.com/~rsc/regexp/regexp1.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-275979984453917073?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/275979984453917073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=275979984453917073&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/275979984453917073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/275979984453917073'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/converting-regular-expressions-to-dfa.html' title='Converting Regular Expressions to a DFA'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5264819318606484107</id><published>2012-01-08T01:29:00.000-08:00</published><updated>2012-01-17T14:17:29.201-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><title type='text'>Solve this recurrence</title><content type='html'>T(n) = T(n - √n) + T(√n) + n&lt;br /&gt;&lt;br /&gt;Post your answers/solutions in the comments. We were asked this in the final exam in &lt;a href="http://www.cs.sunysb.edu/~bender/548/index.html"&gt;CSE-548 (Analysis Of Algorithms taught by Dr. Michael Bender)&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5264819318606484107?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5264819318606484107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5264819318606484107&amp;isPopup=true' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5264819318606484107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5264819318606484107'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/solve-this-recurrence.html' title='Solve this recurrence'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3062847028055301274</id><published>2012-01-08T00:43:00.000-08:00</published><updated>2012-01-08T00:45:03.934-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='health'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Ayurvedic Way of Life</title><content type='html'>Follow the principles mentioned &lt;a href="http://www.tmscotland.org/mav/tips.html"&gt;here&lt;/a&gt; and you should be good to go!!&lt;br /&gt;&lt;br /&gt;Also, &lt;a href="http://www.rajasthanvisit.com/ayurveda-food.htm"&gt;here&lt;/a&gt; is another very informative article about the ayurvedic view on food.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3062847028055301274?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3062847028055301274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3062847028055301274&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3062847028055301274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3062847028055301274'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/ayurvedic-way-of-life.html' title='Ayurvedic Way of Life'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1287487051506220468</id><published>2012-01-08T00:35:00.000-08:00</published><updated>2012-01-08T00:35:52.643-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Broccoli should be had fresh!!</title><content type='html'>Broccoli should be had fresh and not frozen.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Frozen Broccoli is not as firm as Fresh Broccoli&lt;/li&gt;&lt;li&gt;Frozen Broccoli doesn't taste as good as Fresh Broccoli&lt;/li&gt;&lt;li&gt;Frozen Broccoli doesn't contain the &lt;a href="http://caloriecount.about.com/calories-broccoli-stalks-i11741"&gt;broccoli stalk&lt;/a&gt; which is not only more nutritious, but also more fibrous than the &lt;a href="http://caloriecount.about.com/calories-market-day-broccoli-florets-i85378"&gt;brocolli florets&lt;/a&gt;&lt;/li&gt;&lt;li&gt;According to &lt;a href="http://www.fruitsandveggiesmatter.gov/month/broccoli.html"&gt;this page&lt;/a&gt;, &lt;i&gt;"frozen broccoli has twice as much sodium as fresh (up to 68 mg per 10 oz. package), about half the calcium, and smaller amounts of iron, thiamin, riboflavin, and vitamin C."&lt;/i&gt;&lt;/li&gt;&lt;/ol&gt;Peas on the other hand should be had frozen since (from &lt;a href="http://www.food-skills-for-self-sufficiency.com/freezing-peas.html"&gt;here&lt;/a&gt;), &lt;i&gt;"Peas are high in sugar content, so they have to be blanched before freezing to stop enzyme activity from turning sugars into starch and ruining their fresh sweet flavor."&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1287487051506220468?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1287487051506220468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1287487051506220468&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1287487051506220468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1287487051506220468'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/broccoli-should-be-had-fresh.html' title='Broccoli should be had fresh!!'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6004612446177866788</id><published>2012-01-08T00:12:00.000-08:00</published><updated>2012-01-08T00:12:49.204-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Focaccia reprise</title><content type='html'>Made Focaccia Bread again after about 2 years of absence from baking. Contains garlic, thyme, oregano, dried basil and olive oil. This is how it turned out: &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-O-YwjlKWi-U/TwlN9t5p2uI/AAAAAAAACRM/TgjONQ9m_5U/s1600/focaccia.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-O-YwjlKWi-U/TwlN9t5p2uI/AAAAAAAACRM/TgjONQ9m_5U/s320/focaccia.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Ingredients, motivation and housing courtesy Poorvi Matani, and oven &amp;amp; electricity courtesy Panna Parikh. A big thanks to my teachers at IHM and my mom who encouraged my culinary streak ;)&lt;br /&gt;This version lacked rosemary since I didn't have ready access to any. Next batch should have that plus olives, sun-dried tomatoes and maybe some other veggies.&lt;br /&gt;The last post about something similar was &lt;a href="http://dhruvbird.blogspot.com/2009/07/garlic-and-herb-chapati.html"&gt;this recipe for Garlic &amp;amp; Herb Chapati&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6004612446177866788?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6004612446177866788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6004612446177866788&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6004612446177866788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6004612446177866788'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2012/01/focaccia-reprise.html' title='Focaccia reprise'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://3.bp.blogspot.com/-O-YwjlKWi-U/TwlN9t5p2uI/AAAAAAAACRM/TgjONQ9m_5U/s72-c/focaccia.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8820227179351862531</id><published>2011-10-24T15:34:00.000-07:00</published><updated>2011-11-16T07:16:02.952-08:00</updated><title type='text'>What business do you want to be in?</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;a href="http://www.cs.sunysb.edu/%7Ebender/"&gt;Dr. Bender&lt;/a&gt; once mentioned in class that memories are created when one experiences &lt;strike&gt;joy&lt;/strike&gt; &lt;i&gt;strong emotions&lt;/i&gt;, and in the context of algorithms, when one discovers a solution to some problem.&lt;br /&gt;&lt;br /&gt;About a month down, I've noticed that experiences are what I'm going to die with. I vividly remember this episode in school when we were in the middle of the gymnastics competition and were one short of someone on the &lt;a href="http://en.wikipedia.org/wiki/Rings_%28gymnastics%29"&gt;roman rings&lt;/a&gt;. I decided to give it a shot (even though I had never tried it before) and 2 minutes later I found myself stuck &lt;i&gt;"up there"&lt;/i&gt;. I don't remember all the things that I did &lt;i&gt;right&lt;/i&gt; or who eventually won, but I surely do remember getting stuck!&lt;br /&gt;&lt;br /&gt;If you are from my school and remember this episode, please leave a comment. I'll be looking forward to hearing from you!&lt;br /&gt;&lt;br /&gt;I want to be in the &lt;i&gt;business of creating memories&lt;/i&gt;.&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Edit:&lt;/b&gt; Fixed the incorrect quote in the first line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8820227179351862531?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8820227179351862531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8820227179351862531&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8820227179351862531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8820227179351862531'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/10/what-business-do-you-want-to-be-in.html' title='What business do you want to be in?'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6340804171429061341</id><published>2011-09-20T19:42:00.000-07:00</published><updated>2011-09-21T07:04:07.543-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The Integer (0/1) Bounded Knapsack Problem</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Quoting the problem statement from &lt;a href="http://petr-mitrichev.blogspot.com/2011/07/integral-bounded-knapsack-problem.html"&gt;Petr's blog&lt;/a&gt;: &lt;br /&gt;&lt;br /&gt;&lt;i&gt;"The bounded knapsack problem is: you are given n types of items, you have u&lt;sub&gt;i&lt;/sub&gt; items of i&lt;sup&gt;th&lt;/sup&gt; type, and each item of i&lt;sup&gt;th&lt;/sup&gt; type weighs w&lt;sub&gt;i&lt;/sub&gt; and costs c&lt;sub&gt;i&lt;/sub&gt;. What is the maximal cost you can get by picking some items weighing at most W in total?"&lt;/i&gt; &lt;br /&gt;&lt;br /&gt;Here, we assume that the knapsack can hold a weight at most &lt;i&gt;W&lt;/i&gt;. &lt;br /&gt;&lt;br /&gt;We shall describe 3 solutions to this problem, each of which reduces the complexity of the solution by a certain amount. &lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;O(W*n*M):&lt;/b&gt; The first solution is the standard &lt;a href="http://www.cse.unl.edu/%7Egoddard/Courses/CSCE310J/Lectures/Lecture8-DynamicProgramming.pdf"&gt;knapsack solution&lt;/a&gt; solution, which just involves copying each item instance of every item type to get a total of u&lt;sub&gt;i&lt;/sub&gt; items of the i&lt;sup&gt;th&lt;/sup&gt; type, each of which represents a different item having the same w&lt;sub&gt;i&lt;/sub&gt; &amp;amp; c&lt;sub&gt;i&lt;/sub&gt; as the item of the i&lt;sup&gt;th&lt;/sup&gt; type.&lt;br /&gt;&lt;br /&gt;It is easy to see that if we have a mean of &lt;i&gt;M&lt;/i&gt; number of items of each type, the complexity of this solution is &lt;i&gt;O(W * n * M)&lt;/i&gt; &lt;/li&gt;&lt;li style="padding-top: 0.7em;"&gt;&lt;b&gt;O(W*n*log(M)):&lt;/b&gt; The second solution involves rewriting items such that we have a fewer number of items of each type to work with. We shall then solve the transformed problem using the same technique as the standard knapsack problem (as in the solution above). &lt;br /&gt;&lt;br /&gt;Suppose we have 21 instance of the i&lt;sup&gt;th&lt;/sup&gt; item type, weighing w&lt;sub&gt;i&lt;/sub&gt; and costing c&lt;sub&gt;i&lt;/sub&gt;. We observe that 21 can be rewritten (using powers of 2) as: &lt;br /&gt;21 = 1 + 2 + 4 + 8 + 6 &lt;br /&gt;&lt;br /&gt;We basically add powers of 2 till we exceed the sum we want to create (21 in this case). When we exceed the sum, we just add the difference (6 in this case) between the number (21 in this case) and the current sum we have (15 in this case = 1+2+4+8). &lt;br /&gt;&lt;br /&gt;Now, the new items are created by combining 1, 2, 4, 8, and 6 items of the original instances. These newly created items will have the following weights and costs: &lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Number of items of the original type combined&lt;/th&gt;&lt;th&gt;Weight&lt;/th&gt;&lt;th&gt;Cost&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1*w&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;1*c&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2*w&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;2*c&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4*w&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;4*c&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;8*w&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;8*c&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;6*w&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;6*c&lt;sub&gt;i&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Hence, we have effectively reduced the number of items from 21 to 5. In general, we replace the &lt;i&gt;M&lt;/i&gt; in the solution above with &lt;i&gt;log(M)&lt;/i&gt; to get a final complexity of &lt;i&gt;O(W * n * log(M))&lt;/i&gt;. &lt;/li&gt;&lt;li style="padding-top: 0.7em;"&gt;&lt;b&gt;O(W*n):&lt;/b&gt; The third and most efficient solution to this problem is the one that took me almost 3 days to figure out and understand. The &lt;a href="http://petr-mitrichev.blogspot.com/2011/07/integral-bounded-knapsack-problem.html"&gt;short post on petr's blog&lt;/a&gt; actually contains all the information needed to understand it, but I'm not that comfortable reading terse explanations; especially when it's a new idea, so here is something more verbose. &lt;br /&gt;&lt;br /&gt;There is also a &lt;a href="http://www.springerlink.com/content/6vwed32pkyf4df6k/"&gt;paper&lt;/a&gt; and a section in a &lt;a href="http://books.google.com/books?id=u5DB7gck08YC&amp;amp;lpg=PA211&amp;amp;pg=PA194#v=onepage&amp;amp;q&amp;amp;f=false"&gt;book&lt;/a&gt; dedicate to this problem, but I find them quite hard as well. &lt;br /&gt;&lt;br /&gt;Let's assume that this row (in the DP table of Items (rows) v/s Weight (columns)) represents the best configuration (maximum cost) for the knapsack at every weight 0 .. W that includes all item types from 1 .. (k-1)&lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="text-align: center; width: 500px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Weight&lt;/th&gt;&lt;th&gt;0&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;6&lt;/th&gt;&lt;th&gt;7&lt;/th&gt;&lt;th&gt;8&lt;/th&gt;&lt;th&gt;9&lt;/th&gt;&lt;th&gt;10&lt;/th&gt;&lt;th&gt;11&lt;/th&gt;&lt;th&gt;12&lt;/th&gt;&lt;th&gt;13&lt;/th&gt;&lt;th&gt;14&lt;/th&gt;&lt;th&gt;15&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Item (k-1)&lt;/th&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;51&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;So, the basic observation is that DP&lt;sub&gt;(k-1), w&lt;/sub&gt; represents the best solution including all items from 1 .. (k-1) (both inclusive) with a knapsack of weight (w). &lt;br /&gt;&lt;br /&gt;To compute DP&lt;sub&gt;k, w&lt;/sub&gt;, we consider the possibility of including either 0, 1, 2, 3, 4, ..., u&lt;sub&gt;i&lt;/sub&gt; items of the i&lt;sup&gt;th&lt;/sup&gt; type to the previous best solution including all items from 1 .. (k-1). This gives us the following recurrence: &lt;br /&gt;&lt;br /&gt;DP&lt;sub&gt;k, w&lt;/sub&gt; = max (&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DP&lt;sub&gt;(k-1), w&lt;/sub&gt;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DP&lt;sub&gt;(k-1), w-1*w&lt;sub&gt;k&lt;/sub&gt;&lt;/sub&gt; + 1*c&lt;sub&gt;k&lt;/sub&gt;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DP&lt;sub&gt;(k-1), w-2*w&lt;sub&gt;k&lt;/sub&gt;&lt;/sub&gt; + 2*c&lt;sub&gt;k&lt;/sub&gt;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DP&lt;sub&gt;(k-1), w-3*w&lt;sub&gt;k&lt;/sub&gt;&lt;/sub&gt; + 3*c&lt;sub&gt;k&lt;/sub&gt;, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;..., &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DP&lt;sub&gt;(k-1), w-u&lt;sub&gt;i&lt;/sub&gt;*w&lt;sub&gt;k&lt;/sub&gt;&lt;/sub&gt; + u&lt;sub&gt;i&lt;/sub&gt;*c&lt;sub&gt;k&lt;/sub&gt;) &lt;br /&gt;&lt;br /&gt;An interesting observation here would be that we only deal with every w&lt;sub&gt;k&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; element from the previous array to decide on any element in the current array.&lt;br /&gt;&lt;br /&gt;Let us for a minute ignore the fact that we have only a fixed number of items of each type and assume that we have an infinite number of items of each type. Let us assume that item &lt;i&gt;i&lt;/i&gt; weighs 3 and costs 9. If we make that assumption, we see that the best solution at DP&lt;sub&gt;k, 12&lt;/sub&gt; is represented by taking the maximum of the cells colored in green below.  &lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="text-align: center; width: 500px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Weight&lt;/th&gt;&lt;th&gt;0&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;6&lt;/th&gt;&lt;th&gt;7&lt;/th&gt;&lt;th&gt;8&lt;/th&gt;&lt;th&gt;9&lt;/th&gt;&lt;th&gt;10&lt;/th&gt;&lt;th&gt;11&lt;/th&gt;&lt;th&gt;12&lt;/th&gt;&lt;th&gt;13&lt;/th&gt;&lt;th&gt;14&lt;/th&gt;&lt;th&gt;15&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Item (k-1)&lt;/th&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;51&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;td style="background-color: lime;"&gt;36&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td style="background-color: lime;"&gt;34&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td style="background-color: lime;"&gt;37&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td style="background-color: lime;"&gt;32&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td style="background-color: lime;"&gt;42&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;What we have essentially done is added:&lt;br /&gt;0*9 to DP&lt;sub&gt;(k-1), 12&lt;/sub&gt; &lt;br /&gt;1*9 to DP&lt;sub&gt;(k-1), 9&lt;/sub&gt; &lt;br /&gt;2*9 to DP&lt;sub&gt;(k-1), 6&lt;/sub&gt; &lt;br /&gt;3*9 to DP&lt;sub&gt;(k-1), 3&lt;/sub&gt; and &lt;br /&gt;4*9 to DP&lt;sub&gt;(k-1), 0&lt;/sub&gt; &lt;br /&gt;&lt;br /&gt;The problem has reduced to finding the maximum value among all the values of the array that is formed by picking every w&lt;sub&gt;k&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; element from the previous best solution and adding some multiple of c&lt;sub&gt;k&lt;/sub&gt; to it. &lt;br /&gt;&lt;br /&gt;We also notice that it doesn't matter what values we add to DP&lt;sub&gt;(k-1), w&lt;/sub&gt; (for the k&lt;sup&gt;th&lt;/sup&gt; item) as long as they are c&lt;sub&gt;k&lt;/sub&gt; apart. Hence, if the weight of the knapsack is &lt;i&gt;W&lt;/i&gt; and the weight of the k&lt;sup&gt;th&lt;/sup&gt; item is w&lt;sub&gt;k&lt;/sub&gt;, we can add the following to every w&lt;sub&gt;k&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; element in the previous array.&lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Column to which value is added&lt;/th&gt;&lt;th&gt;Value Added&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;(W/w&lt;sub&gt;k&lt;/sub&gt;)*c&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1*w&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;(W/w&lt;sub&gt;k&lt;/sub&gt; - 1)*c&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2*w&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;(W/w&lt;sub&gt;k&lt;/sub&gt; - 2)*c&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3*w&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;(W/w&lt;sub&gt;k&lt;/sub&gt; - 3)*c&lt;sub&gt;k&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;If we now reintroduce the requirement that each item type will have only a &lt;i&gt;bounded&lt;/i&gt; or fixed number of instances (i.e. u&lt;sub&gt;k&lt;/sub&gt; is bounded), then the problem reduces to finding the maximum value in every window of size u&lt;sub&gt;k&lt;/sub&gt; in the array we just computed. This can easily be done by pre-processing the array to maintain the largest element in every window of size u&lt;sub&gt;k&lt;/sub&gt;. &lt;br /&gt;&lt;br /&gt;However, the actual value of the cost at any cell needs to be computed by adding back any differential that we may have subtracted to ensure that every entry is c&lt;sub&gt;k&lt;/sub&gt; apart and that the first entry is the one which has the maximum multiple of c&lt;sub&gt;k&lt;/sub&gt; subtracted from it. For the j&lt;sup&gt;th&lt;/sup&gt; array element in the current sub-array we are processing, this value will be (W/w&lt;sub&gt;k&lt;/sub&gt; - j)*c&lt;sub&gt;k&lt;/sub&gt; (since we have subtracted that much &lt;i&gt;extra&lt;/i&gt; from those elements, we must now add it back).&lt;br /&gt;&lt;br /&gt;Hence, we can safely say that we shall have w&lt;sub&gt;k&lt;/sub&gt; different sub-problems that we shall try to solve assuming that we can use a single computation for every w&lt;sub&gt;k&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; element. &lt;br /&gt;&lt;br /&gt;This allows us to find the value at every cell of the (k x W) matrix is amortized time O(1) (we have amortized the cost of pre-computing the w&lt;sub&gt;k&lt;/sub&gt; arrays across all the element accesses). This makes the total running time of this algorithm &lt;i&gt;O(W * n)&lt;/i&gt;.  &lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6340804171429061341?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6340804171429061341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6340804171429061341&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6340804171429061341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6340804171429061341'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/09/integer-01-bounded-knapsack-problem.html' title='The Integer (0/1) Bounded Knapsack Problem'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-88452064591376132</id><published>2011-07-14T01:54:00.000-07:00</published><updated>2011-07-14T01:54:54.136-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Vegetable Ratatouille</title><content type='html'>Here we go!!&lt;br /&gt;&lt;br /&gt;So, we'll be following &lt;a href="http://translate.google.com/translate?hl=en&amp;amp;sl=auto&amp;amp;tl=en&amp;amp;u=http%3A%2F%2Fwww.supertoinette.com%2Frecette%2F995%2Fratatouille_nicoise_in_de_di.html"&gt;this&lt;/a&gt; recipe for the most part. There are some diversions that I shall mention here. These are the only things I changed from the recipe above.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tomatoes:&lt;/b&gt; We shall make a Ratatouille that has a red (tomato) gravy and the vegetables will be added to that gravy. For this, you'll need more tomatoes (at least 4 more).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Basil:&lt;/b&gt; I used dry basil instead of fresh basil, since I couldn't find any where I stay. I used lesser oil at every stage (use about half the amount of oil everywhere).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Olive Oil:&lt;/b&gt; Do NOT use Extra-Virgin Olive Oil for cooking. Do NOT use Olive-Pomace Oil since it doesn't have a good taste. Use ONLY Pure-Olive-Oil.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Salt:&lt;/b&gt; ALWAYS use Salt to-taste instead of measuring it since the saltiness of salt varies from region to region.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ginger:&lt;/b&gt; I also use grated ginger (or ginger paste) along with the garlic pieces.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bay Leaf:&lt;/b&gt; I also used bay-leaves (3 nos.) while sautéing the onions (along with the garlic and ginger).&lt;br /&gt;&lt;br /&gt;After the onions are half done, add pieces of (at least) 4 tomatos and cook till the tomatoes are soft. Mash them to form a paste. Follow the recipe mentioned &lt;a href="http://translate.google.com/translate?hl=en&amp;amp;sl=auto&amp;amp;tl=en&amp;amp;u=http%3A%2F%2Fwww.supertoinette.com%2Frecette%2F995%2Fratatouille_nicoise_in_de_di.html"&gt;here&lt;/a&gt;. Once the other vegetables are cooked, add them to this tomato gravy and cook for about 3 mins. (or till steaming). Cover and let the vegetables soak in the smell for about 15 mins. Serve with rice (you can use brown rice since it tastes really good and is a healthier option when compared with white rice).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-88452064591376132?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/88452064591376132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=88452064591376132&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/88452064591376132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/88452064591376132'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/07/vegetable-ratatouille.html' title='Vegetable Ratatouille'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5770587426292444642</id><published>2011-07-06T10:45:00.000-07:00</published><updated>2011-07-06T10:45:21.149-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='duckduckgo'/><title type='text'>You know you are working with the right people when ...</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;... you get this in the post :) :)&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-0tRfWfvWy4w/ThSTAiSd3PI/AAAAAAAACQU/QSMxhdJSANY/s1600/yegg_letter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="396" src="http://2.bp.blogspot.com/-0tRfWfvWy4w/ThSTAiSd3PI/AAAAAAAACQU/QSMxhdJSANY/s400/yegg_letter.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Thanks &lt;a href="https://twitter.com/#%21/yegg"&gt;@yegg&lt;/a&gt; :-)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5770587426292444642?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5770587426292444642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5770587426292444642&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5770587426292444642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5770587426292444642'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/07/you-know-you-are-working-with-right.html' title='You know you are working with the right people when ...'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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/-0tRfWfvWy4w/ThSTAiSd3PI/AAAAAAAACQU/QSMxhdJSANY/s72-c/yegg_letter.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9175943636200749484</id><published>2011-06-10T05:31:00.000-07:00</published><updated>2011-06-10T05:45:11.807-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>A plea to all software developers: Use Units</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Have you ever come across configuration files that read:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;MAX_BUFFER_SIZE = 4096&lt;br /&gt;MAX_TIMEOUT     = 20&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;WTF!!&lt;br /&gt;&lt;br /&gt;4096 what?? bits, bytes, kilobytes, megabytes?&lt;br /&gt;&lt;br /&gt;20 what?? mill-second, second?&lt;br /&gt;&lt;br /&gt;Please, please make units part of the config. option and say:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;MAX_BUFFER_SIZE_BYTES = 4096&lt;br /&gt;MAX_TIMEOUT_MILLISEC  = 20&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9175943636200749484?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9175943636200749484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9175943636200749484&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9175943636200749484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9175943636200749484'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/06/plea-to-all-software-developers-use.html' title='A plea to all software developers: Use Units'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8333033521583880246</id><published>2011-05-15T14:24:00.000-07:00</published><updated>2011-05-15T14:24:55.837-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='duckduckgo'/><title type='text'>Why DuckDuckGo is good for mother</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;My mother wastes a lot of time online visiting MFA sites because she can't differentiate between the legitimate ones and the scammy ones. I've set up &lt;a href="https://duckduckgo.com/"&gt;DuckDuckGo&lt;/a&gt; for her as her default search engine and she's been complaining lesser about &lt;i&gt;"Why does this site not have what I want?"&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;I also feel that DDG can have a bang page organized by target audience (such as Mother/Father/Student, etc...). For Mother, these would need to go in:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;!allrecipes&lt;/li&gt;&lt;li&gt;!epicurious&lt;/li&gt;&lt;li&gt;!w&lt;/li&gt;&lt;li&gt;!yt&lt;/li&gt;&lt;li&gt;!lyrics&lt;/li&gt;&lt;li&gt;!gimages&lt;/li&gt;&lt;li&gt;!gmaps&lt;/li&gt;&lt;li&gt;!bloomberg&lt;/li&gt;&lt;li&gt;!reuters&lt;/li&gt;&lt;li&gt;!songmeanings&lt;/li&gt;&lt;li&gt;!news&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;It would surely make it easier for mother to navigate the shortcuts!!&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8333033521583880246?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8333033521583880246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8333033521583880246&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8333033521583880246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8333033521583880246'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/05/why-duckduckgo-is-good-for-mother.html' title='Why DuckDuckGo is good for mother'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5923272336185929288</id><published>2011-05-08T13:50:00.000-07:00</published><updated>2011-05-08T13:56:36.825-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Faster Javascript String matching</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Ever had to match a lot of strings (say article titles or names of people) to a user entered query? I'm sure you always:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Looped around the values and&lt;/li&gt;&lt;li&gt;Performed a substring search on these strings&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Well, it turns out that both are suboptimal. Let us for a minute assume that the data you have is relatively static (doesn't change too much over time) and that queries really should be satisfied quickly. If that is the case, then you would be better off:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Maintaining all the data to be queried in a single string (delimited by a $ or # sign - something that is not expected to be present in the data itself)&lt;/li&gt;&lt;li&gt;Performing a regular-expression match on the aforementioned string&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Assuming that the regular expression engine is optimized (and mostly they are), a regexp match would be much faster than a naive O(n&lt;sup&gt;2&lt;/sup&gt;) search.&lt;br /&gt;&lt;br /&gt;Let's look at some code and the resulting running time for either approach.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function random_string(len) {&lt;br /&gt;    var s = '';&lt;br /&gt;    for (var i = 0; i &amp;lt; len; ++i) {&lt;br /&gt;        s += String.fromCharCode(Math.round(97 + Math.random()*26));&lt;br /&gt;    }&lt;br /&gt;    return s;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var rs = [ ];&lt;br /&gt;var total_length = 0;&lt;br /&gt;// Stores the start index of each string in rs.join('$');&lt;br /&gt;var indexes = [ ];&lt;br /&gt;&lt;br /&gt;for (var i = 0; i &amp;lt; 300000; ++i) {&lt;br /&gt;    var s = random_string(40);&lt;br /&gt;    rs.push(s);&lt;br /&gt;    indexes.push(total_length);&lt;br /&gt;    total_length += (s.length + 1);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function test_01(rs) {&lt;br /&gt;    // Returns the number of elements in 'rs' that match the regexp /ret/&lt;br /&gt;    var re = /ret/g;&lt;br /&gt;    var cnt = 0;&lt;br /&gt;    for (var i = 0; i &amp;lt; rs.length; ++i) {&lt;br /&gt;        var m = rs[i].match(re);&lt;br /&gt;        if (m) {&lt;br /&gt;            cnt += 1;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return cnt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function test_02(rs) {&lt;br /&gt;    // Returns the number of elements in 'rs' that match the regexp /ret/&lt;br /&gt;    var re = /ret/g;&lt;br /&gt;    var s = rs.join('$');&lt;br /&gt;    // Indexes of strings in 'rs' that matched&lt;br /&gt;    var idx = [ -1 ];&lt;br /&gt;    var ii = 0;&lt;br /&gt;    var m = re.exec(s);&lt;br /&gt;    while (m) {&lt;br /&gt;        while (ii &amp;lt; indexes.length &amp;amp;&amp;amp; indexes[ii] &amp;lt;= m.index) {&lt;br /&gt;            ++ii;&lt;br /&gt;        }&lt;br /&gt;        if (idx[idx.length-1] != ii) {&lt;br /&gt;            idx.push(ii);&lt;br /&gt;        }&lt;br /&gt;        m = re.exec(s);&lt;br /&gt;    }&lt;br /&gt;    return idx.length - 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;d1 = new Date();&lt;br /&gt;console.log("test_01:", test_01(rs));&lt;br /&gt;d2 = new Date()&lt;br /&gt;console.log("test_01 time:", d2-d1);&lt;br /&gt;&lt;br /&gt;d1 = new Date();&lt;br /&gt;console.log("test_02:", test_02(rs));&lt;br /&gt;d2 = new Date()&lt;br /&gt;console.log("test_02 time:", d2-d1);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is the output that running this code produces:  &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;test_01: 665&lt;br /&gt;test_01 time: 935&lt;br /&gt;test_02: 665&lt;br /&gt;test_02 time: 257&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which means that the longer code is actually 2.63 times &lt;i&gt;faster&lt;/i&gt; than the more beautiful looking code.&lt;br /&gt;&lt;br /&gt;When the difference between 200ms and 300ms is felt by the user (for UI centric applications), this can be a &lt;i&gt;huge&lt;/i&gt; improvement to the perceived responsiveness of the application.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5923272336185929288?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5923272336185929288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5923272336185929288&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5923272336185929288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5923272336185929288'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/05/ever-had-to-match-lot-of-strings-say.html' title='Faster Javascript String matching'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7211688785812020357</id><published>2011-05-08T08:48:00.000-07:00</published><updated>2011-05-22T04:51:49.074-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data structures'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='tree'/><title type='text'>Computing the Cumulative sum of a range of elements in a binary tree</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Let us assume the alternative definition of a BST (Binary Search Tree), namely: &lt;i&gt;"The left subtree shall contain elements less than or equal to the element at the root and the right subtree shall contain elements greater than or equal to the element at the root".&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;This means that if we want to compute the cumulative sum of all numbers ≤ to a given number, we need to update some metadata every time a node is added to the tree or removed from the tree (and for a balanced BST also when rebalancing occurs). Fortunately, &lt;a href="https://github.com/directidhruv/algorithm-js"&gt;this implementation of the AVL Tree&lt;/a&gt; allows us to do &lt;i&gt;just&lt;/i&gt; this by way of &lt;i&gt;hook functions&lt;/i&gt; that you can pass to it via the constructor.&lt;br /&gt;&lt;br /&gt;We define a function &lt;tt&gt;binary_tree_summer&lt;/tt&gt; that computes the cumulative sum of a node and its child nodes and stores in the &lt;tt&gt;sum&lt;/tt&gt; attribute of the node.&lt;br /&gt;&lt;br /&gt;To query the cumulative sum, we define a function &lt;tt&gt;query_sum&lt;/tt&gt; that basically employs the following strategy (given our definition of the BST above):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If the value at the node is ≤ than the value we are looking for, we take the cumulative sum at the node, subtract from it the sum in it's right subtree (since we don't know how many elements in the right subtree will match our query) and then add the result of querying the right subtree&lt;/li&gt;&lt;li&gt;If the value at the node is &amp;gt; the value we are looking for, we are assured that the complete range lies in the node's left subtree, so we query the left subtree&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;You can see that at every stage, we decide to go either left or right, which means that our runtime is bounded by the height of the tree, which fortunately in case of an AVL Tree is at most O(log n).&lt;br /&gt;&lt;br /&gt;The code for such a use-case is shown below:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function cumulative_sum() {&lt;br /&gt;    var items = [ 4, 9, 2, 5, 4, 2, 1, 2, 3, 2, 1, 7, 3, 2 ];&lt;br /&gt;&lt;br /&gt;    function binary_tree_summer(node) {&lt;br /&gt;        var _s = (node.left ? node.left.sum : 0) + &lt;br /&gt;         (node.right ? node.right.sum : 0) + node.value;&lt;br /&gt;        node.sum = _s;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    var tree = new algo.AVLTree(algo.cmp_lt, binary_tree_summer);&lt;br /&gt;    for (var i = 0; i &amp;lt; items.length; ++i) {&lt;br /&gt;        tree.insert(items[i]);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function query_sum(node, value) {&lt;br /&gt;        // We do the query like we do for a Segment Tree&lt;br /&gt;        if (!node) {&lt;br /&gt;            return 0;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (node.value &amp;lt;= value) {&lt;br /&gt;        var sub = node.right ? node.right.sum : 0;&lt;br /&gt;        return node.sum - sub + query_sum(node.right, value);&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;        // node.value &amp;gt; value&lt;br /&gt;        return query_sum(node.left, value);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;console.log("&amp;lt;= 2:", query_sum(tree.root, 2));&lt;br /&gt;console.log("&amp;lt;= 5:", query_sum(tree.root, 5));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's look at summing all numbers ≤ 2. The diagram below shows us how the call flow looks like. Note:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The green lines are the paths followed down the root&lt;/li&gt;&lt;li&gt;The red lines are the paths that are rejected (not followed)&lt;/li&gt;&lt;li&gt;The numbers in blue above each node denote the value of the &lt;tt&gt;sum&lt;/tt&gt; attribute for that node&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-5GKBkBg7cDk/Tca6bzrcfPI/AAAAAAAACO8/UNpNcJLFP1A/s1600/AVLTree_sum_le2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="303" src="http://4.bp.blogspot.com/-5GKBkBg7cDk/Tca6bzrcfPI/AAAAAAAACO8/UNpNcJLFP1A/s320/AVLTree_sum_le2.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The sum is obtained in the following manner for values ≤ 2: 47-39+4&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The diagram below shows us the call flow for summing all numbers ≤ 5.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-8uKf_8Pid5Y/Tca7B8J-kfI/AAAAAAAACPE/Y7FjQoUW13E/s1600/AVLTree_sum_le5.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="303" src="http://2.bp.blogspot.com/-8uKf_8Pid5Y/Tca7B8J-kfI/AAAAAAAACPE/Y7FjQoUW13E/s320/AVLTree_sum_le5.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The sum is obtained in the following manner for values ≤ 5: 47-39+39-25+25-16&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7211688785812020357?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7211688785812020357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7211688785812020357&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7211688785812020357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7211688785812020357'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/05/let-us-assume-alternative-definition-of.html' title='Computing the Cumulative sum of a range of elements in a binary tree'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://4.bp.blogspot.com/-5GKBkBg7cDk/Tca6bzrcfPI/AAAAAAAACO8/UNpNcJLFP1A/s72-c/AVLTree_sum_le2.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2048561694064028473</id><published>2011-05-08T03:42:00.000-07:00</published><updated>2011-05-08T03:42:41.990-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data structures'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='tree'/><title type='text'>Binary Trees with Repeated elements</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;In school/college, we all learnt about Binary Trees and while we were taught how to implement them, the implicit assumption was that elements would not be repeated. However, in the real-world, we need to deal with repeated elements all the time. Most standard library implementations of popular languages (read Java) provide a &lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/util/TreeMap.html"&gt;TreeMap&lt;/a&gt; that is reduced to something very powerless because it doesn't support repeated elements!! The C++ &lt;a href="http://www.sgi.com/tech/stl/Multimap.html"&gt;std::multimap&lt;/a&gt; class however supports repetitions and is IMHO much more powerful than any other Balanced Binary Tree implementation that I have come across.&lt;br /&gt;&lt;br /&gt;Today, I shall make an attempt to de-mistify the process of creating a Binary Tree with repeated elements. It's really simple and I think that the process can be summarized in one sentence: &lt;i&gt;"Instead of the left subtree containing elements strictly less than the element at the root and the right subtree containing elements strictly greater than the element at the root, the left subtree shall contain elements less than or equal to the element at the root and the right subtree shall contain elements greater than or equal to the element at the root"&lt;/i&gt;. Simple, isn't it!!&lt;br /&gt;&lt;br /&gt;Now, let's see why it works.&lt;br /&gt;&lt;br /&gt;The simple case of asking whether an element exists or not can be easily implemented the same way as before. If the element is present, we shall hit it while traversing down the tree.&lt;br /&gt;&lt;br /&gt;Finding the first instance of an element (if it exists) is called locating the &lt;i&gt;lower_bound&lt;/i&gt; of that element. This can be accomplished by running down the left of the node till you have an element at the node that is greater than or equal to the element to be found. We keep a track of the node every time we take a left turn. Othwerise (the element at the node is less than the element we are interested in), we go down the right. Since the lower_bound of an element is never less than the element itself, we don't keep track of nodes when we take a right turn.&lt;br /&gt;&lt;br /&gt;Finding one-past the last instance of an elemnt (if it exists) is called locating the &lt;i&gt;upper_bound&lt;/i&gt; of that element. This can be accomplished by running down the left of the node till you have an element at the node that is greater than the element to be found. We keep a track of the node every time we take a left turn. Othwerise (the element at the node is less than or equal to the element we are interested in), we go down the right. Since the upper_bound of an element is never less than or equal to the element itself, we don't keep track of nodes when we take a right turn.&lt;br /&gt;&lt;br /&gt;On the following pages, can find very high quality implementations of a &lt;a href="http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/bits/stl_tree.h?revision=169899&amp;amp;view=markup"&gt;Red-Black Tree&lt;/a&gt; and an &lt;a href="https://github.com/directidhruv/algorithm-js"&gt;AVL Tree&lt;/a&gt; respectively that both support repeated elements as well as the &lt;i&gt;lower_bound&lt;/i&gt; and &lt;i&gt;upper_bound&lt;/i&gt; functions.&lt;br /&gt;&lt;br /&gt;No special handling is required during re-balancing the tree during an insertion or a deletion.&lt;br /&gt;&lt;br /&gt;Here is a sample instance of the AVL Tree after the following elements have been inserted into it in this same order (the numbers after the - sign are just to disambiguate nodes with the same key while plotting the tree):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;4, 9, 2, 5, 4, 2, 1, 2, 3, 2, 1, 7, 3, 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-v88xFDVfpvE/TcZy6cFPa7I/AAAAAAAACO0/dgwv72jqFTQ/s1600/AVLTree01.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="303" src="http://1.bp.blogspot.com/-v88xFDVfpvE/TcZy6cFPa7I/AAAAAAAACO0/dgwv72jqFTQ/s320/AVLTree01.gif" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2048561694064028473?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2048561694064028473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2048561694064028473&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2048561694064028473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2048561694064028473'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/05/binary-trees-with-repeated-elements.html' title='Binary Trees with Repeated elements'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://1.bp.blogspot.com/-v88xFDVfpvE/TcZy6cFPa7I/AAAAAAAACO0/dgwv72jqFTQ/s72-c/AVLTree01.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4322114923806945829</id><published>2011-05-07T08:49:00.000-07:00</published><updated>2011-06-10T21:58:17.582-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data structures'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Computing the Running Median of an Array of numbers</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;The problem of computing the running median of an array of numbers involves choosing a window size for which you want to compute the median for. For example, suppose you have the following list of 10 integers:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;4, 6, 99, 10, 90, 12, 17, 1, 21, 32&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and you want to compute the running median for a window of size 7, you will get the following list of 4 medians:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Median for 4, 6, 99, 10, 90, 12, 17  : 12&lt;br /&gt;Median for 6, 99, 10, 90, 12, 17, 1  : 12&lt;br /&gt;Median for 99, 10, 90, 12, 17, 1, 21 : 17&lt;br /&gt;Median for 10, 90, 12, 17, 1, 21, 32 : 17&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The naive way would be to always sort the array and pick the middle element. This costs O(n.log n) per operation (when an integer leaves and enters the window). A slightly smarter solution will try to keep the array sorted and remove and insert the integers in O(n) time. Even though this is better, we hope to do better.&lt;br /&gt;&lt;br /&gt;You can find many resources online that try to solve the problem, but surprisingly, very few mention an O(log n) solution (per insertion and removal of an integer), which I believe is quite easy to implement.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://mail.python.org/pipermail/python-list/2009-October/1222595.html"&gt;http://mail.python.org/pipermail/python-list/2009-October/1222595.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://stats.stackexchange.com/questions/134/algorithms-to-compute-the-running-median"&gt;http://stats.stackexchange.com/questions/134/algorithms-to-compute-the-running-median&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://stats.stackexchange.com/questions/3372/is-it-possible-to-accumulate-a-set-of-statistics-that-describes-a-large-number-of/3376"&gt;http://stats.stackexchange.com/questions/3372/is-it-possible-to-accumulate-a-set-of-statistics-that-describes-a-large-number-of/3376&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.activestate.com/recipes/577059-running-median/"&gt;http://code.activestate.com/recipes/577059-running-median/&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;I shall first describe a general (data structure independent) procedure for computing the running median of a set of integers and provide other specific tricks that use a balanced binary tree to achieve the same.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;General Technique:&lt;/b&gt;&lt;br /&gt;The general technique involves keeping the data (we assume that all integers are distinct for the sake of simplicity) partitioned into 3 sets, namely:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;All elements less than the median&lt;/li&gt;&lt;li&gt;The median&lt;/li&gt;&lt;li&gt;All elements greater than the median&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Set-1 should support the following operations with the below mentioned runtime complexity:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Insertion of an element: O(log n)&lt;/li&gt;&lt;li&gt;Removal of an arbitrary element (given its handle - the handle is returned by the data structure when the element is inserted): O(log n)&lt;/li&gt;&lt;li&gt;Query the &lt;b&gt;maximum&lt;/b&gt; valued element: O(log n)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Any operation on Set-2 is pretty trivial to implement, so I'll skip to the next set.&lt;br /&gt;&lt;br /&gt;Set-3 should support the following operations with the below mentioned runtime complexity:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Insertion of an element: O(log n)&lt;/li&gt;&lt;li&gt;Removal of an arbitrary element (given its handle - the handle is returned by the data structure when the element is inserted): O(log n)&lt;/li&gt;&lt;li&gt;Query the &lt;b&gt;minimum&lt;/b&gt; valued element: O(log n)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;What we shall do is initially sort the first 'k' (window size) elements in the stream and create the 3 sets as described above. We also maintain a queue of handles as returned by each of the data structures managing Sets 1 &amp;amp; 3. This will help us remove the &lt;i&gt;oldest&lt;/i&gt; element from these sets.&lt;br /&gt;&lt;br /&gt;When a new integer is to be inserted, we follow these steps:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Get the handle to the oldest element and remove it from the set in which it currently is&lt;/li&gt;&lt;li&gt;Insert the new element in Set-1 (or Set-3) and push the returned handle into the queue&lt;/li&gt;&lt;li&gt;If the median element goes missing, we pick up the largest element from Set-1 (or the smallest element from Set-3), remove that from the set and set that as the median element&lt;/li&gt;&lt;li&gt;If we notice that the sizes of Sets 1 &amp;amp; 3 are not within 1 of each other, we shift the elements via the median from one set to the other&lt;/li&gt;&lt;li&gt;We always shift the largest element from Set-1 or the smallest element from Set-3&lt;/li&gt;&lt;li&gt;We ensure that the handles get updated whenever the elements move from one set to the other. This requires the handle to be smart and contain a reference to its parent set&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;If we follow the steps mentioned above, we are ensured that the median is always present in Set-2 (Set-2 always has just 1 element).&lt;br /&gt;&lt;br /&gt;A Max-Heap can be used to implement Set-1 and a Min-Heap can be used to implement Set-3. Alternatively, a Min-Max-Heap can be used to implement both. We can also use a balanced binary tree (such as an AVL Tree, Red-Black Tree, AA Tree) to implement Sets 1 &amp;amp; 3.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Balanced Binary Tree Technique:&lt;/b&gt;&lt;br /&gt;We can use a Balanced Binary Tree such as an AVL Tree or a Red-Black Tree with order-statistic querying to implement the running median algorithm. Insertions and deletions can be performed in O(log n) time (as before, we maintain a queue of the elements - we do not need handles in this case since a balanced binary tree can delete an element in O(log n) given the element's value). Furthermore, we can query the tree for the WINDOW_SIZE/2 ranked element (which is the median) at every stage. This is by far the simplest and cleanest technique. You can find an implementation of an AVL Tree that supports querying the k-th smallest element (for any k) &lt;a href="https://github.com/dhruvbird/algorithm-js"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Example code to use the AVLTree linked above:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var algo = require('algorithm');&lt;br /&gt;var items = [4, 6, 99, 10, 90, 12, 17, 1, 21, 32];&lt;br /&gt;&lt;br /&gt;function print_median(tree) {&lt;br /&gt;  console.log("Median:", tree.find_by_rank(Math.ceil(tree.length / 2)));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function running_median(items, window_size) {&lt;br /&gt;  var tree = new algo.AVLTree();&lt;br /&gt;  for (var i = 0; i &amp;lt; items.length; ++i) {&lt;br /&gt;    tree.insert(items[i]);&lt;br /&gt;    if (tree.length == window_size) {&lt;br /&gt;      print_median(tree);&lt;br /&gt;      tree.remove(items[i - window_size + 1]);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;running_median(items, 7);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4322114923806945829?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4322114923806945829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4322114923806945829&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4322114923806945829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4322114923806945829'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/05/computing-running-median-of-array-of.html' title='Computing the Running Median of an Array of numbers'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4865872973883229923</id><published>2011-04-18T22:57:00.000-07:00</published><updated>2011-04-18T22:59:30.915-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Bajra Roti/Parantha</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;tbody&gt;&lt;tr&gt; &lt;td&gt;Bajra atta (Pearl millet flour):&lt;/td&gt; &lt;td&gt;2 cups&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Water:&lt;/td&gt; &lt;td&gt;Enough to bind the flour (keep 2 cups handy)&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Add little water and try to bind the flour. Keep adding a water as and when required&lt;/li&gt;&lt;li&gt;Once the flour is hydrated, add a little more water to make it smooth&lt;/li&gt;&lt;li&gt;Take a plastic sheet and place it on the chakla (flat circular rolling board)&lt;/li&gt;&lt;li&gt;Ensure that you heat the tava (flat skillet) before you begin. Turn the flame to low when it is heated up&lt;/li&gt;&lt;li&gt;Take about 100-150g of dough and place it on the plastic sheet&lt;/li&gt;&lt;li&gt;Sprinkle some millet flour on top of it so that it doesn't stick to your hand&lt;/li&gt;&lt;li&gt;Using your hand, gently press down the dough ball into a flat circular disc - rotating the dough all the time. It should be easy to rotate the dough since the plastic sheet will slide very easily on the rolling board&lt;/li&gt;&lt;li&gt;Keeping the flame on low, place the flattened dough (top side down) on the tava&lt;/li&gt;&lt;li&gt;Once you see small bubbles popping up on the surface, turn the roti over&lt;/li&gt;&lt;li&gt;Heat till you see brown spots on the under-surface&lt;/li&gt;&lt;li&gt;Turn the flame to high and momentary place the roti (turned over again) on the flame directly. This should make the roti balloon. The ballooning of the roti means that it has been cooked properly&lt;/li&gt;&lt;li&gt;Turn over to other side to cook consistently (as desired - but don't burn it please!!)&lt;/li&gt;&lt;li&gt;Serve with dahi (curd), butter, jaggery and vegetables. Enjoy!!&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4865872973883229923?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4865872973883229923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4865872973883229923&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4865872973883229923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4865872973883229923'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/04/bajra-rotiparantha.html' title='Bajra Roti/Parantha'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2558756472295124646</id><published>2011-04-18T22:15:00.000-07:00</published><updated>2011-04-18T22:15:15.869-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>News these days</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Being the beneficiary of a headache yesterday, I had the privilege of being unproductive and just fixing myself in front the of television set and watching the daily news with the rest of the family. The news reader almost mechanically read out these 2 news items back to back (in the same segment):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Police firing in The Ratnagiri district of Maharashtra kill 1 and injures 5, and&lt;/li&gt;&lt;li&gt;Indian Ministers oppose the wastage of food at weddings and ask the organisers to provision accurately&lt;/li&gt;&lt;/ol&gt;It just seems so weird that these 2 news items come together - any way you look at it!! And I'm not even going into whether the people opposing the wastage are themselves practicing what they preach.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2558756472295124646?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2558756472295124646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2558756472295124646&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2558756472295124646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2558756472295124646'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/04/news-these-days.html' title='News these days'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7097649180990588392</id><published>2011-04-17T05:25:00.000-07:00</published><updated>2011-04-17T09:13:28.741-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='segment tree'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='fenwick tree'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='storage'/><category scheme='http://www.blogger.com/atom/ns#' term='tree'/><title type='text'>Segment Trees and Bitmaps</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Bitmaps are used all the time to keep track of free lists since they are a &lt;i&gt;very&lt;/i&gt; compact way of keeping track of true/false information. They are used in the ext2/ext3 file system to keep track of free inode and data blocks, in the bitmapped_allocator to keep track of free memory blocks, and in many many other places.&lt;br /&gt;&lt;br /&gt;However, in almost all places, a linear search technique is used to get the first set (or unset) bit. This works very well for small sets of data, but say you have 40 million items that you want to keep track of, you'll land up linear searching 40x10&lt;sup&gt;6&lt;/sup&gt;/8/1024/1024 = 4.7 MB of data, which is more than the cache size of most CPUs these days. What this effectively means is that you will land up filling up the CPU cache with the bitmaps, leaving very ittle space for your application's &lt;i&gt;real&lt;/i&gt; data&lt;/div&gt;.&lt;br /&gt;&lt;br /&gt;This is obviously bad news and we should do something to fix it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Welcome Trees!!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;We take some inspiration from &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Segment_tree"&gt;Segment Trees&lt;/a&gt; and use a hierarchial structure to store range data that will help us divide-and-conquor the free list in a zippy fashion.&lt;br /&gt;&lt;br /&gt;Let's assume that each byte on our fictitious machine can hold just 2 bits. A bit &lt;b&gt;0&lt;/b&gt; means that the object that that bit represents is free, whereas a bit &lt;b&gt;1&lt;/b&gt; means that the object that that bit represents is taken (or allocated).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-yYo8_YpYaVA/TaraIAuMvuI/AAAAAAAACOg/hPu9o4ng1Hs/s1600/2bit_segment_tree.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="149" src="http://1.bp.blogspot.com/-yYo8_YpYaVA/TaraIAuMvuI/AAAAAAAACOg/hPu9o4ng1Hs/s320/2bit_segment_tree.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The root node gives us information about the complete range (all the 8 bits in the leaves). The nodes at the 1&lt;sup&gt;st&lt;/sup&gt; level give us information about 4 bits each, and each of the leaf nodes give us information about 2 bits each (this is a base case since each byte on our machine can hold just 2 bits).&lt;br /&gt;&lt;br /&gt;Now, finding a free node is just a simple tree traversal down a path that has &lt;b&gt;0&lt;/b&gt; in one of the bit positions. While coming up, we set the &lt;b&gt;0&lt;/b&gt; bits in each level (that we followed down) to a &lt;b&gt;1&lt;/b&gt; bit ONLY if all the children of that &lt;b&gt;0&lt;/b&gt; bit are all &lt;b&gt;1&lt;/b&gt;. We unconditionally set the &lt;b&gt;0&lt;/b&gt; bit in the leaf node to &lt;b&gt;1&lt;/b&gt; and go up the tree.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7Yll8cgTYCo/TarbZOkUpYI/AAAAAAAACOo/5t81nea5LuQ/s1600/2bit_segment_tree_step1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="149" src="http://1.bp.blogspot.com/-7Yll8cgTYCo/TarbZOkUpYI/AAAAAAAACOo/5t81nea5LuQ/s320/2bit_segment_tree_step1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Hence, you can see that we have converted a &lt;b&gt;O(n)&lt;/b&gt; operation to a &lt;b&gt;O(lg n)&lt;/b&gt; operation by increasing the space requirements to 2x the original. Even though the space requirement is 2x the original, the tree based algorithm is much better not just from a time complexity perspective, but is also much more cache friendly. You should expect to have only the first 2 levels entirely in the cache. The remaining levels will come and go as necessary. This is the exact reason why B-Trees are better as an on-disk indexing and retrieval structure.&lt;br /&gt;&lt;br /&gt;On a 32-bit machine, we need just a little over 5 levels of the tree to store 40 million bits (34 million bits can be stored completely in just 5 levels).&lt;br /&gt;&lt;br /&gt;Update: You can achieve the same runtime complexity of &lt;b&gt;O(lg n)&lt;/b&gt; and &lt;b&gt;NO&lt;/b&gt; extra space overhead (compared to the linear scan solution) by using a &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Fenwick_tree"&gt;Fenwick Tree&lt;/a&gt; instead of a &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Segment_tree"&gt;Segment Tree&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7097649180990588392?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7097649180990588392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7097649180990588392&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7097649180990588392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7097649180990588392'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/04/segment-trees-and-bitmaps.html' title='Segment Trees and Bitmaps'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://1.bp.blogspot.com/-yYo8_YpYaVA/TaraIAuMvuI/AAAAAAAACOg/hPu9o4ng1Hs/s72-c/2bit_segment_tree.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8137430966413154155</id><published>2011-04-16T23:14:00.000-07:00</published><updated>2011-04-16T23:18:35.234-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='node.js'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Lazy Logging on Javascript (2)</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;A few weeks ago, I wrote about how to do &lt;a href="http://dhruvbird.blogspot.com/2011/03/lazy-logging-on-javascript.html"&gt;lazy logging in Javascript&lt;/a&gt; by passing functions to the logging functions. These functions would actually do the CPU intensive evaluation (if needed) and return something that would subsequently be printed on the screen. The key thing to notice is that the item(s) to be printed would be computed ONLY if they were actually going to be printed (using the current logging level and the 1&lt;sup&gt;st&lt;/sup&gt; parameter to the logging function).&lt;br /&gt;&lt;br /&gt;I noticed that a lot of code had started looking quite ugly.&lt;br /&gt;&lt;pre&gt;log_it("DEBUG", function() {&lt;br /&gt;  return [ some_var1.toString(), some_var2.toString() ];&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: Any expensive computation that should be performed MUST go inside the toString() method of some_var1 and some_var2.&lt;br /&gt;&lt;br /&gt;I have instead decided to solve it by making a function do what the caller was earlier doing:&lt;br /&gt;&lt;pre&gt;// toString() Promise&lt;br /&gt;function tsp() {&lt;br /&gt;  var args = arguments;&lt;br /&gt;  return {&lt;br /&gt;    toString: function() {&lt;br /&gt;      return Array.prototype.slice.call(args).map(function(v) {&lt;br /&gt;        return v.toString();&lt;br /&gt;      }).join(' ');&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As a result of this modification, all code that does logging now looks like this:&lt;br /&gt;&lt;pre&gt;log_it("DEBUG", tsp(some_var1, some_var2));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The reason why this works is because &lt;tt&gt;log_it()&lt;/tt&gt; is going to call &lt;tt&gt;toString()&lt;/tt&gt; (if required) on each of its arguments before printing them.&lt;br /&gt;&lt;br /&gt;Much neater eh?&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8137430966413154155?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8137430966413154155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8137430966413154155&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8137430966413154155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8137430966413154155'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/04/lazy-logging-on-javascript-2.html' title='Lazy Logging on Javascript (2)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8946828329844841171</id><published>2011-04-07T22:12:00.000-07:00</published><updated>2011-04-07T22:12:02.444-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>A comment on an email signature</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;I've added this signature to my outgoing emails at work&lt;br /&gt;&lt;pre&gt;n="63686180107683"; n.split('').splice(0,7).map(function() {&lt;br /&gt;   t=n%100; n=n/100; return parseInt(t);&lt;br /&gt;}).reverse().map(function(v) {&lt;br /&gt;   return String.fromCharCode(v+6*6);&lt;br /&gt;}).join('');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and was pleasantly surprised to receive a fairly long and detailed analysis of what &lt;a href="https://twitter.com/finalprefix"&gt;Joel Rosario&lt;/a&gt; had to say about it.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: monospace; border-radius: 5px; -moz-border-radius: 5px; background-color: #FFFFFF; padding: 0.8em;"&gt;I wonder how many people have run and tried to understand your signature. I just did. It is devious, and evil.&lt;br /&gt;&lt;br /&gt;* What you really want is a loop. Why splice the large string from 0 to 7? It unnecessarily misleads the reader into thinking you're doing a meaningful string operation.&lt;br /&gt;* Why use map with an anonymous function that takes no parameter? This trips up the unwary reader into reading the function, and wondering what it is being applied to.&lt;br /&gt;* The use of n % 100 and n=n/100 seems to show off javascript's perlish ability to treat strings as numbers.&lt;br /&gt;* The above might also confuse the hapless reader. What happens when you cross a string with a number, hmmm?&lt;br /&gt;* By this time, having nearly given up trying to understand what's going on, the reader is hit with reverse(), surprised by a map(), and presented with a function that expects a parameter that is clearly an integer. The arithmetic operations of +6*6 applied to an already mystical parameter v will be too much for any sane person to bear. By now your reader has already succumbed to his fear and fled for a coffee break.&lt;br /&gt;&lt;br /&gt;Why lay such traps for the unwary? Why risk the equanimity, the morale, indeed the sanity of your esteemed colleagues? Why couldn't you just have signed your mail with console.log('chat.pw')?&lt;br /&gt;&lt;br /&gt;- Joel Rosario.&lt;/div&gt;&lt;br /&gt;Needless to say, some of my emails will randomly have &lt;tt&gt;console.log("chat.pw")&lt;/tt&gt; in them ;)&lt;/div&gt;Emails like this make my day; nay week!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8946828329844841171?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8946828329844841171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8946828329844841171&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8946828329844841171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8946828329844841171'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/04/comment-on-email-signature.html' title='A comment on an email signature'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8126715522386373402</id><published>2011-03-26T03:01:00.000-07:00</published><updated>2011-03-26T03:04:48.792-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='node.js'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Lazy Logging on Javascript</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;One of the things that I like about C/C++ is compile time macros. What they mean is that if I have debug print statements, not only will they be compiled out, but also the expressions passed to the printing statement will not be evaluated.&lt;br /&gt;&lt;br /&gt;I have code like this in javascript:&lt;br /&gt;&lt;pre&gt;var logging = false;&lt;br /&gt;function log() {&lt;br /&gt;  if (logging)&lt;br /&gt;    console.log.apply(console, arguments);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var xml = an_xml_stanza();&lt;br /&gt;log("XML Stanza:", xml.toString());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even if &lt;i&gt;logging&lt;/i&gt; is &lt;i&gt;false&lt;/i&gt;, the expression &lt;i&gt;xml.toString()&lt;/i&gt; will be evaluated, which can be quite costly in a production setup (I'm talking about node.js and not on a browser).&lt;br /&gt;&lt;br /&gt;The way I've solved this is by making sure that my &lt;i&gt;log()&lt;/i&gt; function can accept functions as well. Hence, code such as this becomes possible:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var xml = an_xml_stanza();&lt;br /&gt;log(function() {&lt;br /&gt;  return ["XML Stanza:", xml.toString()];&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;i&gt;log()&lt;/i&gt; function needs to be patched to look like this:&lt;br /&gt;&lt;pre&gt;log = function() {&lt;br /&gt;  if (!logging)&lt;br /&gt;    return;&lt;br /&gt;&lt;br /&gt;  if (arguments.length == 1 &amp;amp;&amp;amp; typeof arguments[0] == "function")&lt;br /&gt;    arguments = arguments[0]();&lt;br /&gt;&lt;br /&gt;  console.log.apply(console, arguments);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This essentially means that you've gotten rid of the runtime cost of calling &lt;i&gt;xml.toString()&lt;/i&gt; when logging is disabled.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8126715522386373402?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8126715522386373402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8126715522386373402&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8126715522386373402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8126715522386373402'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/lazy-logging-on-javascript.html' title='Lazy Logging on Javascript'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5612496144439624474</id><published>2011-03-21T05:00:00.000-07:00</published><updated>2011-03-21T05:00:00.646-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>Node and the Proxy/Decorator Pattern</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;The &lt;a href="http://en.wikipedia.org/wiki/Proxy_pattern"&gt;proxy pattern&lt;/a&gt;.&lt;br /&gt;The &lt;a href="http://en.wikipedia.org/wiki/Decorator_pattern"&gt;decorator pattern&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Proxies &amp;amp; Decorators are patterns that abstract away or enhance certain functionality in an entiry.&lt;br /&gt;&lt;br /&gt;Since they are both almost the same, it isn't very helpful to know the finer differences between the two, but &lt;a href="http://powerdream5.wordpress.com/2007/11/17/the-differences-between-decorator-pattern-and-proxy-pattern/"&gt;here&lt;/a&gt; &lt;a href="http://www.croczilla.com/%7Ealex/reference/Design_Patterns/lowres/disc4.htm"&gt;they&lt;/a&gt; are just for completeness.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Example of a Proxy:&lt;/b&gt;&lt;br /&gt;When you proxy some functionality, you generally delegate the responsibility to someone else. The most practical example is an HTTP Proxy that forwards requests on your behalf to the real web-server.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Example of a Decorator:&lt;/b&gt;&lt;br /&gt;When you decorate some functionality, you generally add more functionality to it. For this example, I shall discuss the code that is presented below. It is basically an &lt;a href="http://nodejs.org/docs/v0.4.0/api/all.html#events.EventEmitter"&gt;EventEmitter&lt;/a&gt; that transparently decompresses the string that is passed to it and re-raises the events that the original EventEmitter raised. You can use it to transparently read a compressed/secure stream given that you have a decorated EventEmitter for it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;File: gzip.js&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;var compress = require('compress');&lt;br /&gt;&lt;br /&gt;function _inflater(stream) {&lt;br /&gt; this._stream = stream;&lt;br /&gt; var self = this;&lt;br /&gt; var gunzip = new compress.Gunzip;&lt;br /&gt; gunzip.init();&lt;br /&gt;&lt;br /&gt; this._stream.on('data', function(d) {&lt;br /&gt;  var _d = gunzip.inflate(d.toString('binary'), 'binary');&lt;br /&gt;  self.emit('data', _d);&lt;br /&gt; })&lt;br /&gt; .on('end', function() {&lt;br /&gt;  self.emit('end');&lt;br /&gt; })&lt;br /&gt; .on('error', function() {&lt;br /&gt;  var args = Array.prototype.splice.call(arguments, 0);&lt;br /&gt;  args.unshift('error');&lt;br /&gt;  self.emit.apply(self, args);&lt;br /&gt; });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;_inflater.prototype = new process.EventEmitter();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;function _deflater(stream) {&lt;br /&gt; this._stream = stream;&lt;br /&gt; var self = this;&lt;br /&gt; var gzip = new compress.Gzip;&lt;br /&gt; gzip.init();&lt;br /&gt;&lt;br /&gt; this._stream.on('data', function(d) {&lt;br /&gt;  var _d = gzip.deflate(d.toString('binary'), 'binary');&lt;br /&gt;  self.emit('data', _d);&lt;br /&gt; })&lt;br /&gt; .on('end', function() {&lt;br /&gt;  self.emit('end');&lt;br /&gt; })&lt;br /&gt; .on('error', function() {&lt;br /&gt;  var args = Array.prototype.splice.call(arguments, 0);&lt;br /&gt;  args.unshift('error');&lt;br /&gt;  self.emit.apply(self, args);&lt;br /&gt; });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;_deflater.prototype = new process.EventEmitter();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;exports.inflater = function(stream) {&lt;br /&gt; return new _inflater(stream);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;exports.deflater = function(stream) {&lt;br /&gt; return new _deflater(stream);&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;File: test.js&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;var gz   = require("./gzip.js");&lt;br /&gt;var http = require("http");&lt;br /&gt;&lt;br /&gt;var req = http.request({&lt;br /&gt; host: "duckduckgo.com", &lt;br /&gt; port: 80, &lt;br /&gt; path: "/"&lt;br /&gt;}, function(response) {&lt;br /&gt; console.log("response headers:", response.headers);&lt;br /&gt;&lt;br /&gt; //&lt;br /&gt; // We check if we need to create a proxy event emitter that&lt;br /&gt; // inflates data on the go.&lt;br /&gt; //&lt;br /&gt; if (response.headers['content-encoding'].search('gzip') != -1) {&lt;br /&gt;  //&lt;br /&gt;  // The response object is now the proxy object. A similar &lt;br /&gt;  // technique is used to support TLS encrypted sockets too.&lt;br /&gt;  // We just wrap up the original EventEmitter instance in &lt;br /&gt;  // a proxy instance that does its magic.&lt;br /&gt;  //&lt;br /&gt;  response = gz.inflater(response);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; response.on('data', function(d) {&lt;br /&gt;  console.log("Got:", d.toString());&lt;br /&gt; });&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;req.setHeader("Accept-Encoding", "gzip");&lt;br /&gt;req.end();&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5612496144439624474?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5612496144439624474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5612496144439624474&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5612496144439624474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5612496144439624474'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/node-and-proxydecorator-pattern.html' title='Node and the Proxy/Decorator Pattern'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-236210341655414862</id><published>2011-03-20T05:47:00.000-07:00</published><updated>2011-03-20T05:57:54.017-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Issue list in code</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;A few weeks ago, there was a debate/argument/discussion about what issue tracking tool should be used for our project. The 2 main contenders were JIRA and Basecamp. I'll try to capture the essence of the discussion.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Things going for JIRA:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Very customizable and configurable&lt;/li&gt;&lt;li&gt;Offers many workflows and views&lt;/li&gt;&lt;li&gt;Lots of plugins&lt;/li&gt;&lt;li&gt;Easy for manager to generate reports (good reporting features)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Things going against JIRA:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Not the most convenient to use for developers&lt;/li&gt;&lt;li&gt;Too much (unnecessary) detail to be filled in for each task at times&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Things going for Basecamp:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Easy to use. Clean interface. More like a todo list&lt;/li&gt;&lt;li&gt;Developers like it&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Things going against Basecamp:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;No easy custom report generation facility&lt;/li&gt;&lt;li&gt;Not reporting/manager friendly&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;We've decided to try both out for a few months and see which one "works out" for us. We were using JIRA before, but the enthusiasm seems to have died out after a few months and the issue tracker is not in sync. with the actual issues/reports/bugs/features being developed.&lt;br /&gt;&lt;br /&gt;I have an entirely different point of view though. For me what has worked best is comments in code that say something like:&lt;br /&gt;&lt;pre&gt;// TODO: Such and such is broken. It needs to be fixed in so and so way.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The reason is that it's too much tedium for me to update things in 2 places (code and issue tracker) when I fix an issue. If I forget, then it's even harder to related the issue to the actual change made. This method offers a way of alleviating that problem.&lt;br /&gt;&lt;br /&gt;I'm also okay with having comments that are verbose just so that they can explain in sufficient detail the reason (or purpose) of the code below.&lt;br /&gt;&lt;br /&gt;However, this has been mostly for projects where I have been the sole developer. I don't know how well it would scale for projects with 2-3 developers, 5-6 developers and &amp;gt; 6 developers.&lt;br /&gt;&lt;br /&gt;I would guess that it's fine for projects with up to 6 developers, but beyond that it would get cumbersome. Also, this way of tracking issues lacks a good reporting method which allows you to track changes across versions. Of course, if you want to adopt this as a workflow, I'm sure some tooling around this general idea can be developed which works by generating diffs between revisions to track which bugs this revision (or set of revisions) fixes.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-236210341655414862?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/236210341655414862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=236210341655414862&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/236210341655414862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/236210341655414862'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/issue-list-in-code.html' title='Issue list in code'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6453168706883483790</id><published>2011-03-19T06:45:00.000-07:00</published><updated>2011-03-19T06:50:08.119-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scaling'/><category scheme='http://www.blogger.com/atom/ns#' term='jabber'/><category scheme='http://www.blogger.com/atom/ns#' term='node.js'/><category scheme='http://www.blogger.com/atom/ns#' term='xmpp'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>node-xmpp-bosh: A BOSH (connection manager) session manager for the XMPP protocol</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;You can find out all about what BOSH is from &lt;a href="http://xmpp.org/extensions/xep-0124.html"&gt;XEP-0124&lt;/a&gt; and &lt;a href="http://xmpp.org/extensions/xep-0206.html"&gt;XEP-0206&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Most respectable XMPP servers out there such as &lt;a href="http://www.igniterealtime.org/projects/openfire/"&gt;Openfire&lt;/a&gt;, &lt;a href="http://www.process-one.net/en/ejabberd/"&gt;Ejabberd&lt;/a&gt;, and &lt;a href="http://www.tigase.org/"&gt;Tigase&lt;/a&gt; support BOSH as part of the server.&lt;br /&gt;&lt;br /&gt;This is where you can download the &lt;a href="http://code.google.com/p/node-xmpp-bosh/"&gt;node-xmpp-bosh&lt;/a&gt; BOSH server from.&lt;br /&gt;&lt;br /&gt;I'll just discuss some of the reasons that it's good to have a BOSH session manager running outside of the XMPP server.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Easier to scale the BOSH server and the XMPP server independently (&lt;i&gt;independently&lt;/i&gt; being the key here). See my &lt;a href="http://dhruvbird.blogspot.com/2011/03/scale-out-with-services-scale-services.html"&gt;previous blog post&lt;/a&gt; for a detailed explanation&lt;/li&gt;&lt;li&gt;You can support users from multiple domains such as gmail.com, chat.facebook.com, jabber.org, pandion.im, etc... using the single BOSH server&lt;/li&gt;&lt;li&gt;Any customizations to the BOSH server are easier made if it is running out of process (restart it independently of your XMPP server - since getting an XMPP server warmed may be more expensive than getting a BOSH server warmed up)&lt;/li&gt;&lt;li&gt;Use protocols other than XMPP at the back, but still retain the same interface as far as clients are concerned. This will still require you to use XMPP for the actual communication (Note: This can be accomplished via transports [for yahoo, msn, aim, etc...] available for any XMPP server [that supports &lt;a href="http://xmpp.org/extensions/xep-0114.html"&gt;XEP-0114&lt;/a&gt;] as a Jabber Component). But, if you please, you can also write a small drop-in replacement server that speaks basic XMPP, but does something entirely bizarre at the back!!&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6453168706883483790?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6453168706883483790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6453168706883483790&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6453168706883483790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6453168706883483790'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/node-xmpp-bosh-bosh-connection-manager.html' title='node-xmpp-bosh: A BOSH (connection manager) session manager for the XMPP protocol'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4250122797098800596</id><published>2011-03-19T06:21:00.000-07:00</published><updated>2011-03-20T00:16:44.045-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scaling'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>Scale out with services - Scale the services separately</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Whatever I am writing about here has been taught to me by this brilliant write up that I quote from the &lt;a href="http://code.nytimes.com/projects/dbslayer"&gt;DBSlayer&lt;/a&gt; project page.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;"The typical LAMP strategy for scaling up data-driven applications is to replicate slave databases to every web server, but this approach can hit scaling limitations for high-volume websites, where processes can overwhelm their given backend DB's connection limits. Quite frankly, we wanted to scale the front-end webservers and backend database servers separately without having to coordinate them. We also needed a way to flexibly reconfigure where our backend databases were located and which applications used them without resorting to tricks of DNS or other such "load-balancing" hacks."&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;What this means is that it is better (from a scaling perspective) to split your application into services rather than have them run as one monolothic application on some very powerful machine.&lt;br /&gt;&lt;br /&gt;Why is it better to do so? That's because each "service" may have different performance requirements and characteristics which can be better addressed in isolation if the service is running as a separate entity.&lt;br /&gt;&lt;br /&gt;For example, consider a typical web-stack which consists of:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Load balancer (nginx)&lt;/li&gt;&lt;li&gt;Web server/script execution engine (apache/php)&lt;/li&gt;&lt;li&gt;Data Store (mysql/pgsql)&lt;/li&gt;&lt;li&gt;Caching service (redis/memcached)&lt;/li&gt;&lt;li&gt;Asynchronous processing infrastructure (RabbitMQ)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;For a medium to high load web site (a few million hits/day =&amp;gt; about 20-30 req/sec), you could make do with just a single instance of nginx, 2 machines running apache/php, 2 machines running MySQL and one machine running RabbitMQ. Even for this deployment, you can see that each of the components have different requirements as far as the machine (and hardware usage) characteristics are concerned. For example, &lt;br /&gt;&lt;ol&gt;&lt;li&gt;nginx is network I/O heavy. You could deploy it on a machine with a modest (1GB) amount of RAM, no hard disk space, and not a very fast CPU&lt;/li&gt;&lt;li&gt;The Apache/php servers on the other hand would need more RAM as well as more CPU, but no disk space&lt;/li&gt;&lt;li&gt;The MySQL nodes would need a lot of RAM, CPU as well as fast disks&lt;/li&gt;&lt;li&gt;The node running RabbitMQ (a message queue) could again comfortably run on a machine with a configuration similar to nginx (assuming that data is stored on MySQL)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Thus, we have sliced our stack into components we can club together not just based on function, but also based on the charasteristics of the hardware that they would best be able to run on. Aside: This reminds me of &lt;a href="http://en.wikipedia.org/wiki/Principal_component_analysis"&gt;Principal Component Analysis&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4250122797098800596?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4250122797098800596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4250122797098800596&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4250122797098800596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4250122797098800596'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/scale-out-with-services-scale-services.html' title='Scale out with services - Scale the services separately'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7536238574744867801</id><published>2011-03-19T05:12:00.000-07:00</published><updated>2011-03-19T05:12:18.132-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous I/O'/><category scheme='http://www.blogger.com/atom/ns#' term='node.js'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Node.js as "the next BIG thing".</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;I generally hate to talk about "the next BIG thing" because there seem to be (relatively) fewer things (as compared to all the things that we encounter) that influence our lives in significant ways these days (simply because we encounter so many things these days).&lt;br /&gt;&lt;br /&gt;However, I feel that &lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt; is going to be quite influential in the years to come.&lt;br /&gt;&lt;br /&gt;So, what is "node.js"? Node (put briefly) is a javascript execution engine. It incorporates asynchronous I/O as part of its design and hence can be considered asynchronous by design. This is where it starts getting fascinating for me.&lt;br /&gt;&lt;br /&gt;Do you remember all those &lt;a href="http://api.jquery.com/category/ajax/"&gt;$.ajax()&lt;/a&gt; requests you made in the browser? Well, what if you could so the same on the server side? What if stitching together a page on the server was as easy as making AJAX calls and filling in empty &amp;lt;div&amp;gt; tags? You needn't stretch your imagination too much because that's exactly what Node lets you do!!&lt;br /&gt;&lt;br /&gt;Even more exciting is that fact that this asynchronous API isn't limited to just HTTP/AJAX calls, but extends to protocols such as SMTP, POP, XMPP, and Database handlers such as PGSQL, MySQL, and Sqlite. This is because the Socket &amp;amp; file system I/O on Node is asynchronous by design. (almost??) All the APIs which do I/O are asynchronous in nature. For some, blocking counterparts are provided solely for convenience.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Toy task: Make 10 HTTP web calls and present the output as the result of each of these 10 calls separated by a new line. Assume that each HTTP call takes 1 sec to produce the result.&lt;br /&gt;&lt;br /&gt;Sample code (in your favourite programming language; using blocking I/O):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;out = "";&lt;br /&gt;for i = 1 to 10:&lt;br /&gt; # We assume that http_call blocks till the fetch completes.&lt;br /&gt; response = http_call(url);&lt;br /&gt; out += (response + "\n");&lt;br /&gt;write_output(out);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You notice that the code takes 10 sec to run - 1 sec for each HTTP web call. We ignore the time taken to write out the results.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sample code (using non-blocking I/O):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;out = "";&lt;br /&gt;res [10] = [ ];&lt;br /&gt;for i = 1 to 10:&lt;br /&gt; # We assume that http_call does NOT block and retiurns immediately, &lt;br /&gt; # processing the task in the background (another thread or using&lt;br /&gt; # non-blocking I/O)&lt;br /&gt; res[i] = http_call(url);&lt;br /&gt;&lt;br /&gt;for i = 1 to 10:&lt;br /&gt; # We assume that the when statement blocks execution till the &lt;br /&gt; # condition is met&lt;br /&gt; when res[i].complete:&lt;br /&gt;  out += (res[i].data + "\n");&lt;br /&gt;&lt;br /&gt;write_output(out);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You notice that the code takes 1 sec to run - 1 sec for each HTTP web call; all of which fire at the same time. We ignore the time taken to write out the results.&lt;br /&gt;&lt;br /&gt;That's a phenominal increase of 10x from the blocking version. Now, why wouldn't anyone use Node for such a use case??&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7536238574744867801?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7536238574744867801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7536238574744867801&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7536238574744867801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7536238574744867801'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/nodejs-as-next-big-thing.html' title='Node.js as &quot;the next BIG thing&quot;.'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7988872477305680672</id><published>2011-03-08T07:26:00.000-08:00</published><updated>2011-03-08T07:30:08.616-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>jQuery stack == evolution</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;So, twitter was kinda cramped, so I'm writing this here. A bunch of context around this. &lt;a href="http://twitter.com/addyosmani"&gt;@addyosmani&lt;/a&gt; posted a tweet/link:&lt;br /&gt;&lt;br /&gt;"Tools For jQuery Application Architecture – The Printable Chart &lt;a href="http://bit.ly/fryIKW"&gt;http://bit.ly/fryIKW&lt;/a&gt;"&lt;br /&gt;&lt;br /&gt;which I re-tweeted and to which &lt;a href="http://twitter.com/#%21/rakesh314"&gt;@rakesh314&lt;/a&gt; replied with this tweet/link:&lt;br /&gt;&lt;br /&gt;"@addyosmani &lt;a href="http://t.co/xWddsTK"&gt;http://t.co/xWddsTK&lt;/a&gt; /cc: @dhruvbird"&lt;br /&gt;&lt;br /&gt;So, there's one thing I strongly believe, which is that the jQuery stack seems to be a product of evolution, and evolution is generally not wrong - but that's a separate debate. Lots of people have tried different things and jQuery has evolved to permit all of them to co-exist in (relative) harmony.&lt;br /&gt;&lt;br /&gt;The LAMP stack was not THE LAMP STACK before people realized these set of tools that were always used together. It was only after the fact that they decided to bundle them. Different Linux distros. are healthy in the sense that they package what they think their users want.&lt;br /&gt;&lt;br /&gt;Similarly, I feel that the jQuery stack is progressing in that general direction and I won't be surprised to see vendors releasing their "version" or "distro" of the jQuery stack.&lt;br /&gt;&lt;br /&gt;I'm not as much "against" dodo as I am "for" jQuery.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7988872477305680672?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7988872477305680672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7988872477305680672&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7988872477305680672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7988872477305680672'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/jquery-stack-evolution.html' title='jQuery stack == evolution'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8560965994488002397</id><published>2011-03-06T10:18:00.000-08:00</published><updated>2011-03-08T08:40:53.139-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='http'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>HTTPS for all browsing</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;I've been thinking about using HTTPS for everything (even sites that don't support HTTPS) by routing all my traffic through a proxy that makes connections to the actual site (possibly on HTTP). This at least secures my traffic from packet sniffing on the local LAN.&lt;br /&gt;&lt;br /&gt;What this means is that if you are at school or office, no colleague can run Wireshark or TCPDump on the the local LAN and capture/sniff your traffic. Also, you can now safely browse the web over http on insecure/potentially sniffed networks such as stray wireless networks without having to worry about your data being compromised! Welcome starbucks internet :-p&lt;br /&gt;&lt;br /&gt;Traditionally, if the browser connects directly to a public proxy, then HTTP traffic still goes unencrypted (to the best of my understanding). Hence, this is what I've thought of doing.&lt;ol&gt;&lt;li&gt;Set up a local proxy on the same machine, which connects to a remote proxy over HTTPS.&lt;/li&gt;&lt;li&gt;Ensure that the remote proxy is running on a safe/trusted network (it could be your home PC if you want to use insecure wireless networks securely)&lt;/li&gt;&lt;li&gt;This remote proxy can now make HTTP connections and the issue of local packet sniffing is resolved.&lt;/li&gt;&lt;li&gt;However, it doesn't prevent remote packet sniffing (on the network where the remote proxy resides), which is why it is important to have the remote proxy sitting on a secure network.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;If you are seriously planning to use this proxy, and you aren't yet using &lt;a href="https://www.eff.org/https-everywhere/"&gt;HTTPS Everywhere&lt;/a&gt;, I would strongly suggest that you start using it since it will reduce the load on the proxy and is more secure (since the encryption is end-to-end and not proxy-to-end).&lt;br /&gt;&lt;br /&gt;Mamma says that there shall be a day when browsers pop up a warning when you view an http based page (as opposed to an https based one).&lt;br /&gt;&lt;br /&gt;Update: You can grab the code for this proxy &lt;a href="http://code.google.com/p/node-tls-proxy/"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8560965994488002397?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8560965994488002397/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8560965994488002397&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8560965994488002397'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8560965994488002397'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/03/https-for-all-browsing.html' title='HTTPS for all browsing'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6498310985506529494</id><published>2011-02-06T02:45:00.000-08:00</published><updated>2011-03-19T06:26:18.629-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='attitude'/><title type='text'>Attitude - and a bit of tea!!</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Yesterday, I had gone to have Chai (tea) and &lt;a href="http://www.cnngo.com/mumbai/eat/yazdani-bakery-old-school-bread-mumbai-417516"&gt;Brun Pav at Yazdani Bakery&lt;/a&gt;. When I was paying the owner (Rashid Zend in the photograph on the previous link) the amount due, he asked one of the waiter to bring a half eaten pav (bread) that a customer seemed to have left behind. He picked it up from the plate, examined it carefully and asked the waiter (in Hindi) &lt;i&gt;"Why did he leave this uneaten?"&lt;/i&gt; I was quite taken by surprise by what he had just asked, but was also very very excited about having eaten at a place where the owner himself took such a keen interest in produce and tried to rectify even a single small error that may have occurred on his (bakery's) part.&lt;br /&gt;&lt;br /&gt;Now, if we (as programmers) take every customer feedback this seriously and act upon it, I am fairly sure that the quality of the final product will go up by leaps and bounds!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6498310985506529494?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6498310985506529494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6498310985506529494&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6498310985506529494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6498310985506529494'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/02/attitude-and-bit-of-tea.html' title='Attitude - and a bit of tea!!'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3490424140884814425</id><published>2011-01-31T03:44:00.000-08:00</published><updated>2011-01-31T03:44:18.911-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Fixing javascript variable leaks</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Today I happended to have a look at &lt;a href="http://traceitfor.me/"&gt;my application's&lt;/a&gt; window object and was shocked to see that &lt;b&gt;'i'&lt;/b&gt; was defined on it! I doggedly started searching my code to see where the leak had perpetrated itself from. After an hour of debugging, which included using &lt;a href="http://closure-compiler.appspot.com/home"&gt;Google's online closure compiler&lt;/a&gt; (Thanks Sandy!!) and &lt;a href="http://www.jslint.com/"&gt;jslint&lt;/a&gt;, I came up with nothing.&lt;br /&gt;&lt;br /&gt;It struck me that it could be some of the js-libraries that I was using that were causing the leak, and not my code. I tried doing a binary-search on the js-includes, but that didn't work since the code wouldn't even run without most of those includes. At this point I really didn't know how to even go about trying to fix it!!&lt;br /&gt;&lt;br /&gt;A few hacky thoughts later, I decided to introduce this code in my application:&lt;br /&gt;&lt;pre&gt;var _iv = setInterval(function() {&lt;br /&gt;  if (typeof window.i != "undefined") {&lt;br /&gt;    clearInterval(_iv);&lt;br /&gt;  }&lt;br /&gt;}, 10);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, this relied on the fact that I had inserted a verbiage of console.log() statements in my code at various points (It actually helped to have them!!)&lt;br /&gt;&lt;br /&gt;This bit of hackery at least helped me determine that the error happened after a certain set of statements (functions) had executed. From here, it was manual work, checking every function and callback (yes) in my code as well as any library that I was using.&lt;br /&gt;&lt;br /&gt;After about 30 minutes, I was able to trace the leak to embed.ly's jQuery library. I also discovered that the fb-complete jQuery library I was using had this leak, but it wasn't the one responsible for the specific instance of the variable 'i' that I was seeing then.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3490424140884814425?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3490424140884814425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3490424140884814425&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3490424140884814425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3490424140884814425'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/01/fixing-javascript-variable-leaks.html' title='Fixing javascript variable leaks'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3849027992596297578</id><published>2011-01-30T12:37:00.000-08:00</published><updated>2011-01-30T12:37:36.540-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>When to use events &amp; when to use callbacks?</title><content type='html'>With the recent spate of javascript programming that I've been subject to, I've found myself asking this question quite often to myself "Should I use callbacks here or should I use events (jQuery trigger &amp; bind)?"&lt;br /&gt;&lt;br /&gt;I've found this initial approximation quite useful till now: "If the operation that will follow depends upon the exact instance of this operation, then wrap the state in a closure and use a callback. Otherwise, use events." The advantage of using events is that:&lt;br /&gt;1. You need not think of all the actions that could result in this event handler being called.&lt;br /&gt;2. You need not tell anyone that this particular event handler needs to be invoked. Everyone just publishes some data (like a tweet) and all those interested (followers) just gobble that data up.&lt;br /&gt;3. You can dynamically attach event handlers. So, if some component in the system suddenly decided to react to certain events, it can just plonk itself into to the listeners queue for that event.&lt;br /&gt;&lt;br /&gt;jQuery goes one step further and allows you to trigger (and bind) events on specific DOM nodes only!! And then it goes a step further and lets you define an event namespace to try and prevent event name conflicts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3849027992596297578?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3849027992596297578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3849027992596297578&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3849027992596297578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3849027992596297578'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/01/when-to-use-events-when-to-use.html' title='When to use events &amp; when to use callbacks?'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7524521623361260094</id><published>2011-01-18T05:45:00.000-08:00</published><updated>2011-01-18T05:47:40.379-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>SQL Query optimization - Part-8 (Indexes Schmindexes)</title><content type='html'>The "Why are my queries slow despite indexes on all columns" Edition.&lt;br /&gt;&lt;br /&gt;Consider the following table:&lt;br /&gt;&lt;pre&gt;CREATE TABLE florist_orders(&lt;br /&gt; order_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, &lt;br /&gt; basket_color INT (1) NOT NULL, -- This can be either 0, 1, 2 or 3&lt;br /&gt; num_roses INT NOT NULL, &lt;br /&gt; num_sunflowers INT NOT NULL, &lt;br /&gt; num_daisies INT NOT NULL, &lt;br /&gt; processed INT(1) NOT NULL DEFAULT 0, &lt;br /&gt; created TIMESTAMP DEFAULT CURRENT_TIMESTAMP&lt;br /&gt;);&lt;/pre&gt;&lt;br /&gt;Let's assume that the packaging for every basket of a different color is done by a diffrent person. Additionally, baskets packaging also differs based on the maximum number of flowers of each type. If there is more than 1 of any type, the packaging differs from when there is at most 1 of each type of flower. Each packager wants to check the orders that she needs to process.&lt;br /&gt;&lt;br /&gt;The following query works fine:&lt;br /&gt;&lt;pre&gt;SELECT * FROM florist_orders&lt;br /&gt;WHERE&lt;br /&gt; basked_color = 2 AND&lt;br /&gt; processed = 0 AND&lt;br /&gt; (num_roses &amp;gt; 1 OR num_sunflowers &amp;gt; 1 OR num_daisies &amp;gt; 1)&lt;br /&gt;ORDER BY created ASC -- Process older orders first to avoid starvation&lt;br /&gt;;&lt;/pre&gt;&lt;br /&gt;However, as the size of the florist_orders tables gorws, you realize that each query is taking too long. Why is this? You have an index on every column and yet queries are slow?&lt;br /&gt;&lt;br /&gt;You decide to create composite indexes. As a first attempt, you create an index on&lt;br /&gt;&lt;pre&gt;(basket_color, processed, num_roses, num_sunflowers, num_daisies)&lt;/pre&gt;but notice that it hasn't really affected the query's execution time. Additionally, EXPLAIN says that no indexes are being used!! What to do now?&lt;br /&gt;&lt;br /&gt;To solve this issue, you would need to modify your table and make it friendly for this specific query. These are the steps you would need to take:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Add a column named num_flowers which is set to MAX(num_roses, num_sunflowers, num_daisies) on every INSERT and UPDATE via TRIGGERS.&lt;/li&gt;&lt;li&gt;Then, create an index on: &lt;pre&gt;(basket_color, processed, num_flowers)&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Modify your query to read: &lt;pre&gt;SELECT * FROM florist_orders&lt;br /&gt;WHERE&lt;br /&gt; basked_color = 2 AND&lt;br /&gt; processed = 0 AND&lt;br /&gt; num_flowers &amp;gt; 1&lt;br /&gt;ORDER BY created ASC -- Process older orders first to avoid starvation&lt;br /&gt;;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;This should pretty much fix any speed issues with the aforementioned query and ensure that all queries remain responsive. This trick will work ONLY if all the flowers are being checked to be greater than the SAME number.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7524521623361260094?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7524521623361260094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7524521623361260094&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7524521623361260094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7524521623361260094'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/01/sql-query-optimization-part-8-indexes.html' title='SQL Query optimization - Part-8 (Indexes Schmindexes)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5765675377704506818</id><published>2011-01-04T11:03:00.000-08:00</published><updated>2011-01-04T11:03:13.821-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>Firefox, I love you!!</title><content type='html'>The day was saved by Firefox's &lt;a href="http://weblogs.mozillazine.org/ben/archives/009749.html"&gt;page cache&lt;/a&gt;. I typed in a reply on a forum thread, clicked "post reply" and then closed the tab, only to realize that I wasn't logged in and that a login dialog had popped up just as I had closed the tab. I right-clicked the tab-list and clicked "Undo Closed Tab" and poof!! the complete tab with all its state was there, awaiting my credentials!!&lt;br /&gt;&lt;br /&gt;Awesome stuff Firefox... I love you!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5765675377704506818?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5765675377704506818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5765675377704506818&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5765675377704506818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5765675377704506818'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/01/firefox-i-love-you.html' title='Firefox, I love you!!'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2522130489922291061</id><published>2011-01-01T02:32:00.000-08:00</published><updated>2011-01-01T02:32:47.886-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='p2p'/><title type='text'>Peer-2-Peer systems at their best!!</title><content type='html'>Recently, the skype network went down. You can read all about it &lt;a href="http://blogs.skype.com/en/2010/12/cio_update.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, the exciting take away from this is (quoting from the link above) &lt;i&gt;"In order to restore Skype functionality, the Skype engineering and operations team introduced hundreds of instances of the Skype software into the P2P network to act as dedicated supernodes, which we nick-named “mega-supernodes,” to provide enough temporary supernode capacity to accelerate the recovery of the peer-to-peer cloud."&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Which is exactly my idea of how a self-healing peer-2-peer system should behave. Introducing more nodes into the mix should ideally help solve scaling and availability issues, which is &lt;i&gt;exactly&lt;/i&gt; what the Skype team did. I would like to congratulate them for that!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2522130489922291061?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2522130489922291061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2522130489922291061&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2522130489922291061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2522130489922291061'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2011/01/peer-2-peer-systems-at-their-best.html' title='Peer-2-Peer systems at their best!!'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5785196665953847741</id><published>2010-12-29T14:47:00.000-08:00</published><updated>2010-12-29T14:50:35.509-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='svd'/><title type='text'>Practical SVD for the curious reader</title><content type='html'>Let's see an example of how SVD can be used perform seemingly daunting tasks.&lt;br /&gt;Consider that we are implementing a rating system for a contest and we want to know player that may have similar skill. Additionally we would like to know the hardness of the tasks in the contest since they are not known apriori.&lt;br /&gt;&lt;br /&gt;We build a a matrix that holds player number on the y-axis and task number on the x-axis. Let us suppose that we roughly know the first 3 problems to be easy, the next 3 to be of medium difficulty and the next 3 to be hard. The built matrix would look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# 0  1  2  3  4  5  6  7  8&lt;br /&gt;[ 1, 1, 1, 0, 0, 0, 0, 0, 0 ], #0&lt;br /&gt;[ 1, 1, 1, 1, 0, 0, 1, 0, 0 ], #1&lt;br /&gt;[ 1, 0, 0, 0, 0, 1, 1, 1, 0 ], #2&lt;br /&gt;[ 1, 1, 1, 1, 1, 1, 0, 0, 0 ], #3&lt;br /&gt;[ 1, 0, 1, 1, 0, 0, 1, 1, 1 ], #4&lt;br /&gt;[ 1, 0, 1, 0, 0, 0, 0, 0, 0 ], #5&lt;br /&gt;[ 1, 0, 0, 1, 1, 1, 0, 0, 0 ], #6&lt;br /&gt;[ 1, 0, 0, 1, 1, 1, 1, 1, 1 ], #7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, we compute the SVD of this matrix and keep only the best 4 feature sets.&lt;br /&gt;&lt;br /&gt;We get [U=]&lt;br /&gt;&lt;pre&gt;[[-0.22828916 -0.50341088  0.06758431 -0.27577721]&lt;br /&gt; [-0.3796649  -0.38290566  0.21189598  0.16725752]&lt;br /&gt; [-0.29514798  0.33343393  0.18247298 -0.80693185]&lt;br /&gt; [-0.4283514  -0.28592541 -0.50767041  0.05970937]&lt;br /&gt; [-0.42379852  0.09624233  0.5690528   0.38393312]&lt;br /&gt; [-0.18451958 -0.30010945  0.12296957 -0.24683212]&lt;br /&gt; [-0.31511921  0.15603403 -0.56486439  0.03629706]&lt;br /&gt; [-0.46924257  0.53231036 -0.03863212  0.17781851]]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We get [S=]&lt;br /&gt;&lt;pre&gt;[[ 4.86583872  0.          0.          0.        ]&lt;br /&gt; [ 0.          2.40125574  0.          0.        ]&lt;br /&gt; [ 0.          0.          2.02979097  0.        ]&lt;br /&gt; [ 0.          0.          0.          1.29857912]]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We get [V=]&lt;br /&gt;&lt;pre&gt;[[-0.55984867 -0.14756061  0.02109021 -0.38852126]&lt;br /&gt; [-0.21297571 -0.48817872 -0.11242051 -0.03758748]&lt;br /&gt; [-0.33799385 -0.57307894  0.22851232  0.06799022]&lt;br /&gt; [-0.41435336  0.0482063  -0.16268579  0.63532176]&lt;br /&gt; [-0.24923004  0.16758689 -0.54742924  0.21086504]&lt;br /&gt; [-0.3098872   0.30644504 -0.45753181 -0.41053094]&lt;br /&gt; [-0.32221659  0.24115755  0.45560831 -0.06000612]&lt;br /&gt; [-0.24418998  0.40061814  0.35121531 -0.18880653]&lt;br /&gt; [-0.18353282  0.26176     0.26131788  0.43258946]]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we need to compute U*S and V*S.&lt;br /&gt;What is the significance of these matrices?&lt;br /&gt;The matrix U*S will allow us to find user that are similar in skill to each other.&lt;br /&gt;The matrix V*S will allow us to determine problems that have been misclassified or are actually similar in difficulty to each other.&lt;br /&gt;&lt;br /&gt;After computing the U*S and V*S matrices, we will take the row-wise sum of squared differences (SOSD)and check the pairs of rows that have the least such difference. These are the rows that are of interest to us.&lt;br /&gt;&lt;br /&gt;We get [SOSD(U*S)=]&lt;br /&gt;&lt;pre&gt;[(0, 1, 1.0430999999999999),&lt;br /&gt; (0, 2, 4.6740000000000004),&lt;br /&gt; (0, 3, 2.7736000000000001),&lt;br /&gt; (0, 4, 4.7484000000000002),&lt;br /&gt; (0, 5, 0.29770000000000002),&lt;br /&gt; (0, 6, 4.4981999999999998),&lt;br /&gt; (0, 7, 7.9534000000000002),&lt;br /&gt; (1, 2, 4.7319000000000004),&lt;br /&gt; (1, 3, 2.2631000000000001),&lt;br /&gt; (1, 4, 1.9745999999999999),&lt;br /&gt; (1, 5, 1.2628999999999999),&lt;br /&gt; (1, 6, 4.2881999999999998),&lt;br /&gt; (1, 7, 5.2785000000000002),&lt;br /&gt; (2, 3, 5.8609),&lt;br /&gt; (2, 4, 3.7233999999999998),&lt;br /&gt; (2, 5, 3.1476999999999999),&lt;br /&gt; (2, 6, 3.6909999999999998),&lt;br /&gt; (2, 7, 2.7824),&lt;br /&gt; (3, 4, 5.7964000000000002),&lt;br /&gt; (3, 5, 3.2058),&lt;br /&gt; (3, 6, 1.4441999999999999),&lt;br /&gt; (3, 7, 4.8299000000000003),&lt;br /&gt; (4, 5, 3.7522000000000002),&lt;br /&gt; (4, 6, 5.8014999999999999),&lt;br /&gt; (4, 7, 2.7383999999999999),&lt;br /&gt; (5, 6, 3.6880000000000002),&lt;br /&gt; (5, 7, 6.3265000000000002),&lt;br /&gt; (6, 7, 2.5535000000000001)]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Analysis of SOSD(U*S): User #0 &amp;amp; user #5 seem to be skilled similarly. Similarly, the following pairs of users also seem to be of similar calibre: (0, 1), (0, 5), (1, 4), (1, 5), (3, 6), (4, 7), (6, 7)&lt;br /&gt;Some of this analysis may seem questionable especially considering that we designated the last 3 problems to be hard. How are user #1 &amp;amp; #4 similar if this is the case?&lt;br /&gt;Let's look at the V*S matrix.&lt;br /&gt;&lt;br /&gt;We get [SOSD(V*S)=]&lt;br /&gt;&lt;pre&gt;[(0, 1, 3.7989000000000002),&lt;br /&gt; (0, 2, 2.7381000000000002),&lt;br /&gt; (0, 3, 2.629),&lt;br /&gt; (0, 4, 4.7946),&lt;br /&gt; (0, 5, 3.6124999999999998),&lt;br /&gt; (0, 6, 3.1680999999999999),&lt;br /&gt; (0, 7, 4.6081000000000003),&lt;br /&gt; (0, 8, 5.6936999999999998),&lt;br /&gt; (1, 2, 0.9093),&lt;br /&gt; (1, 3, 3.3931),&lt;br /&gt; (1, 4, 3.3944000000000001),&lt;br /&gt; (1, 5, 4.5884),&lt;br /&gt; (1, 6, 4.6798999999999999),&lt;br /&gt; (1, 7, 5.5022000000000002),&lt;br /&gt; (1, 8, 4.2117000000000004),&lt;br /&gt; (2, 3, 3.5369999999999999),&lt;br /&gt; (2, 4, 5.8647999999999998),&lt;br /&gt; (2, 5, 6.8044000000000002),&lt;br /&gt; (2, 6, 4.0688000000000004),&lt;br /&gt; (2, 7, 5.8483000000000001),&lt;br /&gt; (2, 8, 4.8121),&lt;br /&gt; (3, 4, 1.6414),&lt;br /&gt; (3, 5, 2.8456000000000001),&lt;br /&gt; (3, 6, 2.806),&lt;br /&gt; (3, 7, 3.6351),&lt;br /&gt; (3, 8, 2.3344),&lt;br /&gt; (4, 5, 0.88270000000000004),&lt;br /&gt; (4, 6, 4.4261999999999997),&lt;br /&gt; (4, 7, 3.9102999999999999),&lt;br /&gt; (4, 8, 2.931),&lt;br /&gt; (5, 6, 3.6707999999999998),&lt;br /&gt; (5, 7, 2.931),&lt;br /&gt; (5, 8, 3.7172000000000001),&lt;br /&gt; (6, 7, 0.36359999999999998),&lt;br /&gt; (6, 8, 1.0225),&lt;br /&gt; (7, 8, 0.88270000000000004)]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following pairs of problems seem to be equally difficult/easy: (3, 4), (1, 2), (4, 5), (6, 8), (7, 8), (6, 7)&lt;br /&gt;You can immediately see a clique of (6, 7, 8) which were all originally designated as hard problems!&lt;br /&gt;Similarly, (3, 4, 5) seem to be related and so do (1, 2). The reason that problems #0 can not be classified by the system could be that since it has been solved by everyone, not much can be said conclusively about it.&lt;br /&gt;You would have noticed that users #1 &amp;amp; #4 have both solved problems #0, #2, #3 &amp;amp; #6. As it turns out, it seems that the features (as deduced by the system) have given a higher weightage to one or more of these problems, which is why users #1 &amp;amp; #4 have been clustered together.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5785196665953847741?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5785196665953847741/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5785196665953847741&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5785196665953847741'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5785196665953847741'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/practical-svd-for-curious-reader.html' title='Practical SVD for the curious reader'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1847990060079586389</id><published>2010-12-28T10:08:00.000-08:00</published><updated>2011-01-02T23:30:26.587-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='svd'/><title type='text'>SVD 101</title><content type='html'>I've been trying to find out more about SVD (Singular Value Decomposition) in the last few days, and here is what I have come up with (links marked with a star[*] MUST be READ). I'll try updating it as and when possible. &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://www.funpecrp.com.br/GMR/year2007/vol4-6/xm0017_full_text.html"&gt;http://www.funpecrp.com.br/GMR/year2007/vol4-6/xm0017_full_text.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cs.um.edu.mt/%7Ecsaw/CSAW04/Proceedings/09.pdf"&gt;http://www.cs.um.edu.mt/~csaw/CSAW04/Proceedings/09.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.math.umn.edu/%7Elerman/math5467/svd.pdf"&gt;http://www.math.umn.edu/~lerman/math5467/svd.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.seobook.com/lsi/tdm.htm"&gt;http://www.seobook.com/lsi/tdm.htm&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://web.mit.edu/snikolov/www/topicweb_verbose.pdf"&gt;http://web.mit.edu/snikolov/www/topicweb_verbose.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cs.carleton.edu/cs_comps/0607/recommend/recommender/svd.html"&gt;http://www.cs.carleton.edu/cs_comps/0607/recommend/recommender/svd.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Latent_semantic_indexing"&gt;http://en.wikipedia.org/wiki/Latent_semantic_indexing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;* &lt;a href="http://web.ics.purdue.edu/%7Epark283/wp-content/uploads/2010/10/Singular_Value_Decomposition_Tutorial.pdf"&gt;http://web.ics.purdue.edu/~park283/wp-content/uploads/2010/10/Singular_Value_Decomposition_Tutorial.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://web.mit.edu/be.400/www/SVD/Singular_Value_Decomposition.htm"&gt;http://web.mit.edu/be.400/www/SVD/Singular_Value_Decomposition.htm&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.miislita.com/information-retrieval-tutorial/singular-value-decomposition-fast-track-tutorial.pdf"&gt;http://www.miislita.com/information-retrieval-tutorial/singular-value-decomposition-fast-track-tutorial.pdf&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-1-understanding.html"&gt;http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-1-understanding.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-2-computing-singular-values.html"&gt;http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-2-computing-singular-values.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-3-full-svd.html"&gt;http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-3-full-svd.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;* &lt;a href="http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-4-lsi-how-to-calculations.html"&gt;http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-4-lsi-how-to-calculations.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;* &lt;a href="http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-5-lsi-keyword-research-co-occurrence.html"&gt;http://www.miislita.com/information-retrieval-tutorial/svd-lsi-tutorial-5-lsi-keyword-research-co-occurrence.html&lt;/a&gt; (explains the significance of the U &amp;amp; V matrices)&lt;/li&gt;&lt;li&gt;* &lt;a href="http://www.dcs.shef.ac.uk/%7Egenevieve/lsa_tutorial.htm"&gt;http://www.dcs.shef.ac.uk/~genevieve/lsa_tutorial.htm&lt;/a&gt; (Thanks to &lt;a href="http://rationalpie.wordpress.com/about/"&gt;Shashank Singh&lt;/a&gt; for this link)&lt;/li&gt;&lt;/ol&gt;SVD can be used to:  &lt;br /&gt;&lt;ol&gt;&lt;li&gt;Determine Document Similarity&lt;/li&gt;&lt;li&gt;Determine User similarity in a competition (or in a rating-based system such as movie recommendations)&lt;/li&gt;&lt;li&gt;Perform Image compression&lt;/li&gt;&lt;li&gt;Perform Principal Component Analysis&lt;/li&gt;&lt;li&gt;and much more...&lt;/li&gt;&lt;/ol&gt;You can use the following tools to compute the SVD of a matrix:  &lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://www.scilab.org/"&gt;SciLab&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.scipy.org/"&gt;NumPy (SciPy)&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1847990060079586389?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1847990060079586389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1847990060079586389&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1847990060079586389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1847990060079586389'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/svd-101.html' title='SVD 101'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1146255023311258059</id><published>2010-12-25T12:53:00.000-08:00</published><updated>2011-01-02T00:10:05.616-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Suggest more recipients (in a chat conference)</title><content type='html'>If you don't already know, gmail has had a feature called &lt;a href="http://gmailblog.blogspot.com/2009/04/new-in-labs-suggest-more-recipients.html"&gt;Suggest more recipients&lt;/a&gt; for a while now.&lt;br /&gt;&lt;br /&gt;From the link above, &lt;i&gt;"Gmail will suggest people you might want to include based on the groups of people you email most often. So if you always email your mom, dad, and sister together, and you start composing a message to your mom and dad, Gmail will suggest adding your sister. Enter at least two recipients and any suggestions will show up like this."&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I am planning on extending this even for the chat conversation start page in the chat client that I'm working on. So, if you start entering JIDs of people you want to chat with, the client should automatically suggest more participants.&lt;br /&gt;&lt;br /&gt;Thanks to &lt;a href="http://sandesh247.com/journal/"&gt;Sandy&lt;/a&gt; for the providing the link to gmail blog!!&lt;br /&gt;&lt;br /&gt;Update (02/01/2011): This feature is now implemented and part of the dev. versions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1146255023311258059?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1146255023311258059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1146255023311258059&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1146255023311258059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1146255023311258059'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/suggest-more-recipients-in-chat.html' title='Suggest more recipients (in a chat conference)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2521032625876815028</id><published>2010-12-23T21:40:00.000-08:00</published><updated>2010-12-23T21:40:46.297-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Debugging PHP's $_REQUEST</title><content type='html'>The &lt;a href="http://www.php.net/manual/en/reserved.variables.request.php"&gt;$_REQUEST&lt;/a&gt; page in the PHP manual, says that "$_REQUEST is an associative array that by default contains the contents of $_GET, $_POST and $_COOKIE. The variables in $_REQUEST are provided to the script via the GET, POST, and COOKIE input mechanisms and therefore could be modified by the remote user and cannot be trusted. The presence and order of variables listed in this array is defined according to the PHP variables_order configuration directive."&lt;br /&gt;&lt;br /&gt;I was under the impression that $_REQUEST would only contain data from the $_GET and the $_POST variables. In my application, I was setting cookies that had the same key as one of the GET parameters that I was expecting. The script was hence getting data set by the cookie and not the GET parameter!! I went half mad trying to debug this and was suspecting some caching issue. Things got queerer when changing some of the GET parameters worked, but others refused to budge from their original value!! You can imagine the perplexing situation I was in. Thankfully, though it dawned on me to check the PHP manual to see what the $_REQUEST was supposed to contain and I was able to fix my code to use $_GET.&lt;br /&gt;&lt;br /&gt;Since then, I have come across &lt;a href="http://devlog.info/2010/02/04/why-php-request-array-is-dangerous/"&gt;this very useful page&lt;/a&gt; which mentions the exact problem I had and also says why one should avoid using $_REQUEST.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2521032625876815028?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2521032625876815028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2521032625876815028&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2521032625876815028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2521032625876815028'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/debugging-phps-request.html' title='Debugging PHP&apos;s $_REQUEST'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3427019738821491278</id><published>2010-12-23T13:15:00.000-08:00</published><updated>2010-12-23T13:15:27.599-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Shortest Slowest path</title><content type='html'>A few weeks ago, I had was presented with a question: "You are given an &lt;i&gt;n&lt;/i&gt; X &lt;i&gt;n&lt;/i&gt; matrix. Each cell has a non-negative integer that denotes the cost of landing on that cell. Start from the top-left corner, compute the cost of the least-cost-path to reach the bottom-right corner of the matrix such that the sum of all costs incurred during the traversal is the least. Additionally, you are allowed to move only down or right."&lt;br /&gt;&lt;br /&gt;Of course, this is just a slight variation of Dijkstra's Shortest Path problem, but I was surprised at the first solution that came to mind. I was trying really hard to not use Dijkstra's shortest path algorithm to solve this problem. I landed up linearizing all the n&lt;sup&gt;2&lt;/sup&gt; cells into n&lt;sup&gt;2&lt;/sup&gt; vertices in a graph and applied the Floyd-Warshall algorithm on the resulting matrix (of having side n&lt;sup&gt;2&lt;/sup&gt;). Hence, the total complexity shot up to a whooping O(n&lt;sup&gt;6&lt;/sup&gt;) (n being the side of the original matrix). If I had used a standard BFS or Dijkstra's algorithm, I would have been able to solve it with a runtime complexity of O(n&lt;sup&gt;2&lt;/sup&gt; log(n)).&lt;br /&gt;&lt;br /&gt;However, this was a very interesting exercise in functional programming since I was able to piece together a solution by chaining solutions to existing problems. The composition looks something like this:&lt;br /&gt;Solution(matrix) = FloydWarshall(Linearize(matrix))&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3427019738821491278?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3427019738821491278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3427019738821491278&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3427019738821491278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3427019738821491278'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/shortest-slowest-path.html' title='&lt;strike&gt;Shortest&lt;/strike&gt; Slowest path'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4205412974748584894</id><published>2010-12-23T12:44:00.000-08:00</published><updated>2010-12-23T12:44:29.239-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dyslexia'/><title type='text'>Dyslexia Friendly</title><content type='html'>I made my home page Dyslexia Friendly today. Click &lt;a href="http://dhruvbird.com/dyslexia.html"&gt;here&lt;/a&gt; to find out more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4205412974748584894?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4205412974748584894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4205412974748584894&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4205412974748584894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4205412974748584894'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/dyslexia-friendly.html' title='Dyslexia Friendly'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7459460969893397195</id><published>2010-12-17T02:31:00.000-08:00</published><updated>2010-12-24T22:42:09.125-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Help on selected text</title><content type='html'>Today, I've added this new feature that I call &lt;i&gt;"Help on selected text."&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;If you select any word or phrase on my blog, a small window will pop-up that will ask you to if you want more information on the selected phrase. If you click on the &lt;i&gt;More Info&lt;/i&gt; link, you will be shown some more information if it is available. Pictures if available will also be shown. I am using Duck Duck Go's &lt;a href="http://duckduckgo.com/api.html"&gt;API&lt;/a&gt; to fetch this information. If nothing is found, an appropriate message will be displayed. If you select a block of text that is greater than 60 characters in length, it will be ignored and no prompt for more information will be shown.&lt;br /&gt;&lt;br /&gt;Let me know if you find it useful or want to integrate it on your site!!&lt;br /&gt;&lt;br /&gt;Update: &lt;a href="http://sandesh247.com/journal/"&gt;Sandy&lt;/a&gt; suggested making the opacity of the box depend upon the distance of the mouse pointer from the info-box. Implemented this.&lt;br /&gt;&lt;br /&gt;Update: A separate page documenting this widget, providing samples and examples has been put up &lt;a href="http://dhruvbird.com/ddb/zc.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Update: My homepage: &lt;a href="http://dhruvbird.com/"&gt;http://dhruvbird.com/&lt;/a&gt; is now enabled with the zero-click plugin!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7459460969893397195?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7459460969893397195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7459460969893397195&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7459460969893397195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7459460969893397195'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/help-on-selected-text.html' title='Help on selected text'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7097394040744337462</id><published>2010-12-16T22:22:00.001-08:00</published><updated>2010-12-16T22:22:54.809-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>TDDB: What I would do differently if I were to re-implement it</title><content type='html'>Working on TDDB was a lot of fun (for all of us) and we learnt a lot!! Probably much more than what we initially thought. From not knowing anything about databases to implementing our own meant that we really hit the jackpot in terms on learning stuff and being hand-on. Watching theory and practice collide head on is something we experienced then!!&lt;br /&gt;&lt;br /&gt;If I were to work on TDDB from scratch all over again, these are the things I would do differently:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use HTTP for the transport layer. We implemented our own protocol for the transport. We got to learn a lot while doing so. However, since there's such a huge eco-system already built around HTTP, it would make sense to use it. Another advantage of using HTTP is that making clients in various languages (and even a client for the browser) is much less of an issue.&lt;/li&gt;&lt;li&gt;Use sqlite/MySQL/postgreSQL/BerkeleyDB or some other underlying DB for the actual storage. We implemented our own disk format and transaction subsystem instead of using something already available. We learnt a HELLUVA LOT while doing so. However, if I were to do it again, I'd go with something I can just build on. I would need to implement the query parser, the optimizer, the execution engine and the lock manager though since for a distributed DB, these things would define the system.&lt;/li&gt;&lt;li&gt;In general, use as many existing systems and build on them rather than implementing your own. While the latter approach means that one can learn much more, it also means that some of the "interesting" things won't get as much time and thought as they should.&lt;/li&gt;&lt;li&gt;Research the common use-cases more thoroughly. I think we just scratched the surface of use-cases when it came to distributed query processing and I think that there is a much wider audience for such a system. Exploring this in more detail is something I hope to do in the future.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7097394040744337462?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7097394040744337462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7097394040744337462&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7097394040744337462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7097394040744337462'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/tddb-what-i-would-do-differently-if-i.html' title='TDDB: What I would do differently if I were to re-implement it'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7647428674730426904</id><published>2010-12-16T22:21:00.000-08:00</published><updated>2010-12-16T22:21:35.242-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Interesting problem (string based)</title><content type='html'>Find the longest prefix of a string that is a palindrome.&lt;br /&gt;O(n&lt;sup&gt;2&lt;/sup&gt;) is easy, but you need come up with something that's O(n).&lt;br /&gt;&lt;br /&gt;Hint: KMP&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7647428674730426904?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7647428674730426904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7647428674730426904&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7647428674730426904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7647428674730426904'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/interesting-problem-string-based.html' title='Interesting problem (string based)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5758834885076606304</id><published>2010-12-16T12:16:00.000-08:00</published><updated>2010-12-16T12:16:36.258-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The psychological effects of a bad API</title><content type='html'>It makes everyone sick and your developers lose their creativity and grapple with the API rather than solving interesting problems and innovating.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5758834885076606304?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5758834885076606304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5758834885076606304&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5758834885076606304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5758834885076606304'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/psychological-effects-of-bad-api.html' title='The psychological effects of a bad API'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2502472911151492834</id><published>2010-12-16T12:14:00.000-08:00</published><updated>2010-12-16T12:14:54.702-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Anyone can code, but only the fearless can be great</title><content type='html'>I personally think that every programming task needs to be approached from the perspective of being completely bug-free. When you write code, think that it is going to be deployed on a rocket or a space-craft and that the lives of a few hundred depend on the robustness of the code. Of course, you can cut yourself some slack by making practical simplifying assumptions about the specific task at hand, but I'd rather programmers started with the strictest requirements and then loosened them rather than the other way around (which seems to be happening more often these days).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2502472911151492834?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2502472911151492834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2502472911151492834&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2502472911151492834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2502472911151492834'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/anyone-can-code-but-only-fearless-can.html' title='Anyone can code, but only the fearless can be great'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4074849964438150310</id><published>2010-12-16T12:13:00.000-08:00</published><updated>2010-12-16T12:13:15.589-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Library v/s Framework</title><content type='html'>I sort of dislike frameworks (except for the ones that actually make your life easier). Libraries on the other hand are really great things!! Frameworks say: "This is all you are allowed to do and these are the ways you could do it. Additionally, I allow you the freedom of doing this insignificant operation in any way you see fit." On the other hand, libraries say: "If this is the input you feed me, this is the output you shall get. It's up to you to feed me the input and then you are free to do whatever you wish with the output I give you."&lt;br /&gt;&lt;br /&gt;This change of mindset makes a world of difference while writing applications that do different things or just things differently from what the "framework" architect thought to be "the" way of doing things.&lt;br /&gt;&lt;br /&gt;Django is a GOOD framework. It goes a long way to obviate all the mundane tasks, but at the same time provides a rich library-like interface to many convenience functions. IMHO, the Library way is the functional way where you can compose a complex operation from many small operations chained or composed together. A framework on the other hand reminds me of OO and something like the a template method pattern. Now, I really like the template method pattern when it is used minimally and with care. However, using this pattern for everything you do is just brainless. In the same way, frameworks aren't the solution to all design related problems.&lt;br /&gt;&lt;br /&gt;jQuery is an awesome library. It's been true to its slogan of "Write less, do more". It's literally packed with a lot of convenience functions that lets you create really nice looking and functional UIs and web-applications without breaking a sweat. I think it gels in very well with the javascript and DOM eco-system. It doesn't try to be different and OOish (like Dojo) which is where I think it scores.&lt;br /&gt;&lt;br /&gt;Frameworks cramp creativity whereas Libraries liberate the artist within the programmer. I hear a lot of people say "tell, don't ask", but I would say "ask and you shall be told."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4074849964438150310?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4074849964438150310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4074849964438150310&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4074849964438150310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4074849964438150310'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/12/library-vs-framework.html' title='Library v/s Framework'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4317519638456021384</id><published>2010-11-16T04:16:00.000-08:00</published><updated>2010-12-25T12:57:24.810-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-7</title><content type='html'>(The you need referential integrity for more than referential integrity edition)&lt;br /&gt;&lt;br /&gt;Why do we tag our blog posts with well, tags, so that people can glean meaningful information from these terse blocks of text. A post tagged with &lt;i&gt;vacation&lt;/i&gt; or &lt;i&gt;tutorial&lt;/i&gt; says so much about the blog post. Similarly, hinting the DB that a certain column has a similar looking entry in some other table means a lot to the execution planner. Yes, I'm talking about Foreign Key references.&lt;br /&gt;&lt;br /&gt;If it finds such a hint, it can infer that:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Every value in this column has some unique value in the column that it refers to.&lt;/li&gt;&lt;li&gt;Since it is pointing to a UNIQUE column, joining a column from this table will match exactly 1 row from the table to which it refers to. Hence, while NATURAL JOINing the 2 tables, the execution engine can stop when it finds one match.&lt;/li&gt;&lt;li&gt;A Foreign Key constraint forces you to make the referenced column UNIQUE. This means that a WHERE clause on that column will never match more than one row and the optimizer can take advantage of that. If such a query occurs as a subquery, then the optimizer can evaluate the sub-query first and replace the sub-query with a single constant which is the result of the inner query's execution.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;All these optimizations can significantly reduce your query's execution time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4317519638456021384?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4317519638456021384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4317519638456021384&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4317519638456021384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4317519638456021384'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-7.html' title='SQL Query optimization - Part-7'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7625649525231355872</id><published>2010-11-15T22:23:00.000-08:00</published><updated>2010-12-25T12:59:13.980-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-6 (How NATURAL JOINs work)</title><content type='html'>(The devil lies in the details edition)&lt;br /&gt;&lt;br /&gt;To show how JOINing in databases works, we'll take up a very simple example and make some simplifying assumptions (which are true) about databases &amp;amp; indexes so that we can reason clearly about certain concepts and yet still understand the key insights required for comprehending how NATURAL JOINs work.&lt;br /&gt;&lt;br /&gt;Let's assume that we have 2 tables X &amp;amp; Y that we want to join on a column named 'c'.&lt;br /&gt;&lt;tt&gt;SELECT * FROM X JOIN Y ON X.c=Y.c WHERE &amp;lt;condition&amp;gt;&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;For the sake of simplicity, we shall draw parallels between the tables X &amp;amp; Y and 2 arrays AX &amp;amp; AY that are in-memory. Also let the size of the table X &amp;amp; Y be sA &amp;amp; sY. We shall consider a few cases and try to analyze how JOINing would work in these scenarios:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;No index on 'c' in either table:&lt;/b&gt; The DB is forced to perform a O(sX.sY) operation by running code that looks like a nested loop. Hence, this type of join is called a &lt;a href="http://en.wikipedia.org/wiki/Nested_loop_join"&gt;nested loop join&lt;/a&gt;. This is the most expensive type of join and must be avoided if the tables X &amp;amp; Y are very large.&lt;/li&gt;&lt;li&gt;&lt;b&gt;B-Tree index on 'c' only in table X:&lt;/b&gt; (This case is symmetrical to a B-Tree index being present on 'c' only in table Y -- but not really). This case is akin to one of the arrays being sorted and the other being unsorted. Here, the execution engine can loop over the table that doesn't have an index (the unsorted array in our analogy) and for each element, lookup the corresponding value in the table with the index (sorted array lookup using binary search). The cost for joining is O(sX.log sY) or O(sY.log sX). This is why which table has the index is important and the 2 cases aren't &lt;i&gt;exactly&lt;/i&gt; symmetrical. It is advisable to have an index on the larger table -- to keep the linear component in the complexity as low as possible.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Hash index on 'c' only in table X:&lt;/b&gt; (This case is symmetrical to a Hash index being present on 'c' only in table Y -- but not really). Again, this case is similar to the one above and depending up on whether table X or table Y has an index, the JOIN complexity will be either O(sY) or O(sX) respectively. Again, it is advisable to have an index on the larger table.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Hash index on 'c' in both tables:&lt;/b&gt; This case isn't much different from the previous case except for the fact that the execution planner can decide whether to iterate over table X or table Y depending up on which table has a fewer number of rows.&lt;/li&gt;&lt;li&gt;&lt;b&gt;B-Tree index on 'c' in both tables:&lt;/b&gt; This is a very interesting and common case and is similar to the problem of trying to find the intersection of 2 sorted arrays AX &amp;amp; AY. It should be clear that the complexity of doing so is O(sX + sY). This bound is NOT tight and may be as low as theta(min(sX, sY)). Hence, it should be clear that some of the join strategies above can (in theory) be faster than this one. However, the execution planner may not always choose to use both indexes if it has heuristic data that suggests that iterating over one of the tables would be much cheaper (say because it contains only 2 rows). This join is most commonly referred to as the &lt;a href="http://en.wikipedia.org/wiki/Sort-merge_join"&gt;Merge Join&lt;/a&gt; because it looks like you are merging 2 arrays.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7625649525231355872?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7625649525231355872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7625649525231355872&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7625649525231355872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7625649525231355872'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-6-how.html' title='SQL Query optimization - Part-6 (How NATURAL JOINs work)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2033451746157072871</id><published>2010-11-15T13:49:00.000-08:00</published><updated>2010-11-15T13:54:09.519-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-5 (Motivation)</title><content type='html'>(The better late than never edition)&lt;br /&gt;&lt;br /&gt;I'm sure many of you have worked on web-apps (you know stuff like wikipedia, gmail, facebook, codechef, reddit, digg, duckduckgo or even your personal web-site which has some dymnamic/interactive content). I'm sure that out of these many, many of you have worked on sites that get traffic to the tune of a few hundred visitors per second. I'm sure you know how painstaking it is to run such a site and keep it up and running and responsive for all its users. Lots of things like the web-server, DB, file system, OS, etc... need to be tweaked and tuned and retro-fitted to suit the needs of the things around it.&lt;br /&gt;&lt;br /&gt;The one place (in my experience) that can easily introduce bottlenecks is the DB (database). That's because it is something that is a real work-horse and is programmed by &lt;i&gt;you&lt;/i&gt; (the application writer), who may not know what food it likes. It's very easy to write a query that runs 10x slower than what it really should be running and not notice this for a very long time.&lt;br /&gt;&lt;br /&gt;Imagine the time you get slashdotted and need to support 5000 requests instead of the customary 50 requests/second. Don't you wish your DB doesn't just die due to the load?? Well, if you are one such person who is pedantic about the performance of your database, you are thinking right!&lt;br /&gt;&lt;br /&gt;You will mostly learn how to optimize &lt;i&gt;your&lt;/i&gt; DB through experimentation, but since others have been burned before, you might as well stay and take advantage of their experiences.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2033451746157072871?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2033451746157072871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2033451746157072871&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2033451746157072871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2033451746157072871'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-5.html' title='SQL Query optimization - Part-5 (Motivation)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1322745542070914238</id><published>2010-11-15T13:17:00.000-08:00</published><updated>2010-12-25T13:00:51.673-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-4 (Myths)</title><content type='html'>&lt;ol&gt;&lt;li&gt;&lt;i&gt;If I have an index on every column of my table, all my queries will run fast.&lt;/i&gt; This is the equivalent of saying that adding all the good ingredients in a bowl and mixing away to glory will result in the world's best dish. Doesn't happen. The sooner you snap out of it, the better it is for you.&lt;/li&gt;&lt;li&gt;&lt;i&gt;Joins are expensive. Everything should be completely denormalized.&lt;/i&gt; This isn't an SQL optimization myth, just a SQL myth in general. Loosely speaking though, if your joins are all natural joins and the join columns are mostly unique and all your filter conditions are connected with a conjunction (AND clause) and you don't go crazy with GROUP BY, ORDER BY and HAVING, you will have made yourself a pretty fast query. The key to remember is to try and (anyhow) maximize the ratio of &lt;i&gt;Rows returned&lt;/i&gt; to &lt;i&gt;Rows visited&lt;/i&gt; by the DB engine to produce the result set.&lt;/li&gt;&lt;li&gt;&lt;i&gt;If I have an index on a column that I want to ORDER BY then that index will be used and the DB won't use a temporary table to sort the result set.&lt;/i&gt; Not really. If the JOIN is not on the first column that you want to ORDER BY, then the DB will most likely use a temporary table and sort. Having said that, even if you join on a column that you will later ORDER BY, the execution planner might decide to not order intermediate results by that column because it thinks that that would be the most efficient query execution plan. This can happen when exactly one table in the JOIN doesn't have an index on the join column and that will be the column that the DB will decide to iterate over.&lt;/li&gt;&lt;li&gt;&lt;i&gt;Streaming result sets.&lt;/i&gt; I really don't know how many DBs actually do this, but it is a killer feature to have. Of course, you need to craft your queries very smartly to take advantage of this.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;I you've gotten this far and don't feel lost, you are into this shit big time, and it's time to read some stuff by the big boys (&lt;a href="http://redbook.cs.berkeley.edu/redbook3/lec7.html"&gt;here&lt;/a&gt; and &lt;a href="http://computerauthor.blogspot.com/2010/01/sql-query-optimization-faq-part-1-sql.html"&gt;here&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1322745542070914238?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1322745542070914238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1322745542070914238&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1322745542070914238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1322745542070914238'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-4-myths.html' title='SQL Query optimization - Part-4 (Myths)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8542953551648501912</id><published>2010-11-14T12:26:00.000-08:00</published><updated>2011-02-06T03:00:36.613-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-3</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Now for some trivial stuff:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Make sure that every column you query on has an (appropriate) INDEX. Creating a full-text index on something that you will do exact matching won't help. Neither will creating a HASH index on a column on which you intend doing &amp;lt; and &amp;gt; comparisons&lt;/li&gt;&lt;li&gt;Avoid complex mathematical expressions in your WHERE clauses if you want indexes to be used. Databases are pretty dumb that way and practice and theory is still some way off in this area (which is why I want to bridge this gap!!). Don't do stuff like: WHERE 2*col1+3 &amp;lt; col2. This will probably NOT be optimized by the execution engine. Of course, if there is no other way out, then you must do it. I suggest you create another column (col3) which always gets the value: 2*col1+3, and query using col3 whenever you need this expression&lt;/li&gt;&lt;li&gt;(This is an ugly condition and probably won't be optimized). Suppose you have 2 columns (col1 &amp;amp; col2) on the same table and want to select all rows WHERE col1 &amp;lt; col2. This will most likely result in a full table scan even if all columns have an index on them. Try to create a new column to flag this condition and select on that column if you need speed. &lt;b&gt;Update:&lt;/b&gt; I can definitely see a way in which databases could use an index on (col1, col2) or (col2, col1) (if one exists), but you'll have to check with your particular database if that is the case (by doing an EXPLAIN or its equivalent).&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8542953551648501912?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8542953551648501912/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8542953551648501912&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8542953551648501912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8542953551648501912'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-3.html' title='SQL Query optimization - Part-3'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4182421306396983846</id><published>2010-11-14T12:12:00.001-08:00</published><updated>2010-11-14T12:12:52.186-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><title type='text'>I just noticed</title><content type='html'>This has been my most active year of blogging. 36 posts including this one till now. (see the bar on the right). Which probably means that I haven't been working as much this year as compared to the last few years.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4182421306396983846?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4182421306396983846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4182421306396983846&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4182421306396983846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4182421306396983846'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/i-just-noticed.html' title='I just noticed'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9114388626453226672</id><published>2010-11-14T12:06:00.000-08:00</published><updated>2010-11-15T06:26:27.155-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-2</title><content type='html'>Today we'll talk about whether to use JOINs or nested queries. TDDB (my db engine :D) doesn't support nested queries so I don't talk about them much, but that's just selfish of me (actually even MySQL didn't support them for a long time), but I've grown out of that, so here it goes. Nested queries can actually speed up your query by a HUGE amount.&lt;br /&gt;&lt;br /&gt;Consider you have say 6 tables which you are joining and only 2 of them are actually involved in any logical reasoning like generating unique rows. The rest of the tables are vestiges of an over-enthusiastic DB architect or a graduate fresh out of college who thinks that everything should be in BCNF (been there; done that).&lt;br /&gt;&lt;br /&gt;Now the other 4 tables are being used just to provide data for the purpose of projection and really have no reason being part of the JOIN. Hence, we &lt;i&gt;refactor&lt;/i&gt; the query and create a temporary relation from the 2 tables that actually mean something. Most real queries are interested in the top 10 or 20 results only (ordered by some criteria) so we do whatever we want to on the join of these 2 tables.&lt;br /&gt;&lt;br /&gt;This temporary relation is then joined with the other 4 relations and viola! This recipe serves 100 queries for the cost of 1 ;)&lt;br /&gt;&lt;br /&gt;This is possible since all the extraneous tables have been avoided in the join that involved ordering, grouping and potentially many other expensive operations.&lt;br /&gt;&lt;br /&gt;This can be optimized even more by using a materialized view for the inner query. However, that will increase the insertion time for those tables, and should be used with care. Besides, I don't think all DB engines support materialized views.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9114388626453226672?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9114388626453226672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9114388626453226672&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9114388626453226672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9114388626453226672'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-2.html' title='SQL Query optimization - Part-2'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8684936716620042137</id><published>2010-11-14T11:48:00.000-08:00</published><updated>2010-11-15T06:26:50.550-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqloptimization'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>SQL Query optimization - Part-1</title><content type='html'>I'm planning to write some random posts on query optimization. Here's the first one. I'll get straight to the point 'cause I don't like beating about the bush (well not usually), so here it goes.&lt;br /&gt;&lt;br /&gt;This post will mainly talk about optimizing queries that use the &lt;b&gt;LIKE&lt;/b&gt; keyword.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Try to avoid it if you can. Databases are generally bad at optimizing LIKE queries no matter how good they may be. More often than not, a LIKE query will result in a full-table-scan&lt;/li&gt;&lt;li&gt;If your LIKE query looks something like this: &lt;i&gt;colname LIKE 'something%'&lt;/i&gt; then there is some good news for you. You can convert this to &lt;i&gt;colname ≤ 'something' AND colname ≥ 'somethingz'&lt;/i&gt; (I'm assuming that colname contains only ASCII text. If it contains unicode, use the last UNICODE character in place of 'z')&lt;/li&gt;&lt;li&gt;No matter how hard you pray, &lt;i&gt;colname LIKE '%something'&lt;/i&gt; will NOT be optimized by your favoirite DB engine. You will HAVE to reverse the contents of &lt;i&gt;colname&lt;/i&gt;, store them in &lt;i&gt;colname_reversed&lt;/i&gt; and query on that as &lt;i&gt;colname_reversed LIKE 'gnihtemos%'&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;colname LIKE '%something%'&lt;/i&gt; is just asking for trouble&lt;/li&gt;&lt;li&gt;And so is stuff like &lt;i&gt;LOWER(colname) = 'something'&lt;/i&gt;. This is because anything that transforms the column data can NOT use the index since the index indexes the ORIGINAL value, not the transformed value&lt;/li&gt;&lt;li&gt;If you are using MySQL, the LIKE operator is case-insensitive so you can always replace all statements that look like &lt;i&gt;LOWER(colname) = 'something'&lt;/i&gt; with &lt;i&gt;colname LIKE 'something'&lt;/i&gt; and it will always work. Silly but useful&lt;/li&gt;&lt;li&gt;To get a more detailed understanding of how indexes work, read up on &lt;a href="http://en.wikipedia.org/wiki/B-tree"&gt;B-Trees&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8684936716620042137?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8684936716620042137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8684936716620042137&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8684936716620042137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8684936716620042137'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/sql-query-optimization-part-1.html' title='SQL Query optimization - Part-1'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3953319743696789621</id><published>2010-11-14T11:30:00.000-08:00</published><updated>2010-11-14T11:32:16.012-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>Undecidable</title><content type='html'>Many times have I come across concepts that mimic the concept of an Undecidable problem. So, for all you people who don't know what an Undecidable (or partially decidable -- please pardon the terminoloty) problem is, you can check out the wiki page &lt;a href="http://en.wikipedia.org/wiki/Undecidable_problem"&gt;here&lt;/a&gt; or read the layman's definition below:&lt;br /&gt;&lt;br /&gt;If a solution to the problem has a "yes" answer then it WILL be found, but if it has a "NO" answer, you'll never know so because you'll keep running the algorithm in the hope of finding the "YES" answer.&lt;br /&gt;&lt;br /&gt;Now, it turns out that in the last few months I've come across many instances of such "undecidable" problems. I'll list out a few here:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;If you have good marks on a test, you are a good student, but if you don't then it doesn't mean that you are a bad student&lt;/li&gt;&lt;li&gt;If you have lots of fans on your facebook page, then you are famous but if you don't, it doesn't necessarily mean that you aren't&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So, the next time I have an argument/discussion with anyone and I mention "this is undecidable", I'll try to give them a link to this post as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3953319743696789621?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3953319743696789621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3953319743696789621&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3953319743696789621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3953319743696789621'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/11/undecidable.html' title='Undecidable'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9093069494629039394</id><published>2010-10-29T20:30:00.000-07:00</published><updated>2010-10-29T20:31:18.462-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tool'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel2'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Why Mongrel2 Rocks</title><content type='html'>I used &lt;a href="http://mongrel2.org/home"&gt;Mongrel2&lt;/a&gt; for my latest project &lt;a href="http://code.google.com/p/lib-face/"&gt;lib-face&lt;/a&gt; and it was actually quite a pleasure to work with. Without wasting any more of your time, here are the reasons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It's fast&lt;/li&gt;&lt;li&gt;It's simple to install and use. Though getting all dependencies is a bit time consuming&lt;/li&gt;&lt;li&gt;It's very well documented&lt;/li&gt;&lt;li&gt;The documentation isn't outdated&lt;/li&gt;&lt;li&gt;I mailed the author asking for some clarifications and got a reply pretty fast. This means that if I get stuck using it, there is someone out there to help me till a community develops&lt;/li&gt;&lt;li&gt;It gives out meaningful error messages that actually help&lt;/li&gt;&lt;li&gt;Writing aync handling code is very very simple because you need to explicitly call a function to return. This means that you can hold on to a request for as long as you want&lt;/li&gt;&lt;li&gt;Mongrel2 uses ZeroMQ as the transport. This means that I can potentially implement some other (non-HTTP) protocol and have my code handle requests for that protocol since it is now only talking to ZeroMQ&lt;/li&gt;&lt;li&gt;It gives out meaningful error messages that actually help&lt;/li&gt;&lt;li&gt;It's simple yet powerful&lt;/li&gt;&lt;li&gt;It supports a lot of languages -- remember it's language agnostic!!&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9093069494629039394?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9093069494629039394/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9093069494629039394&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9093069494629039394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9093069494629039394'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/10/why-mongrel2-rocks.html' title='Why Mongrel2 Rocks'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5526042650497270366</id><published>2010-09-15T04:29:00.000-07:00</published><updated>2010-10-29T02:04:03.704-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>A very fast approach to auto complete (or search suggestions)</title><content type='html'>You've seen search engines suggest queries when you begin typing the first few letters of your search string. This is being done by &lt;a href="http://duckduckgo.com/"&gt;Duck Duck Go&lt;/a&gt; as well as &lt;a href="http://google.com/"&gt;Google&lt;/a&gt; (to name a few). This is typically done by maintaining a list of past queries and/or important strings that the search engine thinks are worthy of being suggested to a user that is trying to find something similar. These suggestions are effective only if the search engine spits them out very fast since these should show up on the screen before the user has finished typing what he/she wanted to type. Hence the speed with which these suggestions are made is very critical to the usefulness of this feature.&lt;br /&gt;&lt;br /&gt;Let us consider a situation (and a possible way of approaching this problem) in which when a user enters the first few letters of a search query, he/she is presented with some suggestions that have as their prefix, the string that the user has typed. Furthermore, these suggestions should be ordered by some score that is associated with each such suggestion.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Approach-1:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Our first attempt at solving this would probably involve keeping the initial list of suggestions sorted in lexicographic order so that a simple binary search can give us the 2 ends of the list of strings that serve as candidate suggestions. These are all the strings that have the user's search query as a prefix. We now need to sort all these candidates by their associated score in non-increasing order and return the first 6 (say). We will always return a very small subset (say 6) of the candidates because it is not feasible to show all candidates since the user's screen is of bounded size and we don't want to overload the user with too many options. The user will get better suggestions as he/she types in more letters into the query input box.&lt;br /&gt;&lt;br /&gt;We immediately notice that if the candidate list (for small query prefixes say of length 3) is large (a few thousand), then we will be spending a lot of time sorting these candidates by their associated score. The cost of sorting is O(n log n) since the candidate list may be as large as the original list in the worst case. Hence, this is the total cost of the approch. Apache's &lt;a href="http://wiki.apache.org/solr/TermsComponent"&gt;solr&lt;/a&gt; uses this approach. Even if we keep the scores bound within a certain range and use bucket sort, the cost is still going to be O(n). We should definitely try to do better than this.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Approach-2:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One way of speeding things up is to use a Trie and store (pointers or references to) the top 6 suggestions at or below that node in the node itself. This idea is mentioned &lt;a href="http://stackoverflow.com/questions/1783652/what-is-the-best-autocomplete-suggest-algorithm-datastructure-c-c#1785340"&gt;here&lt;/a&gt;. This results in O(m) query time, where m is the length of the prefix (or user's search query).&lt;br /&gt;&lt;br /&gt;However, this results in too much wasted space because:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Tries are wasteful of space and&lt;/li&gt;&lt;li&gt;You need to store (pointers or references to) 6 suggestions at each node which results in a lot of redundancy of data&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;We can mitigate (1) by using &lt;a href="http://en.wikipedia.org/wiki/Radix_tree"&gt;Radix&lt;/a&gt;(or &lt;a href="http://www.csse.monash.edu.au/%7Elloyd/tildeAlgDS/Tree/PATRICIA/"&gt;Patricia&lt;/a&gt;) Trees instead of Tries.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Approach-3:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;There are also other approaches to auto-completion such as prefix expansion that are using by systems such as &lt;a href="http://antirez.com/post/autocomplete-with-redis.html"&gt;redis&lt;/a&gt;. However, these approaches use up memory proportional to the square of the size of each suggestion (string). The easy way to get around this is to store all the suggestions as a linear string (buffer) and represent each suggestion as an (index,offset) pair into that buffer. For example, suppose you have the strings:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;hello world&lt;/li&gt;&lt;li&gt;hell breaks lose&lt;/li&gt;&lt;li&gt;whiteboard&lt;/li&gt;&lt;li&gt;blackboard&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Then your buffer would look like this:&lt;br /&gt;hello worldhell breaks losewhiteboardblackboard&lt;br /&gt;And the 4 strings are actually represented as:&lt;br /&gt;(0,11), (11,16), (27,10), (37,10)&lt;br /&gt;&lt;br /&gt;Similarly, each prefix of the suggestion "whiteboard" is:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;w =&amp;gt; (27,1)&lt;/li&gt;&lt;li&gt;wh =&amp;gt; (27,2)&lt;/li&gt;&lt;li&gt;whi =&amp;gt; (27,3)&lt;/li&gt;&lt;li&gt;whit =&amp;gt; (27,4)&lt;/li&gt;&lt;li&gt;white =&amp;gt; (27,5)&lt;/li&gt;&lt;li&gt;whiteb =&amp;gt; (27,6)&lt;/li&gt;&lt;li&gt;and so on...&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Approach-4:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;We can do better by using &lt;a href="http://en.wikipedia.org/wiki/Segment_tree"&gt;Segment (or Interval) Trees&lt;/a&gt;. The idea is to keep the suggestions sorted (as in approach-1), but have an additional data structure called a Segment Tree which allows us to perform range searches very quickly. You can query a range of elements in Segment Tree very efficiently. Typically queries such as min, max, sum, etc... on ranges in a segment tree can be answered in O(log n) where n is the number of leaf nodes in the Segment Tree. So, once we have the 2 ends of the candidate list, we perform a range search to get the element with the highest score in the list of candidates. Once we get this node, we insert this range (with the maximum score in that range as the key) into the priority queue. The top element in the queue is popped and split at the location where the element with the highest score occurs and the scores for the 2 resulting ranges are computed and pushed back into the priority queue. This continues till we have popped 6 elements from the priority queue. It is easy to see that we will have never considered more than 2k ranges (here k = 6).&lt;br /&gt;&lt;br /&gt;Hence, the complexity of the whole process is the sum of:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The complexity for the range calculation: O(log n) (omitting prefix match cost) and&lt;/li&gt;&lt;li&gt;The complexity for a range search on a Segment Tree performed 2k times: O(2k log n) (since the candidate list can be at most 'n' in length)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Giving a total complexity of:&lt;br /&gt;O(log n) + O(2k log n)&lt;br /&gt;which is: O(k log n)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; (29&lt;sup&gt;th&lt;/sup&gt; October, 2010): I have implemented the approach described above in the form of a stand-alone auto-complete server using Pyhton and Mongrel2. You can download it from &lt;a href="http://code.google.com/p/lib-face/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Some links I found online:&lt;/b&gt;&lt;br /&gt;&lt;a href="http://alias-i.com/lingpipe/"&gt;LingPipe - Text Processing and Computationsal Linguistics Tool Kit&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.quora.com/What-is-the-best-open-source-solution-for-implementing-fast-auto-complete"&gt;What is the best open source solution for implementing fast auto-complete?&lt;/a&gt;&lt;br /&gt;&lt;a href="http://suggesttree.sourceforge.net/"&gt;Suggest Trees (SourceForge)&lt;/a&gt;: Though these look promising, &lt;a href="http://en.wikipedia.org/wiki/Treap"&gt;treaps&lt;/a&gt; in practice can degenerate into a linear list.&lt;br /&gt;&lt;a href="http://stevedaskam.wordpress.com/2009/05/17/autocomplete-data-structures/"&gt;Autocomplete Data Structures&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.quora.com/Why-is-the-autocomplete-for-Quora-so-fast"&gt;Why is the autocomplete for Quora so fast?&lt;/a&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Autocomplete"&gt;Autocomplete at Wikipedia&lt;/a&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/73095/how-to-implement-a-simple-auto-complete-functionality"&gt;How to implement a simple auto-complete functionality? (Stack Overflow)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/autocomplete-server/"&gt;Auto Compete Server (google code)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://sujitpal.blogspot.com/2007/02/three-autocomplete-implementations.html"&gt;Three Autocomplete implementations compared&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.phpfreaks.com/forums/index.php?topic=240881.0"&gt;Trie Data Structure for Autocomplete?&lt;/a&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/1783652/what-is-the-best-autocomplete-suggest-algorithm-datastructure-c-c"&gt;What is the best autocomplete/suggest algorithm,datastructure [C++/C] (Stack Overflow)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://igoro.com/archive/efficient-auto-complete-with-a-ternary-search-tree/"&gt;Efficient auto-complete with a ternary search tree&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5526042650497270366?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5526042650497270366/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5526042650497270366&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5526042650497270366'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5526042650497270366'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/09/very-fast-approach-to-search.html' title='A very fast approach to auto complete (or search suggestions)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2219705301193756214</id><published>2010-08-18T21:31:00.000-07:00</published><updated>2010-08-18T21:31:10.740-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GRE'/><title type='text'>What you've always wanted to know about GRE preparation</title><content type='html'>When I was preparing for the General GRE, it was a completely mad rush, with me having to ask anyone and everyone for help and advice. So, the conglomeration of all that information is what guided me throughout. Today, I am going to put that all down (combined with my own experiences) so that others don't have to grapple with the same questions and uncertainties and can really concentrate on studying for the exam.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Don't fall into the trap of not booking a date!! You will find it hard to study if you haven't booked a date&lt;/li&gt;&lt;li&gt;Book a date sufficiently into the future (say 2-3 months) and work towards it as if the date can not be postponed. I've seen many people postpone the date just because they can. Don't fall into that trap&lt;/li&gt;&lt;li&gt;I used the Barron's book for everything&lt;/li&gt;&lt;li&gt;Don't take the quant section lightly. Even though it is easy, it is time critical and there are many trick questions&lt;/li&gt;&lt;li&gt;Even though the word list will seem daunting at first, keep at at and try to do as much of it as you can. I must have landed up knowing close to 3000 (out of the 3500) words in the Barron's word list&lt;/li&gt;&lt;li&gt;Attack the High Frequency Words first (about 350). Make sure you use the list from both Barron and Kaplan. &lt;a href="http://dhruvbird.com/words.xls"&gt;This&lt;/a&gt; file contains all such words and words that I found hard. Use the Barron's and Kaplan's list from here and make you own list of "hard" words (everyone will find a different set hard)&lt;/li&gt;&lt;li&gt;Then do the words list-by-list. There are about 50 word lists in the Barron's book. Each word list can take you anywhere from 1 to 3 hours to do if you are doing it the first time. Towards the end, you should be able to do all the word lists in 2-3 days (assuming 5-6 working hours each day)&lt;/li&gt;&lt;li&gt;Solve as many exercise problems in the Barron's book as you can in the quant section&lt;/li&gt;&lt;li&gt;For quant practice, use The Nova GRE prep. book. The quant in this book is easier than what you will get in the GRE, so use this as your first round of preparation&lt;/li&gt;&lt;li&gt;Take the 5 sample tests given at the end of the Barron's book. They are very close to the actual GRE test in terms of difficulty&lt;/li&gt;&lt;li&gt;The Kaplan tests too are of GRE difficulty level (I would say a tiny bit more difficult, but that shouldn't be something you should factor in). The 6 practice exercises in Kaplan are way too easy (for quant) and the 30 min you get to solve them is sufficient. The quant in the 3 sample tests is harder and is of GRE difficulty level&lt;/li&gt;&lt;li&gt;POWERPREP (the official GRE prep. software) is something you must do before the test. They give you 2 sample tests. Take them 1-3 days before the real test when you are sudo prepared for the test. These will give you a great indicator of what score you might land up getting. If you do badly in some section, try to spruce up your act in that section. The drawback of taking it so late (just before the test) is that you don't give yourself enough time to rectify your weak areas. However, I wouldn't want anyone to "waste" these tests before they are fully prepared for the simple reason that these tests are really good indicators of how much you might land up getting&lt;/li&gt;&lt;li&gt;None of the other tests I took were of the GRE difficulty level, so make sure that you take all of the tests above before you take the GRE&lt;/li&gt;&lt;li&gt;On the day of the test, make sure you arrive at the test center on time and have all the required documents&lt;/li&gt;&lt;li&gt;That's about it and all the best!!&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2219705301193756214?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2219705301193756214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2219705301193756214&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2219705301193756214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2219705301193756214'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/08/what-youve-always-wanted-to-know-about.html' title='What you&apos;ve always wanted to know about GRE preparation'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3001500010385472977</id><published>2010-08-11T14:16:00.000-07:00</published><updated>2010-08-11T14:16:35.349-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>How things go wrong (reprise)</title><content type='html'>About 3 years ago, I wrote a post on &lt;a href="http://dhruvbird.blogspot.com/2007/05/how-things-go-wrong.html"&gt;How things go wrong&lt;/a&gt;. I should have made everyone I know read it since we just ran into an issue that bit us in the ass... Yes, it was caused as a result of:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Lack of return value checking from system calls and&lt;/li&gt;&lt;li&gt;Lack of sufficient error checking/handling code&lt;/li&gt;&lt;/ul&gt;and further complicated by a &lt;i&gt;lack of logging&lt;/i&gt; in the application.&lt;br /&gt;&lt;br /&gt;I can't begin saying how important each of them are. Oh well, I only have 2 things to say:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You need to learn for yourself&lt;/li&gt;&lt;li&gt;You can never be too careful&lt;/li&gt;&lt;li&gt;You can't learn from other's mistakes (yes, this and the 1&lt;sup&gt;st&lt;/sup&gt; one are the same thing)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3001500010385472977?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3001500010385472977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3001500010385472977&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3001500010385472977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3001500010385472977'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/08/how-things-go-wrong-reprise.html' title='How things go wrong (reprise)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-763313999793699096</id><published>2010-08-05T22:18:00.000-07:00</published><updated>2010-08-09T02:31:36.978-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Using Titanium Desktop to test web sites</title><content type='html'>I have been using &lt;a href="http://developer.appcelerator.com/apidoc/desktop/latest/Titanium-module"&gt;Titanium Desktop&lt;/a&gt; for a while now at work, and am not at all impressed with the implementation or stability of the product and wouldn't want to see it being used in any serious project. HOWEVER, it is a brilliant concept which if done well can be humongously successful.&lt;br /&gt;&lt;br /&gt;I was faced with the daunting task of testing a web-site written in Drupal the other day. Now, I don't know Drupal but have been told that its got these magic form elements which it used for validation. That couples with cookies meant that I would have to do a lot of work to test even slightly complex flows using python/perl/ruby::Mechanize. That apart, I can NEVER simulate the javascript and other asset loads that a normal user would exercise. For complete end-to-end-testing, I couldn't find anything that I would like using.&lt;br /&gt;&lt;br /&gt;Here is where Titanium comes in. It doesn't have the sandboxing rules that normal browsers have, so you can load pretty much any page in a Titanium IFrame and do pretty much anything you want with it!!!!&lt;br /&gt;&lt;br /&gt;I have given &lt;a href="http://pastebin.com/BdLYUBMd"&gt;here&lt;/a&gt; an example of using Titanium to search for some text on Duck Duck Go.&lt;br /&gt;&lt;br /&gt;To run the example, start Titanium Desktop as kboot --query "Your Search String"&lt;br /&gt;&lt;br /&gt;Enjoy!!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; &lt;a href="http://www.youtube.com/watch?v=W8CdhAoXj2Y"&gt;Here is a video&lt;/a&gt; showing how the automation works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-763313999793699096?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/763313999793699096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=763313999793699096&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/763313999793699096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/763313999793699096'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/08/using-titanium-desktop-to-test-web.html' title='Using Titanium Desktop to test web sites'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7356833321046106058</id><published>2010-07-03T22:51:00.000-07:00</published><updated>2010-07-03T22:51:16.923-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Aaloo, mutter and tamater sabzi</title><content type='html'>This is the recipe for my current favourite vegetable. Translated to english, it means &lt;em&gt;Potato, peas and tomatoes&lt;/em&gt;. (makes sabzi to serve 4). Dedicated to my mom since she makes this dish in the most awesome way I have ever tasted it to be and to sandy since he is going to learn it this time ;)&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Medium sized aaloo(potatoes); boiled:&lt;/td&gt;&lt;td&gt;about 3 nos&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fresh green peas:&lt;/td&gt;&lt;td&gt;about 100g&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Medium sized tomates:&lt;/td&gt;&lt;td&gt;about 4 nos.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Clarified Butter (Ghee):&lt;/td&gt;&lt;td&gt;1 Tbsp&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hing (asafoetida):&lt;/td&gt;&lt;td&gt;&lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt; tsp&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cumin seeds (jeera):&lt;/td&gt;&lt;td&gt;1 &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt; tsp&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Turmeric (haldi):&lt;/td&gt;&lt;td&gt;&lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;4&lt;/sub&gt; tsp&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Salt (namak):&lt;/td&gt;&lt;td&gt;to taste&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Red chilli powder (this is mainly used for the red color):&lt;/td&gt;&lt;td&gt;&lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;4&lt;/sub&gt; tsp (or to taste)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fresh green chilli (cut and seeded):&lt;/td&gt;&lt;td&gt;&lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt; nos.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dhania and jeera powder:&lt;/td&gt;&lt;td&gt;1 tsp&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fresh corriander leaves:&lt;/td&gt;&lt;td&gt;(to garnish)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You need not skin the boiled potatoes. Just cut it into about &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt;" X &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt;" cubes&lt;/li&gt;&lt;li&gt;Heat the ghee and when it is hot, add the hing, jeera and green chilli to it. When the jeera starts popping, and water to it&lt;/li&gt;&lt;li&gt;Add peas to the water, and boil them till they are soft&lt;/li&gt;&lt;li&gt;Now, cut the tomatoes (unskinned) into small pieces and add it to the boiling water and peas. Keep boiling till the tomatoes have softened&lt;/li&gt;&lt;li&gt;Now, add the cut potatoes into the mixture and add red chilli powder, dhania and jeera powder, turmeric and salt&lt;/li&gt;&lt;li&gt;Boil for about 2 more minutes till the potatoes have soaked the gravy&lt;/li&gt;&lt;li&gt;Garnish with &lt;em&gt;fresh corriander&lt;/em&gt; leaves and cover or serve&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7356833321046106058?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7356833321046106058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7356833321046106058&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7356833321046106058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7356833321046106058'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/07/aaloo-mutter-and-tamater-sabzi.html' title='Aaloo, mutter and tamater sabzi'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3729113170307631549</id><published>2010-06-28T10:42:00.003-07:00</published><updated>2010-06-28T10:42:52.885-07:00</updated><title type='text'>Actions speak...</title><content type='html'>... louder than words&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3729113170307631549?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3729113170307631549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3729113170307631549&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3729113170307631549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3729113170307631549'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/actions-speak.html' title='Actions speak...'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8041455597933578340</id><published>2010-06-24T10:06:00.000-07:00</published><updated>2010-06-24T10:12:51.576-07:00</updated><title type='text'>Guess what?</title><content type='html'>&lt;pre&gt;            _________&lt;br /&gt;            |*      |&lt;br /&gt;            |       |&lt;br /&gt;    (------------   |-------)&lt;br /&gt;    (               |       )&lt;br /&gt;    (       |--------       )&lt;br /&gt;    (       |               )&lt;br /&gt;     -------|   ------------&lt;br /&gt;            |       |&lt;br /&gt;            |      *|&lt;br /&gt;            ---------&lt;br /&gt;&lt;br /&gt;Or this (if you are having a problem with the one above ;))&lt;br /&gt;            _________&lt;br /&gt;            I *     |&lt;br /&gt;            I       |&lt;br /&gt;    (------------   |-------)&lt;br /&gt;    (               |       )&lt;br /&gt;    (       |--------       )&lt;br /&gt;    (       |               )&lt;br /&gt;     -------|   ------------&lt;br /&gt;            |       I&lt;br /&gt;            |       I&lt;br /&gt;            |_____*_I&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;+5 to the first one who guesses correctly&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8041455597933578340?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8041455597933578340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8041455597933578340&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8041455597933578340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8041455597933578340'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/guess-what.html' title='Guess what?'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-582395172330480730</id><published>2010-06-24T02:20:00.000-07:00</published><updated>2010-06-24T02:20:37.885-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Murphy's Law (for Python programmers)</title><content type='html'>&lt;a href="http://www.dhruvbird.com/murphy.txt"&gt;Download it here&lt;/a&gt;&lt;br /&gt;Please rename the file to murphy.py and from your interpreter:&lt;br /&gt;import murphy&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-582395172330480730?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/582395172330480730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=582395172330480730&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/582395172330480730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/582395172330480730'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/murphys-law-for-python-programmers.html' title='Murphy&apos;s Law (for Python programmers)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4111988295559104194</id><published>2010-06-23T22:12:00.000-07:00</published><updated>2010-06-23T22:19:13.915-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>YASNOSC</title><content type='html'>Yet another SQL and NoSQL comparison&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Scalability != NoSQL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Performance != NoSQL [not always]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Scalability == Good schema design&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Performance == Good schema design&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;But...&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Performance != Scalability&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Scalability != Performance&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;(but we should also note that NoSQL isn't NOt SQL, but Not Only SQL)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4111988295559104194?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4111988295559104194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4111988295559104194&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4111988295559104194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4111988295559104194'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/yasnoqc.html' title='YASNOSC'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-7377406316069594563</id><published>2010-06-22T09:11:00.000-07:00</published><updated>2010-06-22T09:21:29.911-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>A new water pump for our washing machine</title><content type='html'>(This blog post has the potential to be discursive and go in no particular direction, so I'll try my best to remain as unwavering as I can)&lt;br /&gt;&lt;br /&gt;It all started off when our washing machine konked off and decided to stop working. My grandma sent it off to the repairers who fixed it and then had the &lt;i&gt;brilliant&lt;/i&gt; idea to suggest attaching a &lt;i&gt;water pump&lt;/i&gt; so that water from the tank &lt;i&gt;above&lt;/i&gt; would flow in much faster. Now, anyone who has played with a syringe would know that no matter how hard you push (or pull), you just can NOT for the love of anything (or anyone) get more than a certain amount of liquid through the syringe (or in this case the pipe).&lt;br /&gt;&lt;br /&gt;Now, our pipes are really old and are rusting from the inside. This is the reason that the flow of water has gone down as compared to the time when they were first installed. Even though I suggested getting the pipes changed due to the above mentioned reason (since that is the &lt;i&gt;real&lt;/i&gt; problem, my grandma would hear nothing from me... Oh well... so be it...)&lt;br /&gt;&lt;br /&gt;The &lt;i&gt;brilliant&lt;/i&gt; engineer's suggestion came as a massive shock to me and it almost felt funny. Anyways, the pump has been installed and guess what... &lt;i&gt;The flow of water in the pipe is still the same!!&lt;/i&gt; I wonder how that happened. Isn't the pump working (scratch head, etc...)&lt;br /&gt;&lt;br /&gt;The rate of flow of water in a pipe depends upon many things. One of them being the inner diameter of the pipe and another being the friction offered by the sides of the pipe. I don't know the exactly formula, but I am guessing it is easy to come by. The pressure exerted by the water in the tank is more than sufficient to max. out the flow rate in the pipe and I would have guessed those engineers to know that. I guess it's back to the drawing board for them ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-7377406316069594563?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/7377406316069594563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=7377406316069594563&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7377406316069594563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/7377406316069594563'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/new-water-pump-for-our-washing-machine.html' title='A new water pump for our washing machine'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8191649476213596745</id><published>2010-06-11T05:36:00.000-07:00</published><updated>2010-06-11T05:41:29.727-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>How to write bug-free code</title><content type='html'>Listen hard and listen fast. Listen once; I won't repeat myself...&lt;br /&gt;&lt;br /&gt;While writing code, if you ever say to yourself:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Oh! but this will hardly ever happen OR&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The user will never do this OR&lt;/li&gt;&lt;br /&gt;&lt;li&gt;What are the odds of this happening??&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Please please please go ahead and write some code and comments to handle that situation. You can't ever be too careful.&lt;br /&gt;&lt;br /&gt;Signing off,&lt;br /&gt;Captain S.K.Eptic&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8191649476213596745?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8191649476213596745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8191649476213596745&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8191649476213596745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8191649476213596745'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/how-to-write-bug-free-code.html' title='How to write bug-free code'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8043374442845569311</id><published>2010-06-01T22:21:00.001-07:00</published><updated>2010-06-20T00:15:19.390-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Python generators and the dict.items() function</title><content type='html'>During the course of yesterday's session, there was an example of iterating over a dictionary(dict) in python. The code looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;d = {&lt;br /&gt;     "name": "chuck norris",&lt;br /&gt;     "age": "positive infinity",&lt;br /&gt;}&lt;br /&gt;for k,v in d.items():&lt;br /&gt;    print "Key: " + str(k) + ", Value: " + str(v)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The d.items() function returns a list which will take up a non-trivial amount of memory for large dicts that we wish to iterate over. For simplicity's sake if we assume that each pointer takes up 8 bytes and the python tuple type takes up 32 bytes(say) and there are 1 million(10&lt;sup&gt;6&lt;/sup&gt;) entities in the dict(d), then we land up with 10&lt;sup&gt;6&lt;/sup&gt; x 56 (Two 8 byte pointers, each to each the key and the value objects, 8 for a pointer to the tuple and 32 for the tuple object itself) which is about &lt;em&gt;56MB&lt;/em&gt; of memory used for just iterating over the dictionary.&lt;br /&gt;&lt;br /&gt;If however, we use generators, then we can save all that memory.&lt;br /&gt;However, you can directly &lt;em&gt;print&lt;/em&gt; the result of &lt;em&gt;d.items()&lt;/em&gt;. Printing a generator object doesn't do anything spectacular, so we will need to create a proxy object to print the result of generation if string coercion is requested. The code for doing so is shown below. Notice that we use the .keys() function within our custom generator. We won't need to use it if we really were the dict object and had handles to the internal variables.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;d = {&lt;br /&gt;     "name": "chuck norris",&lt;br /&gt;     "age": "positive infinity",&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def dict_items(d):&lt;br /&gt;    """&lt;br /&gt;    Returns an object which can be iterated over in a for loop&lt;br /&gt;    as well as printed. This is achieved by returning an object&lt;br /&gt;    which is both iterable as well as convertible to a string.&lt;br /&gt;    However, iteration involves using a generator for fetching&lt;br /&gt;    the next value in the sequence&lt;br /&gt;    """&lt;br /&gt;    def dict_generator():&lt;br /&gt;        dkeys = d.keys()&lt;br /&gt;        for k in dkeys:&lt;br /&gt;            yield (k, d[k])&lt;br /&gt;    dg = dict_generator()&lt;br /&gt;&lt;br /&gt;    def dgen_to_str():&lt;br /&gt;        return str(d.items())&lt;br /&gt;&lt;br /&gt;    class Dummy(object):&lt;br /&gt;        def __getattr__(self, attr):&lt;br /&gt;            return getattr(dg, attr)&lt;br /&gt;        def __str__(self):&lt;br /&gt;            return dgen_to_str()&lt;br /&gt;        def __iter__(self):&lt;br /&gt;            return dg&lt;br /&gt;&lt;br /&gt;    proxy = Dummy()&lt;br /&gt;    return proxy&lt;br /&gt;&lt;br /&gt;diter = dict_items(d)&lt;br /&gt;print diter&lt;br /&gt;&lt;br /&gt;for k,v in diter:&lt;br /&gt;    print "Key: " + str(k) + ", Value: " + str(v)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, the major difference comes when you try to do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for k,v in d.items():&lt;br /&gt;    d.clear()&lt;br /&gt;    print "Key: " + str(k) + ", Value: " + str(v)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code above will work as expected and print all the elements of the dict(d)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for k,v in diter:&lt;br /&gt;    d.clear()&lt;br /&gt;    print "Key: " + str(k) + ", Value: " + str(v)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, the code above will throw an exception since our generator caches all the keys and dereferences the dict(d) to get the value for the corresponding key.&lt;br /&gt;&lt;br /&gt;So, applications that mutate the dict while iterating over it won't quite work as expected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8043374442845569311?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8043374442845569311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8043374442845569311&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8043374442845569311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8043374442845569311'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/python-generators-and-dictitems.html' title='Python generators and the dict.items() function'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8367933861774903289</id><published>2010-06-01T21:26:00.000-07:00</published><updated>2010-06-01T22:15:35.870-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Python Interest Group(PIG) Act-1; Scene-1</title><content type='html'>We had the first session of the python interest group(affectively called PIG) at Directi yesterday (1st June, 2010).&lt;br /&gt;&lt;br /&gt;Here are the links to the presentation in:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://uploading.com/files/425f78f5/pig-act1-scene1.odp/"&gt;.odp(open office)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://uploading.com/files/ba27de88/pig-act1-scene1.ppt/"&gt;.ppt(excel) format&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;along with the code snippets in a &lt;a href="http://uploading.com/files/352827d3/pig-act1-scene1.zip/"&gt;.zip archive&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;You will need &lt;a href="http://www.python.org/download/releases/2.5/"&gt;python 2.5&lt;/a&gt; to run the sample programs.&lt;br /&gt;The documentation for python 2.5 is available &lt;a href="http://docs.python.org/release/2.5.4/"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Happy Hacking!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8367933861774903289?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8367933861774903289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8367933861774903289&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8367933861774903289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8367933861774903289'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/python-interest-grouppig-act-1-scene-1.html' title='Python Interest Group(PIG) Act-1; Scene-1'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8088535248947460299</id><published>2010-06-01T21:16:00.000-07:00</published><updated>2010-06-01T21:25:50.586-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='health'/><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>5 months of salad... and counting...</title><content type='html'>I started having salad for lunch daily sometime early &lt;a href="http://dhruvbird.blogspot.com/2010/01/one-week-of-salad.html"&gt;January, 2010&lt;/a&gt;. Since then, it's been fairly smooth sailing and I've added a few ingredients to the mix.&lt;br /&gt;&lt;br /&gt;A lot of people have asked me if I got bored of the monotony of salad, but I highly disagree with them all. The deal with salad is that you get so many flavours in every bit that it's really hard to get bored of it. Besides, it's fairly juicy and crunchy that you enjoy having it -- really!!&lt;br /&gt;&lt;br /&gt;Either ways, I have added a few extra ingredients over the months, so would like to share those with the very few and faithful readers of this blog.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Hazelnuts (halves)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cashews (halves)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pomegranates (in season)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dates&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dried Anjeer (figs)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I've pretty much knocked off the mixed herbs in the dressing and just stick to Olive Oil and Honey&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The baby corn is fresh baby corn, which needs to be stripped off it's covering; like you would for normal corn. The baby corn which results is really so sweet that you can eat it as-is!!&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Enjoy your salad people!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8088535248947460299?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8088535248947460299/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8088535248947460299&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8088535248947460299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8088535248947460299'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/06/5-months-of-salad-and-counting.html' title='5 months of salad... and counting...'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-971991241816910120</id><published>2010-05-31T11:23:00.000-07:00</published><updated>2010-06-01T01:10:23.137-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Can't come to terms with inheritance</title><content type='html'>I've been racking my brains over inheritance for a while now, but am still not completely able to get around it.&lt;br /&gt;&lt;br /&gt;For example, the other day I was thinking about relating an Infallible Human and a Fallible Human. Let's first define the two:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Infallible Human:&lt;/b&gt; A human that can never make a mistake. It's &lt;em&gt;do_task()&lt;/em&gt; method will never throw an exception&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Fallible Human:&lt;/b&gt; A human that will occasionally make a mistakes. It's &lt;em&gt;do_task()&lt;/em&gt; method may occasionally throw a ErrorProcessingRequest Exception&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The question was:&lt;br /&gt;&lt;em&gt;IS an infallible human A fallible human&lt;/em&gt; OR &lt;em&gt;IS a fallible human AN infallible human?&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;The very nice answer I received was in the form of a question (I love these since it gives me rules to answer future questions I may have).&lt;br /&gt;&lt;br /&gt;&lt;em&gt;"Can you pass an infallible human where a fallible human is expected OR can you pass a fallible human where an infallible human is expected?"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;It seems apparent that you can pass an infallible human where a fallible human is expected, but not the other way around. I guess that answered my question.&lt;br /&gt;&lt;br /&gt;However, it still feels funny saying "An infallible human is a fallible human". Does anyone else feel queasy when they say it? It almost feels as if speaking out inheritance trees is like reading out statements from propositional calculus in plain English (the if/then implication connectives don't mean the same as that in spoken English). Does anyone else feel the same?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;update:&lt;/b&gt; This &lt;a href="http://stackoverflow.com/questions/2945363/problem-understanding-inheritance"&gt;thread&lt;/a&gt; on stackoverflow discusses the same issue.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-971991241816910120?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/971991241816910120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=971991241816910120&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/971991241816910120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/971991241816910120'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/cant-come-to-terms-with-inheritance.html' title='Can&apos;t come to terms with inheritance'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1416471613866549421</id><published>2010-05-29T22:11:00.000-07:00</published><updated>2010-11-15T13:32:59.013-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>A few rules to live by</title><content type='html'>&lt;ul&gt;&lt;li&gt;you can only lie to others, never to yourself&lt;/li&gt;&lt;li&gt;don't hurt others -- it will only come back to you&lt;/li&gt;&lt;li&gt;you can justify anything -- we all can -- but don't try too hard. you might end up lying&lt;/li&gt;&lt;li&gt;python rocks&lt;/li&gt;&lt;li&gt;java bites shite&lt;/li&gt;&lt;li&gt;java has resulted in more deaths and cases of insanity than road accidents world-wide&lt;/li&gt;&lt;li&gt;the average lifespan of a java programmer is 10 years less than that of a python programmer and 7 years less than the average for a human&lt;/li&gt;&lt;li&gt;what can kill chuck norris -- programming in java&lt;/li&gt;&lt;li&gt;listen to music you like -- don't pretend&lt;/li&gt;&lt;li&gt;eat food you like -- don't pretend&lt;/li&gt;&lt;li&gt;wear what you like (if you want to wear anything that is) -- don't pretend&lt;/li&gt;&lt;li&gt;criticize -- don't be diplomatic when you don't need to&lt;/li&gt;&lt;li&gt;praise -- it spreads good vibes -- but don't praise falsely -- it doesn't help at all&lt;/li&gt;&lt;li&gt;music will set you free ;)&lt;/li&gt;&lt;li&gt;do what you want to, not what you have to or need to -- sometimes you may "need/have" to do the things though; but don't stretch it too much&lt;/li&gt;&lt;li&gt;listen to "sunscreen" if you haven't yet&lt;/li&gt;&lt;li&gt;don't recommend it to others because it's "cool", but because you think it's worth doing&lt;/li&gt;&lt;li&gt;don't do anything "just" because it's "cool"&lt;/li&gt;&lt;li&gt;don't not to anything just because it's not cool&lt;/li&gt;&lt;li&gt;the arts are purgative and cathartic; write, act, sing, cook, code... anything is art and art is everything...&lt;/li&gt;&lt;li&gt;you may need to twist your finger to get the fat out(transated from a hindi proverb); but do it only if it's absolutely necessary&lt;/li&gt;&lt;li&gt;talk is cheap; actions are not&lt;/li&gt;&lt;li&gt;neither is code&lt;/li&gt;&lt;li&gt;show me the code you piece of shite&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1416471613866549421?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1416471613866549421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1416471613866549421&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1416471613866549421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1416471613866549421'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/few-rules-to-live-by.html' title='A few rules to live by'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2998319389226572680</id><published>2010-05-22T06:55:00.000-07:00</published><updated>2010-05-22T07:18:11.028-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='thecruftoftheworld'/><title type='text'>We aren't responsible</title><content type='html'>I was reading the paper and The Mumbai Mirror's headlines a few days ago went something like this "Criminal Negligence Leaves WR Commuter Critical". The article was with reference to a commuter who had been injured by a plank that fell on him because of negligence on part of the workers working at Churchgate station. This injury unfortunately has left him comatose and he is currently in a critical state in hospital. I wish him a speedy recovery.&lt;br /&gt;&lt;br /&gt;Coming from a software centric world where EULAs and licenses accompany any piece of code that is shipped, it seems only natural that no one is really responsible for anything that they do and that any action on their behalf may have negative influences on others. Others need to watch out for anything that may hurt or otherwise injure them. It is the user's and only the user's sole responsibility to ensure his/her fitness and properiety and the user must account for anything that may go wrong during the course of using the product/software mentioned.&lt;br /&gt;&lt;br /&gt;I have yet to see any license that says that they take sole responsibility for their product and that they could be held responsible for any damage arising due to negligence on their behalf. The closest I have come to it is &lt;a href="http://www.truetex.com/knuthchk.htm"&gt;Knuth's exponentially increasing reward for finding bugs in TeX&lt;/a&gt;. As far as free software is concerned, we shouldn't expect it. However in case we are paying for the software, shouldn't we demand it? I mean we are literally shelling out hard cash for that piece of code. We seem to have a right to want it to be bug-free...&lt;br /&gt;&lt;br /&gt;Either ways, it seems no one wants to take responsibility for their code, so why not make it a de-facto standard. Instead, if someone out there is willing to say that they will take responsibility for screw ups (of any) then they should include it in the license agreement.&lt;br /&gt;&lt;br /&gt;It starts off with software saying that it may not work well and ends up at coffee cups and hot dogs saying that their contents may be too hot for consumption.&lt;br /&gt;&lt;br /&gt;What next? From what I see, there may now be notices on every pavement saying that people should walk on it on their own risk. Each lift will say that it could crash any minute and if it does; even due to any manufacturing defect or installation glitch, then company providing it should not be held responsible. Airplane and trains will warn travelers before they enter saying "Please account for the fact that you may not make it if you step in". Books may say "Read at your own peril. Your eyes may gouge out while reading this book, but don't blame us!!". When will all this end???&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2998319389226572680?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2998319389226572680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2998319389226572680&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2998319389226572680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2998319389226572680'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/we-arent-responsible.html' title='We aren&apos;t responsible'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5977353475369665608</id><published>2010-05-15T07:33:00.000-07:00</published><updated>2010-05-15T08:56:25.916-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Events: How to use them to your advantage</title><content type='html'>A very interesting conversation between a colleague and me resulted in some equally interesting learnings as far as I was concerned.&lt;br /&gt;&lt;br /&gt;The topic of discussion was about whether certain functionality in a software should be implemented via &lt;a href="http://en.wikipedia.org/wiki/Event-driven_programming"&gt;events&lt;/a&gt; or via function calls directly to the concerned component. More concretely, should the coupling be via events as in a &lt;a href="http://en.wikipedia.org/wiki/Publish/subscribe"&gt;pubsub&lt;/a&gt; based system or should it be via known function end-points on specific objects.&lt;br /&gt;&lt;br /&gt;If the event based approach is used, there is lose coupling, and the coupling is on events. Using the other approach, there is strong coupling and one component needs to know about the interfaces of the other component.&lt;br /&gt;&lt;br /&gt;Let's take a toy example to analyze this situation.&lt;br /&gt;&lt;br /&gt;Consider an interior stylist that is developing an integrated home theatre system. Say there are multiple components involved:&lt;br /&gt;1. Speaker system&lt;br /&gt;2. Television&lt;br /&gt;3. CD player&lt;br /&gt;4. Satellite system&lt;br /&gt;&lt;br /&gt;For all practical purposes, users should not need to turn on each of the separate components separately (as they do today). They need not even know what the individual constituents of the system are. For that purpose, having a separate button that turns on the Television, one that turns on the speakers and one that controls the CD player seem overkill. Just the press of a single "start CD" button should do the needful.&lt;br /&gt;&lt;br /&gt;Let's discuss various ways in which we can achieve the desired effect:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Strongly couple all the components together so that starting the CD player also triggers the TV and the speaker system.&lt;br /&gt;&lt;br /&gt;What happens if the satellite system is turned on? It must also turn on the TV and speaker system. Tomorrow if there is a new device "USB photo viewer" that needs to use the display that the Television provides, it will also need to do the needful.&lt;br /&gt;&lt;br /&gt;This method seems to waste a lot of developer time and seems to be prone to errors. What if someone forgets to turn on the TV? Also each component needs to handle the case of when the TV/speaker is already on, etc...&lt;br /&gt;&lt;br /&gt;The CD player will work only with that TV set and speaker system that speaks a language that it understands (tied to an interface). Vendors will try to tie users to their interface in the hope of selling them all the components.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;br /&gt;Instead, if we just decouple all the system and couple them on events, things become much clearer and easier to work with.&lt;br /&gt;&lt;br /&gt;The "button.enterteinment.cdplayer", "button.enterteinment.satellite" and "button.enterteinment.usbdevice" events should be listened to by each of the components and they should react accordingly.&lt;br /&gt;&lt;br /&gt;Another thing to remember is how to name the events. We should NOT name events as "start.enterteinment.cdplayer". That sounds very imperative. Naming plays a very important role in how the rest of our system is built around these events. Wrongly named events can cause a lot of confusion and screw up the system even more than what you thought capable of doing!!! Event names should be suggestive of "something happening" and not of "something being asked for".&lt;br /&gt;&lt;br /&gt;Accordingly, events should not be named "do-this" and "do-that". Instead, prefer names like "this-happened" and "that-happened" so that listeners can &lt;i&gt;react&lt;/i&gt; to these events in the way they deem most fit.&lt;br /&gt;&lt;br /&gt;By naming events imperatively, we immediately constrict their use cases to what we think they should be; defeating the original purposes of using events -- which is to free event raisers from thinking of what is to be done in reaction to a raised event.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5977353475369665608?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5977353475369665608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5977353475369665608&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5977353475369665608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5977353475369665608'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/events-how-to-use-them-to-your.html' title='Events: How to use them to your advantage'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5402491899953574273</id><published>2010-05-09T21:47:00.000-07:00</published><updated>2010-05-09T21:49:15.875-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>One line to teach you Object Oriented Programming (OOP)</title><content type='html'>"When modeling objects, model behaviour, not attributes"&lt;br /&gt;&lt;i&gt;Courtesy: Ramki&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5402491899953574273?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5402491899953574273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5402491899953574273&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5402491899953574273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5402491899953574273'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/one-line-to-teach-you-object-oriented.html' title='One line to teach you Object Oriented Programming (OOP)'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5019276607872017739</id><published>2010-05-09T11:34:00.000-07:00</published><updated>2010-06-01T01:09:02.011-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weather'/><category scheme='http://www.blogger.com/atom/ns#' term='mumbai'/><title type='text'>Probably the hottest summer I've seen in Mumbai</title><content type='html'>This is by far the hottest and stickiest summer I have seen in Mumbai since my stint on this planet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5019276607872017739?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5019276607872017739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5019276607872017739&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5019276607872017739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5019276607872017739'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/probably-hottest-summer-ive-seen-in.html' title='Probably the hottest summer I&apos;ve seen in Mumbai'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2387597327442787293</id><published>2010-05-02T11:10:00.000-07:00</published><updated>2010-05-02T11:32:17.340-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Protocol Buffers v/s HTTP</title><content type='html'>I was discussing serialization costs with my immediate superior, Ramki when one thing lead to another and I landed up wanting to compare the serialization and deserialization costs of &lt;a href="http://code.google.com/p/protobuf/"&gt;protocol buffers&lt;/a&gt; (a binary format) to those of &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html"&gt;HTTP&lt;/a&gt; (a text based protocol) in python.&lt;br /&gt;&lt;br /&gt;After performing some tests (in python), I observed that protobuf was taking almost 4 times the amount of time to deserialize data as compared to the simplistic HTTP based header parsing. Of course, these 2 are meant for different purposes and Ramki mentioned that a fixed format protocol would save data on the wire since the attribute names (header names in HTTP) need not be sent on the wire; just the values (header values in HTTP) are sufficient.&lt;br /&gt;&lt;br /&gt;Also a binary protocol should be much faster as far as serialization and deserialization is concerned, but we found out that python's pack and unpack are a bit slow OR it is blazingly fast at doing string operations.&lt;br /&gt;&lt;br /&gt;Here is a representative output from one such run of the program below:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ramki serialization time: 0.125000 seconds&lt;br /&gt;ramki deserialization time: 0.156000 seconds&lt;br /&gt;protobuf serialization time: 0.453000 seconds&lt;br /&gt;protobuf deserialization time: 0.453000 seconds&lt;br /&gt;http serialization time: 0.047000 seconds&lt;br /&gt;http deserialization time: 0.125000 seconds&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import sys&lt;br /&gt;import message_pb2&lt;br /&gt;import time&lt;br /&gt;import struct&lt;br /&gt;&lt;br /&gt;def multi_serialize(o, n):&lt;br /&gt;    fragments = []&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        data = o.SerializeToString()&lt;br /&gt;        fragments.append("%d\r\n" % len(data))&lt;br /&gt;        fragments.append(data)&lt;br /&gt;    return "".join(fragments)&lt;br /&gt;&lt;br /&gt;def multi_parse(input, n):&lt;br /&gt;    il = len(input)&lt;br /&gt;    start = 0&lt;br /&gt;    objects = []&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        rnPos = input.find("\r\n", start)&lt;br /&gt;        if rnPos == -1:&lt;br /&gt;            print "Premature end of input. Terminating..."&lt;br /&gt;            return None&lt;br /&gt;        lenStr = input[start:rnPos]&lt;br /&gt;        start = rnPos + 2&lt;br /&gt;        lenInt = int(lenStr)&lt;br /&gt;        # Read off lenInt bytes off the stream&lt;br /&gt;        data = input[start:start + lenInt]&lt;br /&gt;        start += lenInt&lt;br /&gt;        obj = message_pb2.Header()&lt;br /&gt;        obj.ParseFromString(data)&lt;br /&gt;        objects.append(obj)&lt;br /&gt;    return objects&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def http_header_create(request, headers):&lt;br /&gt;    line1 = "GET %s HTTP/1.1" % request&lt;br /&gt;    hLines = [line1]&lt;br /&gt;    for k,v in headers.items():&lt;br /&gt;        hLines.append(k + ": " + v)&lt;br /&gt;    return "\r\n".join(hLines) + "\r\n\r\n"&lt;br /&gt;&lt;br /&gt;def http_header_parse(input):&lt;br /&gt;    parts = input.split("\r\n")&lt;br /&gt;    line1 = tuple(parts[0].split())&lt;br /&gt;    headers = { }&lt;br /&gt;    for i in xrange(1, len(parts)):&lt;br /&gt;        h = parts[i].split(": ")&lt;br /&gt;        if len(h) == 2:&lt;br /&gt;            k,v = h&lt;br /&gt;            headers[k] = v&lt;br /&gt;    return (line1, headers)&lt;br /&gt;&lt;br /&gt;def http_multi_serialize(request, headers, n):&lt;br /&gt;    fragments = []&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        fragments.append(http_header_create(request, headers))&lt;br /&gt;    return "".join(fragments)&lt;br /&gt;&lt;br /&gt;def http_multi_parse(input, n):&lt;br /&gt;    il = len(input)&lt;br /&gt;    start = 0&lt;br /&gt;    objects = []&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        delimPos = input.find("\r\n\r\n", start)&lt;br /&gt;        if delimPos == -1:&lt;br /&gt;            print "Premature end of input. Terminating..."&lt;br /&gt;            return None&lt;br /&gt;        headerString = input[start:delimPos]&lt;br /&gt;        headerObject = http_header_parse(headerString)&lt;br /&gt;        objects.append(headerObject)&lt;br /&gt;        start = delimPos + 4&lt;br /&gt;    return objects&lt;br /&gt;&lt;br /&gt;def ramki_serialize(obj):&lt;br /&gt;    totalLength = 0&lt;br /&gt;    attrs = [ ]&lt;br /&gt;    for k,v in obj.__dict__.items():&lt;br /&gt;        totalLength += (2 + len(v))&lt;br /&gt;        attr = struct.pack("H", len(v)) + v&lt;br /&gt;        attrs.append(attr)&lt;br /&gt;    attrs.insert(0, struct.pack("H", totalLength))&lt;br /&gt;    return "".join(attrs)&lt;br /&gt;&lt;br /&gt;class RamkiDummy(object):&lt;br /&gt;    pass&lt;br /&gt;&lt;br /&gt;shortStruct = struct.Struct("H")&lt;br /&gt;&lt;br /&gt;def ramki_deserialize(input):&lt;br /&gt;    # For now, we lose attribute names&lt;br /&gt;    d = RamkiDummy()&lt;br /&gt;    packetLength = shortStruct.unpack(input[0:2])[0]&lt;br /&gt;    s = 2&lt;br /&gt;    ctr = 0&lt;br /&gt;    while s &lt; packetLength+2:&lt;br /&gt;        # print "CTR: " + str(ctr)&lt;br /&gt;        attrLength = shortStruct.unpack(input[s:s+2])[0]&lt;br /&gt;        s += 2&lt;br /&gt;        # Read attrLength bytes of data&lt;br /&gt;        attrValue = input[s:s+attrLength]&lt;br /&gt;        s += attrLength&lt;br /&gt;        setattr(d, "attr" + str(ctr), attrValue)&lt;br /&gt;        ctr += 1&lt;br /&gt;&lt;br /&gt;    return d&lt;br /&gt;&lt;br /&gt;def ramki_multi_serialize(obj, n):&lt;br /&gt;    stream = []&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        stream.append(ramki_serialize(obj))&lt;br /&gt;    return "".join(stream)&lt;br /&gt;&lt;br /&gt;def ramki_multi_deserialize(input, n):&lt;br /&gt;    objects = []&lt;br /&gt;    s = 0&lt;br /&gt;    for i in xrange(0, n):&lt;br /&gt;        objectLength = shortStruct.unpack(input[s:s+2])[0] + 2&lt;br /&gt;        obj = ramki_deserialize(input[s:s+objectLength])&lt;br /&gt;        s += objectLength&lt;br /&gt;        objects.append(obj)&lt;br /&gt;    return objects&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&lt;br /&gt;    class Dummy(object):&lt;br /&gt;        pass&lt;br /&gt;&lt;br /&gt;    d = Dummy()&lt;br /&gt;    d.request = "GET"&lt;br /&gt;    d.resource = "/user/ramki/getVcard/"&lt;br /&gt;    d.version = "1.1"&lt;br /&gt;    d.destination = "localhost:8080"&lt;br /&gt;    d.custom1 = "434552"&lt;br /&gt;    d.custom2 = "no"&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    input = ramki_multi_serialize(d, 10000)&lt;br /&gt;    print "ramki serialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    ramki_multi_deserialize(input, 10000)&lt;br /&gt;    print "ramki deserialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    h = message_pb2.Header()&lt;br /&gt;    h.request = "GET"&lt;br /&gt;    h.resource = "/user/ramki/getVcard/"&lt;br /&gt;    h.version = "1.1"&lt;br /&gt;    h.destination = "localhost:8080"&lt;br /&gt;    h.custom1 = "434552"&lt;br /&gt;    h.custom2 = "no"&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    stream = multi_serialize(h, 10000)&lt;br /&gt;    print "protobuf serialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    objs = multi_parse(stream, 10000)&lt;br /&gt;    print "protobuf deserialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    hh = { "Host": "localhost",&lt;br /&gt;           "X-MessageID": "33",&lt;br /&gt;           "X-ACKMessage": "100",&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    stream = http_multi_serialize("/user/ramki/getVcard/", hh, 10000)&lt;br /&gt;    print "http serialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    s = time.time()&lt;br /&gt;    objs = http_multi_parse(stream, 10000)&lt;br /&gt;    print "http deserialization time: %f seconds" % (time.time() - s)&lt;br /&gt;&lt;br /&gt;    return 0&lt;br /&gt;&lt;br /&gt;sys.exit(main())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking"&gt;This page&lt;/a&gt; claims that protobuff deserialization time is 3478ns whereas I am seeing a time of 4530ns which is expected since we are running on different hardware.&lt;br /&gt;&lt;br /&gt;From: &lt;a href="http://bouncybouncy.net/ramblings/posts/json_vs_thrift_and_protocol_buffers_round_2/"&gt;here&lt;/a&gt;, it seems as it protobuf is good at packing ints but not strings.&lt;br /&gt;&lt;br /&gt;In fact, none of the deserialization times even come close to the 1250ns that I see for http parsing. This is mainly because those methods do type conversion which HTTP is not doing.&lt;br /&gt;If that is introduced into the mix, I guess those costs will add up too. However, the application that I want it for doesn't really need it, and there will be many applications that don't.&lt;br /&gt;&lt;br /&gt;In the link &lt;a href="http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking"&gt;above&lt;/a&gt;, many of the methods, serialization takes more time than deserialization which is slightly curious.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2387597327442787293?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2387597327442787293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2387597327442787293&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2387597327442787293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2387597327442787293'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/05/protocol-buffers-vs-http.html' title='Protocol Buffers v/s HTTP'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5897983314513306932</id><published>2010-04-22T21:59:00.000-07:00</published><updated>2010-05-03T22:57:25.205-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Inheritance and extension</title><content type='html'>I have been stumped by this issue for a while, so I decided to write about it.&lt;br /&gt;&lt;br /&gt;Say you have an element container called SimpleList which exposes the following methods(apart from others of course):&lt;br /&gt;1. add(element): Adds 'element' to the SimpleList&lt;br /&gt;2. addAll(elements): Adds 'elements' to the SimpleList&lt;br /&gt;&lt;br /&gt;These methods are extensible(can be overriden and hence extended).&lt;br /&gt;&lt;br /&gt;They are implemented as follows in SimpleList.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Contract: Will add 'element' to SimpleList or throw an &lt;br /&gt;# exception in case of an error.&lt;br /&gt;method add(element)&lt;br /&gt;  # Adds element to the underlying data store/array&lt;br /&gt;&lt;br /&gt;# Contract: Will add 'elements' to SimpleList or throw an &lt;br /&gt;# exception in case of an error. This method may throw an &lt;br /&gt;# exception after adding 'some' of the elements from 'elements'.&lt;br /&gt;method addAll(elements)&lt;br /&gt;  for element in elements&lt;br /&gt;    this.add(element)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Fair enough till now.&lt;br /&gt;&lt;br /&gt;Up comes someone who decides they can do better and they extend SimpleList and create ABetterSimpleList!!&lt;br /&gt;&lt;br /&gt;They are implemented as follows in ABetterSimpleList.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Contract: Will add 'element' to ABetterSimpleList or throw &lt;br /&gt;# an exception in case of an error.&lt;br /&gt;method add(element)&lt;br /&gt;  # Adds element to an underlying cache since SimpleList &lt;br /&gt;  # may take a while to add() this element.&lt;br /&gt;  if cache.size() &gt; 20:&lt;br /&gt;    this.addAdd(cache)&lt;br /&gt;    cache.clear()&lt;br /&gt;&lt;br /&gt;# Contract: Will add 'elements' to ABetterSimpleList or throw &lt;br /&gt;# an exception in case of an error. This method may throw an &lt;br /&gt;# exception after adding 'some' of the elements from 'elements'.&lt;br /&gt;method addAll(elements)&lt;br /&gt;  # Just call the base class' addAll method&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All contracts have been satisfied, but can you spot the subtle bug?&lt;br /&gt;&lt;br /&gt;Yes, there is an infinite recursion here!! Calling add() on ABetterSimpleList will add elements to the cache till the cache grows to 20 elements in length. Once that happens, it will call addAll() which will call the base class' addAll() which will call (what it thinks is) it's add() function, except that it had been overridden by us to call addAll()!!&lt;br /&gt;&lt;br /&gt;Well, how do you solve this?? I don't have a concrete answer myself, but just scratching the surface has lead me to believe that there are 2 type of class extensions:&lt;br /&gt;1. Extending for the base class (I call this framework extension)&lt;br /&gt;2. Extending for users/subclasses (I call this normal extension)&lt;br /&gt;&lt;br /&gt;In case [1], it is expected that the base class will consume your interfaces so you program for the base class. However, in case [2], your consumers are externals and not internals like your base class. Hence, you should now program for them.&lt;br /&gt;&lt;br /&gt;An important side effect of this is that classes that are meant to be consumed by subclasses (case [2]) should NOT use their own overridable methods in their own implementation or else they CAN NOT give any guarantees to their consumers (subclasses in this case). However, framework extended classes (case [1]) SHOULD use their own public interfaces since that IS the purpose of them allowing method overriding on those interfaces. They are expected to be the consumers of their subclass' funtionality.&lt;br /&gt;&lt;br /&gt;Examples of each type of class:&lt;br /&gt;1. Framework Extension: Java's Runnable, C++'s streambuf&lt;br /&gt;2. Normal Extension: Java's LinkedList, AbstractList. Typically anything that is layered falls into this category&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5897983314513306932?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5897983314513306932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5897983314513306932&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5897983314513306932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5897983314513306932'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/04/inheritance-and-extension.html' title='Inheritance and extension'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6509713137405193010</id><published>2010-04-22T21:54:00.000-07:00</published><updated>2010-04-22T21:59:03.774-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='technical'/><title type='text'>Patterns for Concurrent, Parallel, and Distributed Systems</title><content type='html'>I happened to chance up on this great site which describes Patterns for Concurrent, Parallel, and Distributed Systems.&lt;br /&gt;&lt;br /&gt;I landed up here searching for the proactor and reactor patterns, both of which are described very well.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.cs.wustl.edu/~schmidt/patterns-ace.html"&gt;http://www.cs.wustl.edu/~schmidt/patterns-ace.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6509713137405193010?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6509713137405193010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6509713137405193010&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6509713137405193010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6509713137405193010'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/04/patterns-for-concurrent-parallel-and.html' title='Patterns for Concurrent, Parallel, and Distributed Systems'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-3098145011947152626</id><published>2010-02-02T20:32:00.000-08:00</published><updated>2010-02-02T20:40:59.566-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='health'/><title type='text'>An experiment with carbs</title><content type='html'>About a month ago, Appu, Abbas and myself met and Appu happened to mention that an increased intake of carbs. generally lent itself to hunger pangs if one did not eat for a while.&lt;br /&gt;For the last month, I've been having only salad for lunch, and daal, vegetables and chapati(3 nos.) for dinner and have also been limiting my carb. intake by way of reducing the amount of sugar I add to my hot beverages.&lt;br /&gt;However, yesterday I had 4 liquor chocolates (which Devdas had so generously brought back from New Zealand), 5 milk pedhas (courtesy Jaineev) and 2 Kaju Katris (courtesy Mukesh), which considerably increased my carb intake for that day. Furthermore, I had 5 chapatis and extra helpings of other stuff yesterday for dinner. I don't know if it had any direct link to the carb. intake, but I will be monitoring this more closely now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-3098145011947152626?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/3098145011947152626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=3098145011947152626&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3098145011947152626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/3098145011947152626'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/02/experiment-with-carbs.html' title='An experiment with carbs'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-585278853180055630</id><published>2010-02-02T00:19:00.000-08:00</published><updated>2010-02-02T03:44:09.423-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vacation'/><category scheme='http://www.blogger.com/atom/ns#' term='moments'/><title type='text'>Moments</title><content type='html'>I visited Sandeep in Bangalore when I had gone for the Agile 2010 conference in Jan 2010. Good times followed ;)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://dhruvbird.com/moments/Record001.mp3"&gt;Neela Aasmaan Soo Gaya&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dhruvbird.com/moments/Record002.mp3"&gt;Tere Khushboo Mein Basee Khat&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dhruvbird.com/moments/Record003.mp3"&gt;Jhuki Jhuki See Nazaar&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dhruvbird.com/moments/Record004.mp3"&gt;Mainee Tere Liye Hii Saat Rang Kee (Part-1)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://dhruvbird.com/moments/Record005.mp3"&gt;Mainee Tere Liye Hii Saat Rang Kee (Part-2)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-585278853180055630?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/585278853180055630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=585278853180055630&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/585278853180055630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/585278853180055630'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/02/moments.html' title='Moments'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1208869439840711336</id><published>2010-02-01T22:57:00.000-08:00</published><updated>2010-02-03T08:14:11.379-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vanilla'/><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Vanilla Milk using natural vanilla extract</title><content type='html'>I started making vanilla extract in July 2009 using adaptations of the innumerable recipes &lt;a href="http://www.google.co.in/search?q=how+to+make+vanilla+extract"&gt;available online&lt;/a&gt;. It's now been 7 months since I started so I decided to try out the extract for making vanilla milk (which btw I absolutely love and up to this point made using pulverized vanilla beans).&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Attempt-1:&lt;/b&gt; Measure about 1 cup (approx. 200 ml) of milk in a glass, pour it in a vessel and heat. Add 1/2 tsp vanilla extract to the glass (which has traces of cool milk). The last bit caused the trace amounts of milk in the glass to curdle. However, pouring the heated milk and sugar in the glass did not curdle the rest of it. The whole drink smelt of alcohol though.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Attempt-2:&lt;/b&gt; This time, I decided to not leave trace amounts of milk before adding the extract to the glass. Again however, I did get some smell of alcohol.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Attempt-3:&lt;/b&gt; This time, I decided to do some research online and figure out if anyone else is facing the problem of an alcoholic smell in their extract. As it turns out, it is expected to be this way for a &lt;em&gt;real&lt;/em&gt; extract!! (feature, not a bug ;) ). I also happened to read that adding sugar reduces the alcoholic smell, and that when this extract is used in cooking, most of the alcohol will in fact evaporate and hence not leave behind that alcoholic smell. So, this time, I added some sugar to the glass before adding the vanilla extract, and after pouring in the hot milk, I stirred for about 4-5 minutes before consuming the drink. Viola!! No smell now!!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Another nice &lt;a href="http://stores.ebay.com/Vanilla-Products-USA/Make-Your-Own-Vanilla-Extract.html"&gt;link&lt;/a&gt; explaining a lot of things.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1208869439840711336?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1208869439840711336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1208869439840711336&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1208869439840711336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1208869439840711336'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/02/vanilla-milk-using-natural-vanilla.html' title='Vanilla Milk using natural vanilla extract'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9049100952702809367</id><published>2010-01-29T12:37:00.000-08:00</published><updated>2010-03-03T19:13:05.764-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Eggless Brownies Recipe</title><content type='html'>Makes 1kg Eggless Brownies&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Flour (sifted):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;195g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Dark Chocolate:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;160g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Fat (Butter + Oil)&lt;/td&gt;&lt;br /&gt;&lt;td&gt;120g (80g + 40g)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Water:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;255ml&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Cocoa powder:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;2-3tsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Sugar (castor or powder sugar):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;2 cups&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Salt (if not using salter butter):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1/2 tsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Vanilla extract/essence:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1 &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt; - 1 &lt;sup&gt;4&lt;/sup&gt;/&lt;sub&gt;5&lt;/sub&gt; tsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Baking powder:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1 &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;5&lt;/sub&gt; tsp(for guey brownies) - 1 &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;2&lt;/sub&gt; tsp(for cakey/fluffy brownies)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Chopped walnuts:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;40g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Grease and dust an 8" X 9" pan (basically 72 square inches in area), and pre-heat the oven to 180 degree celcius.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Heat the water on a low flame till it is warm/mildly hot and add to it &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;3&lt;/sub&gt;&lt;sup&gt;rd&lt;/sup&gt; of the flour (65g). Do not remove the utensil from the flame, and continue stirring till the mixture thickens.&lt;/li&gt; &lt;br /&gt;&lt;br /&gt;&lt;li&gt;Add the oil to the chocolate and &lt;a href="http://www.pastrywiz.com/archive/recipe/0084.htm"&gt;melt the chocolate&lt;/a&gt; either in a microwave oven or on a double boiler.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;For making &lt;b&gt;cakey&lt;/b&gt;(not guey) brownies, mix the sugar with the butter and whisk/beat till the mixture is light and fluffy. For making &lt;b&gt;guey&lt;/b&gt; brownies, just mix the sugar with the flour and water mixture you just made.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Mix the baking powder, cocoa powder and salt (if using unsalted butter) with the rest of the flour and sift it again a couple of times to mix them uniformly well.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Add the vanilla extract with the flour and water mixture and mix it well.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Now, mix all the portions that you have with each other and add the walnuts to this mixture. Make sure you mix it well. Perform this step quickly and briskly so that the baking powder doesn't wear out by the time you start baking it.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Transfer the contents to the greaded and dusted pan, and bake in the oven for 45-55 minutes (or till you think it is done).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Remove frm over and wait for it to cool a bit (about 30-45 minutes), cut into pieces and enjoy!!&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9049100952702809367?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9049100952702809367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9049100952702809367&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9049100952702809367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9049100952702809367'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/01/eggless-brownies-recipe.html' title='Eggless Brownies Recipe'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6085413897418953644</id><published>2010-01-16T23:35:00.000-08:00</published><updated>2010-01-29T12:46:23.958-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='health'/><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>One week of salad</title><content type='html'>I started having salad last week for lunch every day, and it's going pretty well as of now. Bought supplies for the next week today.&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Iceberg lettuce&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Lettuce&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Purple/Green cabbage&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Brocolli&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Red/Green/Yellow Capsicum&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Tomatoes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cherry Tomatoes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Baby corn&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fresh Green Peas&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finely chopped carrots&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Green/Black Olives&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Soaked and sprouted moong and moth&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Extra Virgin olive Oil (for the dressing)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Honey (for the dressing)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Salt (for the dressing, added just before eating)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Mixed Herbs (for the dressing)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Cut and mix whatever you want to have in the salad. I generally choose about 6-8 items every day, and mix and match in any order I feel fit.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Introduce the dressing (generally just olive oil and honey) except for the salt since the salt will make the vegetables dehydrate and lose their water. I take this tossed salad to office, so it is about 3hrs time from the time I mix it to the time that I consume it.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6085413897418953644?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6085413897418953644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6085413897418953644&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6085413897418953644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6085413897418953644'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/01/one-week-of-salad.html' title='One week of salad'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-5816087370410339871</id><published>2010-01-09T10:14:00.000-08:00</published><updated>2010-01-09T13:04:24.608-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>It's funny to know....</title><content type='html'>.... that something you have thought of doing and have done has been thought of by someone else across the world and done in almost &lt;em&gt;exactly&lt;/em&gt; the same way as you have....&lt;br /&gt;&lt;br /&gt;It's saddening and encouraging at the same time. Saddening because it's not something novel any more. Someone has "been there.. and done that..". Encouraging because you know that you are not having any insanely weird and impractical thoughts and ideas and that you are solving some &lt;em&gt;real&lt;/em&gt; problem out there.&lt;br /&gt;&lt;br /&gt;This is the second time that this has happened to me, and it is a real confidence booster I should say!!&lt;br /&gt;&lt;br /&gt;The project under question is &lt;a href="http://dhruvbird.com/stickylinks/"&gt;StickyLinks&lt;/a&gt;. The exact same thing was done in 1997 and is called &lt;a href="http://webcitation.org/archive.php"&gt;WebCite&lt;/a&gt;. The funny thing is that it was made for the exact same problem that I was trying to solve, which probably explains why it turned out to be so similar.&lt;br /&gt;&lt;br /&gt;The thing that still gets to me is that even the layout of the version page (view where a version of a certain page is displayed) is almost identical. Both projects use frames. Both projects have a bookmarklet.&lt;br /&gt;&lt;br /&gt;However, there are ways I plan to extend StickyLinks, so stay posted!!&lt;br /&gt;&lt;br /&gt;ps. My next idea: To develop a search engine that will let you search for already existing ideas/concepts/implementations of thoughts that you may have and may think are novel. I use the internet (esp. the search engines) a lot to search for projects that I may want to undertake, just to check if it's already been done by someone else. Most of the time I have to search a lot of times, tweaking my search keywords ever so slightly in the hope of a match. Many a times, I fail to chance upon the right set of keywords (the words that the original author used to describe his/her idea/project) and am under the impression that the idea I have is novel/un-thought of. However, many times it does happen that such a thing does not exist. However, it is only time that lets you ascertain this (if a similar concept exists, you hear of it sooner rather than later, assuming you have shared it with friends).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-5816087370410339871?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/5816087370410339871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=5816087370410339871&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5816087370410339871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/5816087370410339871'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/01/its-funny-to-know.html' title='It&apos;s funny to know....'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8650111674079917492</id><published>2010-01-09T09:07:00.000-08:00</published><updated>2010-01-17T10:02:40.227-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Chocolate Rum Balls</title><content type='html'>This is been pending for a while now.&lt;br /&gt;Barbara asked me how to make these last year!!!! (realistically, about a month ago) and I haven't yet gotten back to her. That is pretty bad of me.&lt;br /&gt;Here goes nothing....&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;br /&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Chocolate sponge cake:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 250g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Dark chocolate:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 250g (preferably &amp;ge 40% cocoa)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Fresh Cream:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 100g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Walnuts:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 100g (or to your taste)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Rum:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;to taste&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Crumble the cake well into.... well crumbles.... Use very soft hands to do this.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Mix the rum with the crumbled cake, but don't form a paste.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Melt the chocolate (always over steam).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Heat the cream in a saucepan till it is simmering and mix it well with the melted chocolate.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make small pieces of the walnuts and add them to the chocolate and cream mixure and mix well again.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Empty the cake crumbs into the mixture above and mix till it forms a solid paste which can be rolled into balls. &lt;b&gt;Note:&lt;/b&gt; You may need more/less of the cake crumbs depending on how watery/viscous the chocolate and cream mixture is.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Roll them into balls!!!! You can put a thin layer of melted butter or ghee (clarified butter) on your palms so that the stuff doesn't stick to your palms while mixing.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Merry Christmas, and have a Great New Year!! :) :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8650111674079917492?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8650111674079917492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8650111674079917492&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8650111674079917492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8650111674079917492'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/01/chocolate-rum-balls.html' title='Chocolate Rum Balls'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6099777861586290755</id><published>2010-01-09T08:24:00.000-08:00</published><updated>2010-01-09T09:07:30.620-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Gaajar Halwa</title><content type='html'>I met Appu and Abbas today, and we had this nice talk about carbs. and how the lesser there are around and within us, the better it is for our kind of lifestyle. I have steadily reduced the amount of sugar I have with my cup of tea or &lt;a href="http://www.indianexpress.com/news/remedial-recipes/520070/0"&gt;ukada (or ukado since it is a gujarati recipe)&lt;/a&gt; and am okay with it.&lt;br /&gt;The recipe below is in complete defiance of the statements above. However in my defense, I used much less sugar than I usually would have.&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;br /&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Grated fresh red carrots(gaajar):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 1kg&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Cow's Milk (preferably full fat containing ≥ 9% SNF):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;about 2ltr&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Sugar:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;to your taste, but you could start off with 200g&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Cow's Ghee (clarified butter):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;2-3 Tbsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Elaichi:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;seeds of 4-5 pods, well separated&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Elaichi powder:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1 tsp (or to taste)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Take the Ghee in a saucepan and add the elaichi seeds, and heat the ghee till it is hot, and &lt;i&gt;just&lt;/i&gt; till you hear a pop sound.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;br /&gt;Add the grated carrots and coat every bit of them with the Ghee (on a slow flame).&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_18pHo--rYtE/S0iyOIuK8ZI/AAAAAAAACHE/mIWtpsgtNFU/s1600-h/Image0052.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_18pHo--rYtE/S0iyOIuK8ZI/AAAAAAAACHE/mIWtpsgtNFU/s320/Image0052.jpg" alt="" id="BLOGGER_PHOTO_ID_5424781707096813970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;br /&gt;Heat the milk in a saucepan till it is hot.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_18pHo--rYtE/S0izB4FuMZI/AAAAAAAACHM/609c0NErIac/s1600-h/Image0053.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_18pHo--rYtE/S0izB4FuMZI/AAAAAAAACHM/609c0NErIac/s320/Image0053.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424782595985387922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;br /&gt;Pour some (about 500ml) of the hot milk into the carrot and ghee mixture, turn up the flame to medium and stir continuously.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_18pHo--rYtE/S0izowvg2NI/AAAAAAAACHU/8ckjtCLZcVI/s1600-h/Image0054.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/_18pHo--rYtE/S0izowvg2NI/AAAAAAAACHU/8ckjtCLZcVI/s320/Image0054.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424783264028088530" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;The milk will come to a boil.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_18pHo--rYtE/S0iz__blRPI/AAAAAAAACHc/Yq6wONQdTEU/s1600-h/Image0055.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_18pHo--rYtE/S0iz__blRPI/AAAAAAAACHc/Yq6wONQdTEU/s320/Image0055.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424783663108015346" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;We want to make mawa (or khoa) from the 1.5ltr of remaining milk. You can find out how this is done. Basically it involves simmering the milk till the water almost vaporizes. The semi-solid mass that is left will be used later in the making of our halwa.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Once the mawa is made, mix it with the rest of the carrot mixture (which should have considerably thickened by now. I hope you were continuously stirring it all this while!!).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Also add the sugar. The sugar will immediately melt and give off a lot of water. We need to burn off all this water.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_18pHo--rYtE/S0i1257aQ3I/AAAAAAAACHk/gM0Qa4JbRrI/s1600-h/Image0056.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_18pHo--rYtE/S0i1257aQ3I/AAAAAAAACHk/gM0Qa4JbRrI/s320/Image0056.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424785706035331954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Stir continuously till the water evaporates and we are left with a semi-solid mass (more liquidy though).&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_18pHo--rYtE/S0i2V8smhgI/AAAAAAAACHs/-XsnujeLdRQ/s1600-h/Image0057.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_18pHo--rYtE/S0i2V8smhgI/AAAAAAAACHs/-XsnujeLdRQ/s320/Image0057.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424786239354471938" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Evenly sprinkle the elaichi powder over this mass and stir continuously.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_18pHo--rYtE/S0i2nG-aQVI/AAAAAAAACH0/ymMdivfymMM/s1600-h/Image0059.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_18pHo--rYtE/S0i2nG-aQVI/AAAAAAAACH0/ymMdivfymMM/s320/Image0059.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424786534171296082" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Till the mixture thickens to a consistency of your choice.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_18pHo--rYtE/S0i3P_zQrhI/AAAAAAAACH8/VLp8qmZJE0o/s1600-h/Image0060.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/_18pHo--rYtE/S0i3P_zQrhI/AAAAAAAACH8/VLp8qmZJE0o/s320/Image0060.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5424787236620119570" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Let it cool, and enjoy!!!! :)&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/div&gt;&lt;br /&gt;btw, this is the 100&lt;sup&gt;th&lt;/sup&gt; post on this blog!!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6099777861586290755?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6099777861586290755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6099777861586290755&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6099777861586290755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6099777861586290755'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2010/01/gaajar-halwa.html' title='Gaajar Halwa'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://1.bp.blogspot.com/_18pHo--rYtE/S0iyOIuK8ZI/AAAAAAAACHE/mIWtpsgtNFU/s72-c/Image0052.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-4346536353420017101</id><published>2009-11-24T08:12:00.000-08:00</published><updated>2009-11-29T06:04:54.057-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>An O(1) approach to the LFU page replacement algorithm</title><content type='html'>&lt;b&gt;LFU: Least Frequently Used&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The LFU algorithm evicts pages that have been &lt;b&gt;least frequently&lt;/b&gt; used. If the cache is full, and one more page needs to be added, the page that has been used the &lt;b&gt;least&lt;/b&gt; number of times is evicted from the cache.&lt;br /&gt;&lt;br /&gt;How can we efficiently implement this algorithm? Each operation of the LRU algorithm can be implemented in worst case O(1) complexity. We can do the same for LFU as well.&lt;br /&gt;&lt;br /&gt;For this, we maintain a doubly linked list where each node of the linked list looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;struct CountNode&lt;br /&gt;{&lt;br /&gt; CountNode *prev, *next;&lt;br /&gt; PageNode *root;&lt;br /&gt; int count;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each node of this linked list points to the root of another doubly linked list which holds the actual pages. All the pages in this 2&lt;sup&gt;nd&lt;/sup&gt; linked list have the same usage count. The node for this linked list looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;struct PageNode&lt;br /&gt;{&lt;br /&gt; PageNode *prev, *next;&lt;br /&gt; CountNode *parent;&lt;br /&gt; char pageData[4096];&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_18pHo--rYtE/SwwRdF3uTKI/AAAAAAAACGE/H8it1gJfDNk/s1600/lfu_O1_impl.PNG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 232px; height: 320px;" src="http://1.bp.blogspot.com/_18pHo--rYtE/SwwRdF3uTKI/AAAAAAAACGE/H8it1gJfDNk/s320/lfu_O1_impl.PNG" alt="" id="BLOGGER_PHOTO_ID_5407716444054899874" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The PageNode's parent member points to that CountNode of which it is a child. So there is a direct pointer from every PageNode to the corresponding CountNode it falls under. These extra links are not shown in the diagram to keep it simple.&lt;br /&gt;&lt;br /&gt;The CountNode linked list's count member holds the usage count of each element under that node. So each PageNode under a CountNode having a count value of 6 will have been accessed 6 times.&lt;br /&gt;&lt;br /&gt;&lt;b style="font-size:larger;"&gt;How to implement the various operations?&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Insetion:&lt;/b&gt; To insert, we always add the new PageNode to the CountNode list with the count value of 1. This will always be the first node in the linked list pointed to by &lt;i&gt;head&lt;/i&gt;. If the first CountNode pointed to by &lt;i&gt;head&lt;/i&gt; is greater than 1, we just add a new CountNode with a count value of 1 and then perform the insertion. All this can be done in time &lt;b&gt;O(1)&lt;/b&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Deletion:&lt;/b&gt; We will delete nodes, only if the cache is full, and this deletion will always happen from the CountNode list which has the least count. So, we just follow &lt;i&gt;head&lt;/i&gt;, and delete a PageNode in such a CountNode. Additionally, if that CountNode happens to become empty because of this deletion, we also delete that CountNode. This too takes time &lt;b&gt;O(1)&lt;/b&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Access:&lt;/b&gt; Access involves incrementing the count of a PageNode, i.e. moving the accessed PageNode from it's CountNode list to a CountNode list with a count value that is &lt;b&gt;1&lt;/b&gt; greater than it's current count value. We maintain pointers to the CountNode in the PageNode. This makes deleting the PageNode(and possibly the CountNode to which it belonged) a constant time operation. After that is done, we can insert a new CountNode that has a count value of 1 more than the CountNode from which the accessed PageNode was removed. If such a CountNode does not exist, we can always create it first. All this can also be done in constant time, making the whole process &lt;b&gt;O(1)&lt;/b&gt;.&lt;p&gt;A lot of people have asked me how they can get to a PageNode in the first place. The answer to that question is that we maintain a hash table which hashes an object(page address, integer, etc...) such that accessing it (getting to the PageNode that that object corresponds to) can be accomplished in O(1) time.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;This implementation takes advantage of the fact that:&lt;ol&gt;&lt;li&gt;When we add a new object to the cache, it is always going to start off with a use count of 1, and&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Whenever we access an object, it's use count will go up by exactly 1.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;table border="0" style="width:100%;"&gt;&lt;tr&gt;&lt;td&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;digg_url = 'http://dhruvbird.blogspot.com/2009/11/o1-approach-to-lfu-page-replacement.html';&lt;br /&gt;&lt;/script&gt;&lt;script src="http://digg.com/tools/diggthis.js" type="text/javascript"&gt;&lt;/script&gt;&lt;/td&gt;&lt;br /&gt;&lt;td style="width:10px;"&gt;&lt;/td&gt;&lt;td&gt;I haven't found any resource that says that LFU can be implemented in time O(1). If you (reader) find any, please do let me know.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-4346536353420017101?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/4346536353420017101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=4346536353420017101&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4346536353420017101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/4346536353420017101'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/11/o1-approach-to-lfu-page-replacement.html' title='An O(1) approach to the LFU page replacement algorithm'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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://1.bp.blogspot.com/_18pHo--rYtE/SwwRdF3uTKI/AAAAAAAACGE/H8it1gJfDNk/s72-c/lfu_O1_impl.PNG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8799806804591346528</id><published>2009-11-21T08:11:00.000-08:00</published><updated>2009-11-21T09:05:23.584-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The Missionaries and Cannibals Problem</title><content type='html'>Some reading before you start: &lt;a href="http://en.wikipedia.org/wiki/Missionaries_and_cannibals_problem"&gt;http://en.wikipedia.org/wiki/Missionaries_and_cannibals_problem&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b style="font-size:larger;"&gt;Problem Statement:&lt;/b&gt;&lt;br /&gt;&lt;i&gt;[From the link above] "In the missionaries and cannibals problem, three missionaries and three cannibals must cross a river using a boat which can carry at most two people, under the constraint that, for both banks, if there are missionaries present on the bank, they cannot be outnumbered by cannibals (if they were, the cannibals would eat the missionaries.) The boat cannot cross the river by itself with no people on board."&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b style="font-size:larger;"&gt;Solution:&lt;/b&gt;&lt;br /&gt;We will solve the generalized version of this problem, wherein there are C cannibals and M missionaries, and at most P people are allowed on the boat simultaneously.&lt;br /&gt;&lt;br /&gt;This problem can be solved by noticing that at every stage, we can encode the state of the problem by using 3 variables, &lt;b&gt;(nc, nm, shore)&lt;/b&gt;. nc is the number of cannibals, nm is the number of missionaries and shore is either 0 or 1, which signifies whether nc and nm represent the count on the source or the destination shore respectively.&lt;br /&gt;&lt;br /&gt;Thus, there are at most 2 x n&lt;sup&gt;2&lt;/sup&gt; states that the problem can be at any point in time.&lt;br /&gt;&lt;br /&gt;We can use BFS (breadth first search) to perform an exhaustive search on the search space to find a solution if one exists. The run-time complexity of doing so is O(CM P&lt;sup&gt;2&lt;/sup&gt;). Below is the Python2.5 code that solves this problem using the BFS search method.&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt; pre.code { font-style: Lucida,"Courier New"; } .number { color: #0080C0; } .operator { color: #000000; } .string { color: #008000; } .comment { color: #808080; } .name { color: #000000; } .error { color: #FF8080; border: solid 1.5pt #FF0000; } .keyword { color: #0000FF; font-weight: bold; } .text { color: #000000; } &lt;/style&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;sys&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;string&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; &lt;span class="name"&gt;collections&lt;/span&gt; &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;deque&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;states&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;dq&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;deque&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;nc&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;nm&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;c&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;combinations&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="operator"&gt;[&lt;/span&gt; &lt;span class="operator"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;gen_combinations&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;global&lt;/span&gt; &lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;combinations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;xrange&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;j&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;xrange&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="name"&gt;j&lt;/span&gt;&lt;span class="operator"&gt;&amp;gt;&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="name"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="name"&gt;j&lt;/span&gt;&lt;span class="operator"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="name"&gt;combinations&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;j&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;init_states&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;global&lt;/span&gt; &lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;xrange&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;j&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;xrange&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;is_valid_state&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;or&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;==&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;or&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;==&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;bfs&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;global&lt;/span&gt; &lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;mn&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;combinations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;while&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;len&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="operator"&gt;&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt; &lt;span class="operator"&gt;==&lt;/span&gt; &lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;depth&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;boat&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;popleft&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="name"&gt;present&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;boat&lt;/span&gt;&lt;br /&gt;        &lt;span class="name"&gt;next&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;boat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;present&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt; &lt;span class="operator"&gt;!=&lt;/span&gt; &lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="keyword"&gt;continue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;present&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;depth&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;comb&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;combinations&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;dx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;dy&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;comb&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="name"&gt;dx&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="name"&gt;dy&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="name"&gt;new_st&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;dx&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;dy&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;depth&lt;/span&gt;&lt;span class="operator"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;next&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;lx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;ly&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;x&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;dx&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;y&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;dy&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;rx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;ry&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;lx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="name"&gt;ly&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;                &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;is_valid_state&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;lx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;ly&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="name"&gt;is_valid_state&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;rx&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;ry&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt; \&lt;br /&gt;                       &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;next&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;lx&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;ly&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt; &lt;span class="operator"&gt;==&lt;/span&gt; &lt;span class="operator"&gt;-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;new_st&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;main&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;global&lt;/span&gt; &lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;c&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;combinations&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;input&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;string&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;split&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;raw_input&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;nc&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;int&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;input&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;nm&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;int&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;input&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;c&lt;/span&gt;  &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;int&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;input&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="name"&gt;init_states&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;gen_combinations&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="name"&gt;dq&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;append&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;bfs&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt; &lt;span class="name"&gt;states&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;nc&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;span class="name"&gt;nm&lt;/span&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;__name__&lt;/span&gt; &lt;span class="operator"&gt;==&lt;/span&gt; &lt;span class="string"&gt;"__main__"&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;sys&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;exit&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;main&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="text"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8799806804591346528?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8799806804591346528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8799806804591346528&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8799806804591346528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8799806804591346528'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/11/missionaries-and-cannibals-problem.html' title='The Missionaries and Cannibals Problem'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8827865583138450241</id><published>2009-11-20T08:32:00.000-08:00</published><updated>2009-11-20T20:20:12.585-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The Capacity C Torch Problem</title><content type='html'>I stumbled upon the Capacity C Torch problem, and found it really interesting.&lt;br /&gt;I'll start off with a few resources:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://aps.cs.nott.ac.uk/2008/05/06/the-capacity-c-torch-problem-2/"&gt;http://aps.cs.nott.ac.uk/2008/05/06/the-capacity-c-torch-problem-2/&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.tutor.ms.unimelb.edu.au/bridge/"&gt;http://www.tutor.ms.unimelb.edu.au/bridge/&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Bridge_and_torch_problem"&gt;http://en.wikipedia.org/wiki/Bridge_and_torch_problem&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.springerlink.com/content/935641175550x527/"&gt;http://www.springerlink.com/content/935641175550x527/&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;b style="font-size:larger;"&gt;Problem Definition:&lt;/b&gt;&lt;br /&gt;There is a group (G) of people that wishes to cross a bridge in the dark. Each person takes a certain amount of time to cross the bridge. There is only 1 torch available. The bridge can hold at most C people at any one time. Every time a group of people cross the bridge, they need to carry the torch with them. One torch is sufficient to light the path for at most C people at a time. When multiple people cross at the same time, the entire group moves at the speed of the slowest person in the group. Given the values for G, C and the time that each person takes to cross, find the least amount of time that would be required to get the whole group across to the other side.&lt;br /&gt;&lt;br /&gt;Expected Complexity of the solution: This solution can be solved using a computer program having a run-time complexity of O(n&lt;sup&gt;2&lt;/sup&gt;), where n is the number of people in the group G  [ |G| = n ].&lt;br /&gt;&lt;br /&gt;&lt;b style="font-size:larger;"&gt;Solution:&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Step-1:&lt;/b&gt; Let us first concentrate only on how we will divide up the people into sets and in what order we will take them across(from size A to side B). We shall re-visit the problem of which person to get back to bring the torch back from side B to side A.&lt;br /&gt;&lt;br /&gt;Let us take some values for the variables G and C.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Example-1:&lt;/i&gt; Let |G|=15 and C=5&lt;br /&gt;&lt;br /&gt;In this case, we can easily divide people into 3 sets of size 5 each.&lt;br /&gt;&lt;br /&gt;We'll sort all the people according to the time they take and group them starting from the slowest person. It can be seen that it is beneficial to club all the slow people together. So, we group the 15&lt;sup&gt;th&lt;/sup&gt;, 14&lt;sup&gt;th&lt;/sup&gt;, 13&lt;sup&gt;th&lt;/sup&gt;, 12&lt;sup&gt;th&lt;/sup&gt; and 11&lt;sup&gt;th&lt;/sup&gt; person in one group, and so on..&lt;br /&gt;&lt;br /&gt;We'll take the 1&lt;sup&gt;st&lt;/sup&gt; group(the one with the 5 fastest people) and move them across first. Since someone needs to come back with the torch, it had rather be the quickest person, so we make sure he is across so that he can return.&lt;br /&gt;&lt;br /&gt;We then take the 2nd group followed by the 3rd.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Example-2:&lt;/i&gt; Let |G|=16 and C=5&lt;br /&gt;&lt;br /&gt;In this case, we are left with 1 person after dividing into sets of 5 each. No matter what we do, we will have 4 sets to deal with. We also note that it makes no sense to move just 1 person across. So, the way we make sets is {2,4,5,5}.&lt;br /&gt;Why not divide as {3,3,4,4}? Well, if we do it like the latter case, then the 1&lt;sup&gt;st&lt;/sup&gt; group will have the time of the 3&lt;sup&gt;rd&lt;/sup&gt; person, and not the 2&lt;sup&gt;nd&lt;/sup&gt; like in the former case. In both case, everything else remains the same, except if the 3&lt;sup&gt;rd&lt;/sup&gt; person is slower than the 2&lt;sup&gt;nd&lt;/sup&gt; one, we will have increased the total time required to cross. This is why we optimally divide in the former way.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Example-3:&lt;/i&gt; Let |G|=17 and C=5&lt;br /&gt;&lt;br /&gt;Again, in this case, we optimally divide as {2,5,5,5}. We note that no other division is possible.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Example-4:&lt;/i&gt; Let |G|=18 and C=5&lt;br /&gt;&lt;br /&gt;Again, in this case, we optimally divide as {3,5,5,5}. We note that no other division is possible.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Example-5:&lt;/i&gt; Let |G|=19 and C=5&lt;br /&gt;&lt;br /&gt;Again, in this case, we optimally divide as {4,5,5,5}. We note that no other division is possible.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step-2:&lt;/b&gt; Now, we shall concentrate on the problem of which people should bring the torch back from side B to side A, so that the next group on side A can cross the bridge to side B. Let us call these people &lt;i&gt;torch bearers.&lt;/i&gt; The same problem also asks and how many at a time to bring back (i.e. How many torch bearers should move from side B to side A without returning) when sets of people are being transferred from side A to side B.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;[This is where you should try figuring out for yourself whether you will send 1, 2, 3, 4, 5 (or more) groups at a time (without the torch bearers returning to side B)]&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;For example, suppose we have a group of people, [1,1,1,1,1,1,4,5,6,7] and C=3.&lt;br /&gt;It is easy to see that once we have 2 people across, we can always get more across without having the torch bearers coming back to side B.&lt;br /&gt;&lt;br /&gt;If we decide to work such that every time 2 torch bearers cross over to side A, they return to side B, we get the following scenario: The mean cost of transferring a set from side A to side B, and back would be 1(1&lt;sup&gt;st&lt;/sup&gt; torch bearer from B to A) + 1(2&lt;sup&gt;nd&lt;/sup&gt; torch bearer from B to A) + 1(1 and 2 back from side A to side B) = 3/1 (since we were able to get only 1 set across).&lt;br /&gt;&lt;br /&gt;If we decide to work such that every time 3 torch bearers cross over to side A, they return to side B, we get the following scenario: The mean cost of transferring a set from side A to side B, and back would be 1 + 1 + 1 + 1(1, 2 and 3 back from side A to side B) = 4/2 (since we were able to get 2 sets across).&lt;br /&gt;&lt;br /&gt;Similarly, if we decide to work such that every time 5 torch bearers cross over to side A, they return to side B, we get the following scenario. The mean cost of transferring a set from side A to side B would be 5+1(1, 2 and 3 back from side A to side B)+1(1 from side B to side A)+1(1, 4 and 5 back from side A to side B) = 8/4 (since we were able to get 4 sets across).&lt;br /&gt;&lt;br /&gt;We observe that the mean cost went down if we took more torch bearers at a time. However, it is also easy to see that this is not always true. It depends entirely on the time that each torch bearer takes to cross the bridge.&lt;br /&gt;&lt;br /&gt;We could also have a case where we have more than 3 torch bearers crossing from B to A, without coming back (like we sent 5 torch bearers at once in the example above).&lt;br /&gt;&lt;br /&gt;We can NOT make a greedy choice as to how many sets to take across. i.e. How many torch bearers to send from B to A without them returning. This is where dynamic programming comes to the rescue.&lt;br /&gt;&lt;br /&gt;Suppose |G|=47 and C=5&lt;br /&gt;We will optimally divide into sets {2,5,5,5,5,5,5,5,5,5}.&lt;br /&gt;&lt;br /&gt;The best way to take all 10 sets across is the best of:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The best way to take all across without any torch bearer coming back unless all &lt;i&gt;initial&lt;/i&gt; groups are across&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The best way to take the first 9 groups &lt;i&gt;plus&lt;/i&gt; the best way to take the last group&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The best way to take the first 8 groups &lt;i&gt;plus&lt;/i&gt; the best way to take the last 2 groups&lt;/li&gt;&lt;br /&gt;&lt;li&gt;...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The best way to take the first group &lt;i&gt;plus&lt;/i&gt; the best way to take the last 9 groups&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;The cost for computing these values using dynamic programming or memoization is O(n&lt;sup&gt;2&lt;/sup&gt;). We can use these techniques because there are many overlapping sub-problems, and the other requirements for dynamic programming match.&lt;br /&gt;&lt;br /&gt;We can pre-compute the cost required to take 1, 2, 3, 4, etc... groups at a time and store it in a lookup table. The time required to do this is O(n&lt;sup&gt;2&lt;/sup&gt;).&lt;br /&gt;&lt;br /&gt;Thus, we can solve the whole problem in time O(n&lt;sup&gt;2&lt;/sup&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8827865583138450241?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8827865583138450241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8827865583138450241&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8827865583138450241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8827865583138450241'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/11/capacity-c-torch-problem.html' title='The Capacity C Torch Problem'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-8888084833018688485</id><published>2009-10-25T05:30:00.000-07:00</published><updated>2009-10-25T05:32:56.177-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tool'/><title type='text'>Wordlist for GRE</title><content type='html'>Hello people, I have made a simplistic wordlist tool for GRE preparation. I've been asked by a well-wisher to post it here, so here it is: &lt;a href="http://wordlist.dhruvbird.com/"&gt;http://wordlist.dhruvbird.com/&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-8888084833018688485?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/8888084833018688485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=8888084833018688485&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8888084833018688485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/8888084833018688485'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/10/wordlist-for-gre.html' title='Wordlist for GRE'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-22421039527517806</id><published>2009-10-17T23:53:00.000-07:00</published><updated>2011-08-09T08:25:32.436-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Masala Matrix</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;Just a ready reckoner for me to look up to figure out how vegetables and lentils are made at my home.&lt;br /&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;table border="1" style="width: 100%;"&gt;&lt;tbody&gt;&lt;tr&gt; &lt;th&gt;Recipe&lt;/th&gt; &lt;th&gt;Fat&lt;/th&gt; &lt;th&gt;Rai (mustard seeds)&lt;/th&gt; &lt;th&gt;Jeera&lt;/th&gt; &lt;th&gt;Methi seeds&lt;/th&gt; &lt;th&gt;Hing (asafoetida)&lt;/th&gt; &lt;th&gt;Green chilli (whole)&lt;/th&gt; &lt;th&gt;Kadi patta&lt;/th&gt; &lt;th&gt;Haldi&lt;/th&gt; &lt;th&gt;Salt&lt;/th&gt; &lt;th&gt;Ginger paste&lt;/th&gt; &lt;th&gt;Dhania and jeera powder to season&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Dudhi (bottle gourd)&lt;/td&gt; &lt;td&gt;Ghee&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Tendli&lt;/td&gt; &lt;td&gt;Ghee/Oil&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Karela (bitter gourd)&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#karela"&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Ghee&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Dudhi + Dal (chana or tur daal)&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#dudhidaal"&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Ghee&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Cauliflower (fool gobi)&lt;/td&gt; &lt;td&gt;Oil&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Cabbage (patta gobi)&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#cabbage"&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Oil&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Tur Daal&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#turdaal"&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Ghee&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Aaloo mutter and tomato sabzi&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#aaloomuttertomato"&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Ghee&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Pumpkin (bhopda) sabzi&lt;/td&gt; &lt;td&gt;Ghee/Oil&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Maybe&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Fanshi (french beans) sabzi&lt;/td&gt; &lt;td&gt;Ghee/Oil&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806#fanshi"&gt;[6]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt; &lt;td&gt;Moong daal (yellow)&lt;/td&gt; &lt;td&gt;Ghee/Oil&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;Yes&lt;/td&gt; &lt;td&gt;No&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="karela"&gt;&lt;/a&gt;[1]: Also put jaggery(gud), kaju(cashew) and kishmish(sultanas).&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="dudhidaal"&gt;&lt;/a&gt;[2]: Also put jaggery(gud), kokam and corriander leaves to season.&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="cabbage"&gt;&lt;/a&gt;[3]: You may optionally add tomatoes halfway through cooking the cabbage, and also add jaggery(gud) if you add tomatoes.&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="turdaal"&gt;&lt;/a&gt;[4]: You can also add jaggery(gud) and kokam. It gives a very good taste to the daal. If you don't want to add gud and kokam, you can add lasun(garlic) to give it a nice taste and aroma. A 3&lt;sup&gt;rd&lt;/sup&gt; option is to add tomatoes. If adding tomatoes, leave out the kokam, but you should add jaggery and may optionally add garlic.&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="aaloomuttertomato"&gt;&lt;/a&gt;[5]: You can also put a bit of jaggery(gud) into this preparation.&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=5113647&amp;amp;postID=22421039527517806" name="fanshi"&gt;&lt;/a&gt;[6]: Add Haldi only if you are adding potatoes to the sabzi.&lt;br /&gt;* Dhania powder is generally added only to vegetable preparations and not to daal preparations.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-22421039527517806?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/22421039527517806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=22421039527517806&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/22421039527517806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/22421039527517806'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/10/recipe-cheat-sheet.html' title='Masala Matrix'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2468860619994423514</id><published>2009-10-08T10:40:00.000-07:00</published><updated>2009-10-08T10:47:11.462-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Online hindi dictionary</title><content type='html'>I've been trying to find an online hindi dictionary where I can enter the text in english, and the system transcribes it and searches for the meaning. However, the best results I have got are by using google's transliteration tool(&lt;a href="http://www.google.com/transliterate/indic"&gt;http://www.google.com/transliterate/indic&lt;/a&gt;) followed by a google search for the resulting word.&lt;br /&gt;That apart, this site: &lt;a href="http://www.websters-dictionary-online.org/"&gt;http://www.websters-dictionary-online.org/&lt;/a&gt; seems to have a lot of words. Make sure you choose the "non-english" option when you search.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2468860619994423514?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2468860619994423514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2468860619994423514&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2468860619994423514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2468860619994423514'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/10/online-hindi-dictionary.html' title='Online hindi dictionary'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-719321376007852468</id><published>2009-07-26T21:43:00.000-07:00</published><updated>2009-07-29T00:11:42.789-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Lip-smacking Rasgulla recipe</title><content type='html'>I have tried for long to make rasgullas that taste like the ones I had a Ganguram's in Kolkata. I tried buying rasgullas from many shops, and tried making on my own in the hope of being able to reproduce that taste and texture that had lulled me into have a lot many Rasgullas that rainy day in Kolkata, but to no avail. None came even a close second.... Then, I read somewhere the milk that is curdled should be curdled slowly, taking care not to shock it, so I decided to try it out.... The rest is history....&lt;br /&gt;&lt;br /&gt;The secret of making good, no GREAT tasting rasgullas is in what milk you use and how you go about curdling it and then processing it. Every step is very important, but I wasn't able to get the 2&lt;sup&gt;nd&lt;/sup&gt; one right for quite a while. I finally tasted success (and moth-watering rasgullas) after a long time yesterday. I'll mention how you can go about reproducing them at home....&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Buy cow's milk for making rasgullas. 3.5% to 6.0% fat content is alright. Do not get low fat milk&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Boil it till about 10% of it boils off, taking care to not let it stick to the vessel. To ensure that, keep stirring as you boil it&lt;/li&gt;&lt;br /&gt;&lt;li&gt;For 1 litre of milk, you will need about 2-3 tsp of vinegar or about &lt;sup&gt;3&lt;/sup&gt;/&lt;sub&gt;4&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; of a lime's juice. &lt;b&gt;IMPORTANT: Dilute this juice in about 1 cup(240ml) of water&lt;/b&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, slowly keep adding this juice to the boiling milk, making sure to stir all the time. You can add about &lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;4&lt;/sub&gt;&lt;sup&gt;th&lt;/sup&gt; every time, and stir for a minute. Do this about 4 times, and keep stirring and heating the milk on a low-medium flame. The milk should continuously keep curdling&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The milk is completely curdled when it separates into while solid and a greenish liquid&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make sure that you do this curdling VERY VERY slowly, otherwise the paneer will result in &lt;b&gt;rubbery&lt;/b&gt; rasgullas&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, separate the solid part(paneer) using a muslin cloth and squeeze out as much water as your possibly can. You can wash the paneer to remove the acidic part and squeeze out again, as much water content as you can&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Let this paneer dry up for about 30-45mins&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now, you can follow any standard rasgulla recipe that tells you to knead the dough, form balls and cook them in boiling sugar syrup. However, I would like to clarify a few things&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The ratio of sugar:water in the boiling syrup should be 1:6 or lesser. I have heard some people boil the paneer balls in just water&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make sure you roll the paneer balls tight and their surface is smooth. You can smear a bit of ghee(clarified butter) on your hands while rolling to ensure that they don't stick to your palm, and result in smooth balls&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Use these rasgullas as rasgullas by dipping them in 1:2(sugar:water) syrup or by squeezing out the water(on cooling) and dipping them in rabri(to make yummy rasmalai!!!!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-719321376007852468?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/719321376007852468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=719321376007852468&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/719321376007852468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/719321376007852468'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/lip-smacking-rasgulla-recipe.html' title='Lip-smacking Rasgulla recipe'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9146887607693198430</id><published>2009-07-26T21:37:00.000-07:00</published><updated>2009-07-26T21:43:14.345-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='insightful'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Some lessons learnt</title><content type='html'>I have been working closely with this person called "Ramki", and have learnt a lot of interesting things from him that I wish to share. I don't know if I'll keep appending them to this post or create a new one each time. I guess it depends on my mood and the context and other such stuff....&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Never use filenames or variable names in an application that have the name of the product in it. If you ever decide to change the name of the application, you'll have to change all those file and variable names too. This can be a nightmare.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When creating database tables which hold the email address of the user, also use a unique user ID. This allows users to change their email IDs when they please. If you use the email ID as the primary key(like how I was going to), it would not allow a change of email address in a cheap fashion. You would probably have to do an ON UPDATE CASCADE, etc.... and it would be quite an expensive operation.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9146887607693198430?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9146887607693198430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9146887607693198430&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9146887607693198430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9146887607693198430'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/some-lessons-learnt.html' title='Some lessons learnt'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-1741491355615742955</id><published>2009-07-19T10:22:00.000-07:00</published><updated>2009-07-26T21:37:33.946-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='movie'/><title type='text'>Sita Sings The Blues</title><content type='html'>I have to start off by saying that "Sita Sings The Blues" has to be one of the most influential movie I have ever seen. In the sense that it has prompted me to notice and think about a lot of things, and I think especially since I am from India.&lt;br /&gt;&lt;br /&gt;There are many aspects of the movie I really like and I'll try talking about each one in as much detail as I feel necessary.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Lots of things have been left to the imagination of the audience even though a lot many have been explained by way of the random conversations that happen between the 3 narrators in the movie. I think it was very natural to make them say the things that they did, and the way that they casually argue with each other. Also, the conversations weren't casual enough to call them loose.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The music given is quite apt and makes you want to listen to it more often. The title track "Sita in Space" is trance-like in nature and makes use of instruments that sound Indian as well. I felt that the way Lord Rama is first shown being waited upon by Sita and the last scene which has the same scene with the roles reversed is quite an outstanding and representative of a lot of things.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The way that all the characters and their relationships to each other are shown upfront is something really neat. There have been times when I've seen movies and only towards the end have I fully understood how each of the characters are related to each other(when the story depended upon the viewer knowing these relationships up front).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I found the scene is which Kaikayee is shown to be a nurse taking care of Raja Dashrath quite hilarious.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There is another scene in which Nina is shown almost completely undressed, sleeping by her husband's side and her husband wants nothing to do with her when we hear a dog barking in the background. This was a scene that I was most impressed by because it felt so real, since that is exactly what I hear at many places(a dog barking in the night time). The level of attention to detail at places in the movie is really commendable!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;This is a post in progress, and I shall keep updating it regularly.... Till then, you can watch the movie for free &lt;a href="http://www.sitasingstheblues.com/"&gt;here&lt;/a&gt;. The song "Sita In Space" is really nice....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-1741491355615742955?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/1741491355615742955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=1741491355615742955&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1741491355615742955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/1741491355615742955'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/sita-sings-blues.html' title='Sita Sings The Blues'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-2131579262647954860</id><published>2009-07-18T23:08:00.000-07:00</published><updated>2009-07-18T23:36:47.846-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><title type='text'>Garlic and Herb Chapati</title><content type='html'>As always, I'm a bit lazy and don't want to spend time baking bread(it is a very long and drawn out process). However I felt like having focaccia(yes the yummy bread with herbs, garlic and loooots of olive oil), so I decided to make a similarly flavoured chapati and see how it turns out.&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ingredients:&lt;/b&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;table border="0"&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Whole Wheat Flour:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;2 cups + more for dusting&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Water:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;Enough to make a really hard dough(we'll soften it up using lots of Olive Oil!!)&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Softened Salted Butter:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;5 Tbsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;The Best Extra Virgin Olive Oil that you can get your hands on and afford:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;5 Tbsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Mixed Dried Herbs(Parsley, Basil, Oregano, Rosemary, Thyme):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;1-2 Tbsp&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Salt:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;sup&gt;1&lt;/sup&gt;/&lt;sub&gt;4&lt;/sub&gt; tsp or to taste&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Fresh Garlic Paste(it needs to be FRESH!!):&lt;/td&gt;&lt;br /&gt;&lt;td&gt;of about 10 cloves&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;An appetite:&lt;/td&gt;&lt;br /&gt;&lt;td&gt;as much as you can squeeze in!!&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Procedure:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Make a very hard dough from the whole wheat flour, water, herbs and garlic paste(take a really deep sniff, close your eyes, and savour the smell for a while).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Use all the Olive Oil and mix it with the dough to make it hard(very hard to hard).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Use this dough to make about 8-10 portions for rolling out.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Keep tava(pan) for heating up on a slow flame. This will ensure that your tava is heated up evenly and your chapati won't get burnt or cooked unevenly.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Roll out the chapati apply dusting flour regularly to prevent it from sticking. Roll out slightly thicker than standard chapatis because we have garlic and herbs in the dough. They may cause the chapati to puncture if we roll it out too thin. If the chapati gets punctured, then it won't balloon like we want it to.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Cook the first size on a slow flame on the tava just until small boils begin to appear on the top surface.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Turn the chapati over and cook on medium flame, turning the chapati so that the sides are cooked well. Always keep the sides in the center of the flame. The center of the chapati will get cooked well automatically. Now, larger boils will appear on the surface, and the under-surface will have brown spots on it.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Make the flame high, remove the chapati from the tava quickly, remove the tava from the stove and turn the chapati up-side-down on the direct flame. Watch it balloon!!!! (if you've done everything well till now. It took me a month and lots of flat chapatis to get to this stage).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Turn it around for just 1 second(or for symmetry as Appu would put it).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Take it off the flame and apply a little of the melted butter while it is still steaming hot.... Smell it again and again till you think you've attained nirvana....&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Eat it!!!!&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-2131579262647954860?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/2131579262647954860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=2131579262647954860&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2131579262647954860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/2131579262647954860'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/garlic-and-herb-chapati.html' title='Garlic and Herb Chapati'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-9198864969024330839</id><published>2009-07-17T08:30:00.000-07:00</published><updated>2009-07-18T23:09:58.263-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='filesystems'/><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='storage'/><title type='text'>YAVFS</title><content type='html'>Yet Another Versioning File System....&lt;br /&gt;&lt;br /&gt;I've been reading about Versioning and Snapshoting File Systems and here is a mini-dump of what I think so far. Please correct me if I'm mistaken....&lt;br /&gt;&lt;br /&gt;I saw a few file systems with slightly more than a cursory glance, and here are my comments:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.ext3cow.com/Welcome.html"&gt;ext3cow&lt;/a&gt;:&lt;/b&gt; This is very close to what I have in mind for a versioning and continuous snapshoting file system. However, it does not agree with some of my &lt;i&gt;"requirements of a versioning and continuous snapshoting file system"&lt;/i&gt;(below).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.hpl.hp.com/personal/Alistair_Veitch/papers/elephant-hotos/"&gt;Elephant File System&lt;/a&gt;:&lt;/b&gt; This is probably one of the earlier snapshoting/versioning file systems and has been implemented on BSD. It is modeled on BSD's FFS. It does a great job of doing what it says it does. I like everything about it except that I can't use it on Linux.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://en.wikipedia.org/wiki/Write_Anywhere_File_Layout"&gt;NetApp's WAFL&lt;/a&gt;:&lt;/b&gt; This again is mainly solving a different problem, that of providing very fast and safe storage on a storage appliance. To the best of my knowledge, you need to trigger a snapshot(though the process itself does not take more than a few seconds), and the number of snapshots are restricted to a fixed number(32 or 256). This may be done using hourly cron jobs, etc.... The interface of getting previous versions of files is a little clumsy though.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Requirements of a Versioning and Continuous Snapshoting file system:&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;It should be able to store a large number of versions of files and directories alike&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It should be able to do so very fast and without any user intervention. ie. Snapshoting should not be user-triggered, but should be done automatically on every &lt;i&gt;close()&lt;/i&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should not need to re-compile the kernel to get this functionality&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should be able to apply this to a specific directory root. This means that you should be able to snapshot parts of your directory tree. You should not incur a performance penalty for accessing file out of this region of interest&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should have policies for controlling which file to snapshot and version files based on file attributes such as size, name, MIME type, etc....&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The interface for accessing continuous-in-time file versions should be very clean and fairly intuitive, and it should be fairly easy to write tools around it so that GUI/web-based access can be enabled&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should be able to enable this functionality on any file system(and not require to re-format the partition on which you wish to enable it)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should be able to disable this operation at any point in time and get back to accessing the most recent versions of the versioned files using the earlier access methods without any performance penalty. ie. Users should be able to turn on and off this functionality at will and it should allow users to proof this on real workloads without them having to make any drastic changes in their environments&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The operations performed should be safe, and should not result in data corruption&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The system should be easy to install and set up&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;I have in mind something that satisfies the requirements mentioned above....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-9198864969024330839?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/9198864969024330839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=9198864969024330839&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9198864969024330839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/9198864969024330839'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/yavfs.html' title='YAVFS'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5113647.post-6134541635201515987</id><published>2009-07-14T23:54:00.000-07:00</published><updated>2009-07-17T10:05:31.161-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='filesystems'/><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='storage'/><title type='text'>NextGen Storage??</title><content type='html'>I've been thinking about what storage systems have in store for us in the future. I mean I've been trying to follow the progress of these beasts, and the next big thing I think should be storage devices that have in-built support for some form of transactional memory. Lots of file-system drivers are building consistency mechanisms into their implementation by using logging in some form or the other. This logging is happening on the same medium as the one where data is being stored. A consistent view of the data is formed by looking at (data+log) as a unit of storage.&lt;br /&gt;&lt;br /&gt;I think that logging implementations would benefit a great deal if they had some sort of hardware support. For example, NetApp's WAFL is utilizing Flash memory to store it's writes. This I think is a nice way to look at the problem at hand, and if device manufacturers could integrate some of this on to their device using some nice interface, I think that file-systems of the future could be much faster than they presently are.&lt;br /&gt;&lt;br /&gt;The only problem that I do however see with this approach is that the flash memory may wear out and cease to function much faster than the magnetic stable storage. A log-based file-system(or storage structure) would be needed to store the writes on the flash storage(something like JFFS maybe).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5113647-6134541635201515987?l=dhruvbird.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dhruvbird.blogspot.com/feeds/6134541635201515987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5113647&amp;postID=6134541635201515987&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6134541635201515987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5113647/posts/default/6134541635201515987'/><link rel='alternate' type='text/html' href='http://dhruvbird.blogspot.com/2009/07/nextgen-storage.html' title='NextGen Storage??'/><author><name>dhruv</name><uri>http://www.blogger.com/profile/09979424322533140869</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><thr:total>0</thr:total></entry></feed>
