<?
/* 
   This file is part of the 'Science Fair In A Box' project
   SFIAB Website: http://www.sfiab.ca

   Copyright (C) 2005 Sci-Tech Ontario Inc <info@scitechontario.org>
   Copyright (C) 2005 James Grant <james@lightbox.org>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation, version 2.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/
?>
<?
 require_once('../common.inc.php');
 require_once('../user.inc.php');
 require_once('../questions.inc.php');
 require_once('../projects.inc.php');
 require_once('judges.inc.php');
 require_once('anneal.inc.php');

if($_SERVER['SERVER_ADDR']) {
	echo "This script must be run from the command line";
	exit;
}

/*
send_header("Judging teams automatic scheduler");


echo i18n("The scheduler is running.  Depending on the effort it may take
several minutes to complete.  It will generate a lot of output on this page,
most of it is just information about what the scheduler is doing.  You can copy
this page and save it as a text file to have a record of what the scheduler
did.  (And this output helps us to debug problems with it if you encounter
something that clearly isn't correct, just send us this output)");
echo "<br />";
echo "<br />";
echo i18n("When complete, a green bar will appear at the BOTTOM of this page
saying that everything has completed successfully.  When complete, you can use
the following links to manage the Judging Teams and the Judges assigned to them
(clicking on these links now will stop the scheduler).");
echo "<br />";
echo "<br />";
echo "<a href=\"judges_teams.php\">".i18n("Manage Judge Teams")."</a>";
echo "<br />";
echo "<a href=\"judges_teams_members.php\">".i18n("Manage Judge Members")."</a>";
echo "<br />";
echo "<br />";
echo i18n("If you get an error like: \"Fatal error:  Maximum execution time of
30 seconds exceeded...\" you will need to talk to your system admin and have
them adjust the \"max_execution_time\" variable in the \"php.ini\" file from
30(seconds) to something larger, like 900 (15 minutes).  And then have them
restart the webserver for the change to take effect");
echo "<br />";
echo "<br />";

*/


//function TRACE() { }
//function TRACE_R() { }
function TRACE($str) { print($str); }
function TRACE_R($array) { print_r($array); }


TRACE("<pre>");


function set_status($txt)
{
	TRACE("Status: $txt\n");
	mysql_query("UPDATE config SET val='$txt' WHERE 
			var='judge_scheduler_activity' AND year=0");
}

$set_percent_last_percent = -1;
function set_percent($n)
{
	global $set_percent_last_percent;
	$p = floor($n);
	if($p == $set_percent_last_percent) return;
	TRACE("Progress: $p\%\n");
	mysql_query("UPDATE config SET val='$p' WHERE 
			var='judge_scheduler_percent' AND year=0");
	$set_percent_last_percent = $p;
}

set_status("Initializing...");
set_percent(0);

/* The cost function is:
	+ 50 * each judge below the min for each team
	+ 10 * each judge above the max for each team
	+  2 * each level of preference away from the 
		max level for each judge
	+ 40 if the team doesn't have a chair.
	+ 25 for each memember on the team that can't speak the language
	     of the judging team

	( ex: if a judge has selected LS->2, PS->0, CS->-1 
	 	then matching that judge with a:
		LS = -4,
		PS = 0,
		CS = -2,
		else = 0
	)
*/

/* Compute the cost of adding a judge to a team */

function judges_cost_function($annealer, $bucket_id, $ids)
{
	global $config;
	global $jteam;
	global $judges;
	/* Bucket ID is the team number */
	/* ids are the judge ids currently in the bucket */

//	TRACE("Bucket id=$bucket_id, ids=");
//	TRACE_R($ids);

	$cost = 0;
	$have_chair = 0;

	if($bucket_id == 0) {
		/* This is the placeholder */
		$cost = count($ids) * 5;
//		TRACE("Extra judge team cost=$cost\n");
		return $cost;
	}
	
	
	$t =& $jteam[$bucket_id];
	
	/* Compute the over max / under min costs */
	$c = count($ids);
	$min = ($c < $t['min_judges']) ? $t['min_judges'] - $c : 0;
	$max = ($c > $t['max_judges']) ? $c - $t['max_judges'] : 0;
	$cost += $min * 50;
	$cost += $max * 10;

//	TRACE("Under min=$min, over max=$max\n");

	/* For each judge on the team, score their preferences */
	for($x=0; $x<count($ids); $x++) {
		$j =& $judges[$ids[$x]];
		/* Get the division, and see where it fits with this
		 * judges preferences */
		$cpref = 0;
		for($y=0; $y < count($t['cats']); $y++) {
			$l = $t['cats'][$y];
			/* Lookup the judge cat pref for this category */
			$pref = -$j['catprefs'][$l] + 2;
			/* $pref = 0 (best match) --- 4 (worst match) */
			$cpref += $pref;
		}
		$dpref = 0;
		for($y=0; $y < count($t['divs']); $y++) {
			$l = $t['divs'][$y];
			/* Lookup the judge cat pref for this category */
			$pref = -$j['divprefs'][$l] + 2;
			/* $pref = 0 (best match) --- 4 (worst match) */
			$dpref += $pref;
		}
		 
//		TRACE("Judge {$ids[$x]}({$j['name']}) cp=$cpref, dp=$dpref\n");

		$cost += 2 * $cpref;
		$cost += 2 * $dpref;

		/* See if the judge is willing to chair a team */
		if($j['willing_chair'] == 'yes') $have_chair = 1;

		/* For each lang the team needs that the judge doesn't have, 
		 * increase the cost */
		for($y=0; $y < count($t['langs']); $y++) {
			$l = $t['langs'][$y];
			if(!in_array($l, $j['languages'])) $cost += 25;
		}
	}
	/* Huge penalty for a team without a willing chair, but only if the min judges per team >1 */
	if(!$have_chair && $config['min_judges_per_team']>1) 
		$cost += 40;
 
//	TRACE("Team $bucket_id, cost is $cost\n");

	return $cost;
}


$current_jdiv = array();


function jdiv_compute_cost($annealer, $bucket_id, $ids)
{
	/* IDS is a list of project ids for a judging team */
	global $current_jdiv;
	
	$cost = 0;
	$t_div = array();
	$t_cat = array();
	$t_lang = array();

	/* Foreach project this jteam is judging, record the 
	 * div/cat/lang */
	for($x=0; $x<count($ids); $x++) {
		$proj =& $current_jdiv['projects'][$ids[$x]];
		if(!in_array($proj['div'],$t_div)) $t_div[] = $proj['div'];
		if(!in_array($proj['cat'],$t_cat)) $t_cat[] = $proj['cat'];
		if(!in_array($proj['lang'],$t_lang)) $t_lang[] = $proj['lang'];
	}
	/* Square the project count for highter penalties for more projects */
	$cost += floor (abs(count($ids) - $annealer->items_per_bucket)) * 100;
	/* Score 100 pts for multiple languages */
	$cost += (count($t_lang) - 1) * 75;
	/* Score 25pts for multiple divs/cats */
	$cost += (count($t_div) - 1) * 25;
	$cost += (count($t_cat) - 1) * 25;

	/* Score +200 pts for each duplicate project this team is judging, we
	 * really don't want a jteam judging the same project twice */
	for($x=0; $x<count($ids) - 1; $x++) {
		for($y=$x+1; $y<count($ids); $y++) {
			if($ids[$x] == $ids[$y]) $cost += 200;
		}
	}
	return $cost;
}


set_status("Loading Data From Database...");
TRACE("\n\n");
$div = array();
TRACE("Loading Project Divisions...\n");
$q=mysql_query("SELECT * FROM projectdivisions WHERE year='".$config['FAIRYEAR']."' ORDER BY id");
while($r=mysql_fetch_object($q))
{
	$divshort[$r->id]=$r->division_shortform;
	$div[$r->id]=$r->division;
	TRACE("   {$r->id} - {$div[$r->id]}\n");
}

TRACE("Loading Project Age Categories...\n");
$cat = array();
$q=mysql_query("SELECT * FROM projectcategories WHERE year='".$config['FAIRYEAR']."' ORDER BY id");
while($r=mysql_fetch_object($q)) {
	$cat[$r->id]=$r->category;
	TRACE("   {$r->id} - {$r->category}\n");
}

TRACE("Loading Languages...\n");
$langr = array();
$q=mysql_query("SELECT * FROM languages WHERE active='Y'");
while($r=mysql_fetch_object($q)) {
	$langr[$r->lang] = $r->langname;
	TRACE("   {$r->lang} - {$r->langname}\n");
}

$jdiv = array();
TRACE("Loading Judging Division Configuration and Projects...\n");
$q=mysql_query("SELECT * FROM judges_jdiv");
while($r=mysql_fetch_object($q)) {
	/* Ignore jdiv 0 (all unassigned div/cats) */
	if($r->jdiv_id == 0) continue;

	$jdiv[$r->jdiv_id]['config'][] = array('div' => $r->projectdivisions_id,
					'cat' => $r->projectcategories_id,
					'lang' => $r->lang);
}

$keys = array_keys($jdiv);
for($k=0; $k<count($keys); $k++) {
	$jdiv_id = $keys[$k];
	TRACE("    $jdiv_id\t- ");
	$jdiv[$jdiv_id]['projects'] = array();
	for($x=0;$x<count($jdiv[$jdiv_id]['config']); $x++) {
		$d = $jdiv[$jdiv_id]['config'][$x];
		if($x > 0) TRACE("\t- ");
		TRACE($cat[$d['cat']]." ".$div[$d['div']]." - ".$langr[$d['lang']]);
		$qp = mysql_query("SELECT projects.* FROM projects, registrations WHERE ".
					" projects.year='".$config['FAIRYEAR']."' AND ".
					" projectdivisions_id='{$d['div']}' AND ".
					" projectcategories_id='{$d['cat']}' AND ".
					" language='{$d['lang']}' AND " .
					" registrations.id = projects.registrations_id " .
					getJudgingEligibilityCode()
				);
		$count = 0;
		while($rp = mysql_fetch_object($qp)) {
			$jdiv[$jdiv_id]['projects'][$rp->id] = array( 
					'div' => $d['div'],
					'cat' => $d['cat'],
					'lang' => $d['lang']);
			$count++;
		}
		TRACE(" ($count projects)\n");
	}
	if(count($jdiv[$jdiv_id]['projects']) == 0) {
		TRACE("\t- This div has no projects, removing.\n");
		unset($jdiv[$jdiv_id]);
	}
}

set_status("Computing required judging teams");
TRACE("   Each judging team may judge {$config['max_projects_per_team']} projects\n");
TRACE("   Each project must be judged {$config['times_judged']} times\n");

$keys = array_keys($jdiv);
for($k=0; $k<count($keys); $k++) {
	$jdiv_id = $keys[$k];
	$c = count($jdiv[$jdiv_id]['projects']);
	$t=ceil($c/$config['max_projects_per_team']*$config['times_judged']);
	if($t < $config['times_judged']) $t = $config['times_judged'];
	TRACE("   $jdiv_id has $c projects, requires $t judging teams\n");
	$jdiv[$jdiv_id]['num_jteams'] = $t;
}

$jteam = array();
$jteam_id = 0;
/* Create one more jteam, for anyone the annealer doesn't want to place */
$jteam[$jteam_id]['id'] = $jteam_id;
$jteam[$jteam_id]['projects'] = array();
$jteam[$jteam_id]['divs'] = array();
$jteam[$jteam_id]['cats'] = array();
$jteam[$jteam_id]['langs'] = array();
$jteam[$jteam_id]['min_judges'] = 0;
$jteam[$jteam_id]['max_judges'] = 0;
$jteam_id++;

set_status("Assigning projects to judging teams");
$keys = array_keys($jdiv);
for($k=0; $k<count($keys); $k++) {
	$jdiv_id = $keys[$k];
	TRACE("Judging Division $jdiv_id ({$jdiv[$jdiv_id]['num_jteams']} teams): \n");
	$project_ids = array();
	for($x=0; $x<$config['times_judged']; $x++) {
		$project_ids = array_merge($project_ids, array_keys($jdiv[$jdiv_id]['projects']) );
	}
	$current_jdiv = $jdiv[$jdiv_id];

	$e = 100 + 10 * ($config['effort'] / 1000);
	$a = new annealer($jdiv[$jdiv_id]['num_jteams'], 125, $e, 0.9, 
			jdiv_compute_cost, $project_ids);
	$a->anneal();

	$jdiv[$jdiv_id]['jteams'] = array();
	for($x=0;$x<$a->num_buckets; $x++) {
		$bkt = $a->bucket[$x];
		TRACE("   SubTeam $x: (jteam $jteam_id)\n");
		$jdiv[$jdiv_id]['jteams'][] = $jteam_id;
		
		$jteam[$jteam_id]['id'] = $jteam_id;
		$jteam[$jteam_id]['projects'] = $a->bucket[$x];
		$jteam[$jteam_id]['sub'] = $x;
		$jteam[$jteam_id]['jdiv_id'] = $jdiv_id;
		$jteam[$jteam_id]['divs'] = array();
		$jteam[$jteam_id]['cats'] = array();
		$jteam[$jteam_id]['langs'] = array();
		$jteam[$jteam_id]['min_judges'] = $config['min_judges_per_team'];
		$jteam[$jteam_id]['max_judges'] = $config['max_judges_per_team'];
		
		for($y=0;$y<count($bkt); $y++) {
			$projid = $bkt[$y];
			$p = $jdiv[$jdiv_id]['projects'][$projid];
			TRACE("      $projid - ".$cat[$p['cat']]." ".$div[$p['div']]." - ".$langr[$p['lang']]."\n");
			if(!in_array($p['cat'], $jteam[$jteam_id]['cats'])) {
				$jteam[$jteam_id]['cats'][] = $p['cat'];
			}
			if(!in_array($p['div'], $jteam[$jteam_id]['divs'])) {
				$jteam[$jteam_id]['divs'][] = $p['div'];
			}
			if(!in_array($p['lang'], $jteam[$jteam_id]['langs'])) {
				$jteam[$jteam_id]['langs'][] = $p['lang'];
			}
		}
		$jteam_id++;
	}
}

TRACE("There are ".(count($jteam) - 1)." judging teams\n");


$willing_chair_question_id = questions_find_question_id("judgereg", $config['FAIRYEAR'], "Willing Chair");
printf("Judge Willing Chair = Question ID $willing_chair_question_id\n");

/* Clean out the judging teams that were autocreated */
TRACE("Deleting autocreated divisional and special award judging teams:");
$q = mysql_query("SELECT * FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
while($r = mysql_fetch_object($q)) {
	$id = $r->id;
	print(" $id");
	/* Clean out the judges_teams_link */
	mysql_query("DELETE FROM judges_teams_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
	print mysql_error();
	/* Awards */
	mysql_query("DELETE FROM judges_teams_awards_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
	print mysql_error();
	/* Timeslots */
	mysql_query("DELETE FROM judges_teams_timeslots_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
	print mysql_error();
	/* Timeslots projects */
	mysql_query("DELETE FROM judges_teams_timeslots_projects_link WHERE judges_teams_id='$id' AND year={$config['FAIRYEAR']}");
	print mysql_error();
}
/* Finally, delete all the autocreated judges teams */
mysql_query("DELETE FROM judges_teams WHERE autocreate_type_id=1 AND year={$config['FAIRYEAR']}");
print mysql_error();

/* Also delete any judges_teams_link that link to teams that dont exist */
$q=mysql_query("SELECT judges_teams_link.id, judges_teams.id AS judges_teams_id FROM judges_teams_link LEFT JOIN judges_teams ON judges_teams_link.judges_teams_id=judges_teams.id WHERE judges_teams_link.year={$config['FAIRYEAR']}");
$n=0;
while($r=mysql_fetch_object($q)) {
	if(!$r->judges_teams_id) {
		mysql_query("DELETE FROM judges_teams_link WHERE id='$r->id'");
		$n++;
	}
}
print("Deleted $n orphaned team linkings\n");
TRACE(" Done.\n");

set_status("Loading Judges");

$judges = judges_load_all();
$sa_judges = array();

foreach($judges as $j) {
	$q = mysql_query("SELECT users_id FROM judges_teams_link WHERE 
				users_id='{$j['id']}'
				AND year='{$config['FAIRYEAR']}'");
	if(mysql_num_rows($q) != 0) {
		TRACE("   {$j['name']} is already on a judging team, skipping.\n");
		unset($judges[$j['id']]);
		continue;
	}

	if($j['special_award_only'] == 'yes') {
		TRACE("Judge {$j['name']} is a special awards only.\n");
		/* Find their special award id */
		$q = mysql_query("SELECT award_awards.id,award_awards.name FROM 
				judges_specialaward_sel,award_awards 
				WHERE
				award_awards.id=judges_specialaward_sel.award_awards_id
				AND judges_specialaward_sel.users_id='{$j['id']}'
				AND award_awards.year='{$config['FAIRYEAR']}'");
		echo mysql_error();
		if(mysql_num_rows($q) == 0) {
			TRACE("   - NO special award selected! (removing special award only request)\n");
			$judges[$j['id']]['special_award_only'] = 'no';
		} else if(mysql_num_rows($q) > 1) {
			TRACE("   - More than ONE special award selected (removing special award only request):\n");
			$judges[$j['id']]['special_award_only'] = 'no';
		} 
	}

	$judges[$j['id']]['special_awards'] = array();
	while($r = mysql_fetch_object($q)) {
		TRACE("      {$r->name}\n");
		/* Add them to the SA judge list (modify the actual list, not
		 * $j, which is a copy */
		$judges[$j['id']]['special_awards'][] = $r->id;
	}

	if($j['special_award_only'] == 'yes') {
		/* Add to sa judge list, remove from the eligible judge list */
		$sa_judges[$j['id']] = $judges[$j['id']];
		unset($judges[$j['id']]);
	}
}

TRACE("Loaded ".count($judges)." judges, and ".count($sa_judges)." special-award-only judges.\n");
$jteam[0]['max_judges'] = count($judges);


function judges_to_teams_update($progress, $total)
{
	set_percent(($progress * 50) / $total);
}
set_status("Assigning Judges to Teams");

$judge_ids = array_keys($judges);
$e = $config['effort'];
$a = new annealer(count($jteam), 25, $e, 0.98, judges_cost_function, $judge_ids);
$a->set_update_callback(judges_to_teams_update);
$a->anneal();



function pr_judge(&$jt, $jid)
{
	global $judges;
	$j =& $judges[$jid];
	print("   - {$j['name']} (".join(' ', $j['languages']).')');
	print("(");
	for($x=0; $x<count($jt['cats']); $x++) {
		$pref=$j['cat_prefs'][$jt['cats'][$x]];
		print("c{$jt['cats'][$x]}=$pref ");
	}
	for($x=0; $x<count($jt['divs']); $x++) {
		$pref=$j['div_prefs'][$jt['divs'][$x]];
		print("d{$jt['divs'][$x]}=$pref ");
	}
	print(")");
	if($j['willing_chair'] == 'yes') {
		print(" (chair) ");
	}
	
	print("\n");
}	


/* Find the maximum judging team number */
$q = mysql_query("SELECT MAX(num) as max FROM judges_teams WHERE year='{$config['FAIRYEAR']}'");
$r = mysql_fetch_object($q);
$max_jteam_num = $r->max;

TRACE("Max Judging Team Number is currently $max_jteam_num\n");

for($x=1;$x<count($jteam); $x++) {
	$t =& $jteam[$x];
	$max_jteam_num++;
	print("Judging Team $max_jteam_num: cost={$a->bucket_cost[$x]} ");
	$lang_array = $t['langs'];
	asort($lang_array);
	$langstr = implode(' ', $lang_array);

	//sort the cats and divs too, so we dont end up with "int/sen" <--> "sen/int"
	asort($t['cats']);
	asort($t['divs']);

	print("langs=($langstr)");
	print("cats=(");
	$catstr="";
    if(count($t['cats'])) {
        $first=true;
        foreach($t['cats'] AS $cid) {
            print("c".$cid." ");
            if(!$first) $catstr .= "+";
            $catstr .= $cat[$cid];
            $first=false;
        }
    }
	print(")");
	print("divs=(");
	$divstr="";
    if(count($t['divs'])) {
        $first=true;
        foreach($t['divs'] AS $did) {
            print("d".$did." ");
            if(!$first) $divstr.="/";
            $divstr .= $div[$did];
            $first=false;
        }
	}
	print(")\n");

	/* Add this judging team to the database */
	$tn = $catstr." ".$divstr." (".$langstr.") ".($t['sub']+1);
	mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year) ".
			" VALUES ('$max_jteam_num','$tn','1','{$config['FAIRYEAR']}')");
			
        $team_id=mysql_insert_id();
	$t['team_id'] = $team_id;

	$ids = $a->bucket[$x];
	for($y=0; $y<count($ids); $y++) {
		pr_judge($t, $ids[$y]);
		$j =& $judges[$ids[$y]];
		
		/* Link Judges to the judging team we just inserted */
		mysql_query("INSERT INTO judges_teams_link ".
			" (users_id,judges_teams_id,captain,year) ".
			" VALUES ('{$ids[$y]}','$team_id','{$j['willing_chair']}',".
						"'{$config['FAIRYEAR']}')");
	}

	/* Get the original jdiv that this team was created from.  The exact 
	 * breakdown of each and every div/cat/lang that this team is judging
	 * is in the jdiv['config'] array */
	$jd = $jdiv[$t['jdiv_id']];
	for($y=0; $y<count($jd['config']); $y++) {
		$cfg = $jd['config'][$y];
		$q=mysql_query("SELECT award_awards.id FROM 
						award_awards,
						award_awards_projectcategories,
						award_awards_projectdivisions
					WHERE 
						award_awards.year='{$config['FAIRYEAR']}'
						AND award_awards.id=award_awards_projectcategories.award_awards_id
						AND award_awards.id=award_awards_projectdivisions.award_awards_id
						AND award_awards_projectcategories.projectcategories_id='{$cfg['cat']}'
						AND award_awards_projectdivisions.projectdivisions_id='{$cfg['div']}'
						AND award_awards.award_types_id='1'
					");
		if(mysql_num_rows($q)!=1) {
			echo error(i18n("Cannot find award for %1 - %2",array($cat[$cfg['cat']],$div[$cfg['div']])));
		} else {
			$r=mysql_fetch_object($q);
			mysql_query("INSERT INTO judges_teams_awards_link (award_awards_id,judges_teams_id,year) VALUES ('$r->id','$team_id','{$config['FAIRYEAR']}')");
		}
	}

}

print("Unused Judges:\n");
$ids = $a->bucket[0];
for($y=0; $y<count($ids); $y++) {
	pr_judge($jteam[0], $ids[$y]);
	$sa_judges[$ids[$y]] = $judges[$ids[$y]];
}

/* ====================================================================*/
/* Two functions for the Special Award Annealer, if special award
 * scheduling is disabled, these will never get called */
function judges_sa_cost_function($annealer, $bucket_id, $ids)
{
	global $sa_jteam;
	global $sa_judges;

	/* Bucket ID is the team number */
	/* ids are the judge ids currently in the bucket */

	$cost = 0;
	if($bucket_id == 0) {
		/* This is the placeholder */
		$cost = count($ids) * 50;
		return $cost;
	}
	
	$t =& $sa_jteam[$bucket_id];
	
	/* Compute the over max / under min costs */
	$c = count($ids);
	$min = ($c < $t['min_judges']) ? $t['min_judges'] - $c : 0;
	$max = ($c > $t['max_judges']) ? $c - $t['max_judges'] : 0;
	$cost += $min * 50;
	$cost += $max * 10;

	/* For each judge on the team, score their preferences */
	for($x=0; $x<count($ids); $x++) {
		$j =& $sa_judges[$ids[$x]];
		$apref = 0;

		/* See if the sa_jteam award id (what the team is judging)
		 * is in the judges selection list */
		/* Run through all awards this team is judging */
		foreach($t['award_ids'] as $aid) {
			if(in_array($aid, $j['special_awards'])) {
				/* This judge wants to judge this award */
				/* No cost */
			} else {
				if($j['sa_only'] == 'yes') {
					/* This judge is for an award, but
					 * NOT assigned to the proper one,
					 * HUGE cost */
					$cost += 500;
				}
				$apref++;
			}
		}
		$cost += 5 * $apref;
	}
//	TRACE("Team $bucket_id, cost is $cost\n");

	return $cost;
}
function judges_sa_pick_move($a)
{
	global $sa_judges;
	/* Use the existing pick move, but we never want to
	 * select a judge that has sa_only=='yes', they are
	 * already in the correct place and are unmovable */
	/* FIXME: this will spin forever if there aren't at least
	 * 2 judges to swap */
	while(1) {
		list($b1, $i1, $b2, $i2) = $a->pick_move();

		/* See if $b1,$i1 is movable */
		$id1 = $a->bucket[$b1][$i1];
		$j1 =& $sa_judges[$id1];
//		print("J1:");
//		print_r($j1);
		if($j1['sa_only'] == 'yes') continue;

		if($i2 != -1) {
			$id2 = $a->bucket[$b2][$i2];
			$j2 =& $sa_judges[$id2];
//		print("J2:");
//		print_r($j2);
			if($j2['sa_only'] == 'yes') continue;
		}

		return array($b1, $i1, $b2, $i2);
	}
}


if($config['scheduler_enable_sa_scheduling'] == 'yes') {

	set_status("Creating Special Award Judging Teams (one team per award)");
	$q = "SELECT award_awards.name,award_awards.id FROM award_awards,award_types
		WHERE 
			award_awards.year='{$config['FAIRYEAR']}'
			AND award_types.id=award_awards.award_types_id
			AND award_awards.schedule_judges='yes'
			AND award_types.year='{$config['FAIRYEAR']}'
			AND award_types.type='Special' 
		";
	$r = mysql_query($q);
	print(mysql_error());
	/* sa_jteam for leftover judges, if any */
	$sa_jteam = array();
	$sa_jteam[0]['id'] = 0;
	$sa_jteam[0]['projects'] = array();
	$sa_jteam[0]['langs'] = array();
	$sa_jteam[0]['min_judges'] = 0;
	$sa_jteam[0]['max_judges'] = 0;
	$sa_jteam[0]['award_ids'] = array();

    /* Reload the jteam_id */
    $jteam_id = count($jteam);

	$x=1;
	while($i = mysql_fetch_object($r)) {
		$projects = getProjectsNominatedForSpecialAward($i->id);

		$max_jteam_num++;  /* Pre-increment before using */

		$pids = array_keys($projects);
		$sa_jteam[$x]['id'] = $jteam_id;
		$sa_jteam[$x]['projects'] = $pids;
		$sa_jteam[$x]['sub'] = 0;
		$sa_jteam[$x]['langs'] = array();
		$min = floor(count($pids) / $config['projects_per_special_award_judge']) + 1;
		$sa_jteam[$x]['min_judges'] = $min;
		$sa_jteam[$x]['max_judges'] = $min;
		$sa_jteam[$x]['award_ids'] = array($i->id);

		$tn = "{$i->name}";
		/* Write this team to the DB */
		mysql_query("INSERT INTO judges_teams (num,name,autocreate_type_id,year)
				VALUES ('$max_jteam_num','".mysql_escape_string($tn)."','1','{$config['FAIRYEAR']}')");
		$sa_jteam[$x]['id'] = mysql_insert_id();

			/* Link the award to this team */
		mysql_query("INSERT INTO judges_teams_awards_link (award_awards_id,judges_teams_id,year) 
				VALUES ('{$i->id}','{$sa_jteam[$x]['id']}','{$config['FAIRYEAR']}')");

		TRACE("Created Team: $tn {$sa_jteam[$x]['id']}\n");
		$jteam_id++;
		$x++;
	}

	/* ====================================================================*/
	set_status("Assigning Judges to Special Award Teams\n");


	$judge_ids = array_keys($sa_judges);
	$e = $config['effort'];
	$a = new annealer(count($sa_jteam), 25, $e, 0.98, judges_sa_cost_function, $judge_ids);
	//$a->set_update_callback(judges_to_teams_update);
	//$a->set_pick_move(judges_sa_pick_move);
	$a->anneal();

	$x=0;
    unset($t);
    unset($tid);
	foreach($sa_jteam as $tid => $t) {
		if($tid == 0) {
			$x++;
			continue;
		}

		print("Judging Team {$t['id']}: cost={$a->bucket_cost[$x]} #=({$t['min_judges']},{$t['max_judges']}) ");

	//	print("langs=(");
	/*	$langstr="";
		for($y=0; $y<count($t['langs']); $y++) {
			if($y != 0) $langstr .= " ";
			$langstr .= $t['langs'][$y];
		}
		print("$langstr)");*/
		print("\n");

		$ids = $a->bucket[$x];
		for($y=0; $y<count($ids); $y++) {
	//		pr_judge($t, $ids[$y]);

			$j =& $sa_judges[$ids[$y]];
			print("   - {$j['name']}\n");
			
			/* Link Judges to the judging team we just inserted */
			mysql_query("INSERT INTO judges_teams_link 
					(users_id,judges_teams_id,captain,year) 
					 VALUES ('{$ids[$y]}','{$t['id']}',
						'{$j['willing_chair']}',
						'{$config['FAIRYEAR']}')");
		}
		$x++;
	}
}

/* Resume normal flow now */

/* ====================================================================*/
set_status("Assigning Judging Teams and Projects to Timeslots");

TRACE("Loading Timeslot Data\n");
$available_timeslots=array();
$q=mysql_query("SELECT * FROM judges_timeslots WHERE ".
				" year='{$config['FAIRYEAR']}' AND ".
				" allowdivisional='yes' ORDER BY date,starttime");
$x=0;
while($r=mysql_fetch_object($q)) {
        $available_timeslots[]=array("id"=>$r->id,
					"date"=>$r->date,
					"starttime"=>substr($r->starttime,0,-3),
					"endtime"=>substr($r->endtime,0,-3));
	print("   ".$available_timeslots[$x]['starttime']." -> ".
			$available_timeslots[$x]['endtime']."\n");
	$x++;
}

$n_timeslots = count($available_timeslots);


	/* First, check to see if the project is being judged 3 or
	 * more times in a row, OR, if it has large gaps that aren't
	 at the end of the judging */
/* I'm going to leave this here, for now, we shoudl do something like
 * this at some point in evaluating projects, but right now 
 * the randomness is pretty good. */
/*	for($x=0; $x<count($project_index); $x++) {
		$i_start = $x * $n_timeslots;
		$i_end = ($x+1) * $n_timeslots;

		$z_count = 0;
		$r_count = 0;
		for($y=$i_start; $y<$i_end; $y++) {
			$jteam_id = $ids[$y];

			if($jteam_id == 0) {
				$z_count++;
				$r_count=0;
			} else {*/
				/* Do the z_count cost here so we don;t
				 * count any zcount cost for the end
				 * of the timetable */
/*				if($z_count > 2) $cost += $z_count;
				$r_count++;
				$z_count=0;
				if($r_count > 2) $cost += $r_count;
			}
		}
	}
	*/

function timeslot_pick_move($a)
{
	/* Use the existing pick move, but we want the item numbers
	 * in each bucket to always be the same */
	list($b1, $i1, $b2, $i2) = $a->pick_move();
	$i2 = $i1;
	return array($b1, $i1, $b2, $i2);
}


function timeslot_cost_function($annealer, $bucket_id, $ids)
{	
	$cost = 0;

	/* Check to make sure a judge isn't judging two projects
	 * at the same time */
	$n_pids = count($ids);
	for($x=0; $x<$n_pids-1; $x++) {
		$jteam_id1 = $ids[$x];
		if($jteam_id1 == 0) continue;
		for($y=$x+1; $y<$n_pids; $y++) {
			$jteam_id2 = $ids[$y];
			if($jteam_id1 == $jteam_id2)
				$cost += 50;
		}
	}
	return $cost;
}


$keys = array_keys($jdiv);
$keys_count = count($keys);
for($k=0; $k<$keys_count; $k++) {
	$jdiv_id = $keys[$k];

	$pids = array_keys($jdiv[$jdiv_id]['projects']);
	$n_projects = count($pids);

	if($n_projects == 0) continue;

	unset($project_rlookup);
	$project_rlookup = array();

	for($x=0; $x<count($pids); $x++) {
		$project_rlookup[$pids[$x]] = $x;
	}

//	$current_jdiv = $jdiv_id;

	printf("jdiv $jdiv_id, $n_projects projects in this jdiv\n");
	unset($jteams_ids);
	$jteams_ids = array();
	/* Pad to the correct length */
	for($x=0; $x<($n_timeslots * $n_projects); $x++) 
		$jteams_ids[] = 0;

    printf("total of ".count($jteams_ids)." slots (should be $n_timeslots * $n_projects)\n");

	/* Fill out the jteam array with a jteam_id for each time the 
	 * jteam_id is supposed to judge a project */
	$jteams = $jdiv[$jdiv_id]['jteams'];

	foreach($jteams as $jteam_id) {

		for($y=0;$y<count($jteam[$jteam_id]['projects']); $y++) {
			$pid = $jteam[$jteam_id]['projects'][$y];
			$idx = $project_rlookup[$pid];

			for($o = $idx; ; $o+= $n_projects) {
				if($jteams_ids[$o] != 0) continue;

				$jteams_ids[$o] = $jteam_id;
				break;
			}
		}
	}


/*
	$y = 0;
        foreach($jteams as $jteam_id) {
		$o = 0;
	    print("setting up jteam $jteam_id\n");
        print_r($jteam[$jteam_id]);
		foreach($jteam[$jteam_id]['projects'] as $pid) {
			$jteams_ids[$y * $n_timeslots + $o] = $jteam_id;
			$o++;
		}
		$y++;
	}
	printf("jteams_ids=\n");
        print_r($jteams_ids);
*/	
	print("Jteams ids len=".count($jteams_ids));
	print("\n");

	set_percent(50 + ($k / $keys_count) * 50);

	$e = 500 + 50 * ($config['effort'] / 1000);
	$a = new annealer($n_timeslots, 100, $e, 0.98, timeslot_cost_function, $jteams_ids);
	$a->set_pick_move(timeslot_pick_move);
	$a->anneal();

	for($x=0; $x<count($pids); $x++) {
		$pid = $pids[$x];
		TRACE("Project $pid: ");

		for($y=0;$y<$n_timeslots; $y++) {
			$jteam_id = $a->bucket[$y][$x];
			TRACE(($y+1).":$jteam_id  ");

			if($jteam_id == 0) continue;

			/* if jteam_id isn't 0, instert it into the db */
			mysql_query("INSERT INTO judges_teams_timeslots_link ".
				" (judges_teams_id,judges_timeslots_id,year)".
				" VALUES ('{$jteam[$jteam_id]['team_id']}', ".
				" '{$available_timeslots[$y]['id']}', ".
				" '{$config['FAIRYEAR']}')");
														 
			mysql_query("INSERT INTO judges_teams_timeslots_projects_link ".
				" (judges_teams_id,judges_timeslots_id,projects_id,year) ".
				" VALUES ('{$jteam[$jteam_id]['team_id']}', ".
				" '{$available_timeslots[$y]['id']}', ".
				" '$pid', '{$config['FAIRYEAR']}')");

		}
		TRACE("\n");
	}

}

TRACE("All Done.\n");
echo "</pre>";

set_percent(-1);
set_status("Done");

//echo happy("Scheduler completed successfully");

//send_footer();
?>
