Evan A. Sultanik, Ph.D.

Evan's First Name @ Sultanik .com

Chief Scientist
Digital Operatives, LLC

Adjunct Professor
Drexel University College of Computing & Informatics
Department of Computer Science

Recent Content:

鯖の味噌煮 (Saba no Misoni)

A Receipt for My Favorite Japanese Dish

Software

  • Fillets from one large mackerel cut in 4cm pieces.
  • 2.5cm ginger cut in matchsticks
  • 2 tbsp. mirin
  • 1 tbsp. sake
  • 1.5 tbsp. sugar
  • 1 tbsp. soy sauce
  • 1 cup dashi stock (or water)
  • 5 tbsp. red (“aka”) miso paste
  • 2 scallions cut in 2cm pieces, whites and greens separated.

Algorithm

  1. Combine dashi, soy, mirin, sake, 3 tbsp. miso, and sugar in a sauce pan and bring to boil.
  2. Add ginger, scallion whites, and mackerel, put on tinfoil otoshi buta, and simmer on medium heat for 10 minutes, basting every few.
  3. Stir in remaining miso and cook for an additional 5 minutes.
  4. Remove from heat, add scallion greens, replace otoshi buta, and cool to room temperature in pan.

Azerbaijani Sous

A recipe for my wife’s favorite stew.

Although they are not Azeri, my wife and her family lived in Bakı from the mid-1980s through the first half of the Nagorno-Karabakh war (my father-in-law, a Colonel in the Soviet Army, was stationed there). During that time my mother-in-law developed both an appreciation for and ability to cook Azerbaijani cuisine. One dish that became my wife's favorite is called “Азербайджанский соус” (Pronounced: /ˌæzərbaɪˈdʒɑːnskʲɪj sos/ Translit.: Azerbaidzhanskiy sous, English: Azerbaijani sauce). Sous, when pronounced by a Russian, sounds like the English word “so” with an “s” consonant added at the end. It is more of a stew than a sauce, at least in my mother-in-law's interpretation. The main ingredients include eggplant, peppers, tomato, cilantro, and a whole chicken. Given that my mother-in-law's rendition is so delicious, I wanted to learn how to reproduce it in my own kitchen. Having mastered the art of cookery through observation, my mother-in-law—like most other old world home cooks—is not a woman of weights and measures; her recipes are conveyed in units of “pinches of this” and “splashes of that.” This makes reproducing a dish in foreign kitchens quite difficult. One of the problems with finding a formal, written recipe for Sous is that the same word has many other meanings in French with respect to gastronomy (e.g., “Sous-chef” and “Sous-vide”), which makes Google searches (even in Russian) ripe with false-positives. Furthermore, I can't seem to find any English-language recipes. For all of these reasons, I set out to grok my mother-in-law's method. I think I have converged upon a fairly stable recipe (with some of my own modifications) which I have outlined below. In doing so, I have tried to highlight the changes I have made to the original recipe.

Biggest

A solution for digital hoarding.

I have a problem. I admit it. I have a problem deleting files. In the “good times”—vi&., when I have gigabytes to spare on my hard drive—I simply don’t bother deleting temporary files. That video I encoded/compressed to MPEG? Sure, I’ll keep the raw original! Why not? Just in case I ever need to re-encode it at a higher bitrate, you see.

Inevitably, I run low on disk space months later, at which point I’ve forgotten where all of those pesky large files are living.

Enter my script, which I simply call biggest. This script will conveniently print the $n$ biggest files that are rooted at a given directory. Here’s an example:

$ biggest 10
. [92MB]
|- art [15MB]
|  |- .svn [7MB]
|  |  `- text-base [7MB]
|  |     |- heat.png.svn-base [2MB]
|  |     `- SWATipaq.png.svn-base [2MB]
|  |
|  |- heat.png [2MB]
|  `- SWATipaq.png [2MB]
|
|- os [7MB]
|  `- os.pdf [3MB]
|
|- .svn [9MB]
|  `- text-base [9MB]
|     `- proposalpresentation.pdf.svn-base [8MB]
|
|- eas28@palm [14MB]
|- ESultanikPhDProposalPresentation.tar.gz [12MB]
|- APLTalk.pdf [9MB]
|- proposalpresentation.pdf [8MB]
`- proposalhandouts.pdf [7MB]

It is available on GitHub, here:

Ballmer Peak

A Generalization

Through my many years of coding, I have come to this realization:

The so-called “Ballmer Peak”, as it is currently understood, is but a two dimensional projection of what in reality is a higher dimensional space, vi&.,

The

AAMAS 2010

The Ninth International Conference on Autonomous Agents and Multiagent Systems

If you've been keeping up, you know that I was attending the Ninth International Conference on Autonomous Agents and Multiagent Systems (AAMAS) last week in Toronto, Canada. The conference went really well, as did the two workshops I also attended:

  1. The Third International Workshop on Optimisation in Multi-Agent Systems (OptMAS); and
  2. The Twelfth International Workshop on Distributed Constraint Reasoning (DCR), which Rob and I co-chaired.

I presented my paper on distributedly solving art gallery and dominating set problems on Thursday. AAMAS also allows for full papers to additionally present a poster for the work. This was my first time making a poster purely in LaTeX, and it was a very smooth experience. I created a poster template for the Drexel CS department which can be downloaded here. You can view and download the presentation slides and poster for my paper here.

Success in Linux

Linux: Simultaneously the source of and solution to all of my computing problems.

Oh Linux, you’re simultaneously the source of and solution to all of my computing problems. I recently had my own version of Linux “success”. Read on to hear the whole story.

Retirement Planning through Treasure Hunting

A metaphor for the process of earning a Ph.D.

I've deliberated long and hard to decide whether or not to publish this blog entry.

I inevitably end up liking things that require extensive deliberation as to whether or not I like them.
@ESultanik
Evan Sultanik

I'm afraid that, given I've not yet completed my Ph.D., it will be misconstrued as boastful or, worse yet, exculpating. This is not my intention. If anything, I hope that my observations below will help others pursuing a Ph.D. to both rationalize their own situation and educate others on the process.


It's been a long while since I've posted to this blog, in large part due to the fact that I've been in the throes of wrapping up my degree and hunting for a job. As such, I get a lot of questions from non-academics as to when I will finish. It seems like many people are under the impression that a Ph.D. is “what you get if you tough it out and stay in school and study a few more years after undergrad.” While this is technically true, many either don't realize or don't understand that a Ph.D.—at least in the sciences, engineering, and mathematics—requires independent research. A Ph.D. isn't earned through studying hard and passing tests. It's particularly frustrating when people see that I've been pursuing my Ph.D. for ~4 years (an average, if not short, duration for the degree) and immediately draw the conclusion that, “Since [he] hasn't graduated yet, [he] must not be studying hard enough!” Over the years, I've developed a metaphor that I give to people to explain the process:

Earning a Ph.D. is like trying to fund one's retirement through treasure hunting.

When I started my Ph.D., I had to choose a topic to study. That's like choosing a location in a vast field on which to begin digging for buried treasure. You know that the treasure is out there somewhere, but you're not sure where or how deep. This is not entirely up to chance: One's advisor(s)—experts in gold digging—do help in choosing the location, and there are ways to intelligently predict where the treasure might lie. The trouble is that one might dig for years and years and only be left with a huge pile of dirt. Worse yet, one might be only a few millimeters of soil away from the gold and not even know it. Every once in a while one might find a small nugget of gold in the dirt, egging him or her on, but there is no guarantee that one will eventually hit the mother lode. In other instances, one knows exactly where the treasure lies, but it is underneath an impenetrable rock that requires years and years of chipping away to exhume. In some cases one does find a huge treasure chest, but more often than not one's fortune is amassed from accumulation of the small nuggets. Defending one's dissertation, then, is like choosing to go into early retirement based off of one's fortune amassed from treasure hunting. Is it enough? Will I be able to support myself on what I've found so far? In order to complete one's degree, one has to defend his or her work to a committee of experts—all of whom are expert treasure hunters and, in a sense, one's competition. You have to convince them that your fortune will be enough to support yourself. And you have to do all of that without wasting too much time creating fanciful metaphors of debatable import.

iPhone Toolchain on Linux

A Tutorial

I have an iPhone. I also use Google Mail as my web-based mail client. Unfortunately, there is really no good way to get push Gmail on an iPhone. Even now, post firmware 3.0, these are the best ways:
  1. Pay for a service like MobileMe.
    Problem: service fees seem like overkill, and the push E-mail would be the only benefit I’d get from it.
  2. Wrap Gmail’s IMAP service in an exchange server. There are some paid services that do this, however, Z-Push is free (if one can host it one’s self).
    Problem: the iPhone only supports a single Exchange server at a time. Therefore, I’d have to choose between getting push E-mail versus over-the-air calendar/contacts synchronization that is currently provided through Google’s own “Sync” Exchange wrapper.
  3. Write an app that uses the new Push Notification service in firmware 3.0 to remotely push mail to the phone.
    Problem: this would probably be a very lucrative solution (i.e., I’ll bet lots of people would pay a nominal one-time fee for this app), but it would almost definitely be rejected from the App Store. Furthermore, it would require me to set up a back-end server running 24/7 to push the updates.
  4. Jailbreak the phone and write a daemon that runs in the background, connects to Google’s IMAP service, and goes into IDLE mode.
    Problem: the only Apple device I own is my iPhone; how might I compile my own apps for it? (Sure, my wife does have a PowerBook, but that would be cheating, right? Right‽)
Read on to discover how I was able to set up an iPhone development toolchain on Linux.

File Drop

Computer-to-Computer File Transfer for the Masses

Continuing the recent theme of posting-random-scripts-as-blog-entries…

I recently needed a quick and dirty way to send a really large (~1 gigabyte) file to someone. We were both on the same LAN, so it didn’t really make sense for me to upload it to my externally hosted web server. I do not have a web server installed on my laptop and, at the time, it seemed like overkill to install a web server just so I could send him my file. Using a thumb drive or scp would have been an option, but each would require the recipient to be physically at my computer (despite being on the same LAN, he was a 10 minute walk away). Therefore, I gave myself a 10 minute deadline to code my own solution (plus it would be a fun diversion from writing my journal paper due later that day).

Given that I had a whole 10 minutes (an eternity when it comes to Perl hacking), I figured I might as well make my method generalized (i.e., not only should my script be able to send files, but it should also be able to receive).

First, I had to decide on a method. FTP seemed like a logical choice, but, besides really tech savvy people, who has full-blown FTP clients installed these days? In keeping with my generality goal, my solution would ideally be usable by, say, my mom. And moms don’t know ‘bout my FTP. Everyone, my mom (and my mom’s mom) included, has a web browser and knows how to use it. Therefore, good ol’ HTTP it was. And I even had a bunch of old code to hack together!

I ended up with a script that I call filedrop. Here’s the usage:

$ filedrop
Version: filedrop 0.1 2009-07-01 http://www.sultanik.com/
Copyright (C) 2009 Evan A. Sultanik

Usage: filedrop [OPTIONS] FILE_PATH

Options:
  -s           send a file by hosting it on a local web server (default)
  -r           receive a file by accepting it from a local web server.
               FILE_PATH should be a directory to which the files should be
               saved.  FILE_PATH will default to ‘.’ in this mode.
  -n, --num=N  quit after sending/receiving N files.  If N is less than zero
               the program will send/receive files until manually
               terminated.  If N is zero then the program will immediately
               quit.  Default is -1.

And here’s an example of how the file transfer went down:


LeEtH4X0r: Y0 Home Skillet! Can you fry me up some juarez‽
Me: Indubitably!
$ filedrop -s -n1 ./hugefile.tar.gz
Server running at: http://my_ip:47489/
Me: Go to http://my_ip:47489/
LeEtH4X0r: ZOMGKTHXBAI!!!!1ONE

Here’s the code:

#!/usr/bin/perl -w

use HTTP::Daemon;
use HTTP::Status;

my $version = “0.1”;
my $date = “2009-07-01”;
my $copyright = “2009”;

my $port = 80;

sub print_usage {
    print “Version: filedrop $version $date http://www.sultanik.com/\n”;
    print “Copyright (C) $copyright Evan A. Sultanik\n\n”;
    print “Usage: filedrop [OPTIONS] FILE_PATH\n\n”;
    print “Options:\n”;
    print “  -s           send a file by hosting it on a local web server (default)\n”;
    print “  -r           receive a file by accepting it from a local web server.\n”;
    print “               FILE_PATH should be a directory to which the files should be\n”;
    print “               saved.  FILE_PATH will default to ‘.’ in this mode.\n”;
    print “  -n, --num=N  quit after sending/receiving N files.  If N is less than zero\n”;
    print “               the program will send/receive files until manually\n”;
    print “               terminated.  If N is zero then the program will immediately\n”;
    print “               quit.  Default is -1.\n”;
    print “\n”;
}

my $mode = “s”;
my $num = -1;

my $last = “”;
my $nextIsN = 0;
foreach my $arg (@ARGV) {
    if($arg eq “-s”) {
        $mode = “s”;
    } elsif($arg eq “-r”) {
        $mode = “r”;
    } elsif($arg eq “-n”) {
        $nextIsN = 1;
        next;
    } elsif($arg =~ /-n(\d+)/) {
        $num = $1;
    } elsif($arg =~ m/--num=(\d+)/) {
        $num = $1;
    } elsif($nextIsN) {
        $num = $arg;
    } else {
        if(!($last eq “”)) {
            print_usage() && die(”Invalid option: “ . $last . “\n”);
        }
        $last = $arg;
    }
    $nextIsN = 0;
}
if($last eq “” && $mode eq “s”) {
    print_usage() && die(”Path to a file to host expected!\n”);
} elsif($last eq “” && $mode eq “r”) {
    $last = “.”;
}

my $file = $last;

exit(0) if($num == 0);

my $d = HTTP::Daemon->new(LocalPort => $port) || HTTP::Daemon->new() || die;
print “Server running at: “, $d->url, “\n”;
my $servings = 0;
while(my $c = $d->accept) {
    while(my $r = $c->get_request) {
        if($mode eq “s”) {
            if($r->method eq ‘GET’) {
                print “Someone’s downloading!\n”;
                $c->send_file_response($file);
                print “Download finished!\n”;
                $servings++;
            } else {
                $c->send_error(RC_FORBIDDEN)
            }
        } elsif($mode eq “r”) {
            if($r->method eq ‘POST’) {
                print “Someone is uploading!\n”;
                $servings++;
                my $url = $r->content;
                while($url =~ m/.*?-+(\d+)\r\nContent-Disposition:.*? filename=”([^”]+)”.*?\r\n\r\n(.*?)\r\n-+\1-+(.*)$/ism){
                    my $id = $1;
                    my $filename = $2;
                    my $content = $3;
                    $url = $4;
                    my $newName = $filename;
                    my $i = 0;
                    $newName = $filename . “.” . ++$i while(-e $file . “/” . $newName);
                    if($i > 0) {
                        print “A file of named $filename already exists in $file!\n”;
                        print “Saving to “ . $file . “/” . $newName . “ instead.\n”;
                        $filename = $newName;
                    }
                    open(OUTFILE,”>” . $file . “/” . $filename) or die(”Error opening $file/$filename for writing!\n”);
                    binmode OUTFILE;
                    print OUTFILE $content;
                    close(OUTFILE);
                    print “Received $filename (ID: $id)\n”;                    
                }
                $h = HTTP::Headers->new;
                $h->header(’Content-Type’ => ‘text/html’);
                my $msg = “Uploaded

Uploaded!

”; $msg .= “

Click here to upload another file.

” if($num < 0 || $servings < $num); $msg .= “”; $r = HTTP::Response->new( HTTP_OK, “”, $h, $msg); $c->send_response($r); } elsif($r->method eq ‘GET’) { print “Someone connected! Sending the upload form...\n”; $h = HTTP::Headers->new; $h->header(’Content-Type’ => ‘text/html’); $r = HTTP::Response->new( HTTP_OK, “”, $h, “Upload

Please specify a file, or a set of files:

“); $c->send_response($r); print “Sent!\n”; } else { $c->send_error(RC_FORBIDDEN); } last if($num > 0 && $servings >= $num); } last if($num > 0 && $servings >= $num); } $c->close; undef($c); last if($num > 0 && $servings >= $num); } close($d);

Mail Notifier

Gmail Notifications in Linux

Screenshot of the notifier notifying.

An example of the notifier, well, notifying.

I recently caught a glimpse of how Gmail Notifier works on a friend’s Mac. It looked pretty cool. Unfortunately for me, though, there’s no reasonable facsimile in Linux. Sure, there are a couple options, but they aren’t available in Gentoo’s package management system. Given my recent experience dealing with E-mail from Perl, I figured it would be just as easy to write my own E-mail notifier as it would be to manually install these programs (along with their dependencies). I was right. I just spent the last ~20 minutes (while idling through a meeting) writing such an app. The code follows below. Its only dependency is XOSD.

Disclaimer: I blatantly cribbed some of my code from Flavio Poletti (for the MTA stuff) and Bill Luebkert (for the password input).

Future work: right now the code simply polls the mail server once every three minutes. In the future I’ll post an update that uses IMAP Idle to reduce bandwidth.

#!/usr/bin/perl -w

use Term::ReadKey;	END { ReadMode (’restore’); }	# just in case
use Mail::IMAPClient;
use IO::Socket::SSL;
use File::HomeDir;

my $username = ‘youremail@domain.com’;
my $sleeptime = 180; # Time between checks, in seconds.
my $conffile = File::HomeDir->my_home . “/.checkmail”;

######################################################

$canceled = 0;
$inwhile = 0;

sub get_passwd {
    # legal clear passwd chrs (26+26+10+24=86): “a-zA-Z0-9!#$%&()*+,-./:;<=> ?@[\]^”;
    my @legal_clear = (’a’..’z’, ‘A’..’Z’, ‘0’..’9’, split //,
                       ‘!#$%&()*+,-./:;<=> ?@[\]^’);
    my %legal_clear; foreach (@legal_clear) { $legal_clear{$_} = 1; }
    $| = 1;	# unbuffer stdout to force unterminated line out
    ReadMode (’cbreak’);
    my $ch = ‘’;
    while (defined ($ch = ReadKey ())) {
	last if $ch eq “\x0D” or $ch eq “\x0A”;
	if ($ch eq “\x08”) {	# backspace
            print “\b \b” if $passwd;	# back up 1
            chop $passwd;
            next;
	}
	if ($ch eq “\x15”) {	# ^U
            print “\b \b” x length $passwd;	# back 1 for each char
            $passwd = ‘’;
            next;
	}
	if (not exists $legal_clear{$ch}) {
            print “\n’$ch’ not a legal password character\n”;
            print ‘Password: ‘;
            next;
	}
	$passwd .= $ch;
    }
    print “\n”;
    ReadMode (’restore’);
    return $passwd;
}

$SIG{’INT’} = ‘INT_handler’;

sub INT_handler {
    exit(0) if(!$inwhile);
    $canceled = 1;
    print “\nCaught Signal; exiting gracefully!\n”;
}

print “Password: “;
my $password = &get_passwd();

while(!$canceled) {
    $inwhile = 1;

    my $socket = IO::Socket::SSL->new(
        PeerAddr => ‘imap.gmail.com’,
        PeerPort => 993,
        )
        or (print STDERR “Warning: lost internet connection!\n” && next); # Perhaps we lost the internet connection?
    my $greeting = <$socket>;
    my ($id, $answer) = split /\s+/, $greeting;
    die “problems logging in: $greeting” if $answer ne ‘OK’;

    my $client = Mail::IMAPClient->new(
        Socket   => $socket,
        User     => $username,
        Password => $password,
        Uid => 1,
        )
        or die “new(): $@”;
    $client->State(Mail::IMAPClient::Connected());
    $client->login() or die ‘login(): ‘ . $client->LastError();

    die(”Failed authentication!\n”) unless $client->IsAuthenticated();

    $client->examine(’INBOX’) or die “Could not examine: $@\n”;
    my @msgs = $client->unseen or die “Could not search the inbox! $@\n”;

    my $last_max = -2;
    if(-e $conffile) {
        # Load the old largest
        open(CONFFILE, “<” . $conffile) or die(”Error opening “ . $conffile . “\n”);
        while() {
            my $line = $_;
            $last_max = $1 if($line =~ /^\s*last_max_uid\s*=\s*(\d+)\s*$/i);
        }
        close(CONFFILE);
    }

    my $max = -1;
    my @over;
    for my $msg (@msgs) {
        $max = $msg if $msg > $max;
        push(@over, $msg) if $msg > $last_max;
    }

    if($max >= 0) {
        open(CONFFILE, “>” . $conffile) or die(”Error opening $conffile for writing!\n”);
        print CONFFILE “last_max_uid = “ . $max . “\n”;
        close(CONFFILE);
    }

    if($last_max >= 0) {
        open(OSDC, “| osd_cat -c green -p middle -A center -s 2 -l 5 -f \”-bitstream-bitstream vera serif-*-*-*-*-17-*-*-*-*-*-*-*\””);
        for my $m (@over) {
            my $hashref = $client->parse_headers($m, “From”)
                or die “Could not parse_headers: $@\n”;
            print OSDC “New mail from “ . $hashref->{”From”}->[0] . “!\n”;
        }
        close(OSDC);
    }

    $client->logout();
    sleep $sleeptime;
}