.
 */
//**************************************************
// class.schedule.php	Author: Nathan Gelderloos
//
// Represents a schedule of a week. Stores the
// classes that are part of that week and calculates
// all the possible permutations.
//**************************************************
include_once('inc/class.course.inc');
include_once 'class.section.php';
include_once 'inc/class.page.php';
/*
 * Load a Classes -> Course converter class for the sake of the
 * Schedule::__wakeup() magic function.
 */
require_once('inc/class.classes_convert.inc');
class Schedule
{
  /*
   * Variables for upgrading from saved schedules created when there
   * was a class called Classes.
   */
  private $classStorage;			// array of courses
  private $nclasses;				// Integer number of classes
  /* My member variables. */
  private $courses;
  private $nPermutations = 0;		// Integer number of real permutations
  private $possiblePermutations;	// Integer number of possible permutations
  private $scheduleName;			// String name of schedule
  private $storage;				// Integer array of valid schedules
  private $title;
  /**
   * \brief
   *   My global identification number. Not defined until the schedule
   *   is processed and first saved.
   */
  private $id;
  /**
   * The input format of the sections. Only used for the UI. Valid
   * values are 'numerous' for custom, 'numbered' for numeric, and 'lettered' for
   * alphabetical.
   */
  public $section_format;
  /**
   * \brief
   *   Create a schedule with the given name.
   */
  function __construct($name)
  {
    $this->courses = array();
    $this->scheduleName = $name;
    $this->storage = array();
    $this->title = "SlatePermutate - Scheduler";
    $this->section_format = 'numerous';
    /* mark this as an upgraded Schedule class. See __wakeup() */
    $this->nclasses = -1;
  }
  //--------------------------------------------------
  // Mutators and Accessors
  //--------------------------------------------------
  public function getName()
  {
    return $this->scheduleName;
  }    
  //--------------------------------------------------
  // Adds a new class to the schedule.
  //--------------------------------------------------
  function addCourse($n)
  {
    $this->courses[] = new Course($n);
  }
  //--------------------------------------------------
  // Adds a section to the desired class.
  //--------------------------------------------------
  function addSection($course_name, $letter, $time_start, $time_end, $days, $synonym = NULL, $faculty = NULL, $location = NULL, $type = 'lecture')
  {
    $found = false;
    $counter = 0;
    foreach ($this->courses as $course)
      if (!strcmp($course_name, $course->getName()))
	{
	  $section = $course->section_get($letter);
	  if (!$section)
	    {
	      $section = new Section($letter, array(), $synonym, $faculty);
	      $course->section_add($section);
	    }
	  $section->meeting_add(new SectionMeeting($days, $time_start, $time_end, $location, $type));
	  return;
	}
    echo 'Could not find class: ' . $course_name . "
\n";
  }
  //--------------------------------------------------
  // Finds all of the possible permutations and stores
  // the results in the storage array.
  //--------------------------------------------------
	function findPossibilities()
	{
		$this->possiblePermutations = 1;
		/* special case: there is nothing entered into the schedule and thus there is one, NULL permutation */
		if (!count($this->courses))
		{
			/* have an empty schedule */
			$this->nPermutations = 1;
			return;
		}
		$position = 0;
		$counter = 0;
		$i = 0;
		foreach ($this->courses as $course)
		{
			$this->possiblePermutations = $this->possiblePermutations * $course->getnsections();
			$cs[$i] = 0;	// Sets the counter array to all zeroes.
			$i ++;
		}
        
		// Checks for conflicts in given classes, stores if none found
		do
		{
			$conflict = false;
         
			// Get first class to compare
			for ($upCounter = 0; $upCounter < count($this->courses) && !$conflict; $upCounter ++)
			{
	    
			  for ($downCounter = count($this->courses) - 1; $downCounter > $upCounter && !$conflict; $downCounter --)
				{
				  if ($this->courses[$upCounter]->getSection($cs[$upCounter])
				      ->conflictsWith($this->courses[$downCounter]->getSection($cs[$downCounter])))
				    {
				      $conflict = TRUE;
				      break;
				    }
				}
			}
	
	// Store to storage if no conflict is found.
	if(!$conflict)
	  {
	    for($i = 0; $i < count($this->courses); $i++)
	      {
		$this->storage[$this->nPermutations][$i] = $cs[$i];
	      }
	    $this->nPermutations++;
	  }
			
	// Increase the counter by one to get the next combination of class sections.
	$cs[$position] = $cs[$position] + 1;
			
	// Check to make sure the counter is still valid.
	$valid = false;
	while(!$valid)
	  {
	    if($cs[$position] == $this->courses[$position]->getnsections())
	      {
		$cs[$position] = 0;
		$position++;
					
		// This is for the very last permutation. Even 
		// though the combination is not actually true
		// the larger while loop will end before any 
		// new combinations are performed.
		if($position == count($this->courses))
		  {
		    $valid = true;
		  } else {
		  $cs[$position]++;
		}
	      } else {
	      $valid = true;
	      $position = 0;
	    }
	  }
            
	$counter++;
      } while($counter < $this->possiblePermutations);
  }
    
  //--------------------------------------------------
  // Prints out the possible permutations in tables.
  //--------------------------------------------------
  function writeoutTables()
  {
    $filled = false;
    $time = array(700,730,800,830,900,930,1000,1030,1100,1130,1200,1230,1300,1330,1400,1430,1500,1530,1600,1630,1700,1730,1800,1830,1900,1930,2000,2030,2100,2130, 2200);
    define('SP_PERMUTATIONS_PER_PAGE', 256); /** @TODO: Define this in config.inc */
    $npages = ceil($this->nPermutations / SP_PERMUTATIONS_PER_PAGE);
    $page = 0;
    if (isset($_REQUEST['page']))
      $page = $_REQUEST['page'];
    /*
     * only display the ``this page doesn't exist'' 404 if there is at
     * least one permutation. Otherwise, we give an irrelevant 404 for
     * users with no permutations.
     */
    if ($this->nPermutations && $page >= $npages)
      Page::show_404('Unable to find page ' . $page . ', there are only ' . $this->nPermutations . ' non-conflicting permutations, for a total of ' . $npages . ' pages.');
    /* zero-based */
    $first_permutation = $page * SP_PERMUTATIONS_PER_PAGE;
    $last_permutation = min($this->nPermutations, $first_permutation + SP_PERMUTATIONS_PER_PAGE);
    $footcloser = '';
    if(isset($_REQUEST['print']) && $_REQUEST['print'] != ''){
      $headcode = array('jQuery', 'jQueryUI', 'uiTabsKeyboard', 'outputStyle', 'outputPrintStyle', 'displayTables');
    }
    else {
      $headcode = array('outputStyle',  'jQuery', 'jQueryUI', 'uiTabsKeyboard', 'displayTables');
    }
    $outputPage = page::page_create(htmlentities($this->getName()), $headcode);
    $outputPage->head();
    if(isset($_REQUEST['print'])) {
 
     echo '';
      echo '
Having problems? Let us know.
' . "\n"; echo 'Keyboard Shortcut: Left and right arrow keys switch between schedules
' . "\n"; } echo "\n"; if($this->nPermutations > 0) { /* * Figure out if we have to deal with Saturday and then deal * with it. */ $max_day_plusone = 5; $have_saturday = FALSE; foreach ($this->courses as $course) { for ($si = 0; $si < $course->getnsections(); $si ++) foreach ($course->getSection($si)->getMeetings() as $meeting) if ($meeting->getDay(5)) { $max_day_plusone = 6; $have_saturday = TRUE; break; } if ($have_saturday) break; } echo 'Enter these codes into your school\'s online course registration system to register for classes:
| ' . ($i + 1) . " | \n" . "Monday | \n" . "Tuesday | \n" . "Wednesday | \n" . "Thursday | \n" . "Friday | \n"; if ($have_saturday) echo "Saturday | \n"; echo "
| " . $this->prettyTime($time[$r]) . " | \n"; /* currently, 0-5 = monday-saturday */ for($dayLoop = 0; $dayLoop < $max_day_plusone; $dayLoop++) { /* Makes sure there is not a class already in progress */ if($rowspan[$dayLoop] <= 0) { for($j = 0; $j < count($this->courses); $j++) { $class = $this->courses[$j]; $section_index = $this->storage[$i][$j]; $section = $class->getSection($section_index); /* iterate through all of a class's meeting times */ $meetings = $section->getMeetings(); /* find any meeting which are going on at this time */ $current_meeting = NULL; foreach ($meetings as $meeting) { if ($meeting->getDay($dayLoop) && $meeting->getStartTime() >= $time[$r] && $meeting->getStartTime() < $time[$r+1]) { $current_meeting = $meeting; } } if ($current_meeting) { /* calculate how many rows this section should span */ for ($my_r = $r; $current_meeting->getEndTime() > $time[$my_r]; $my_r ++) ; $rowspan[$dayLoop] = $my_r - $r; $single_multi = 'single'; if ($rowspan[$dayLoop] > 1) $single_multi = 'multi'; echo '' . htmlentities($class->getName(), ENT_QUOTES) . '-' . htmlentities($section->getLetter(), ENT_QUOTES) . "\n" . '' . htmlentities($section->getProf(), ENT_QUOTES) . "\n" . '' . htmlentities($current_meeting->getLocation(), ENT_QUOTES) . "\n" . '' . htmlentities($section->getSynonym(), ENT_QUOTES) . "\n" . " | \n"; $syns[$section->getSynonym()] = $section->getSynonym(); $filled = TRUE; } } } if ($rowspan[$dayLoop] > 0) { $filled = TRUE; $rowspan[$dayLoop] --; } /* If the cell was not filled, fill it with an empty cell. */ if(!$filled) { echo "\n"; } $filled = FALSE; } // End of row echo " | 
There are no possible schedules. Please try again.
'; } echo "There were a total of " . $this->possiblePermutations . " possible permutations. Only " . $this->nPermutations . " permutations had no class conflicts.
"; $outputPage->foot(); } //-------------------------------------------------- // Changes the title of the page. //-------------------------------------------------- function changeTitle($t) { $this->title = $t; } //-------------------------------------------------- // Make the time "pretty" //-------------------------------------------------- function prettyTime($t){ if($t > 1259) { $t = ($t-1200); return substr($t, 0, strlen($t)-2) . ":" . substr($t, strlen($t)-2, strlen($t)) . " PM"; } else { return substr($t, 0, strlen($t)-2) . ":" . substr($t, strlen($t)-2, strlen($t)) . " AM"; } } /** * \brief * fetch the number of classes */ function nclasses_get() { return count($this->courses); } /** * \brief * fetch a specified class by its key */ function class_get($class_key) { return $this->courses[$class_key]; } /** * \brief * Set my global ID. * * Only to be called by schedule_store_store(). */ function id_set($id) { $this->id = $id; } /* * \brief * Get my global ID. */ function id_get() { return $this->id; } /** * \brief * Write out a relative URL for a particular schedule. * * Takes into account the $clean_urls setting. * * \param $id * The ID of the schedule to link to. Defaults to the current schedule object. * \param $page * The page of the schedule to link to. Defaults to 0. * \return * A string, the URL used to access this schedule. Remember that * if this string is inserted into an XHTML document, * htmlentities() must be called on it. */ function url($id = NULL, $page = 0) { global $clean_urls; $url = ''; if (!$clean_urls) $url .= 'process.php?s='; if (!$id) $id = $this->id; $url .= (int)$id; if ($clean_urls) $url .= '?'; else $url .= '&'; if ($page) $url .= 'page=' . (int)$page . '&'; return $url; } /** * \brief * A magic function which tries to upgrade old serialized sections * to the new format. */ function __wakeup() { if ($this->nclasses == -1) /* this Schedule doesn't need to be upgraded from Classes to Course */ return; $this->courses = array(); foreach ($this->classStorage as $classes) { $this->courses[] = $classes->to_course(); } $this->nclasses = -1; } }