#!/usr/local/bin/perl # LongSequence.pm - Perl class module for manipulating long sequences # # Copyright (C) 2003 by John J. Chew, III # All Rights Reserved. # # This module is designed for working with long sequences that are # hard to compute, and assumes the existence of an external generator # program (typically written in C) that will compute values and store them # in a disk file for us. The generator is passed an integer and a # filename on the command line, and is expected to update the named # file to include a sequence value for the corresponding integer. # 'hof.c' is an example of one such program, and calculates # Hofstadter's Q function. # # Typical usage: # # tie @Q, LongSequence, '/u/jjchew/math/thesis/2003/bin/hof', # '/scratch/today/jjchew/hof.dat'; # print "Q[1000000]=$Q[1000000]\n"; package Math::LongSequence; use strict; use warnings; use Symbol; use Fcntl qw(:DEFAULT :seek); require 5.000; BEGIN { use Exporter (); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); $VERSION = do { my @r = (q$Revision: 1.00 $ =~ /\d+/g); sprintf("%d."."%02d" x $#r, @r); }; @ISA = qw(Exporter); @EXPORT = (); %EXPORT_TAGS = (); @EXPORT_OK = qw(); } ### PROTOTYPES sub DESTROY (); sub FETCHSIZE ($); sub FETCH ($$); sub STORE ($$$); ### CLASS METHODS ### CONSTRUCTORS # my $tie = tie @sequence, LongSequence, $generator, $filename; sub TIEARRAY ($$$) { my $proto = shift; my $class = ref($proto) || $proto; my $generator = shift; my $filename = shift; my $handle = gensym; my $self = { 'filename' => $filename, 'generator' => $generator, 'handle' => $handle, }; bless($self, $class); unless (sysopen($handle, $filename, O_RDWR)) { my $rv = system $self->{'generator'}, 100, $filename; if ($rv == -1) { die "Can't launch generator: $!\nAborting"; } unless (sysopen($handle, $filename, O_RDWR)) { die "Generator didn't create file.\nAborting"; } } return $self; } ### OBJECT METHODS sub DESTROY () { } # $size = $#sequence; sub FETCHSIZE ($) { my $this = shift; die "FETCHSIZE called on infinite sequence $this.\nAborting"; return 1 << 30; } # $value = $sequence[$n]; sub FETCH ($$) { my $this = shift; my $n = shift; die "Large n not yet implemented.\nAborting" if $n >= (1<<30); my $handle = $this->{'handle'}; unless (sysseek($handle, $n<<2, SEEK_SET)) { die "sysseek failed: $!\nAborting"; } my $buffer = ' '; if (4 != sysread($handle, $buffer, 4)) { # don't panic yet my $rv = system $this->{'generator'}, $n, $this->{'filename'}; if ($rv == -1) { die "Could not call generator for n=$n\nAborting"; } unless (sysseek($handle, $n<<2, SEEK_SET)) { die "sysseek failed: $!\nAborting"; } if (4 != sysread($handle, $buffer, 4)) { # *now* panic die "Generator did not compute sequence value for n=$n.\nAborting"; } } return unpack('N', $buffer); } sub STORE ($$$) { my $this = shift; my $n = shift; my $f = shift; die "Can't modify read-only LongSequence to set f($n)=$f.\nAborting"; } 1;