#!/usr/local/bin/perl
#
# OpSys Fall 2005 - Virtual Memory Page Replacement algo homework.
#
# This is a perl program that can generate input for your simulator.
# To run this program, install the file as "generate.pl" and type
# perl generate.pl 
# at the Unix prompt. Many parameters are defined below, these
# determine the output generated by this program...
#

# some parameters that control the simulated processes and memory
$nprocs = 6;		# max number of processes
$npages = 32;		# total number of pages (in virtual address space of each process)
$pagesize=32;		# bytes per page
$numlines = 200;	# number of memory accesses generated (lower limit, can actually be a few more...)

# at each step we can either start a new process,
# terminate a process, or run a process.
# the start/stop probabilites are defined below

$start_process_probability = 0.1;
$stop_process_probability = 0.1;

# each time a process runs, the number of memory accesses
# generated is based on these values:
$run_process_mean_accesses = 5; # 100;
$run_process_std_accesses = 1; # 20;


# derived values

$maxaddress = $pagesize * $npages;

%procstate = init_processes();

# start with 1/2 the processes active
for ($i=0;$i<$nprocs/2;$i++) {
	start_process($i);
}



$lines=0;
while ($lines < $numlines) {
	# choices: start new process, end a process,
	#  or run a process (generate memory addresses).
	# show_processes();   # generator debugging - display active processes
	if ($active_processes==0) {
		start_process(0);
	} else {
		# random number controls what we do
		$num = rand();
		if (($active_processes < $nprocs) && ($num < $start_process_probability)) {
			# start a new process (find an empty slot)
			$i=0;
			while ($procstate{$i,'running'}==1) { $i++; }
			start_process($i);
		} elsif ($num < $start_process_probability + $stop_process_probability) {
			# pick a random process to stop
			stop_process(pick_random_active_process());
		} else {
			# pick a random process to run
			run_process(pick_random_active_process());
		}
	}
}
			

# pick randomly among all active processes
sub pick_random_active_process {
	my(@active);
	my($i);

	for ($i=0;$i<$nprocs;$i++) {
		if ($procstate{$i,'running'}==1) {
			push @active,$i;
		}
	}

	my($p) = int(rand(@active));
	return($active[$p]);
}
	


# generate a random address given a mean address and standard deviation
#
# small but finite probability the address will be negative !
sub random_address{
	my($mean,$std) = @_;
	my($num);
	do {
		$num = int(gaussian_rand()*$std+$mean);
	} while ($num<0);
	return($num);
}

	

# Generating random numbers according to a normal distribution
# (code from the Perl Cookbook).

sub gaussian_rand {
    my ($u1, $u2);  # uniformly distributed random numbers
    my $w;          # variance, then a weight
    my ($g1, $g2);  # gaussian-distributed numbers

    do {
        $u1 = 2 * rand() - 1;
        $u2 = 2 * rand() - 1;
        $w = $u1*$u1 + $u2*$u2;
    } while ( $w >= 1 );

    $w = sqrt( (-2 * log($w))  / $w );
    $g2 = $u1 * $w;
    $g1 = $u2 * $w;

    return $g1;
}


# initialize each process
#
sub init_processes{
	my($i);
	for ($i=0;$i<$nprocs;$i++) {
		$procstate{$i,'running'}=-1;
	}
}

# debugging
sub show_processes{

	my($i);
	for ($i=0;$i<$nprocs;$i++) {
		if ($procstate{$i,'running'}==1) {
			printf("Process $i:\n");
			printf("  address: %d\n",$procstate{$i,'last_address'});
			printf("  std: %f\n",$procstate{$i,'std'});
		}
	}
}

sub start_process{
	my($p) = $_[0];
	if ($procstate{$p,'running'}==1) {
		printf("ERROR - CAN'T START PROCESS $p - already running!\n");
		exit;
	}

	printf("S $p\n");
	# initial address is generated uniformly somewhere in the middle of the address range.
	$procstate{$p,'last_address'} = int((rand()*($maxaddress/2)) + $maxaddress/4);
	# std used for addresses is based on random gaussian centered at 10, std 5
	$procstate{$p,'std'} = 11+(gaussian_rand()*5);
	$procstate{$p,'accesses'} = 0;
	$procstate{$p,'running'}=1;
	$active_processes++;
}

sub stop_process{
	my($p) = $_[0];
	if ($procstate{$p,'running'}!=1) {
		printf("ERROR - CAN'T STOP PROCESS $p - not running!\n");
		exit;
	}
	printf("T $p\n");
	$procstate{$p,'running'}=-1;
	$active_processes--;
}


sub run_process{
	my($p) = $_[0];

	if ($procstate{$p,'running'}!=1) {
		printf("ERROR - CAN'T RUN PROCESS $p - not running!\n");
		exit;
	}

	# number of memory accesses this step is selected
	# randomly from a gaussian
	my($refs) = int(gaussian_rand()*$run_process_std_accesses + $run_process_mean_accesses);
	my($i);
	my($address);
	# now generate the memory accesses, for now just read ('R');
	# each address is picked randomly using a gaussian centered at the 
	# last address picked and the standard deviation.
	for ($i=0;$i<$refs;$i++) {
		$address = random_address($procstate{$p,'last_address'},$procstate{$p,'std'});
		printf("R $p %d\n",$address);
		$procstate{$p,'last_address'} = $address;
		$procstate{$i,'accesses'}++;
		$lines++;
	}
}
