<?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'><id>tag:blogger.com,1999:blog-4376899357092952542</id><updated>2009-09-21T11:39:58.510-07:00</updated><title type='text'>The Eclectic Engineer</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-90538920451590482</id><published>2009-06-20T14:32:00.000-07:00</published><updated>2009-06-20T15:20:38.865-07:00</updated><title type='text'>VGA Pixelclock in Verilog</title><content type='html'>I've been teaching myself Verilog and just recently got VGA working.  More info and full source soon.

&lt;div style="width:320px;text-align:right;"&gt;&lt;embed width="320" height="240" src="http://static.photobucket.com/flash/rss_slideshow.swf?rssFeed=http%3A%2F%2Ffeed291.photobucket.com%2Falbums%2Fll298%2Fmrevilgnome%2FVGA%2Ffeed.rss" type="application/x-shockwave-flash" wmode="transparent"&gt;&lt;/embed&gt;&lt;a href="http://s291.photobucket.com/albums/ll298/mrevilgnome/VGA/" target="_blank"&gt;&lt;img src="http://pic.photobucket.com/share/icons/embed/btn_viewall.gif" style="border:none;" /&gt;&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/4376899357092952542-90538920451590482?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/90538920451590482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=90538920451590482' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/90538920451590482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/90538920451590482'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2009/06/vga-pixelclock-in-verilog.html' title='VGA Pixelclock in Verilog'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-7366968857654855420</id><published>2008-08-14T20:32:00.000-07:00</published><updated>2008-12-11T16:55:06.005-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dividends'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='finance'/><title type='text'>Four line program for historical dividend and split data</title><content type='html'>I spent some time reverse engineering how the charts in Google Finance work.  I was mostly interested in the historical dividend and stock split information.  I finally found it, it's stored as a JavaScript variable in the page.  I wrote a 4 line Python program which will pull the page from Google, find the variable and import it as a list of dictionaries.
&lt;pre class="python" name="code"&gt;import urllib2, re
url = "http://finance.google.com/finance?q=GE"
eventReg = re.compile("_chartConfigObject\.corpActionsArray = eval\('\((.*)\)'\);")
eventData = eval(eventReg.search(urllib2.urlopen(url).read()).group(1))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-7366968857654855420?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/7366968857654855420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=7366968857654855420' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/7366968857654855420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/7366968857654855420'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/08/four-line-program-for-historical.html' title='Four line program for historical dividend and split data'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-1671847017187663101</id><published>2008-08-11T22:47:00.000-07:00</published><updated>2008-08-11T23:19:25.063-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dividends'/><category scheme='http://www.blogger.com/atom/ns#' term='app-engine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='finance'/><category scheme='http://www.blogger.com/atom/ns#' term='dividend-data'/><title type='text'>Small update to div-data</title><content type='html'>There was a bug in the div-update application having to do with the growth calculations.  I had just hard coded the division of the sum of dividends regardless of whether or not their was dividend data for the entire period.  Instead if there isn't data for the entire time period I respond with "--" for those columns.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-1671847017187663101?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/1671847017187663101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=1671847017187663101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1671847017187663101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1671847017187663101'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/08/small-update-to-div-data.html' title='Small update to div-data'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-8072435356433119385</id><published>2008-08-05T17:29:00.000-07:00</published><updated>2008-12-11T17:21:36.606-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dividends'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='finance'/><category scheme='http://www.blogger.com/atom/ns#' term='dividend-data'/><title type='text'>I'm back and brought financial goodies.</title><content type='html'>So I'm back after a rather long unplanned hiatus.  In the intervening time I've moved, hosted several sets of out-of-towners, began a career change and blew the disc between vertebrates S1 and L5.  Blowing my disc was the big one, I've been out of work and flat on my ass for over three weeks.  The meds they give you have really intense side effects, namely mania and psychosis.   The mania manifested itself as an obsession with CD rates and dividend producing equities.&lt;br/&gt;&lt;br/&gt;

While doing my research I stumbled  upon a &lt;a href="http://www.thediv-net.com/2008/07/diy-investment-calculator.html"&gt;blog post&lt;/a&gt; which provides a tutorial on using Google Docs as an investment calculator.  This inspired me to attempt to create my own spreadsheet to help me do research on prospective investments.  I was quickly disappointed by Google Docs' GoogleFinance function, it was just way too limiting.  Primarily I wanted to be able to get things like dividend yield and historical dividend growth.  Yahoo provides historical dividend data exportable as a CSV but I couldn't figure out how to import it into Google Docs and aggregate the data cleanly.  Frustrated but not defeated I cranked out a little web app yesterday which does exactly that and deployed it to Google AppEngine.&lt;br/&gt;&lt;br/&gt;

The URL has one parameter, "t" which is the ticker symbol for the equity that you are interested in.  It returns 5 columns of CSV formatted data: the cash value of the last dividend payment, the date of the last payment, the dividend frequency, the average dividend growth for the previous 5 years, the average dividend growth for the previous 10 years.  If the stock doesn't produce a dividend then the first three fields have a value of "--" and the last two will have a value of 0.0.  In order to use this app in conjunction with GoogleDocs use the formula below replacing A2 with the cell that contains your ticker value.&lt;br/&gt;&lt;br/&gt;

If you do use this application and have suggestions, bug reports or feature requests please contact me.  In addition to me providing you with better data it will help me learn what other people think is important when doing research.&lt;br/&gt;&lt;br/&gt;

&lt;b&gt;Example Formula:&lt;/b&gt;&lt;br/&gt;
&lt;pre&gt;
=importData("&lt;a href="http://dividend-data.appspot.com/?t="&gt;http://dividend-data.appspot.com/?t=&lt;/a&gt;" &amp;amp; $A2)
&lt;/pre&gt;
&lt;b&gt;URL Example:&lt;/b&gt;&lt;br/&gt;
&lt;pre&gt;
&lt;a href="http://dividend-data.appspot.com/?t=IBM"&gt;http://dividend-data.appspot.com/?t=IBM&lt;/a&gt;
&lt;/pre&gt;
Source can be found &lt;a href="http://code.google.com/p/mstump-learning-exercises/source/browse/trunk/python/AppEngine/dividend-data/main.py"&gt;here&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;

&lt;iframe src="http://spreadsheets.google.com/pub?key=pjKFdvLYTgB5E22SzCtXxTw&amp;amp;output=html&amp;amp;gid=0&amp;amp;single=true&amp;amp;widget=true" frameborder="0" height="300" width="500"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-8072435356433119385?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/8072435356433119385/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=8072435356433119385' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/8072435356433119385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/8072435356433119385'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/08/im-back-and-brought-financial-goodies.html' title='I&apos;m back and brought financial goodies.'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-6726760411322173894</id><published>2008-06-09T14:47:00.000-07:00</published><updated>2008-06-09T15:18:25.282-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='QT'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>ISight Screen Saver</title><content type='html'>A while ago I wrote a screen saver for OSX as a learning exercise.  I wanted to learn how to access the camera built into my Macbook Pro and get my feet wet with OSX.  I was going to integrate face recognition using the OpenCV or MPT libraries but never got around to it.  I'm posting it now because some of the guys at work saw it and wanted a copy.  I added the source to my &lt;a href="http://code.google.com/p/mstump-learning-exercises/"&gt;SVN repository&lt;/a&gt; and made a &lt;a href="http://code.google.com/p/mstump-learning-exercises/downloads/list"&gt;binary available via the download site&lt;/a&gt;.

The screen saver is really as minimalistic as it gets.   It uses the new QTKit API for accessing the camera and sets the capture view to the frame size of the inherited ScreenSaverVeiw class.  The QTKit API came out around the time of Leopard's release and is much simpler and has better performance then the old streamgrabber API which had been around since the OS 9 days.

To install the binary just download the zip file, unzip and double click.  It will automatically install itself and become available as a screen saver in the system preferences.

&lt;center&gt;&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/Picture1.png" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_Picture1.png" /&gt;&lt;/a&gt;
&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-6726760411322173894?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/6726760411322173894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=6726760411322173894' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/6726760411322173894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/6726760411322173894'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/06/isight-screen-saver.html' title='ISight Screen Saver'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-120670645395319350</id><published>2008-06-08T16:57:00.000-07:00</published><updated>2008-12-11T17:14:20.813-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computational geometry'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Jarvis March Convex Hull Algorithm in F#</title><content type='html'>I recently obtained the "Dutch"&lt;a href="http://www.amazon.com/Computational-Geometry-Applications-Mark-Berg/dp/3540779736/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1212969817&amp;amp;sr=8-1"&gt; computational geometry book&lt;/a&gt; to supplement the other computational &lt;a href="http://www.amazon.com/gp/product/0123694469"&gt;geometry book that I have&lt;/a&gt;.  Instead of doing what I normally do which is skip directly to the topic that I'm interested in, I've resolved to work through the entire book systematically implementing each of the algorithms in F# or some other functional language.  The purpose is two fold; I know almost nothing about the topic and it's hard to find anything about computational geometry implementations in a functional language.  Who knows, someone may actually find my floundering helpful.&lt;br/&gt;&lt;br/&gt;

Chapter one is all about convex hulls and after doing a little bit of reading I determined that the "Jarvis March" algorithm looked like a good place to start.  I've only included core part of the algorithm here in the blog post.  The entire source including the visualization can be found &lt;a href="http://mstump-learning-exercises.googlecode.com/svn/trunk/fsharp/convex_hull_jarvis/convex_hull.fsx"&gt;here&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;

The algorithm is pretty simple I start first by sorting the point list by the Y coordinate and use the minimum as the starting point for my hull.  I start working through the list trying to find the left most point in relation to the previous point added to the hull; this is the fold_left bit of the _jarvis function.   Once I've found the left most point I append it to the head of the hull list and make a recursive call to _jarvis.   When I reach the point with the maximum Y value I know that I have successfully built the left side of the hull.   I then repeat the entire process but this time instead of searching for the left most point I search for the right most point building up the right side of the hull.   When done I append these two lists together to form the complete hull.&lt;br/&gt;&lt;br/&gt;

&lt;pre name="code" class="f#"&gt;type Point = {
   x :double;
   y :double;
   } with
  
   static member ( = ) a b =
       a.x = b.x &amp;&amp; a.y = b.y;
    
   static member ( &lt;&gt; ) a b =
       not(a = b);
  
end;;
  

type Edge = {
   orig:Point;
   dest:Point;
   } with

   static member ( = ) a b =
       a.orig = b.orig &amp;&amp; a.dest = b.dest;
    
   static member ( &lt;&gt; ) a b =
       not(a = b);
      
end;;
  

let sort point_list =
   List.sort( fun p1 p2 -&gt;
              match Pervasives.compare p1.y p2.y with
              | 0 -&gt; Pervasives.compare p1.x p2.x;
              | _ as v -&gt; v;
             
   ) point_list;;


//    Input:  three points P0, P1, and P2
//    Return: &gt;0 for P2 left of the line through P0 and P1
//            =0 for P2 on the line
//            &lt;0 for P2 right of the line
let testSide p0 p1 p2 =
   (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);;


let isLeft p0 p1 p2 =
   (testSide p0 p1 p2) &gt; 0.;;


let isRight p0 p1 p2 =
   (testSide p0 p1 p2) &lt; 0.;;
  

let rec _jarvis cmp max_point (hull_list:Point list) (input_list:Point list) =      
   match input_list with
   | p::tl -&gt;
  
       let last = List.hd hull_list in
       if last = max_point then hull_list
       else (
      
         let best = List.fold_left(fun p1 p2 -&gt;
                                      if (cmp last p1 p2) then p2
                                      else p1;  
                                   ) p input_list in
                                  
         _jarvis cmp max_point (best::hull_list) tl
        
       )
      
   | [] -&gt; hull_list;;


let jarvis point_list =
   if List.length point_list &lt; 3 then failwith "3 points are required for a polygon";
  
   let point_list = sort point_list in
   let miny = point_list |&gt; List.hd in       
   let maxy = point_list |&gt; List.rev |&gt; List.hd in
      
   let left = _jarvis (isLeft) maxy [miny] point_list in
   let right = _jarvis (isRight) maxy [miny] point_list in
   right @ left |&gt; List.rev;;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-120670645395319350?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/120670645395319350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=120670645395319350' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/120670645395319350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/120670645395319350'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/06/jarvis-march-convex-hull-algorithm-in-f.html' title='Jarvis March Convex Hull Algorithm in F#'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-6576455299469254955</id><published>2008-05-18T10:40:00.000-07:00</published><updated>2008-12-11T18:10:02.344-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='boids'/><category scheme='http://www.blogger.com/atom/ns#' term='XNA'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>XNA, Boids and Buffalo!  Oh My!</title><content type='html'>&lt;span style="font-family:arial;"&gt;So I've spent my time since my last post basically re-writing the entire Boids project.  I've thrown out the GDI UI and have replaced it with an &lt;a href="http://creators.xna.com/"&gt;XNA&lt;/a&gt; based one.  I also rewrote my Boid type to be a class which inherits from the Body type in the &lt;a href="http://www.codeplex.com/FarseerPhysics"&gt;Farseer&lt;/a&gt; 2D physics engine, making the simulation much more realistic.  I’ve now got mass, rotation, torque, collisions, drag and friction to play with.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;I've also refined and rewritten most of my rules, fixing the stationary swarm problem, and have added more random noise making things much more interesting.  Random Boids will peel off from the group, wander around and then later rejoin.  Sometimes the herd separates into two or more sub-groups then later coalesces back into a single herd.  There is definitely some additional tweaking of the rules to be done, but I’m definitely headed in the right direction.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;Oh, and this is one of my favorite parts, I replaced the dots that represented the Boids in the old simulations with little buffalo which rotate to reflect the actual heading of the Boid.  So I have a little herd of buffalo roaming around in my computer, for some reason that makes me very happy.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;This being a learning exercise, I separated the F# rules and logic into multiple files so that I could play with modules and namespaces, and have now tackled almost every subject in the &lt;a href="http://www.amazon.com/Expert-F-Experts-Voice-Net/dp/1590598504"&gt;Expert F#&lt;/a&gt; book.  I don’t quite live in F# yet, but I’m getting much faster and am programming with fewer mistakes which is a good sign.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;Playing with XNA was also quite fun.  I am by no means utilizing all of the functionality provided, but from the limited area that I have touched it appears that Microsoft did a pretty good job.  I played with OpenGL a couple of years ago, creating a planetary body orbit/gravity simulation with my roommate, and I remember it being painful.  With XNA this was definitely not the case, it was very simple to get something up and running quickly.  I am by no means a Microsoft fan-boy but you have to give them credit, they do make nice development tools.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;So where do I go next?  I need to do some mundane things like tweak the existing rules, and port all of the XNA code from C# to F#.  I also need to go through and clean up some of the code, I know I’m not doing several things very efficiently and as a result my laptop has trouble with 100 or more Boids.  I also want to implement several more rules: herd leaders, predator/prey behavior, obstacle avoidance etc…  I still want to rewrite the nearest neighbor search portion of the code, but that is slower going.  One of the problems of being a college drop-out is that I never took discreet math or linear algebra, so understanding some of the algorithms takes a little bit more time than I anticipated.  It’s not exactly hard, just requires more research on my part but it’s good for me.  Also now that I’ve been reading &lt;a href="http://www.amazon.com/gp/product/0123694469"&gt;computational geometry books&lt;/a&gt; I see solutions to problems that I encounter all the time, I just wasn’t aware of the body of work that existed.  I don’t feel too guilty though, I don’t think any of these algorithms were taught at an undergraduate level anyways at the U of Arizona.  Who knows, when I was there 2001-2004 the program was in a real state of decline and was rife with in-fighting and I was all too eager to leave.&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;

&lt;span style="font-family:arial;"&gt;&lt;span style="font-weight: bold;"&gt;FYI:&lt;/span&gt; The project has gotten too big to keep posting code in the blog, so I created a &lt;a href="http://code.google.com/p/fsharp-boids/"&gt;project&lt;/a&gt; on &lt;a href="http://code.google.com/"&gt;Google Code&lt;/a&gt;.&lt;/span&gt;
&lt;center&gt;
&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/boids2.png" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_boids2.png" /&gt;&lt;/a&gt;
&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-6576455299469254955?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/6576455299469254955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=6576455299469254955' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/6576455299469254955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/6576455299469254955'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/05/xna-boids-and-buffalo-oh-my.html' title='XNA, Boids and Buffalo!  Oh My!'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-252181174876878665</id><published>2008-05-11T22:07:00.000-07:00</published><updated>2008-12-11T18:03:54.546-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='boids'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><title type='text'>Boids: Revision Two</title><content type='html'>A friend of mine was in town this week, and haven't had much spare time to play with my side projects.  When I finally got down to it I was able to redo my boids simulation to use operator overloading, boundary rules, and improved some of the rendering.  I based my implementation off of a couple of different sources: a &lt;a href="http://www.vergenet.net/%7Econrad/boids/pseudocode.html"&gt;pseudo-code explanation&lt;/a&gt; of the original boids paper&lt;a href="http://www.vergenet.net/%7Econrad/boids/pseudocode.html"&gt;&lt;/a&gt;, a &lt;a href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/502240"&gt;Python implementation&lt;/a&gt;, and &lt;a href="http://www.koders.com/java/fidE8F468B83DF5CCD939CB4AB0675E36070DA41FA3.aspx?s=boid#L7"&gt;this Java implementation&lt;/a&gt;.  There is one remaining problem that I have yet to figure out.  I originally used the rules laid out in the Python implementation, but the animation was very jerky when rule 3 took affect, and once the dots swarmed together they just sit their stationary.  Finding no difference between his implementation and my own I started looking around for other examples that might give me an insight.  That is when I found the previously mentioned Java implementation which resulted in a much more smooth trajectory.  Unfortunately it still did not fix the problem where after swarming together the boids don't go anywhere.  I'm going to continue to work on it but wanted to post my results thus far.

&lt;pre name="code" class="f#"&gt;
open System;;
open System.Windows.Forms;;
open Microsoft.FSharp.Collections;;

let BOID_COUNT = 40;;

let HEIGHT = 700.;;
let WIDTH = 1000.;;

let WALL = 50.;;
let WALL_FORCE = 30.;;

let MAX_VEL = 800.;;

let R1_CONST = 0.2;;
let R2_CONST = 0.2;;
let R2_RANGE = 100.;;
let R3_CONST = 0.2;;

let FPS = 24;;
let RECT_HW = 3;;


type vector = {
  x : double;
  y : double;
} with   

  member v.mag() = 
    (v.x ** 2.0) + (v.y ** 2.0) |&gt; Math.Sqrt;

  static member ( + )(a:vector, b:vector) = 
    {x = a.x + b.x;
     y = a.y + b.y};

  static member ( - )(a:vector, b:vector) = 
    {x = a.x - b.x;
     y = a.y - b.y};

  [&lt;OverloadID("vect_mul_int")&gt;]
  static member ( * ) (a:vector, b:int) = 
    let bd = Convert.ToDouble(b) in
    {x = a.x * bd;
     y = a.y * bd};

  [&lt;OverloadID("vect_mul_doub")&gt;]
  static member ( * ) (a:vector, b:double) = 
    {x = a.x * b;
     y = a.y * b};

  [&lt;OverloadID("vect_pow_int")&gt;]
  static member ( ** ) (a:vector, b:int) = 
    let bd = Convert.ToDouble(b) in
    {x = a.x ** bd;
     y = a.y ** bd};

  [&lt;OverloadID("vect_pow_doub")&gt;]
  static member ( ** ) (a:vector, b:double) = 
    {x = a.x ** b;
     y = a.y ** b};

  [&lt;OverloadID("vect_div_int")&gt;]
  static member ( / ) (a:vector, b:int) = 
    let bd = Convert.ToDouble(b) in
    {x = a.x / bd;
     y = a.y / bd};

  [&lt;OverloadID("vect_div_double")&gt;]
  static member ( / ) (a:vector, b:double) = 
    {x = a.x / b;
     y = a.y / b};

  static member ( &gt; ) (a:vector, b:double) = 
    a.mag() &gt; b;

  static member ( &lt; ) (a:vector, b:double) = 
    a.mag() &lt; b;

end;;

type boid = {
  pos : vector;
  vel : vector;
} with   

  static member ( + )(a:boid, b:boid) = 
    {pos = a.pos + b.pos;
     vel = a.vel + b.vel};

  static member ( - )(a:boid, b:boid) = 
    {pos = a.pos - b.pos;
     vel = a.vel - b.vel};

  member b.distance b2 =
    Math.Sqrt(Math.Abs(((b.pos.x - b2.pos.x) ** 2.0) - ((b.pos.y - b2.pos.y) ** 2.0)));

  member b.update_pos() = 
    {b with
       pos = b.pos + b.vel};

  member b.neighbors(r, bl) = 
    [for n in bl -&gt; 
       (b.distance(n), n)] |&gt; 
        List.filter(fun (d:double, _) -&gt; d &lt; r) |&gt; 
            List.sort(fun (d1:double, _) (d2:double, _) -&gt; d1.CompareTo(d2)) |&gt; 
                List.tl |&gt; 
                    List.unzip;
    
end;;


let rand = new Random();;


let rand_double(max:double) = 
  Convert.ToDouble(rand.Next(Convert.ToInt32(max) - 1)) + rand.NextDouble();;


let vector_init = 
  {x  = 0.;
   y  = 0.};;


let boid_init = 
  {pos  = vector_init;
   vel  = vector_init};;


let boid_sum_list bl = 
  List.fold_left(+) boid_init bl;;


let boid_adjust_v_to_fps b = 
 { b with
      vel = b.vel / (Convert.ToDouble(FPS))};; 
 
  
let boid_limit_vel b = 
  let v = b.vel.mag() in
  match v with
    | _ when v &gt; MAX_VEL -&gt;
        let vl = v / MAX_VEL in
          { b with
              vel = b.vel / vl}

    | _ -&gt; b;;



let boid_check_x_bounds b =
  match b with
    | _ when b.pos.x &lt; WALL -&gt;
        {b with 
           vel = {b.vel with 
                    x = b.vel.x + WALL_FORCE}};

    | _ when b.pos.x &gt; (WIDTH - WALL) -&gt;
        {b with 
           vel = {b.vel with 
                    x = b.vel.x - WALL_FORCE}};

    | _ -&gt; b;;


let boid_check_y_bounds b =
  match b with
    | _ when b.pos.y &lt; WALL -&gt;
        {b with 
           vel = {b.vel with 
                    y = b.vel.y + WALL_FORCE}};

    | _ when b.pos.y &gt; (WIDTH - WALL) -&gt;
        {b with 
           vel = {b.vel with 
                    y = b.vel.y - WALL_FORCE}};

    | _ -&gt; b;;


let boid_rule1 b_sum count =
  fun (b) -&gt;
    {b with
       vel = b.vel + ((((b_sum.pos - b.pos) * (1. / (count - 1.))) - b.pos) * R1_CONST) };;


let boid_rule2 bl count = 
  fun (b:boid) -&gt;
    let (_, nbl) = b.neighbors(R2_RANGE, bl) in
    let r2b = List.fold_left(fun b1 b2 -&gt; b1 - (b2 - b)) boid_init nbl in
    { b with
        vel = b.vel + ((r2b.pos * (1. / (count - 1.))) * R2_CONST) };;


let boid_rule3 b_sum count =
  fun b -&gt;
    {b with
       vel = b.vel + ((((b_sum.vel - b.vel) * (1. / (count - 1.))) - b.vel) * R3_CONST) };;


let boid_apply_rules b bl b_sum count = 
    b |&gt; boid_check_y_bounds |&gt; boid_check_x_bounds |&gt; boid_rule1 b_sum count |&gt; boid_rule2 bl count |&gt;  
        boid_rule3 b_sum count |&gt; boid_limit_vel |&gt;  boid_adjust_v_to_fps |&gt; fun b -&gt; b.update_pos();;


let get_wall() = 
  let height = Convert.ToInt32(HEIGHT) in
  let width = Convert.ToInt32(WIDTH) in
  let wl = Convert.ToInt32(WALL) in
  [|new Drawing.Point(wl, wl);
    new Drawing.Point(wl, height - wl);
    new Drawing.Point(width - wl, height - wl);
    new Drawing.Point(width - wl, wl) 
  |];;


type BoidControl = class
    inherit UserControl as base
    
    val mutable boidList:(boid list);
    val brap:(Drawing.Point array);
    val count:double;
    val timer:Timer;
    
    new(bl:(boid list)) as this = {                                               
        boidList = bl;                                               
        count = bl |&gt; List.length |&gt; Convert.ToDouble;
        timer = new Timer();
        brap = get_wall();
    } then this.init();
    
    member this.init() = 
        let height = Convert.ToInt32(HEIGHT) in
        let width = Convert.ToInt32(WIDTH) in
        let size = new Drawing.Size(width, height) in

        base.Width &lt;- width;
        base.Height &lt;- height;
        base.MinimumSize &lt;- size;
        base.MaximumSize &lt;- size;
        base.BackColor &lt;- Drawing.Color.White;
        base.SetStyle (ControlStyles.UserPaint, true);
        base.SetStyle (ControlStyles.DoubleBuffer, true);
        base.SetStyle (ControlStyles.AllPaintingInWmPaint, true);

        this.timer.Interval &lt;- FPS;
        this.timer.Tick.Add(fun _ -&gt;
                            this.tick(););

        this.timer.Start();

    member this.boids_update() =
        let b_sum = boid_sum_list this.boidList in
        let al = [for b in this.boidList -&gt; async { return boid_apply_rules b this.boidList b_sum this.count }] |&gt; Async.Parallel in 
        this.boidList &lt;- (Async.Run(al) |&gt; Array.to_list);
        ();
    
    override this.OnPaint e =
        let g = e.Graphics in
        List.iter(fun b -&gt; 
            let x = Convert.ToInt32(b.pos.x) in
            let y = Convert.ToInt32(b.pos.y) in
            let bp1 = new Drawing.Point(x - RECT_HW, y - RECT_HW) in 
            let bp2 = new Drawing.Point(x - RECT_HW, y + RECT_HW) in 
            let bp3 = new Drawing.Point(x + RECT_HW, y + RECT_HW) in 
            let bp4 = new Drawing.Point(x + RECT_HW, y - RECT_HW) in 
            g.FillPolygon(Drawing.Brushes.Red, [|bp1; bp2; bp3; bp4|]);) this.boidList;
        
        g.DrawPolygon(Drawing.Pens.Blue, this.brap);
    
    member this.tick() =
        this.boids_update();
        this.Invalidate();
                
    
end;;


[&lt;STAThread()&gt;]
do

  let height = Convert.ToInt32(HEIGHT) in
  let width = Convert.ToInt32(WIDTH) in
  let size = new Drawing.Size(width, height) in

  let bl = [for x in 1..BOID_COUNT -&gt; {pos = {x = 50. + rand_double(WIDTH - 50.); 
                                              y = 50. + rand_double(HEIGHT - 50.)};
                                       vel = vector_init }] in
  let mw = new Form() in
  let bc = new BoidControl(bl) in
  mw.Controls.Add(bc);
  mw.ClientSize &lt;- size;
  mw.MaximumSize &lt;- mw.Size;
  mw.MinimumSize &lt;- mw.Size;
  mw.Text &lt;- "Boids";
  mw.Show();
  Application.Run(mw);;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-252181174876878665?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/252181174876878665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=252181174876878665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/252181174876878665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/252181174876878665'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/05/boids-revision-two.html' title='Boids: Revision Two'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-1499963769340021023</id><published>2008-05-05T19:12:00.000-07:00</published><updated>2008-05-05T19:15:50.836-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='boids'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Boids!</title><content type='html'>I've got revision one of my boids simulation done.  I'll post more details and code later, but for now here is screen capture.

&lt;center&gt;
&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/boids1.jpg" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_boids1.jpg"&gt;&lt;/a&gt;
&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-1499963769340021023?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/1499963769340021023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=1499963769340021023' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1499963769340021023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1499963769340021023'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/05/boids.html' title='Boids!'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-2722212007249369920</id><published>2008-04-30T11:58:00.001-07:00</published><updated>2008-12-11T18:05:18.405-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Asynchronous workflows in F#</title><content type='html'>Work has been really busy and I haven't had time to work on any of my side projects until last night, when I had chance to play with asynchronous workflows in F#.  I've been wanting to explore to a greater extent what is out there in the world of concurrent and distributed computing, but in order to get started I needed to find some project that would be the catalyst.  Not that having a project is absolutely necessary but it helps keep me on task and focused towards an end goal.  Some of the ideas I've been tossing around were creating an engine to decrypt/encrypt PGP MIME messages, or build a better load/stress engine, both of which require a large up-front investment of time.&lt;br/&gt;&lt;br/&gt;

After about a week of going nowhere I stumbled upon the idea of doing swarm animations, and possibly throw in some additional AI behavior if I had time.  The thing about doing swarm animations is that each entity in the swarm needs to move in relation to each of it's neighbors which involves a nearest neighbor search, one of those computationally costly and hard to tackle problems.  I'm going to start with a really naive brute force approach and evolve it over time, into something more efficient.  One of the things that I've been looking at is the Stolfi and Guibas method for creating Voronoi diagrams in order to speed up the nearest neighbor search.  It's a divide an conquer algorithm so it should lend itself to parallelization, but that comes later, today I start with asynchronous workflows.  The bit of code below had the following runtime on my Dell D620 dual-core laptop when executed using fsi:
&lt;pre&gt;
Async time: 00:00:34.7309424
Sync time: 00:00:40.1761800
&lt;/pre&gt;
&lt;pre name="code" class="f#"&gt;
let largest = 800;;

type point = {
    x : double;
    y : double;
}

let rand = new Random();;

let rand_double(max) = 
  System.Convert.ToDouble(rand.Next(max - 1)) + rand.NextDouble();;

let rec quick_sort(x) =
  match x with
  | pivot :: rest -&gt;
      let left, right = List.partition (( &gt; ) pivot) rest in
      quick_sort left @ pivot :: quick_sort right
  | [] -&gt; [];;

let distance(p1, p2) =
  Math.Sqrt(Math.Abs(((p1.x - p2.x) ** 2.0) - ((p1.y - p2.y) ** 2.0)));;

let time(f) =
  let start = DateTime.Now in
  f();
  DateTime.Now - start;;

let neighbors(p, pl) = 
  [for n in pl -&gt; (distance(n, p), n)];;

let async_neighbors(p, pl) = 
  let a = Async.Parallel[for n in pl -&gt; async {return (distance(n, p), n)}] in
  Async.Run(a);;

let sync_test() =
  let pl = [for x in 1..5000 -&gt; {x=rand_double(largest);y=rand_double(largest)}] in
  [for p in pl -&gt; neighbors(p, pl)] |&gt; ignore;;

let async_test() = 
  let pl = [for x in 1..5000 -&gt; {x=rand_double(largest);y=rand_double(largest)}] in
  let a = Async.Parallel[for p in pl -&gt; async { return neighbors(p, pl) }] in
  Async.Run(a) |&gt; ignore;;

let async_test2() = 
  let pl = [for x in 1..5000 -&gt; {x=rand_double(largest);y=rand_double(largest)}] in
  let a = Async.Parallel[for p in pl -&gt; async { return async_neighbors(p, pl) }] in
  Async.Run(a) |&gt; ignore;;  

let cleanup() = 
  GC.Collect();
  GC.WaitForPendingFinalizers();;

do Console.WriteLine("Async time: " + any_to_string(time(async_test)));;  
do cleanup();;
do Console.WriteLine("Sync time: " + any_to_string(time(sync_test)));;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-2722212007249369920?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/2722212007249369920/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=2722212007249369920' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/2722212007249369920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/2722212007249369920'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/04/asynchronous-workflows-and-concurrency.html' title='Asynchronous workflows in F#'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-5846449740613724503</id><published>2008-04-03T21:28:00.000-07:00</published><updated>2008-12-11T18:08:08.713-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><category scheme='http://www.blogger.com/atom/ns#' term='QC'/><title type='text'>Calling asynchronous Twisted Python code from a blocking function</title><content type='html'>So I had to write a VAPI-XP test for Mercury/HP Quality Center, and the way that this particular test type works is that you write a script in Python, VBScript, or Perl.   This script must have a TestMain function that is called and as soon as that function exits the test is over and everything exits.  This test had to SSH into one of the machines in our virtualized test bed, and tell the machine to netboot in order to pick up the newest build of our server product.&lt;br/&gt;&lt;br/&gt;

I could have just used a call to os.popen() to some SSH client on our intermediary testing host but I had have bad experiences with that in the past, mostly weird behavior with OpenSSH and plink.  I had written some SSH code with the &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt; module &lt;a href="http://twistedmatrix.com/projects/conch/documentation/howto/conch_client.html"&gt;Conch&lt;/a&gt; before so I decided to just go ahead and use that.  So got the SSH code up and running no problem, but how was I to block execution until the event was triggered letting me know that the session was complete?  Furthermore how was I going to get my data back without doing something dirty like a global variable?&lt;br/&gt;&lt;br/&gt;

I talked to some guys on IRC and came up with the solution below.  The SSH code itself isn't that interesting and I've only really included it for completeness the important bits to note is the call to "threads.blockingCallFromThread" in RunSSHCommand and note that I'm passing the deferred that I create in "_runSSHCommand" through each of the SSH classes (usually 'd' or 'self._d') and that it is finally being called in the method "CommandChannel.closed".


&lt;pre name="code" class="python"&gt;
#!/usr/bin/python

class ClientTransport(transport.SSHClientTransport):

   def __init__(self, user, command, d):
       self._user = user
       self._command = command
       self._d = d

   def verifyHostKey(self, pubKey, fingerprint):
       #we don't care accept any host key
       return defer.succeed(1)

   def connectionSecure(self):
       #connection made, instiantiate the class that will handle authentication
       #and pass an instance of the class that embodies the user behavior as a param
       self.requestService(ClientUserAuth(self._user, ClientConnection(self._command, self._d)))


class ClientUserAuth(userauth.SSHUserAuthClient):

   def getPassword(self, prompt = None):
       # this says we won't do password authentication        
       return

   def getPublicKey(self):
       #return the public key which is defined up top as a string
       return keys.getPublicKeyString(data=SSH_PUB_KEY)

   def getPrivateKey(self):
       #return the pricate key which is also defined up top as a string
       return defer.succeed(keys.getPrivateKeyObject(data=SSH_PRIV_KEY))


class ClientConnection(connection.SSHConnection):

   def __init__(self, cmd, d, *args, **kwargs):
       connection.SSHConnection.__init__(self)
       self._command = cmd
       self._d = d


   def serviceStarted(self):
       self.openChannel(CommandChannel(self._command, self._d, conn=self))


class CommandChannel(channel.SSHChannel):
   name = 'session'

   def __init__(self, command, d, *args, **kwargs):
       channel.SSHChannel.__init__(self, *args, **kwargs)
       self.command = command
       self.d = d
       self.data = ""

   def channelOpen(self, data):
       #send an execute request passing the user supplied string.
       self.conn.sendRequest(self,
                             'exec',
                             common.NS(self.command),
                             wantReply=True
                             ).addCallback(self._gotResponse)

   def _gotResponse(self, _):
       #command has returned, send an EOF in order to terminate the connection
       self.conn.sendEOF(self)

   def dataReceived(self, data):
       #append the output data to the string
       self.data = self.data + data

   def closed(self):
       #connection closed, execute the callback on the deferred
       #that we have been passing around
       self.d.callback(self.data)


class ClientCommandFactory(protocol.ClientFactory):
   #factory class for our SSH protocol
  
   def __init__(self, user, command, d):
       self._user = user
       self._command = command
       self._d = d

   def buildProtocol(self, addr):
       protocol = ClientTransport(self._user, self._command, self._d)
       return protocol


def RunSSHCommand(hostname, port, user, cmd):
   #Run an SSH command as a user on a given host, we only use key authentication
   thread.start_new_thread(reactor.run, (False,))
   try:
       return threads.blockingCallFromThread(reactor, _runSSHCommand, hostname, port, user, cmd)
   except:
       return None
  
   reactor.stop()

def _runSSHCommand(hostname, port, user, cmd):
   d = defer.Deferred()
   factory = ClientCommandFactory(user, cmd, d)   
   reactor.connectTCP(hostname, 22, factory)
   return d
  

if __name__ == "__main__":
   RunSSHCommand(hostname, port, user, "register_netboot")

&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-5846449740613724503?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/5846449740613724503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=5846449740613724503' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/5846449740613724503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/5846449740613724503'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/04/calling-asynchronous-twisted-python.html' title='Calling asynchronous Twisted Python code from a blocking function'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-7657682697163369187</id><published>2008-04-03T15:46:00.001-07:00</published><updated>2008-04-03T20:55:14.022-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Formatting code for HTML</title><content type='html'>This being my first blog I didn't have any tricks in my bag for formatting code.  After some Google searching I only found two projects that looked useful.  The first being &lt;a href="http://formatmysourcecode.blogspot.com/"&gt;this project&lt;/a&gt; which wasn't language aware and couldn't do any syntax highlighting.  The second more promising project was &lt;a href="http://www.manoli.net/csharpformat/"&gt;this one&lt;/a&gt; and fortunately he posted the C# source for his library.  Only problem is that it didn't support F# or Python.  I went ahead and added support for those two languages, created a front end for the thing and added support for user specified &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;CSS&lt;/span&gt; documents.  In addition to exposing most of the built-in functionality my app has a preview mode which will display the formatted code in an embedded IE pane.  I've posted my version of the &lt;a href="http://www.eclecticengineers.com/FormatCode.zip"&gt;&lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_1"&gt;library&lt;/span&gt; here&lt;/a&gt;, and I've also made available a &lt;a href="http://www.eclecticengineers.com/FormatCodeBinary.zip"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;pre&lt;/span&gt;-compiled binary&lt;/a&gt;.

&lt;center&gt;
&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/preview.png" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_preview.png" /&gt;&lt;/a&gt;
&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/output.png" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_output.png" /&gt;&lt;/a&gt;
&lt;a href="http://i291.photobucket.com/albums/ll298/mrevilgnome/input.png" target="_blank"&gt;
&lt;img src="http://i291.photobucket.com/albums/ll298/mrevilgnome/th_input.png" /&gt;&lt;/a&gt;
&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-7657682697163369187?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/7657682697163369187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=7657682697163369187' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/7657682697163369187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/7657682697163369187'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/04/formatting-code-for-blog.html' title='Formatting code for HTML'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-282138894848830562</id><published>2008-03-31T17:11:00.000-07:00</published><updated>2008-12-11T18:02:09.956-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='COM'/><category scheme='http://www.blogger.com/atom/ns#' term='interop'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>The no-frills guide to .Net COM interop</title><content type='html'>So there a couple of guides out there that already cover some aspects of this post, but none of them cover all of the topics and many of them are convoluted.   When I publish something to COM I usually end up consuming it via a scripting language like Python, Perl or VBScript so this little howto will reflect what I have found to be best practices.  So here is the no-frills guide to exposing a C# class to COM.
&lt;ul&gt;&lt;li&gt;Create an interface&lt;/li&gt;&lt;li&gt;Create a class that inherits from the interface&lt;/li&gt;&lt;li&gt;Add GUIDs to both interface and class using the "Guid" attribute define in "System.Runtime.InteropServices".  You can use the GUID generation tool found in the "Tools" menu of Visual Studio.&lt;/li&gt;&lt;/ul&gt;

&lt;pre class="csharp" name="code"&gt;
using System;
using System.Runtime.InteropServices;

namespace MyNamespace{

    [Guid("EFB47A26-D84F-4092-927D-232E92FB77E8")]
    public interface IMyComponent{
        [DispId(1)]
        void DoStuff();
    }

    [Guid("8F3DFCDB-45F7-461d-9B19-B140CD6F05FA")]
    public class MyComponent : IMyComponent{
        public void DoStuff() { }
    }
}

&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;span&gt;Add the interface type to the interface, you don't need to know all the details but you should probably use the Dispatch type if this component is being consumed by a dynamically typed language such as Python.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;span&gt;&lt;li&gt;Add the DispId to each method exposed by the interface.  Again this helps when being consumed by Python.  A note of caution though, that once these values are set and you register the component they should not change.  It's not the end of the world if they do, but you will cause headaches for yourself.&lt;/li&gt;&lt;/span&gt;&lt;/ul&gt;

&lt;pre class="csharp" name="code"&gt;
[Guid("EFB47A26-D84F-4092-927D-232E92FB77E8")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyComponent {
    [DispId(1)]
    void DoStuff();
}
&lt;/pre&gt;

&lt;span&gt;&lt;ul&gt;&lt;li&gt;Add the ProgId attribute to the class, set the value to Namespace.ClassName.  This is again one of those things that may not be necessary but helps when using Python, Perl etc...&lt;/li&gt;&lt;li&gt;Add the ClassInterface attribute to the class setting the value to "ClassInterfaceType.AutoDispatch"&lt;/li&gt;&lt;li&gt;Add the ComDefaultInterfaceAttribute setting the value to the type of your interface.  Again, not neccessarily required but Python has all sorts of trouble generating the wrapper classes if it isn't present.&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;

&lt;pre class="csharp" name="code"&gt;
[ProgId("MyNamespace.MyComponent")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComDefaultInterfaceAttribute(typeof(IMyComponent))]
[Guid("8F3DFCDB-45F7-461d-9B19-B140CD6F05FA")]
public class MyComponent : IMyComponent {
  public void DoStuff() { }
}
&lt;/pre&gt;


You need to enable two options in Visual Studio for your project.  Open the properties for your project, and go the the application tab.  Click on the "Assembly Information" button, and enable the make COM visible option.  On the build tab you will also need to enable the register for com interop option at the bottom.  You will also need to sign your assembly; you can use a self generated key through the signing tab of your project's properties.
&lt;span&gt;
Once compiled Visual Studio will auto-register the assembly with COM so that you can test the interface with a script or OLEView.  You can use regasm or build a Wix project to have it installed permanently.&lt;/span&gt;

&lt;pre class="csharp" name="code"&gt;
using System;
using System.Runtime.InteropServices;

namespace MyNamespace
{

    [Guid("EFB47A26-D84F-4092-927D-232E92FB77E8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IMyComponent{
        [DispId(1)]
        void DoStuff();
    }

    [ProgId("MyNamespace.MyComponent")]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    [ComDefaultInterfaceAttribute(typeof(IMyComponent))]
    [Guid("8F3DFCDB-45F7-461d-9B19-B140CD6F05FA")]
    public class MyComponent : IMyComponent{
        public void DoStuff() { }
    }
}

&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-282138894848830562?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/282138894848830562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=282138894848830562' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/282138894848830562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/282138894848830562'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/03/quick-guide-to-net-com-interop.html' title='The no-frills guide to .Net COM interop'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-1787727037287016784</id><published>2008-03-28T10:49:00.000-07:00</published><updated>2008-12-11T18:12:09.460-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>Playing around in WMI with F#</title><content type='html'>Started playing around with WMI in F# in order to update DNS records.  After several hours I finally got it working.  I ran into some problems on the last line of the function below.  Apparently function used with iter() must return "unit" which is the F# equivalent of null.  My anonymous function was returning an obj, appending the "; ()" to the end of the function fixed the return type.  "()" in F# is the notation for the unit type.  The guys from F# hub said this could also be done by using "|&gt; ignore".

&lt;pre name="code" class="f#"&gt;
let UpdateCNameDNSRecord(record:string, value:string) =
    let query = String.Format("SELECT * FROM MicrosoftDNS_CNAMEType WHERE OwnerName='{0}'", record) in
    let scope = (@"\\.\root\MicrosoftDNS") in //new ManagementScope
    let searcher = new ManagementObjectSearcher(scope, query) in
    if (not (value.EndsWith("."))) then
        let value = (value + ".") in
    
    let inputP = [|null; box(value)|] in 
    IEnumerable.iter(
        
            fun (x:ManagementObject) -&gt;
                if (x.Item("PrimaryName").ToString() &lt;&gt; value) then
                   x.InvokeMethod("Modify", inputP) |&gt; ignore
            
       ) (IEnumerable.untyped_to_typed(searcher.Get()));;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-1787727037287016784?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/1787727037287016784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=1787727037287016784' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1787727037287016784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/1787727037287016784'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/03/playing-around-in-wmi-with-f.html' title='Playing around in WMI with F#'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4376899357092952542.post-5635576347032285125</id><published>2008-03-26T10:51:00.000-07:00</published><updated>2008-12-11T18:13:08.627-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='.Net'/><title type='text'>An example win32 service in F#</title><content type='html'>&lt;span style="font-size:100%;"&gt;I needed a small single purpose &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;TCP&lt;/span&gt; socket server for work, and I thought it might be an opportune time to try a new programming language.  My choices were &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Scala&lt;/span&gt;, F# or &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Erlang&lt;/span&gt;.  The target platform was a win2k box and I wanted to be able to register it as a service so F# was the obvious choice.  In the code below I modified the &lt;a href="http://stuff.mit.edu/afs/athena/software/fsharp_v1.1.12/FSharp-1.1.12.3/samples/fsharp/Sockets/Doc.html"&gt;echo server&lt;/a&gt; example from the &lt;a href="http://stuff.mit.edu/afs/athena/software/fsharp_v1.1.12/FSharp-1.1.12.3/README-fsharp.html"&gt;MIT F# site&lt;/a&gt; to run as a service.  &lt;/span&gt;

&lt;pre name="code" class="f#"&gt;
// This sample code is provided "as is" without warranty of any kind. 
// We disclaim all warranties, either express or implied, including the 
// warranties of merchantability and fitness for a particular purpose. 

#light
#r "System.ServiceProcess.dll";;
#r "System.Configuration.Install.dll";;


// Echo Server: opens a service on the localhost which bounces data straight back  

open System.Net
open System.Net.Sockets
open System.Threading
open System.ServiceProcess
open System.ComponentModel
open System.Configuration.Install
open Microsoft.FSharp.Compatibility.CompatArray

let spawn f = (new Thread(new ThreadStart(f))).Start()

type EchoServer = class
    inherit ServiceBase as base
    
    new() as this = {} then base.ServiceName &lt;- "SimpleEchoService"
    
    override this.OnStart(args:string[]) = 
        spawn(this.listen)
            
    member this.listen() =
        let sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP) 
        sock.Bind(new IPEndPoint(IPAddress.Any, 5120))

        let backlog = 2 // number of incoming connections that can be queued for acceptance
        sock.Listen(backlog)

        let port = (sock.LocalEndPoint :?&gt; IPEndPoint).Port 
        Printf.printf "server accepting on port %d...\n" port

        while (true) do
            let csock = sock.Accept() 
            Printf.printf "the server has accepted a client on port %d...\n" port;
            spawn(fun () -&gt; 
                let total = ref 0 
                try 
                    let buf = Bytearray.create 64
                    let stream = new NetworkStream(csock) 
                    while true do
                        Printf.printf "server reading...\n";
                        let nread = stream.Read(buf,0,64) 
                        Printf.printf "server read %d...\n" nread;
                        stream.Write(buf,0,nread);
                        total := !total + nread;
                    done;
                with 
                    | :? Sockets.SocketException 
                    | :? System.IO.IOException -&gt; 
                        Printf.printf "client gone: server read %d bytes from that client\n" !total;
                        flush stdout
            )
        done

end&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4376899357092952542-5635576347032285125?l=eclecticengineer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eclecticengineer.blogspot.com/feeds/5635576347032285125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4376899357092952542&amp;postID=5635576347032285125' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/5635576347032285125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4376899357092952542/posts/default/5635576347032285125'/><link rel='alternate' type='text/html' href='http://eclecticengineer.blogspot.com/2008/03/example-win32-service-in-f.html' title='An example win32 service in F#'/><author><name>The Eclectic Engineer</name><uri>http://www.blogger.com/profile/01631646826485085670</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='14732298446578115395'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry></feed>