# Each hour in a week (with days from 7am to 7pm) gets its own # unique bit in an 8-byte string. # Mon 7-8 is the 0th bit, Mon 6-7pm is 11, ... Fri 6-7 (pm) is 60th. use strict; my %base_hours = ( mon => 0, tue => 12, wed => 24 , thu => 36, fri => 48 ); my (%students,%profs,%courses); sub prof_read_file { my ($filename) = @_; my ($line, $curr_prof); open (F, $filename) || die "Could not open $filename"; while ($line = ) { chomp($line); next if $line =~ /^\s*$/; next if $line =~ /^-*$/; if ($line =~ /^id.*:\s*(.*)/) { $profs{$1} = $curr_prof = {}; } elsif ($line =~ m/^name\s*:\s*(.*)/) { $curr_prof->{name} = $1; } elsif ($line =~ /^Office Hours.*:\s*(.*)/) { $curr_prof->{Office_Hours} = interval_parse($1); } elsif ($line =~ /^Courses.*:\s*(.*)/) { my (@courses_taught) = split(/[\s,]+/, $1); $curr_prof->{Courses} = \@courses_taught; } } } sub interval_parse { my ($interval_sequence) = @_; #contains "Mon 3-5, Tue 2-6" print "[debug] interval_sequence = $interval_sequence\n" ; my ($time_range) = ""; foreach my $day_hours (split /,/, $interval_sequence) { # $day_hours contains "Mon 3-5" etc. my ($day, $from, $to) = ($day_hours =~ /([A-Za-z]+).*(\d+)-(\d+)/); # if $from or $to is less than 7, it must be afternoon. Normalize # it by adding 12. Then reduce it to a zero base by subtracting 7 # (that is, 7 hrs to 19 hrs becomes 0 - 12. Finally, # normalize each hour in a day with respect to weekly hours, # by adding in the day's "base hour" #$to = 19 if $to == 7; $from += 12 if $from < 7 ; $to += 12 if $to <= 7; my $base = $base_hours{lc $day}; $from += $base - 7; $to += $base - 7; # At this point Tue 7a.m ==> 12 and Tue 4 p.m => 21 for (my $i = $from; $i < $to; $i++) { # Set the corresponding bit vec($time_range, $i, 1) = 1; } } $time_range; } sub course_get_hours { my $course = shift; my $result_string = `grep -2 $course courses.dat|tail -1`; return interval_parse($1) if ($result_string =~ /^Class Hours.*:\s*(.*)/); return undef; } sub prof_check_constraints { my ($prof) = @_; my $r_prof = $profs{$prof}; # %profs created by prof_read_file my $office_hours = $r_prof->{Office_Hours}; my $rl_courses = $r_prof->{Courses}; for my $i (0 .. $#$rl_courses) { my $course_taught = $rl_courses->[$i]; my $course_hours = course_get_hours($course_taught); print "Prof. ", $r_prof->{name}, " Office hours conflict with course ".$course_taught."\n" if interval_conflicts($office_hours, $course_hours); for my $j ($i + 1 .. $#$rl_courses) { my ($other_course_hours) = course_get_hours($rl_courses->[$j]); if (interval_conflicts ($course_hours, $other_course_hours)){ print "Prof. ", $r_prof->{name}, ": Course conflict: ", $rl_courses->[$i], " with ", $rl_courses->[$j], "\n" } else { print "[debug] ",$rl_courses->[$i], " = ", unpack("b*",$course_hours), "\n", "[debug] ",$rl_courses->[$j], " = ", unpack("b*",$other_course_hours),"\n"; } } } } sub interval_conflicts { my ($t1, $t2) = @_; my ($combined) = $t1 & $t2; # $combined will have at least one bit set if there's a conflict my $offset = length($combined) * 8; # start counting down from last bit, and see if any is set while (--$offset >= 0) { return 1 if vec($combined,$offset,1); } return 0; }