
use Fcntl ':seek';
use Test::More qw(no_plan);

sub search {
  my ($fh, $cmp) = @_;
  my ($lo, $hi) = (0, -s $fh);

  while (1) {
    my $mid = int(($lo + $hi)/2);
    if ($mid) {
      seek $fh, $mid-1, SEEK_SET;
      my $junk = <$fh>;
    } else { seek $fh , $mid, SEEK_SET; }
    my $start = tell $fh;
    my $rec = <$fh>;
    return unless defined $rec;
    chomp($rec);

    if ($hi == $lo) { 
      seek $fh, $start, SEEK_SET;
      return $rec 
    };
    local $_ = $rec;
    if ($cmp->($rec) < 0) { $lo = $mid+1 }
    else                  { $hi = $mid   }
  }
}

sub lookfor { my $key = shift; sub { lc $_ cmp $key } }

open FH, "<", "/usr/dict/words" or die $!;
is(search(\*FH, lookfor('snonk')), 'snook');
is(search(\*FH, lookfor('snob')), 'snob');
is(search(\*FH, lookfor('snoba')), 'snobbery');
is(search(\*FH, lookfor('snobb')), 'snobbery');
is(search(\*FH, lookfor('snobbe')), 'snobbery');
is(search(\*FH, lookfor('snobber')), 'snobbery');
is(search(\*FH, lookfor('snobbery')), 'snobbery');
is(search(\*FH, lookfor('snobberys')), 'snobbish');
is(search(\*FH, lookfor('')), '10th');
is(search(\*FH, lookfor('1')), '10th');
is(search(\*FH, lookfor('11')), '1st');
is(search(\*FH, lookfor('zy')), 'zygote');
is(search(\*FH, lookfor('zz')), undef);
