<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="http://pepijndevos.nl/" xmlns="http://www.w3.org/2005/Atom">
 
	<title>Wishful Coding</title>
	<subtitle>Didn't you ever wish your computer understood you?</subtitle>
	<link href="http://pepijndevos.nl/atom.xml" rel="self"/>
	<link href="http://pepijndevos.nl/"/>
	<updated>2013-05-08T05:29:25-07:00</updated>
	<id>http://pepijndevos.nl/</id>
	<author>
		<name>Pepijn de Vos</name>
		<email>pepijndevos+atom@gmail.com</email>
	</author>

	
	<entry>
		<title>Team Relay Chat: File Sharing and Registration</title>
		<link href="http://pepijndevos.nl/2013/05/08/file-sharing-and-registration.html"/>
		<updated>2013-05-08T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2013/05/08/file-sharing-and-registration</id>
		<content type="html">&lt;p&gt;Almost a month ago I uploaded the first version of Team Relay Chat, now I have a couple of users and some nice new features to show you.&lt;/p&gt;

&lt;h3 id='registration'&gt;Registration&lt;/h3&gt;

&lt;p&gt;Previously it required several not-so-trivial steps to add a user to your IRC server. Now you can just enable the registration module. Your employees will be able to just pick a user name and password and be good to go.&lt;/p&gt;

&lt;p&gt;&lt;img src='http://teamrelaychat.nl/wiki/_media/screen_shot_2013-04-30_at_11.11.05_am.png' alt='registration page' /&gt;&lt;/p&gt;

&lt;h3 id='file_sharing'&gt;File Sharing&lt;/h3&gt;

&lt;p&gt;I realised file sharing was important right from the beginning. It has been possible to share files from the web interface for quite some time now, but I added two important features.&lt;/p&gt;

&lt;p&gt;You can now share files from your desktop client too. Just send them to Hubot via DCC, and Hubot will take care of uploading them.&lt;/p&gt;
&lt;iframe src='http://www.youtube.com/embed/DgTKyHY-3pQ' allowfullscreen='allowfullscreen' frameborder='0' height='315' width='560'&gt; &lt;/iframe&gt;
&lt;p&gt;The web client now displays a preview of uploaded images inline. No more no less.&lt;/p&gt;

&lt;h3 id='documentation'&gt;Documentation&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;m working hard to make TRC as easy to use as possible. Both by just making things simpler and by writing documentation and even doing some videos.&lt;/p&gt;

&lt;p&gt;All of this can be found at &lt;a href='http://teamrelaychat.nl/wiki/'&gt;teamrelaychat.nl/wiki&lt;/a&gt;&lt;/p&gt;

&lt;h3 id='log_viewer'&gt;Log Viewer&lt;/h3&gt;

&lt;p&gt;ZNC comes with a lovely logging module that stores log files of all your chats in a folder deep inside the server.&lt;/p&gt;

&lt;p&gt;It is now possible to browse all these files via the ZNC web interface. This interface will grow even better in the next few weeks.&lt;/p&gt;

&lt;h3 id='firewall_friendly'&gt;Firewall friendly&lt;/h3&gt;

&lt;p&gt;Nowadays almost everyone is behind a NAT or firewall, and I&amp;#8217;ve been working to make sure you can use Team Relay Chat even then.&lt;/p&gt;

&lt;h4 id='alternative_irc_ports'&gt;Alternative IRC ports&lt;/h4&gt;

&lt;p&gt;You can now configure ZNC to listen on any port you want, even lower port numbers like 25(SMPT) or 80(HTTP) are supported. So even if port 6697 is blocked, you can still chat.&lt;/p&gt;

&lt;h4 id='passive_dcc_send'&gt;Passive DCC SEND&lt;/h4&gt;

&lt;p&gt;File sharing from the desktop uses DCC. Normally this means the sender(you) opens a port from which the receiver(hubot) downloads the file. This means you have to forward external ports to your machine.&lt;/p&gt;

&lt;p&gt;However, Hubot also support passive DCC. In this case Hubot opens a port and you simply upload the file. No configuration needed.&lt;/p&gt;

&lt;h3 id='conclusion'&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;You should try the &lt;a href='irc://irc.teamrelaychat.nl:6697'&gt;demo server&lt;/a&gt;(with SSL) and &lt;a href='http://teamrelaychat.nl/'&gt;read more&lt;/a&gt;.&lt;/p&gt;</content>
		
		<category term="teamrelaychat" />
		
		<category term="IRC" />
		
		<category term="registration" />
		
		<category term="filesharing" />
		
	</entry>
	
	<entry>
		<title>Hosted IRC for Teams</title>
		<link href="http://pepijndevos.nl/2013/04/23/hosted-irc-for-teams.html"/>
		<updated>2013-04-23T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2013/04/23/hosted-irc-for-teams</id>
		<content type="html">&lt;p&gt;It has started. A few weeks ago I was just playing around with &lt;a href='http://palletops.com/'&gt;Pallet&lt;/a&gt;, now I have been on &lt;a href='https://news.ycombinator.com/item?id=5584817'&gt;Hacker News&lt;/a&gt; and have a couple of users.&lt;/p&gt;

&lt;p&gt;My workflow is pretty desktop oriented, so when I was required to use a web application to keep in touch with teammates and Hacker School friends, I thought I could make a perfectly fine group communication tool based on IRC with plenty of desktop clients available.&lt;/p&gt;

&lt;p&gt;The idea is pretty simple: Give every team a VPS with an IRC server, but the devil is in the details. A vanilla IRC server is a bit awkward as a collaboration tool. You might as well telnet to each other directly.&lt;/p&gt;

&lt;p&gt;An important piece to the puzzle is ZNC. ZNC is a powerful bouncer with a ton of plugins. It stays connected to the server on your behalf 24/7, replaying all the messages you missed while disconnected. So that is user accounts and persistent chat right there.&lt;/p&gt;

&lt;p&gt;Another important part is Hubot. Hubot is an IRC bot that can execute commands and notify you about things like Git commits, build failures and more.&lt;/p&gt;

&lt;p&gt;There is also a web interface, because… you know, some people do live on the web.&lt;/p&gt;

&lt;p&gt;Finally, I have some grand plans for file sharing, but that is for another time.&lt;/p&gt;

&lt;p&gt;The result speaks for itself, check it out: &lt;a href='http://teamrelaychat.nl/'&gt;teamrelaychat.nl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I realise this is not for everyone, but if you&amp;#8217;re anything like me, chances are it fits your workflow better than these web based services.&lt;/p&gt;</content>
		
		<category term="irc" />
		
		<category term="teamrelaychat" />
		
	</entry>
	
	<entry>
		<title>clojure.core.logic magic square</title>
		<link href="http://pepijndevos.nl/2013/03/31/clojurecorelogic-magic-square.html"/>
		<updated>2013-03-31T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2013/03/31/clojurecorelogic-magic-square</id>
		<content type="html">&lt;p&gt;Last night my brother told me about this puzzle where you have a square of 3x3 that you have to fill with 1-9 in a way that all columns and rows sum up to 15.(this is not a real magic square, but anyway)&lt;/p&gt;

&lt;p&gt;After discussig our strategies for a bit, we went on to think about generalising the problem to bigger squares.&lt;/p&gt;

&lt;p&gt;The next morning we tested our theory in excel and produced a 4x4 square. The method we used was to fill in numbers at random, then switching them around horizontally until all collumns equalled 34 and finally switching them around vertically to make the rows work out.&lt;/p&gt;

&lt;p&gt;Once we started thinking about doing the 5x5 grid, I suggested I might do it faster using core.logic. I was wrong.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;time &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;test/magic&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='mi'&gt;5&lt;/span&gt; &lt;span class='mi'&gt;9&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;6&lt;/span&gt; &lt;span class='mi'&gt;7&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='s'&gt;&amp;quot;Elapsed time: 451.880413 msecs&amp;quot;&lt;/span&gt;
&lt;span class='nv'&gt;nil&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;time &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;test/magic&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt; &lt;span class='mi'&gt;15&lt;/span&gt; &lt;span class='mi'&gt;16&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;7&lt;/span&gt; &lt;span class='mi'&gt;8&lt;/span&gt; &lt;span class='mi'&gt;9&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;12&lt;/span&gt; &lt;span class='mi'&gt;11&lt;/span&gt; &lt;span class='mi'&gt;6&lt;/span&gt; &lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;14&lt;/span&gt; &lt;span class='mi'&gt;13&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='s'&gt;&amp;quot;Elapsed time: 6386.272944 msecs&amp;quot;&lt;/span&gt;
&lt;span class='nv'&gt;nil&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;time &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;test/magic&lt;/span&gt; &lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='nv'&gt;OutOfMemoryError&lt;/span&gt; &lt;span class='nv'&gt;GC&lt;/span&gt; &lt;span class='nv'&gt;overhead&lt;/span&gt; &lt;span class='nv'&gt;limit&lt;/span&gt; &lt;span class='nv'&gt;exceeded&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The code simply generates N&lt;sup&gt;2&lt;/sup&gt; logic variables in the 1-9 domain that are distinct. These are then sliced up in rows and collumns and constrained to sum up to the &amp;#8220;magic number&amp;#8221;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;ns &lt;/span&gt;&lt;span class='nv'&gt;test&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:refer-clojure&lt;/span&gt; &lt;span class='ss'&gt;:exclude&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;==&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:use&lt;/span&gt; &lt;span class='nv'&gt;clojure.core.logic&lt;/span&gt;
        &lt;span class='nv'&gt;clojure.pprint&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:require&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;clojure.core.logic.fd&lt;/span&gt; &lt;span class='ss'&gt;:as&lt;/span&gt; &lt;span class='nv'&gt;fd&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;grid&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;repeatedly&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;* &lt;/span&gt;&lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;lvar&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nv'&gt;rows&lt;/span&gt; &lt;span class='nv'&gt;partition&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;cols&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;grid&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;apply map list &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;rows&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;grid&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;sum&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;ls&lt;/span&gt; &lt;span class='nv'&gt;res&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;conde&lt;/span&gt;
    &lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='nb'&gt;== &lt;/span&gt;&lt;span class='nv'&gt;ls&lt;/span&gt; &lt;span class='p'&gt;[])&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;== &lt;/span&gt;&lt;span class='nv'&gt;res&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
    &lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='nb'&gt;== &lt;/span&gt;&lt;span class='nv'&gt;ls&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;res&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;
    &lt;span class='p'&gt;((&lt;/span&gt;&lt;span class='nf'&gt;fresh&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;h&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt; &lt;span class='nv'&gt;inter&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
       &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;conso&lt;/span&gt; &lt;span class='nv'&gt;h&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt; &lt;span class='nv'&gt;ls&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
       &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;fd/+&lt;/span&gt; &lt;span class='nv'&gt;h&lt;/span&gt; &lt;span class='nv'&gt;inter&lt;/span&gt; &lt;span class='nv'&gt;res&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
       &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;sum&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt; &lt;span class='nv'&gt;inter&lt;/span&gt;&lt;span class='p'&gt;)))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kd'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;magic&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;g&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;grid&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nv'&gt;nums&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;range &lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;inc &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;* &lt;/span&gt;&lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;
        &lt;span class='nv'&gt;ndom&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;apply &lt;/span&gt;&lt;span class='nv'&gt;fd/domain&lt;/span&gt; &lt;span class='nv'&gt;nums&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nv'&gt;lsum&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;apply + &lt;/span&gt;&lt;span class='nv'&gt;nums&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nv'&gt;lines&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;concat &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;rows&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;g&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;cols&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt; &lt;span class='nv'&gt;g&lt;/span&gt;&lt;span class='p'&gt;))]&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;run&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;q&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
           &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;everyg&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;fd/dom&lt;/span&gt; &lt;span class='nv'&gt;%&lt;/span&gt; &lt;span class='nv'&gt;ndom&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;g&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
           &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;fd/distinct&lt;/span&gt; &lt;span class='nv'&gt;g&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
           &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;everyg&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;sum&lt;/span&gt; &lt;span class='nv'&gt;%&lt;/span&gt; &lt;span class='nv'&gt;lsum&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;lines&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
           &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;== &lt;/span&gt;&lt;span class='nv'&gt;q&lt;/span&gt; &lt;span class='nv'&gt;g&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
      &lt;span class='nv'&gt;first&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;rows&lt;/span&gt; &lt;span class='nv'&gt;n&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;map &lt;/span&gt;&lt;span class='nv'&gt;println&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='nv'&gt;dorun&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I suspect I can still beat my brother to 6x6 by implementing one of the techniques outlined on wikipedia in a good old imperative style, especially since he&amp;#8217;s not even trying.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/geeks-vs-nongeeks-repetitive-tasks.png' alt='graph' /&gt;&lt;/p&gt;</content>
		
		<category term="clojure" />
		
		<category term="core.logic" />
		
	</entry>
	
	<entry>
		<title>VMfest Base Image</title>
		<link href="http://pepijndevos.nl/2013/03/20/vmfest-base-image.html"/>
		<updated>2013-03-20T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2013/03/20/vmfest-base-image</id>
		<content type="html">&lt;p&gt;I&amp;#8217;m playing with Pallet to setup an IRC server and bouncer. Pallet uses vmfest to deploy to VirtualBox.&lt;/p&gt;

&lt;p&gt;While vmfest comes with some prepared images, I found that my laptop would not run them, so I made my own, with &lt;em&gt;a lot&lt;/em&gt; of help from Antoni Batchelli.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create an image in VirtualBox with a NAT and host-only network.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Install your favourite distro.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Install an SSH server.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Setup &lt;a href='http://serverfault.com/a/160587'&gt;passwordless sudo&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Make sure /etc/network/interfaces contains both eth0 and eth1.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  auto eth0
  iface eth0 inet dhcp
  auto eth1
  iface eth1 inet dhcp&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Update and upgrade all packages.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Install the &lt;a href='http://www.virtualbox.org/manual/ch04.html'&gt;guest additions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;On some Debian based distros, &lt;a href='http://www.ducea.com/2008/09/01/remove-debian-udev-persistent-net-rules/'&gt;remove persistent-net.rules&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should give you a working machine, but we&amp;#8217;re not done yet. You still need to make the hard disk &lt;a href='http://www.virtualbox.org/manual/ch05.html#hdimagewrites'&gt;multi-attachable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you where to use this image in Pallet, you could only use it once. Multiattach means that every time a machine is made, a new copy-on-write image is created so the original stays intact.&lt;/p&gt;

&lt;p&gt;To do this, delete the VM, &lt;em&gt;but not the vdi file&lt;/em&gt;, and run the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VBoxManage modifyhd the/disk.vdi --type multiattach&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, you need to create a meta file with the same name as the disk image, but with the .meta extension. As an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; {:os-type-id &amp;quot;Ubuntu_64&amp;quot;,
  :sudo-password &amp;quot;vmfest&amp;quot;,
  :no-sudo false,
  :username &amp;quot;vmfest&amp;quot;,
  :os-family :ubuntu,
  :os-version &amp;quot;12.04&amp;quot;,
  :os-64-bit true,
  :password &amp;quot;vmfest&amp;quot;,
  :description &amp;quot;Ubuntu 12.04 (64bit)&amp;quot;
  :packager :apt}&lt;/code&gt;&lt;/pre&gt;</content>
		
		<category term="clojure" />
		
		<category term="vmfest" />
		
	</entry>
	
	<entry>
		<title>The Prolog Cook</title>
		<link href="http://pepijndevos.nl/2013/01/06/the-prolog-cook.html"/>
		<updated>2013-01-06T00:00:00-08:00</updated>
		<id>tag:pepijndevos.nl,2011:/2013/01/06/the-prolog-cook</id>
		<content type="html">&lt;p&gt;&lt;em&gt;brain dump alert&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had read &lt;a href='http://clj-me.cgrand.net/2012/04/06/fair-conjunction-status-report/'&gt;this article on core.logic&lt;/a&gt; a few days back, and while making myself a dinner, I came up with this analogy.&lt;/p&gt;

&lt;p&gt;At first, I was confused about disjunction and conjunction, so let&amp;#8217;s start with the Prolog cook.&lt;/p&gt;

&lt;p&gt;The prolog cook cooks depth-first. The cook has several recipes and steps to produce them. The first recipe on the list is an egg with ham and cheese.&lt;/p&gt;

&lt;p&gt;The cook bakes the egg on top of the ham, adds some salt, finds out there is no cheese, throws everything away and moves on to the next recipe.&lt;/p&gt;

&lt;p&gt;The next recipe is is a simple pasta. The cook checks if there is pasta, herbs, tomato, and finally checks to see how much water is in the tap.&lt;/p&gt;

&lt;p&gt;Next up is the miniKanren cook. This cook has fair disjunction, but unfair conjunction.&lt;/p&gt;

&lt;p&gt;So the cook looks at her recipes, picks the egg recipe, checks if there is an egg. Then, picks another recipe, like a pasta, checks if there is any pasta.&lt;/p&gt;

&lt;p&gt;The cook keeps going over the recipes, discarding ones that can&amp;#8217;t be made. But at some point this cook will also check how much water is in the tap. And keep checking until there is no more water.&lt;/p&gt;

&lt;p&gt;Finally, the fair cook, with fair conjunction.&lt;/p&gt;

&lt;p&gt;This cook goes over his recipes the same way the miniKanren cook does, but upon checking for water, this cook opens the tap and continues cooking while keeping an eye on the tap.&lt;/p&gt;

&lt;p&gt;Maybe the recipe can be adjusted not to check for all the water. Even the fair cook is wasting a lot of water while doing other stuff, but at least she does other stuff in the meantime.&lt;/p&gt;

&lt;p&gt;There have been very successful Prolog cooks, but not all recipes can be adjusted to work for the Prolog cook.&lt;/p&gt;</content>
		
		<category term="clojure" />
		
		<category term="minikanren" />
		
		<category term="prolog" />
		
	</entry>
	
	<entry>
		<title>Philips 162 Post-Mortem</title>
		<link href="http://pepijndevos.nl/2012/12/13/philips-162-post-mortem.html"/>
		<updated>2012-12-13T00:00:00-08:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/12/13/philips-162-post-mortem</id>
		<content type="html">&lt;p&gt;You read that right, I used my phone untill it died. This is not a review or even a preview, but a postview.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I wish there was a gadget website of which the authors are not too happy about ditching their almost-new phone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&amp;#8211; some tweep lost in history&lt;/p&gt;

&lt;p&gt;The upside of a post-mortem about a phone is that I used it for years, so I know all the details and annoyances. The downside is that Philips no longer makes the phone, but no matter, I wouldn&amp;#8217;t recommend you buy it anyway.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s a practical phone. It lies confortably in your hand, it&amp;#8217;s super sturdy, even after years, it lasts a week on a charge, and it even has a few games on it.&lt;/p&gt;

&lt;p&gt;It doesn&amp;#8217;t have a camera, wifi, bluetooth or pretty mutch anything else, but it&amp;#8217;s great for calling and texting. The T9 feature works good, but always uses the interface language and does not allow adding words.&lt;/p&gt;

&lt;p&gt;My number one annoyiance with the phone is that in the phonebook the screen displays only 8 of the 10 digits of a phone number in a hard to follow sliding motion.&lt;/p&gt;

&lt;p&gt;Speaking of the phone book, I recently discovered that copying contacts to the SIM card is hard.&lt;/p&gt;

&lt;p&gt;The menu of the phone is easy to use, but there is hardly any reason to. On a daily basis, I tend to use the directional pad to get to the most frequently used functions. This does mean it is fairly easy to do stuff accidentally if you forget to lock it.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind is that it doesn&amp;#8217;t warn you when the inbox is full. It just doesn&amp;#8217;t receive anymore text messages. Once in a while, when people stop replying to my messages, I need to check if it&amp;#8217;s me or my phone.&lt;/p&gt;

&lt;p&gt;Overall, this phone served me well. R.I.P. phone&amp;#8230;&lt;/p&gt;</content>
		
		<category term="cellphone" />
		
		<category term="philips" />
		
	</entry>
	
	<entry>
		<title>Pure CSS Gauge</title>
		<link href="http://pepijndevos.nl/2012/11/29/pure-css-gauge.html"/>
		<updated>2012-11-29T00:00:00-08:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/11/29/pure-css-gauge</id>
		<content type="html">&lt;p&gt;I got &lt;a href='http://xkcd.com/356/'&gt;nerdsniped&lt;/a&gt;&lt;/p&gt;
&lt;blockquote class='twitter-tweet'&gt;&lt;p&gt;still looking for pure css gauges, ideas?&lt;/p&gt;&amp;mdash; Almog Melamed (@radagaisus) &lt;a href='https://twitter.com/radagaisus/status/273819647168626688' data-datetime='2012-11-28T16:04:25+00:00'&gt;November 28, 2012&lt;/a&gt;&lt;/blockquote&gt;&lt;script src='//platform.twitter.com/widgets.js' charset='utf-8'&gt; &lt;/script&gt;
&lt;p&gt;So I had to make this gauge using CSS animation.&lt;/p&gt;
&lt;iframe src='http://jsfiddle.net/Xj6ua/1/embedded/' allowfullscreen='allowfullscreen' frameborder='0' style='width: 100%; height: 300px'&gt; &lt;/iframe&gt;
&lt;p&gt;I used two nested divs with round borders. The outer one does the quadrants with a top/left/bottom/right border. The inner one has only a top border, and slides over the bottom one.&lt;/p&gt;

&lt;p&gt;Note that halfway through, the inner border changes from sliding out, to sliding in. Otherwise you&amp;#8217;d get in trouble during the last quadrant.&lt;/p&gt;

&lt;p&gt;A good way to see how it works is removing the negative margin, or change the inner border colour.&lt;/p&gt;</content>
		
		<category term="css" />
		
		<category term="gauge" />
		
		<category term="animation" />
		
	</entry>
	
	<entry>
		<title>Incremental E-paper update</title>
		<link href="http://pepijndevos.nl/2012/11/23/incremental-e-paper-update.html"/>
		<updated>2012-11-23T00:00:00-08:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/11/23/incremental-e-paper-update</id>
		<content type="html">&lt;iframe src='http://www.youtube.com/embed/oscur3i5V54' allowfullscreen='allowfullscreen' frameborder='0' height='315' width='560'&gt; &lt;/iframe&gt;
&lt;p&gt;This is what I&amp;#8217;ve been up to since last post. I almost completely rewrote the Arduino code to support different waveforms and both full updates and incremental ones.&lt;/p&gt;

&lt;p&gt;I admit the old update looked smoother, but it faded away over time, defeating on of the main features of E-paper, that it is bi-stable.&lt;/p&gt;

&lt;p&gt;I do think the incremental updates look cool, kind of like an evolving Pokémon.&lt;/p&gt;

&lt;p&gt;&lt;a href='https://github.com/pepijndevos/arduino-epaper'&gt;Get the source code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am using a &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L300'&gt;modified version of shiftOut&lt;/a&gt; that can send any number of bits. This is needed to send 1 bit for the background and common, and 16 bits for a character.&lt;/p&gt;

&lt;p&gt;The EIO pin is no longer used, but just tied low. This causes the first chip to be selected when it wakes up. The enable pins are chained so that when the first chip is full, it pulls the second chip low.&lt;/p&gt;

&lt;p&gt;In short, this is how I update the screen:&lt;/p&gt;

&lt;p&gt;Step one is to convert all 8 bit characters to 16 bit segment data using a &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L60'&gt;big table&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, I create a struct with the changes. This struct contains 6 arrays. One with the added segments, one with the removed segments, one with all changed segments and 3 negatives.&lt;/p&gt;

&lt;p&gt;In case this is a complete update, this struct is very easy to make. In this case the negative of the aded segments equals the deleted segments.&lt;/p&gt;

&lt;p&gt;In the case of a partial update, this struct is generated using a &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L246'&gt;lot of binary logic&lt;/a&gt;, based on an array of the current segments.&lt;/p&gt;

&lt;p&gt;Now that we have a struct of all the changes, we actually need to write this to the shift register and toggle the latch to send it to the display. The &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L316'&gt;writing itself&lt;/a&gt; is fairly trivial, but the waveform is not.&lt;/p&gt;

&lt;p&gt;I will cover the &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L401'&gt;simple waveform&lt;/a&gt; in this post, and leave the 757 for you to figure out. The principle is the same.&lt;/p&gt;

&lt;p&gt;First we &lt;a href='https://github.com/pepijndevos/arduino-epaper/blob/master/ePaper.cpp#L349'&gt;wakeup&lt;/a&gt; the display. After every update, we shut it down again.&lt;/p&gt;

&lt;p&gt;Now, you need to look at &lt;a href='/images/Winstar_E-Paper_Application_Note_V2.pdf'&gt;the datasheet&lt;/a&gt; to see the waveform for segments to white and segments to black. Unchanged segments need to be the same as the common bit, so no current flows through them.&lt;/p&gt;

&lt;p&gt;Note that for white-on-black the added/deleted bits are the other way around, but the unchanged segments aren&amp;#8217;t.&lt;/p&gt;

&lt;p&gt;Finally, for every section of the waveform I take the corresponding array out of my changes, send it, and wait for a bit.&lt;/p&gt;</content>
		
		<category term="epaper" />
		
		<category term="electronics" />
		
		<category term="arduino" />
		
		<category term="sparkfun" />
		
	</entry>
	
	<entry>
		<title>Improved library for Sparkfun E-paper display</title>
		<link href="http://pepijndevos.nl/2012/11/12/improved-library-for-sparkfun-e-paper-display.html"/>
		<updated>2012-11-12T00:00:00-08:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/11/12/improved-library-for-sparkfun-e-paper-display</id>
		<content type="html">&lt;iframe src='http://www.youtube.com/embed/_jO2olSo13Y' allowfullscreen='allowfullscreen' frameborder='0' height='600' width='420'&gt; &lt;/iframe&gt;
&lt;p&gt;I got my E-paper display from Sparkfun, but I was disappointed by the quality of the library.&lt;/p&gt;

&lt;p&gt;My version has more and nicer looking(IMO) characters, uses black text on white background, uses sleep mode, and most importantly, &lt;strong&gt;no ghosting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What remains to be done is implementing temperature awareness. The colder the display, the longer it takes to update. The Atmega chip has a sensor built in.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://bildr.org/2011/06/epaper-arduino/'&gt;Original library&lt;/a&gt;&lt;br /&gt;&lt;a href='https://github.com/pepijndevos/arduino-epaper'&gt;My updates&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the people at &lt;a href='https://hack42.nl/'&gt;Hack42&lt;/a&gt; for helping me out with my bootloader and understanding the E-paper datasheet.&lt;/p&gt;

&lt;p&gt;One little byte of information that I found particularly interesting is that the E-paper controller is basically a &lt;a href='http://en.wikipedia.org/wiki/Shift_register'&gt;shift register&lt;/a&gt; with an extra latch. Maybe the code can be simplified using shiftOut?&lt;/p&gt;

&lt;p&gt;From the library&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;// Second ePrint removes the un-used segments - I know, this is weird&amp;#8230;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought the same, until I understood the COM(mon) bit. This indicates that all specified segments should be pulled either high or low. But not both at the same time.&lt;/p&gt;

&lt;p&gt;This means that the first pass, you put +35v on all the segments you want to use, and the second pass -35v on the ones you don&amp;#8217;t want to use. Order doesn&amp;#8217;t matter, but gives a different intermediate state.&lt;/p&gt;</content>
		
		<category term="epaper" />
		
		<category term="electronics" />
		
		<category term="arduino" />
		
		<category term="sparkfun" />
		
	</entry>
	
	<entry>
		<title>Sparkfun 1.8v USB to serial</title>
		<link href="http://pepijndevos.nl/2012/10/26/sparkfun-18v-usb-to-serial.html"/>
		<updated>2012-10-26T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/10/26/sparkfun-18v-usb-to-serial</id>
		<content type="html">&lt;p&gt;&lt;img src='/images/DSCN1878.JPG' alt='Sparkfun usb serial board with custom stripboard' /&gt;&lt;/p&gt;

&lt;p&gt;I am trying to hack my Kindle 4 so that I can plug in a keyboard and use it as a Raspberry Pi with battery and E-paper screen.&lt;/p&gt;

&lt;p&gt;To support a keyboard, the Kindle needs to use its USB port in host mode. This is called USB On The Go, and someone modified the Kindle kernel to support this.&lt;/p&gt;

&lt;p&gt;So far, it does not work on my Kindle, but since the Kindle does not display any kernel messages while booting, I had no idea what went wrong.&lt;/p&gt;

&lt;p&gt;Thus, I had to open up the Kindle to attach a serial console. My brother is writing a guide about this at &lt;a href='http://jurriaandevos.nl'&gt;http://jurriaandevos.nl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/DSCN1879.JPG' alt='kindle without back cover and with serial header' /&gt;&lt;/p&gt;

&lt;p&gt;The next hurdle was that the &lt;a href='https://www.sparkfun.com/products/718'&gt;Sparkfun USB-serial breakout&lt;/a&gt; comes with a solder jumper for 5v and 3.3v, but the Kindle uses 1.8v.&lt;/p&gt;

&lt;p&gt;Therefore, I devised this small stripboard that lets you select any of these three voltages with 2 jumpers.&lt;/p&gt;

&lt;p&gt;With the top jumper removed, you can switch the lower jumper to either jumper Vcc or the 3.3v reference to the VCIO pin.&lt;/p&gt;

&lt;p&gt;With the top jumper connected and the bottom one in the lowest position, you engage a voltage divider between 3.3v and gnd, resulting in half that voltage(1.65v) on VCIO.&lt;/p&gt;

&lt;p&gt;Before this works, you should desolder the built-in jumper, and remove the LEDs.&lt;/p&gt;

&lt;p&gt;The LEDs are powered from Vcc, so they leak current into the circuit. I measured 2.7v on VCIO with the LEDs still on.&lt;/p&gt;

&lt;p&gt;The voltage divider was made using two 10k resistors, but anything between 1k and 10k should be fine, I&amp;#8217;ve read.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m looking forward to blog about my Kindle 4 keyboard soon.&lt;/p&gt;</content>
		
		<category term="sparkfun" />
		
		<category term="usb" />
		
		<category term="serial" />
		
		<category term="kindle" />
		
	</entry>
	
	<entry>
		<title>Kindle 4 as a paper terminal</title>
		<link href="http://pepijndevos.nl/2012/10/08/kindle-4-as-a-paper-terminal.html"/>
		<updated>2012-10-08T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/10/08/kindle-4-as-a-paper-terminal</id>
		<content type="html">&lt;p&gt;After reading about the &lt;a href='http://projectdp.wordpress.com/2012/09/24/pi-k3w-kindle-3-display-for-raspberry-pi/'&gt;Kindleberry Pi&lt;/a&gt;, I really wanted to experiment with e-paper as a computer monitor.&lt;/p&gt;

&lt;p&gt;Sadly, I own a Kindle 4, which has no keyboard and consequently a lot less hacks. Still, I&amp;#8217;m typing this post from my Kindle, so I will document how I got here.&lt;/p&gt;

&lt;p&gt;Initially, I could not find a jailbreak for the Kindle 4, but at some point, I found &lt;a href='http://wiki.mobileread.com/wiki/Kindle4NTHacking'&gt;this wiki page&lt;/a&gt; that documents everything you need to get started.&lt;/p&gt;

&lt;p&gt;I used the universal method, which involves downloading &lt;code&gt;data.tar.gz&lt;/code&gt;, entering and leaving diagnostic mode and rebooting a few times.&lt;/p&gt;

&lt;p&gt;The jailbreak only installs a developer key, that allows you to install software. The piece of software we need is usbnetwork, which allows you to SSH over USB.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.mobileread.com/forums/showthread.php?t=88004'&gt;Download usbnetwork from here&lt;/a&gt; and follow the instructions.&lt;/p&gt;

&lt;p&gt;Before you enable usbnetwork by renaming the &lt;code&gt;auto&lt;/code&gt; file, go into &lt;code&gt;/usbnet/etc/config&lt;/code&gt; and set &lt;code&gt;USE_VOLUMD=&amp;quot;true&amp;quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If all went well, the next time you connect your Kindle, it will present a network instead of mass storage.&lt;/p&gt;

&lt;p&gt;You should now be able to &lt;code&gt;ssh root@192.168.2.2&lt;/code&gt;. Linux users should run &lt;code&gt;sudo ifconfig usb0 192.168.2.1 netmask 255.255.255.0&lt;/code&gt; first, to bring up the network.&lt;/p&gt;

&lt;p&gt;The terminal emulator used in the Kindleberry hack does not work on the Kindle 4. It expects a keyboard, I&amp;#8217;ve been told.&lt;/p&gt;

&lt;p&gt;However, vdp wrote a Java kindlet terminal emulator called KindleTERM that seems to run on Kindle 4.&lt;/p&gt;

&lt;p&gt;KindleTERM was writen 2 years ago, and while it pretty much worked out of the box, it did not make an SSH connection to my PC.&lt;/p&gt;

&lt;p&gt;I finally got something working using the remote keyboard feature, sshing to localhost and using &lt;code&gt;dbclient&lt;/code&gt; to ssh into my Mac and start a &lt;code&gt;tmux&lt;/code&gt; session.&lt;/p&gt;

&lt;p&gt;Hawhill made a new version that just telnets to localhost, but he removed the remote keyboard. That&amp;#8217;s useless.&lt;/p&gt;

&lt;p&gt;Then hippy dave came around and added back the remote keyboard. Yay!&lt;/p&gt;

&lt;p&gt;Then I came around and added a config file to specify which host/port/username/password/command to use.&lt;/p&gt;

&lt;p&gt;Long story short, follow &lt;a href='http://www.mobileread.com/forums/showthread.php?t=107192'&gt;his instructions&lt;/a&gt;, but upload &lt;a href='https://github.com/pepijndevos/KindleTerm/downloads'&gt;my version&lt;/a&gt; of KindleTerm to your Kindle. You should be using SCP or SFTP, since you can no longer use mass storage.&lt;/p&gt;

&lt;p&gt;By default, my version still telnets to localhost, but it tries to read from &lt;code&gt;/developer/KindleTermPV/work/kindleterm.properties&lt;/code&gt;. Mine looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;host=192.168.2.1
login=pepijn
password=secret
cmd=tmux -S /tmp/kindle&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After rebooting, you should see KindleTermPV on your home screen. When you start it, it should telnet to your PC.&lt;/p&gt;

&lt;p&gt;If you see a blank screen, press back+keyboard or relaunch the app a few times.&lt;/p&gt;

&lt;p&gt;If you are on Mac, you can run &lt;code&gt;telnetd&lt;/code&gt; with &lt;code&gt;sudo launchctl load -w /System/Library/LaunchDaemons/telnet.plist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You might want to &lt;code&gt;export PS1=&amp;quot;&amp;gt;&amp;quot;&lt;/code&gt; to save some precious screen space.&lt;/p&gt;

&lt;p&gt;You should also &lt;code&gt;export TERM=ansi&lt;/code&gt; if you experience any formatting problems. The VT320 implementation is a bit buggy.&lt;/p&gt;

&lt;p&gt;&lt;img src='/images/screen_shot-40716.gif' alt='kindleterm screenshot' /&gt;&lt;/p&gt;</content>
		
		<category term="kindle" />
		
		<category term="java" />
		
		<category term="epaper" />
		
		<category term="terminal" />
		
	</entry>
	
	<entry>
		<title>Linux Time Machine Backup with netatalk 3.0</title>
		<link href="http://pepijndevos.nl/2012/09/14/linux-time-machine-backup-with-netatalk-30.html"/>
		<updated>2012-09-14T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/09/14/linux-time-machine-backup-with-netatalk-30</id>
		<content type="html">&lt;p&gt;There are quite a few lengthy guides on how to back up your Mac to a Linux box with Time Machine and netatalk 2.x.&lt;/p&gt;

&lt;p&gt;You don&amp;#8217;t need any of that. With netatalk 3.0, it is a single line in your config file, &lt;code&gt;time machine = yes&lt;/code&gt;, and you don&amp;#8217;t even need to set &lt;code&gt;TMShowUnsupportedNetworkVolumes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On Arch Linux, all you need to do is&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pacman -S netatalk&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Append &lt;code&gt;dbus avahi-daemon netatalk&lt;/code&gt; to your DAEMONS in &lt;code&gt;/etc/rc.conf&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Add your backup folder to &lt;code&gt;/etc/afp.conf&lt;/code&gt; like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[ShareName]
path = /path/to/backup/drive
time machine = yes&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now start the daemons and you&amp;#8217;re good to go. Even Bonjour is taken care of automatically.&lt;/p&gt;

&lt;p&gt;On Ubuntu, netatalk is still at version 2, so you&amp;#8217;ll need to &lt;a href='http://netatalk.sourceforge.net/3.0/htmldocs/installation.html#id4561068'&gt;compile it from source&lt;/a&gt;. Make sure you install Avahi.&lt;/p&gt;</content>
		
		<category term="raspi" />
		
		<category term="linux" />
		
		<category term="mac" />
		
		<category term="timemachine" />
		
		<category term="netatalk" />
		
	</entry>
	
	<entry>
		<title>This is How I Want to Write Markdown</title>
		<link href="http://pepijndevos.nl/2012/09/03/this-is-how-i-want-to-write-markdown.html"/>
		<updated>2012-09-03T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/09/03/this-is-how-i-want-to-write-markdown</id>
		<content type="html">&lt;p&gt;Most websites us a split-pane approach to Markdown editing. You enter your text in one field, and the resulting HTML appears in another.&lt;/p&gt;
&lt;img src='/images/mac_feature_07.jpg' style='float:right' /&gt;
&lt;p&gt;I think this is silly UI, and would like to see websites adopt the &lt;a href='http://www.iawriter.com'&gt;iA Writer&lt;/a&gt; approach to Markdown editing.&lt;/p&gt;

&lt;p&gt;Sadly, I don&amp;#8217;t know of any library or parser that lets you do this, so I started writing one.&lt;/p&gt;

&lt;p&gt;This thing is currently easier to break than to use, partially because my approach is broken, partially because the content editable API is broken, and partially because it&amp;#8217;s just a one-day hack.&lt;/p&gt;
&lt;div id='editor' style='outline:1px solid black; min-height:200px;'&gt; &lt;/div&gt;&lt;div&gt; &lt;script src='http://pepijndevos.github.com/markin/markin.js'&gt; &lt;/script&gt; &lt;script&gt; edit(&quot;editor&quot;); &lt;/script&gt; &lt;link href='http://pepijndevos.github.com/markin/remarkdown.css' rel='stylesheet' /&gt; &lt;/div&gt;</content>
		
		<category term="html" />
		
		<category term="js" />
		
		<category term="markdown" />
		
	</entry>
	
	<entry>
		<title>Attending Hacker School</title>
		<link href="http://pepijndevos.nl/2012/08/22/attending-hacker-school.html"/>
		<updated>2012-08-22T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/08/22/attending-hacker-school</id>
		<content type="html">&lt;p&gt;As a male without $6000 in savings.&lt;/p&gt;

&lt;p&gt;Hacker School is awesome, so if you enjoy coding, you should &lt;a href='http://hackerschool.com/apply'&gt;apply&lt;/a&gt;, but what really prompted me to write this post is that someone on Reddit or HN said giving grants only to woman is unfair to men who need it.&lt;/p&gt;

&lt;p&gt;While I do think it&amp;#8217;d be good to have more gender balance in programming, giving grants based on need only to woman seemed unfair to me too. After three months of Hacker School, there are 2 things that altered my opinion on the matter.&lt;/p&gt;

&lt;p&gt;First-hand stories of all the crazy little subtle male culture bias. There is no way I can second-hand this to male readers, but let me say there is a lot more to it than just getting the job and getting promotion.&lt;/p&gt;

&lt;p&gt;&amp;#8220;Affirmative action.&amp;#8221; Discrimination is a big part of American culture. If you think giving grants to woman is discriminative to man, you would be completely right.&lt;/p&gt;

&lt;p&gt;But it&amp;#8217;s okay, because woman are a minority. Affirmative action is a very normal and accepted thing over here, and it&amp;#8217;s just that, discrimination in favor of the minority.&lt;/p&gt;

&lt;h4 id='i_still_managed_to_make_it'&gt;I still managed to make it&lt;/h4&gt;

&lt;p&gt;New York is both more expensive and cheaper than you can possibly imagine.&lt;/p&gt;

&lt;p&gt;If I would have rented a decent room and ate out like everyone else, I would not have been able to do this from my savings. But the thing is, you don&amp;#8217;t have to do all that.&lt;/p&gt;

&lt;p&gt;The first thing to do is to contact all your relatives in and around New York. I&amp;#8217;m basically staying here for free in exchange for some help with &lt;a href='http://educationrevolution.org'&gt;AERO&lt;/a&gt; on my free days. I do have a long commute, but I have a Kindle and a Gameboy with Pokemon.&lt;/p&gt;

&lt;p&gt;My friend Six has taken an alternative route, and is just crashing on couches and roofs, with &lt;a href='http://couchsurfing.org'&gt;Couchsurfing&lt;/a&gt; as a backup. With a lot alumni living in the city, and a dangling fire escape here and there&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt;, this is perfectly doable. However, it might be less pleasant in the Fall batch.&lt;/p&gt;

&lt;p&gt;A good lunch can easily cost you $10, but it doesn&amp;#8217;t have to. You can buy bread, there is a fridge, microwave and hot plate&lt;sup id='fnref:2'&gt;&lt;a href='#fn:2' rel='footnote'&gt;2&lt;/a&gt;&lt;/sup&gt;, use them. Me and Six had a lot of cheap and delicious meals that way.&lt;/p&gt;

&lt;p&gt;I hope that helps.&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;WARING ERROR WARNING WARNING don&amp;#8217;t do that, unless you want to get in trouble. Alumni roofs are okay though.&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;li id='fn:2'&gt;
&lt;p&gt;Well, there was, until something started to smell. I&amp;#8217;m convinced that&amp;#8217;s the trash/fridge though, not my hot plate ;)&lt;/p&gt;
&lt;a href='#fnref:2' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content>
		
		<category term="hackerschool" />
		
	</entry>
	
	<entry>
		<title>The Multiple Index Problem</title>
		<link href="http://pepijndevos.nl/2012/08/12/the-multiple-index-problem.html"/>
		<updated>2012-08-12T00:00:00-07:00</updated>
		<id>tag:pepijndevos.nl,2011:/2012/08/12/the-multiple-index-problem</id>
		<content type="html">&lt;p&gt;I would like to name this problem in functional programming. There has been some talk aout how Clojure solves the &lt;a href='http://c2.com/cgi/wiki?ExpressionProblem'&gt;Expression Problem&lt;/a&gt;, but th Multiple Index Problem has deserved so little attention it doesn&amp;#8217;t even have a name.&lt;/p&gt;

&lt;p&gt;A simple example of multiple indices can be seen in relational databases, where you can querry a table like &lt;code&gt;SELECT * FROM foo WHERE id=1&lt;/code&gt; but you can add an index to the table and querry on other fields like &lt;code&gt;SELECT * FROM foo WHERE name=&amp;quot;Pepijn&amp;quot;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you would translate this to hash maps, in imperative languages, you could do the following&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='python'&gt;&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;val1&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[]&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;val2&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;[]&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;index1&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='n'&gt;val1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='n'&gt;val2&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;index2&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;pepijn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='n'&gt;val1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;ben&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='n'&gt;val2&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;index2&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;pepijn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;append&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='o'&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;index1&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want to do this in a functional language, you&amp;#8217;re out of luck. In the most basic case, you&amp;#8217;d have to add some indirection.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nb'&gt;index &lt;/span&gt;&lt;span class='p'&gt;[[]&lt;/span&gt;, &lt;span class='p'&gt;[]])&lt;/span&gt;
&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;user/index&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nv'&gt;index1&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;, &lt;span class='mi'&gt;5&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;user/index1&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nv'&gt;index2&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;pepijn&amp;quot;&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;, &lt;span class='s'&gt;&amp;quot;ben&amp;quot;&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;user/index2&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nv'&gt;new-index&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;update-in&lt;/span&gt; &lt;span class='nb'&gt;index &lt;/span&gt;&lt;span class='p'&gt;[(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;index2&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;pepijn&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)]&lt;/span&gt; &lt;span class='nb'&gt;conj &lt;/span&gt;&lt;span class='s'&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;user/new-index&lt;/span&gt;
&lt;span class='nv'&gt;user=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;new-index&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;index1&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next up, I&amp;#8217;ll show 2 cases where this is a real pain, rather than just anoying.&lt;/p&gt;

&lt;h4 id='dijkstras_algorithm'&gt;Dijkstras Algorithm&lt;/h4&gt;

&lt;p&gt;Dijkstras algorithm is used to find the shortest path in a graph. It is fairly easy to implement using a vector, but also very slow.&lt;/p&gt;

&lt;p&gt;Every itteration you need to find the closest unexplored node. With he flat list, you need to walk them all.&lt;/p&gt;

&lt;p&gt;The way to solve this is to use a &lt;a href='http://en.wikipedia.org/wiki/Fibonacci_heap'&gt;Fibonacci Heap&lt;/a&gt;, which has constant time and logarithmic time opperations for getting and decrementing the lowest key.&lt;/p&gt;

&lt;p&gt;However, it has &lt;em&gt;no&lt;/em&gt; methods for lookingg up other keys, other than&amp;#8230; walking the tree? The way this is done in imparative languages is to just have pointers to the nodes, but in a functional language, youre stuck walking the tree.&lt;/p&gt;

&lt;p&gt;This defeats the whole purpose of using a Fibonacci Heap. Is it at all possible to implement Dijkstra in a pure functional way in an optiomal time complexity?&lt;/p&gt;

&lt;p&gt;I ended up using a mutable heap and just not decrementing keys. Yuk!&lt;/p&gt;

&lt;p&gt;You can see the code me and Mary wrote at Hacker School at &lt;a href='https://github.com/maryrosecook/dijkstra'&gt;her github&lt;/a&gt;(fibbonaccy and flat array), &lt;a href='https://github.com/pepijndevos/dijkstra-clj'&gt;and mine&lt;/a&gt;(Using PriorityBlockingQueue)&lt;/p&gt;

&lt;h4 id='collision_detection'&gt;Collision Detection&lt;/h4&gt;

&lt;p&gt;I struggled with the same problem in my Clojure game engine, before I realised this was a repeating pattern.&lt;/p&gt;

&lt;p&gt;So I started out modeling my world as a list of objects, but quickly found that the bat needed to know where the ball was, so I turned the world into a hash map of IDs to objects, and all was good.&lt;/p&gt;

&lt;p&gt;Until I needed to do collision detection on more than a dozen objects. If you just compare all objects to al others to see if they collide, you&amp;#8217;re doing O(n&lt;sup&gt;2&lt;/sup&gt;), which is no good.&lt;/p&gt;

&lt;p&gt;Lukily there are datastructures that do spatial indexing. Indexing&amp;#8230; see my problem?&lt;/p&gt;

&lt;p&gt;I wrote a &lt;a href='http://en.wikipedia.org/wiki/Quadtree'&gt;Quadtree&lt;/a&gt; in Clojure, but as soon as I stuck all my objects in the tree, there was no way to look them up by ID.&lt;/p&gt;

&lt;p&gt;I think I ended up doing some black magic to sorted maps to have lookup by ID and okayish collision detection.&lt;/p&gt;

&lt;p&gt;That is all I have to say about the problem. I don&amp;#8217;t have a soltuion or a satisfying workaround. You can try adding intermediate indices, or introducing mutability, but it&amp;#8217;s painfull.&lt;/p&gt;</content>
		
		<category term="clojure" />
		
		<category term="haskell" />
		
		<category term="functionalprogramming" />
		
	</entry>
	
	 
</feed>
