contrapunctus, by Christopher League
 

Algorithmic intelligence

Inlay

When I wrote last October in defense of strong AI, I promised to take on the ‘uncharitably narrow’ notion of algorithmic used by Ian Tattersall to argue that machines will never achieve consciousness.

In The Monkey in the Mirror (2002), he writes “Computers by their nature are algorithmic, rapidly applying a fixed set of rules to the solution of well-defined questions” [page 71]. He refers here to IBM’s Deep Blue playing chess, the canonical example of a machine accomplishment that, while impressive, is surely not conscious. From this example, he extrapolates:

Human consciousness is very clearly non-algorithmic. Even if we have to conclude [...] that subjective awareness in ourselves and our relatives is due ultimately to a mass of electrochemical events in the brain [...] it is equally clearly not the product of a mechanism that dutifully clicks through a listing of tasks and ‘if-then’ choices.

I bristle a bit at this formulation, and I imagine it must annoy most software developers. It’s too narrow, in at least two dimensions. First, it seems to exclude non-determinism and parallelism: a genetic algorithm is still an algorithm. But more crucially — even if we assume deterministic sequential code — Tattersall appears to underestimate the complexity of everyday computer programs.

It doesn’t take very much code to produce a system whose precise behavior is practically unpredictable, even by the person who wrote it. We may use strategies such as functional decomposition to design a system to target specific requirements, but as the system evolves complex behaviors emerge. If they didn’t then debugging would be easy.

Yes, ultimately any deterministic program is expressed as a list of simple instructions and branches — millions of them, coupled with sophisticated data structures in an infinite number of configurations! Tattersall makes it sound like little more than a flowchart you could hang on the wall.

I don’t claim that our current programs are conscious, or intelligent in any strong sense. But don’t rely on a naïve notion of algorithmic to argue that machines cannot be conscious.

Growl upon job completion in zsh

Growl is a pretty classy system for notifications among Mac applications; there are similar notifications in other desktop environments. Whenever I run a shell command that is going to take a while to complete, it’s natural to switch to something else in the meantime. So I would like a notification as soon as the original command completes.

Growl comes with a script, ‘growlnotify’, that can trigger notifications from the command-line. You could, for example, type something like ‘make; growlnotify done’ — the problem is that I never remember that I would like the notification until after the command is already running.

So here is an alternative: this bit of zsh code will notify me upon completion of any job that takes longer than 5 seconds.

Note that this is specific to zsh, which is not the default shell on OS X, although it is supplied with the system. Possibly it can be made to work with other shells; I really don’t care. :)

Ideas for improvements: filter out commands that are obviously going to be long, interactive jobs, such as ssh sessions, vi, command-line emacs, etc. Also, is there some way not to rely on ‘/bin/date’?

A productivity pickle

Pierre Loti cafe

The things I do (or plan to do) on any given day fall roughly into three categories:

  1. Tasks that must or should be done, usually under deadline, but that I find somewhat boring or distasteful. Examples: grading, planning committee meetings.
  2. Tasks that I generally enjoy doing, and are productive in a broad sense, but nobody will keep after me if I postpone or abandon them. Examples: writing software, reading research papers deeply, learning new programming languages or libraries.
  3. Tasks that are often considered time-wasters, but do yield some benefits over time, in greater connectedness and awareness of new technologies, issues, attitudes, etc. Examples: reading my dozens of RSS feeds, Twitter and the like.

Okay, that’s not a complete taxonomy, but it will do for my purposes here.

The problem is not so much that I procrastinate — I take that as given — the problem lately is how I procrastinate.  Usually I jump straight to category 3!  That stuff has its place, but if the history of my intellectual life has taught anything, it’s that the real action is in category 2. But I let angst over the unfinished commitments in #1 ruin my enthusiasm for working on the real stuff.

Sometimes I hear (even from adults!) advice like “finish your work before you play.” These adults must have very different notions of work than I do. First, for practical purposes, all three categories are infinite streams. You literally can’t get to the bottom of one and then move on to the next. Second, from my point of view, nothing is ever finished — at best, it’s good enough, and eventually, you just stop working on it.

Maybe that last point deserves further reflection and another post. Possibly it’s an unhealthy approach to work, but I’m sure it has been with me since grade school. It explains the need for deadlines to get things done, and the tendency to leave things for the last minute. If you start too far in advance of a deadline, the job expands to fill the available time! Better to postpone judiciously and work on some interesting stuff in the interim. I’m sure I taught myself computer programming while procrastinating on social studies assignments in junior high.

I suppose I should acknowledge my good luck to have the freedom for category 2 at all. The best way to honor that freedom is to exercise it. Ha — my new summer manifesto.

Send me a wave

I’m catching up on my feeds following a 2-week vacation, and I keep hearing about this “Google Wave” thing. So finally I watched the video. And because I’ve been looking for a reason to try out the timer feature of org-mode, I made some timed notes to go along with the video.

0:01:31
HTML5 app… will forget you’re looking at a pure
browser app.
0:02:02
Lars and Jens Rasmussen, behind Google Maps
0:04:06
Stephanie Hannon, project manager
0:05:11
email invented >40 years ago, before internet itself
0:05:47
what might email look like if it was invented today?
0:06:18
email systems collate related messages into
conversations
0:06:25
wave starts w/conversation = lightweight tree structure
of messages, and users participating.
0:06:40
conversation object = shared object, hosted on a server
somewhere.
0:07:38
built with Google Web Toolkit and HTML5
0:08:19
spelling auto-correction, like on iphone
0:09:35
reply to part of message by splitting it
0:10:35
Shiny.. firefly reference.
0:10:39
Character-by-character echo during IM.. speeds up
conversation.
0:12:47
adding new participants.. avoid the “cat and mouse”
where ppl reply to early one without all participants.
0:13:17
playback to see messages in order
0:14:10
GWT pronounced “gwit”
0:14:39
Private reply possible within the wave.
0:15:10
Any subtree can restrict access to subset of
participants.
0:15:31
Attachments… drag and drop photos.
0:16:26
DnD needs gears.. not part of HTML5 yet.
0:19:10
can embed waves on a page, like embedding maps.
0:21:02
embedded waves continue to be interactive.
0:22:06
unifies (so far): email, IM, blog comments, discussion
forum, photo sharing
0:22:46
aggregate conversations from various places into your
wave client.
0:22:56
more efficient flame wars.
0:24:36
embedded within Orkut (social network) means user list
can come from there.
0:26:52
not nearly as polished as Apple demos, but they’re
reasonably endearing anyway.
0:27:38
editing; updates live.. captions, but also
collaborative editing. Now unifies wiki (google docs).
0:29:45
Shows markup of changes, which also appears through
playback.
0:30:28
Collaboration and discussion in same tool.. inline
reply as before.
0:31:04
No actual distinction between conversation and
document.
0:33:34
extract current version of any document into fresh
wave, so you can omit discussion (branch and merge).
0:33:57
Now unifies version control too.
0:34:55
extensible content model.. starts with rich text
documents.
0:36:09
multiple people editing same doc at the same
time.. tracks remarkably well.
0:38:36
GWT (Java) translates to HTML, JS, CSS, etc.
0:41:19
easy hyperlinks to other waves
0:42:36
search results update immediately
0:44:22
contextual spell checker language model… extremely
cool… “been soup” -> “bean soup”. That alone is a
great invention.
0:45:41
Spelly and Linky.. cute. also Bloggy.
0:46:13
Server-side programs that participate in waves, with
all the power of human participants. And this is how
extensions work, such as Spelly.
0:47:35
Searchy = embedded google search.
0:50:09
switch seamlessly from discussion to wiki.
0:50:36
yes/no/maybe gadget.. an extension. Live polls,
etc. Now unifies evite and its web-2 successors.
0:52:15
playback integrates with gadgets.. games, etc.
0:52:23
persistence is great here.. good for functional data
structures.
0:54:58
client-side extensions (gadgets) vs. server-side
(robots)
0:55:59
Google forms, can fill collaboratively.
1:02:49
AWESOME integration with bug tracker on code.google.com
1:05:41
Open protocol, just like email.
1:05:56
Federation = you will be able to implement your own,
and share waves across those boundaries.
1:06:47
Open source most of the code too.
1:08:13
Initech = office space reference.
1:08:43
command-line curses UI, lol.
1:10:19
private data within an organization stays there, nice.
1:13:08
Rosy = auto-live translate, VERY COOL. Between any pair
of 40 languages. Another robot.
1:14:19
standing O, man.

HTML generated by org-mode 6.27a in emacs 22

Ada Lovelace Day

Ada Lovelace
Ada Lovelace, by Dunechaser, on Flickr

For Ada Lovelace Day, I chose to write about a few women in technology who have personally influenced the course of my work.

The first is Amy Zwarico, a former professor at Johns Hopkins, and a Ph.D. from Penn. I met Prof. Zwarico my first year of college, when she taught a course based on Structure and Interpretation of Computer Programs by Abelson & Sussman. This book is mind-blowing for many young computer scientists, particularly those who enter college with some programming skills and think they know everything already. Amy guided me expertly through that transition, from arrogant git to beginner’s mind. (The route back to credentialed arrogant git is a story for another day.)

Amy’s advanced course in programming language theory crystallized my research interests for at least a dozen years, through my Ph.D. and beyond. She supported me in a wide-ranging independent study of concurrency, model checking, and the ML programming language (which I disrespect sometimes nowadays, but I do it out of love).

I think I see Amy’s influence in my style too. One year, a grad student sent a wonderful thank you note at the end of a semester, saying that I “carry that lazy elegance, as they say back in India.” Although this isn’t an expression in my culture, I recognized what he meant at once, because I know its source. Amy Zwarico was my first mentor in higher education, and I’m grateful to have connected with her.

I could end there, but I feel compelled to add a few honorable mentions, maybe because 1992 seems impossibly distant now. Throughout my research, lots of the most influential works I encountered were authored and/or propelled by women. Underrepresented among computer science faculty, perhaps, but not, it seemed, in my bibliography. In particular, I’ll mention Kathleen Fisher, Sophia Drossopoulou, and Adriana Compagnoni — all excellent, prolific researchers whose interests overlap mine considerably.

One more honorable mention, with regard to the next generation (and in recognition that esoteric research is not the only way to contribute to the world). Sacha Chua is a thoughtful and spirited young woman from the Philippines, who first came to my attention as an Emacs hacker. Sacha’s web presence is full of sincere inquiry and advice on productivity, teaching, coding, and on using technology to connect people. An awesome life indeed, rock on!

Obsolescence, part 2

IMG_0030.JPG

It’s unfortunate that a machine less than 10 years old has become somewhat difficult to use on the ‘modern’ internet. Web services begin to expect certain CSS/ Javascript/ Flash behavior, which require certain browser versions, which require certain OS versions, etc.

We (technologists in general) are not always doing such a great job of graceful degradation, and it’s hard to realize that if you’re following along, upgrading software every year and hardware every 3–4 years. I dug an iBook (ca. 2001) out of the closet, it had Panther (10.3), which has Safari 1.x and won’t run Firefox 3.x or Flash player 10 or Quicktime 7 (needed for more applications than you would think).

This machine will supposedly run Tiger (10.4), so that might open up a lot more possibilities for upgrading software. Or there’s Linux. When people say Linux “breathes new life into old hardware,” I think this is what they mean. The software dependencies are less complex, and you can still get any version of anything.

Idealism v. Knowledge

Me in robes

Periodically, an undergraduate from 101 speaks to me after class. Nicest fellow, and full of interesting ideas about how technology can help people. He pitches his plans to me, I think to feel out what is possible, what has been done, how much and what kind of training it will take, etc. I’m happy to chat, and careful not to discourage him — an intrinsically motivated student is like a rare and delicate orchid, and I don’t want to be a clumsy botanist. (If his actual goal is to be a bazillionaire like Mark Zuckerberg, then technically that’s extrinsic motivation, but still it’s more valuable than “I’m in this class because somebody told me I should take it.”)

Meanwhile, I spend time some weekends in an office with a couple other very smart and capable technologists. We have good ideas too, but it’s awfully difficult to get something off the ground, because we’re constrained by what has been done well enough already, and by what we know is impossible.

Skills are always a blessing, but sometimes knowledge is a curse. Many of my student’s ideas are AI-complete. I know what that means, and see it as a serious barrier, at least in the current era. One approach is to take up basic research and chip away at the barrier — worthwhile, but not a business plan. And so I wonder: if we could adopt the beginner’s mind, ignore known barriers, and follow through… possibly something useful and novel could come of it, even though the barriers are real.

Where is the boundary between naive and visionary?

IT Groundhog Day

Cheeseburger restaurant

Most of our lab computers are reimaged nightly, which means all of the Windows/IE annoyances recur every time I’m using the computer in front of the class: “there are unused icons on your desktop!” “do you want phishing protection?” “should I save your password?” “do you want to turn on auto-complete?” “please wait 10 minutes while Visual Studio initializes settings” etc. Today I likened it to the film Groundhog Day — from the perspective of Windows, every pop-up question is fresh and helpful; it’s me who is stuck in the time loop. :)

testing again from emacs

First one didn’t seem to go through, but here I am trying to post from
Emacs using weblogger.el.

What do you know, it seemed to publish. Now I have to figure out some
things: is auto-fill-mode or longlines-mode best? What’s the difference
between weblogger-save-entry, weblogger-send-entry,
weblogger-publish-entry? This could be useful though.

So interesting, seems like auto-fill-mode was wrong… what I really want
is longlines-mode so that text wraps more naturally (without Emacs
inserting the hard returns). I don’t think the keywords worked, even
though I supposedly had a patch for that. I’ll have to fill such
meta-data on the web?

Bah, even with longlines-mode, it still wrapped after sending. Maybe
this isn’t worth the investment yet. :(

Dynamic languages and TDD

Shops

For a one-time strong static type snob, I’ve become pleasantly enamored with Python lately. Seriously — it’s a Lisp for a new generation. I remember when I first heard about Python, people seemed to place it in the same class as Perl (so-called scripting languages) and since I already let Perl taint my brain, I figured one was enough. That was a mistake.

What really made me take a closer look at Python was the object-relational mapping in Django. Specifying data models in Django is natural, convenient, powerful and — get this — there’s no code-generation step. No turn-the-crank-and-output-a-mess-of-code tool to add to your Makefile. Advocates of Domain Specific Embedded Languages in Haskell might even be pleased, except that it doesn’t take three Ph.D. dissertations’ worth of language extensions to satisfy the type checker. (Ouch — did I just bite the hand that fed me? Whatever, it’s a ’blog… the medium practically invites polemics.)

Anyway, test-driven development (TDD) is a necessity for dynamic languages. When a typo in a method name has no ramifications until run-time, there had better be some unit tests exercising that code frequently and automatically. Once you have the framework in place, it’s a baby step to writing a test that fails before you code the feature that satisfies it.

I ought to take it further. I’m not yet coverage-testing. Ideally, commenting out any single line of code should cause some test to fail. Even changing a ‘<’ operator to ‘≤’ should cause some test to fail. That’s how you get to more lines of test code than normal code.

And now Dijkstra’s spirit is looking over my shoulder, muttering something about “hopelessly inadequate”. Oh well, maybe in a few weeks I’ll be hacking together something in Scala or proving something in Coq, and I’ll change my mind about static type discipline again.

Twenty-five things

Preface: I mostly think these chain things are stupid, but since I enjoyed a few of yours, I felt obligated to reciprocate. If you’re reading this on my site or RSS, sorry, it’s mainly intended for Facebook; I just wanted it archived elsewhere too.

Official Rules, if you decide to do so: Once you’ve been tagged, you write a note with 25 random things, facts, habits, or goals about you. At the end, choose 25 people to be tagged. You must tag the person who tagged you. If I tagged you, it’s because I already received your list (thanks!) or I want to know more about you. (To do this, go to “notes” under tabs on your profile page, paste these instructions in the body of the note, type your 25 random things, tag 25 people (in the right hand corner of the app) then click publish.) You can do it in pieces. Just click on save, and you can come back later and do more.

1. I’m pathologically indecisive; choosing to do X means closing doors on anything that is not X. Therefore I take some comfort in the many-worlds interpretation of quantum mechanics, because in other universes I could still be exploring those roads not taken.

2. I have, shall we say, a rich mental life. One aspect is that there’s usually an ‘other’ to whom my inner chatter is directed. Sometimes it’s a real person I know, sometimes it’s a composite or even more abstract — not a person but another perspective.

3. I’m wondering whether I can get through 25 things about me without someone alerting Creedmoor Psychiatric Center — located conveniently up the street from my home.

4. When I was a kid, #2 manifested itself as imaginary friends. They were so real to me that one day I came home from school to an empty house (latchkey kid) and poured TWO glasses of juice before I realized nobody was there to take the other one. It made me feel lonely, but not ashamed.

5. I have little use for nostalgia. Or at least I don’t seem to experience it as others do. Maybe because I feel the current era of my life is always the best. And even if it isn’t, I’d rather work on improving my situation than live in the past.

6. I hate winter time more every year. The darkness tends to depress me (seasonal affective disorder?) and I hate having to wear bulky layers of garments.

7. The fact that I need sunlight to be happy is absurd, because otherwise I’m not the least bit outdoorsy.

8. I think I got outdoorsiness out of my system before turning 17. With scouts we went camping once a month, even in the dead of winter.

9. I came close to becoming an eagle scout, but I didn’t finish and don’t regret it. My interests simply shifted.

10. I feel lucky that I learned computer programming at a young age. That kind of problem solving has always been fun for me, so it seems perfectly suited to my talent and temperament.

11. In grade school, we were asked to draw a picture of what we wanted to be or do as an adult. I drew a lovely, intricate picture of a doctor’s office and me with a stethoscope and related paraphernalia. After coming home, I was angry with myself. I tore up the drawing and made another one, all in one color (green, I think), of a room full of big 70s-era computer systems, and me typing at the terminal. The second drawing was uglier, but it was the truth.

12. I was not very savvy about applying to college, which is surprising to my friends from high-end prep schools where 90% go to Ivy League, MIT, Stanford, etc. I applied to exactly one university — early decision at Johns Hopkins — which I chose because it was nearby and seemed to be prestigious. I didn’t have any backup plan, but I didn’t need one.

13. I enjoy travel, and (apart from carrying a camera bag) I prefer to “go native” as much as possible. I’ll be wandering around Paris, for example, and have people stop me to ask for directions (in French). This kind of travel is very interesting, but not always relaxing. Sometimes it’s nice just to be pampered at a resort. Or so I’m told.

14. I’ve been to England, Scotland, Ireland, France (incl. Corsica), Germany, Luxembourg, Switzerland, Spain, Poland, Australia, Bermuda, Mexico, Canada, and Japan.

15. High-priority destinations I have not seen yet include Italy, the Netherlands, Taiwan, New Zealand, and the southwest U.S. (never been to Arizona or New Mexico).

16. I have lived only in Maryland, Pennsylvania, Connecticut, and New York. Plus 4 months in southern California when I was in 2nd grade and 3 months in northern California in 1999; so maybe they add up to something.

17. When I went to college, I met many friends from the NYC area. I thought they were snobs about their hometown, but now I live at the center of the world too.

18. A highlight of travel for me (and of living in NYC) is experiencing fantastic food cultures and restaurants.

19. A bank web site asking for favorite food, movie, artist, etc. as a “security question” is ludicrous. Never mind that security questions are inherently insecure; are your tastes and experiences so stagnant that you’d answer the question the same way every time for months and years on end?

20. I have always been a fan and advocate of public radio. Lately, I listen almost entirely via podcast (like TiVo for radio), but independent podcasts now occupy an impressive slice of the pod too.

21. I donate to WNYC public radio, the Free Software Foundation, the Electronic Frontier Foundation, the ACLU, Americans United for the separation of church and state, the Democratic National Committee (grudgingly sometimes), and occasionally to arts organizations like Lincoln Center and the New York Philharmonic.

22. My parents have always been amazing. Even when I was a teenager, they were mostly known as “the cool parents” among my friends, not because they were overly permissive, but I think because they were willing to talk and relate to us as peers.

23. My brother and I have similar tastes and outlook on some things, but we’re polar opposites on many others.

24. For a brief time around 1999, I found the idea of having kids to be somewhat desirable, but this didn’t last long. With global population growth rates, there will be less suffering if cultures fully embrace childlessness and adoption as alternative paths to happiness, on par with procreating. (I’m sure it’s a coincidence that I just watched the Simpsons episode with SSCCATAGAPP: Singles, Seniors, Childless Couples And Teens And Gays Against Parasitic Parents, LOL.)

25. It may be partly because my parents are so great that I don’t feel the need to spawn. We’ve reached the pinnacle of parenthood, so there’s nothing more to do. :)

Smart playlists with weak references

Star

The iTunes smart playlist feature is remarkably easy to use and can do some pretty sophisticated things. But there’s one more feature I’d like to have: I’ll call it a weak reference, by analogy to a similar concept in computer programming languages.

Without going into mind-blowing detail — this isn’t a post about programming languages — a weak reference is a way to refer to some object so that you’re not required to keep it around. Think of bibliographic references from one book to another in the library. A strong reference from book A to book B would mean that you can’t check out book A without also taking along B! In contrast, if it’s a weak reference, then you might check out A, encounter the reference to B, and say “Oh well, I don’t have that book with me right now.”

Playlists (smart or not) are the primary way that I specify which songs are copied to my iPod. I have one dumb catch-all list “iPhone” into which I can dump anything I definitely want synchronized. There’s a smart list “Recently added” that makes sure that any tracks added to the library in the past 3 months end up on the iPod. Also, anything with a star rating gets copied, so that will include lots of individual songs that are (for me) the highlights of their albums, for example.

Maybe you see where I’m going with this.

I want to create smart playlists to specify queries, and I’d like to sync those queries to my iPod, but I don’t want membership in that list to be enough to pull all matching songs onto the player — it would just contain those that match the query and are on the device already by virtue of being on other lists.

Here’s an example: I have a smart playlist on iTunes called “not classical” that simply excludes tracks from certain genres (classical, opera, speech, broadway) that I don’t want to pop up in an otherwise wide-ranging shuffle. Now, if I specified that “not classical” should be synchronized to my iPod, it would pull in much of my library, and would not fit. However, if I could sync it as a weak reference, then I could still use the results of that query applied to tracks already on the player.

Steganography demo

Original

Original, unmodified image

The other day, for kicks, I wrote a small program to embed a hidden image within another image, and another to extract the hidden image. This is called steganography:

…the art and science of writing hidden messages in such a way that no-one apart from the sender and intended recipient even realizes there is a hidden message, a form of security through obscurity. By contrast, cryptography obscures the meaning of a message, but it does not conceal the fact that there is a message. [Wikipedia]

Steganographic

With hidden image embedded; the differences are all but invisible.
RGB << 3 (Stegano)

Embedded image partially revealed by shifting colors; click through to see more.

Step through the demo on flickr, and read the descriptions to understand what’s going on. In this technique, I use 6 bits per channel for the outer, visible image, and 2 bits per channel for the hidden image. However, the composed result is pretty much indistinguishable from the original.

The programs use the GraphicsMagick variant of the ImageMagick library. (I had GM ready to go already because I used it for other projects.) If you want to play with these images yourself, the JPEG conversions on Flickr will not work; download this PNG instead.

// Steganographic decoder, requires GraphicsMagick
// Copyright 2009 Christopher League
// This is free software; you may distribute & modify it under GNU GPLv3
#include <Magick++.h>
#include <iostream>

using std::cout;
using std::string;
using namespace Magick;

Quantum revealDisguisedColor(Quantum q)
{
  // just keep lowest 2 bits, then amplify by 2^6
  return (q & 3) << 6;
}

int main(int argc, char** argv, char** envp)
{
  InitializeMagick(*argv);
  assert(argc == 3);

  const string samplePath = argv[1];
  const string outputPath = argv[2];

  cout << "Loading " << samplePath << '\n';
  Image im (samplePath);
  Geometry g = im.size();

  cout << "Dimensions are " << (string)g << '\n';
  cout << "Depth is " << im.depth() << '\n';
  assert(im.type() == TrueColorType);

  for(unsigned x = 0; x < g.width(); x++) {
    for(unsigned y = 0; y < g.height(); y++) {
      Color c = im.pixelColor(x, y);
      cout << (string)c << " --> ";
      c.redQuantum(revealDisguisedColor(c.redQuantum()));
      c.greenQuantum(revealDisguisedColor(c.greenQuantum()));
      c.blueQuantum(revealDisguisedColor(c.blueQuantum()));
      cout << (string)c << '\n';
      im.pixelColor(x, y, c);
    }
  }

  cout << "Writing " << outputPath << '\n';
  im.write(outputPath);
  return 0;
}
Download this code: 2009/01/decode.cpp

// Steganographic encoder, requires GraphicsMagick
// Copyright 2009 Christopher League
// This is free software; you may distribute & modify it under GNU GPLv3
#include <Magick++.h>
#include <iostream>

using std::cout;
using std::string;
using namespace Magick;

Quantum mergeQuanta(Quantum pub, Quantum priv)
{
  // reduce private color to just 2 bits
  priv >>= 6;
  // zero out lowest 2 bits of public color
  pub &= 0xFC;
  // merge
  return pub | priv;
}

Color mergeColors(Color pub, Color priv)
{
  pub.redQuantum(mergeQuanta(pub.redQuantum(), priv.redQuantum()));
  pub.greenQuantum(mergeQuanta(pub.greenQuantum(), priv.greenQuantum()));
  pub.blueQuantum(mergeQuanta(pub.blueQuantum(), priv.blueQuantum()));
  return pub;
}

int main(int argc, char** argv, char** envp)
{
  InitializeMagick(*argv);
  assert(argc == 4);

  const string publicPath = argv[1];
  const string privatePath = argv[2];
  const string outputPath = argv[3];

  cout << "Loading " << publicPath << '\n';
  Image pub (publicPath);
  Geometry g = pub.size();
  assert(pub.depth() <= 8);
  assert(pub.type() == TrueColorType);

  cout << "Loading " << privatePath << '\n';
  Image priv (privatePath);
  assert(priv.size() == g);
  assert(priv.depth() <= 8);
  assert(priv.type() == TrueColorType);

  for(unsigned x = 0; x < g.width(); x++) {
    for(unsigned y = 0; y < g.height(); y++) {
      pub.pixelColor(x, y, mergeColors(pub.pixelColor(x, y),
                                       priv.pixelColor(x, y)));
    }
  }

  cout << "Writing " << outputPath << '\n';
  pub.write(outputPath);
  return 0;
}
Download this code: 2009/01/encode.cpp

Completion time correlation

For the first time, I decided to carefully maintain the final exams in the order that they were completed, and see if this was at all correlated with the score. This is a 101 course for non-majors and majors with limited prior experience.  There is a somewhat reasonable correlation (0.56), although it gets pretty noisy in the middle.  I used the rank instead of the raw score (which actually would give -0.6).  Blue diamonds are the data points and the gray plus signs are the linear regression (didn’t feel like figuring out how to mix scatter plot and line graph!)

Note this isn’t measuring the amount of time to complete the exam. The lowest grade was also the last to finish, but s/he showed up extremely late and spent a lot less time on the exam than others. Tendency to show up late is also of course correlated with poor performance. :)

FSF v. Cisco

Free Software Foundation Files Suit Against Cisco For GPL Violations – Free Software Foundation.

Kind of glad my last router was an ASUS, not a Linksys.  Down with Cisco, until they choose to meet their obligations for licensing other people’s code!