<?php

##########################################################################
# main_inc.php:  PHP file for the Simcal1 module.                        #
# Copyright (c) 2004 Daniel Dvorkin.                                     #
# Distributed under the Artistic License, WITHOUT "optional clause 8"    #
# (see http://www.opensource.org/licenses/artistic-license.php).         #
# All rights not granted in this license are reserved by the author.     #
##########################################################################

/* INCLUDE OF RECORD CLASS MASTER FILE */

include_once("Viewable.php");


/* SESSION MANAGEMENT */

session_cache_limiter("nocache");
session_start();

/* GLOBAL AND SESSION VARIABLES */
	
// database information
$DBHost = "HOSTNAME"; // database host
$DBName = "DBNAME"; // database name
$DBUser = "USERNAME"; // database user
$DBPass = "PASSWORD"; // database user password

//establish database link
$link = mysql_pconnect($DBHost, $DBUser, $DBPass)
	or die ("Failed to connect to database!");
mysql_select_db($DBName, $link)
	or die ("Failed to open database!");
	
//gather global data values
$maxRank = valq("select max(Rank) from Clusters");

/* QUERY FUNCTIONS */

function setq($queryStr)
{//set query:  makes a change to a database
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement which does not return a value.  (Actually,
 // $queryStr can return a value, but it won't be useful.)
 //POSTCONDITION:  the change specified by $queryStr has been made.
 
	mysql_query($queryStr, $GLOBALS['link'])
		or die ("<big><u>Server error message</u><br />" . 
				mysql_error($GLOBALS['link']) .
				"<br /><br /><u>Failed setq</u><br />$queryStr</big>");
		
	return;
	
}//end function setq

function verq($queryStr)
{//verification query:  returns raw query results
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement.
 //POSTCONDITION:  the server message generated by $queryStr has been
 // returned.

	return mysql_query($queryStr, $GLOBALS['link']);
	
}//end function verq

function rowq($queryStr)
{//row query:  does a query and returns an enumerated array result
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement which returns one row.  (If more than one
 // row is returned, all but the first will be ignored.)
 //POSTCONDITION:  the row generated by $queryStr has been returned as
 // an enumerated array.

	$result = mysql_query($queryStr, $GLOBALS['link'])
		or die ("<big><u>Server error message</u><br />" . 
				mysql_error($GLOBALS['link']) .
				"<br /><br /><u>Failed valq</u><br />$queryStr</big>");
	$row = mysql_fetch_row($result);
	mysql_free_result($result);
	
	return ($row);
	
}//end function arrq

function valq($queryStr)
{//value query:  does a query and returns a scalar result
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement which returns one scalar result.  (If more
 // than one result is returned, all but the first will be ignored.)
 //POSTCONDITION:  the scalar generated by $queryStr has been returned.

	$result = mysql_query($queryStr, $GLOBALS['link'])
		or die ("<big><u>Server error message</u><br />" . 
				mysql_error($GLOBALS['link']) .
				"<br /><br /><u>Failed valq</u><br />$queryStr</big>");
	$row = mysql_fetch_row($result);
	mysql_free_result($result);
	
	return ($row[0]);
	
}//end function valq

function objq($queryStr)
{//object query:  does a query and returns an object result
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement which returns one row.  (If more than one
 // row is returned, all but the first will be ignored.)
 //POSTCONDITION:  the row generated by $queryStr has been returned as
 // an object.
	$result = mysql_query($queryStr, $GLOBALS['link'])
		or die ("<big><u>Server error message</u><br />" . 
				mysql_error($GLOBALS['link']) .
				"<br /><br /><u>Failed objq</u><br />$queryStr</big>");
	$obj = mysql_fetch_object($result);
	mysql_free_result($result);
	
	return ($obj);
}//end function objq

function mobq($queryStr)
{//matrix-objects query:  returns an array of object results
 //PRECONDITION:  properly initialized $GLOBALS['link'], and $queryStr
 // is a valid SQL statement.
 //POSTCONDITION:  the rows generated by $queryStr has been returned
 // as an array of objects.
	$result = mysql_query($queryStr, $GLOBALS['link'])
		or die ("<big><u>Server error message</u><br />" . 
				mysql_error($GLOBALS['link']) .
				"<br /><br /><u>Failed mobq</u><br />$queryStr</big>");
	$theMatrix = array();
	while($row = mysql_fetch_object($result))
		array_push($theMatrix, $row);
	mysql_free_result($result);
	
	return ($theMatrix);
}//end function mobq

function get_table_fields($table)
{//get an array of "Field" column from a table's "describe" function
 //PRECONDITION:  the table named by $table exists in the database.
 //POSTCONDITION:  an enumerated array containing the names of the
 // fields in the table has been returned.

	$retn = array();
	$tableInfo = mobq("describe $table");
	foreach($tableInfo as $fieldInfo)
		array_push($retn, $fieldInfo->Field);
	
	return $retn;
}//end function get_table_fields

/* TEXT PROCESSING FUNCTIONS */

function html_format($str)
{//replace "\n" with "<br />" for HTML display, do any
 //other html-izing things we need for correct display
 //PRECONDITION:  None.
 //POSTCONDITION:  A string suitable for HTML display has been
 // returned.
 
	return eregi_replace
		("\n", "<br />", htmlentities($str));
}

function percent_format($num, $precision = NULL)
{//return a string with num formatted as a percentage,
 //e.g. if ($num == 0.25) return "25%";
 //PRECONDITION:  $num is a valid number (int or float), and if
 // specififed, $precision is an int.
 //POSTCONDITION:  A string suitable for display as a percentage has
 // been returned.

	if($precision) //precision specified
		return number_format($num * 100, $precision, ".", ",") . "%";
	else //leave precision as is
		return $num * 100 . "%";
}

function email_format($email)
{//return a string formatted as "mailto:" for e-mail addresses
 //PRECONDITION:  $email is a valid e-mail address (the function will
 // still work if it isn't, of course, but the return value will be
 // pretty useless.
 //POSTCONDITION:  a text string suitable for use as a "mailto:"
 // link has been returned.

	return "<a href = \"mailto:$email\">$email</a>";
}

function print_data_structure($dataStructure)
{// Take any valid PHP data structure (i.e., an object, an array, or a
 // scalar such as an int or a string) and print its structure in
 // easily read nested form.  This is intended as an improvement over
 // the built-in function print_r().
  
	if(is_object($dataStructure))
		$dataStructure = get_object_vars($dataStructure);
		
	if(is_array($dataStructure))
	{
		echo "<ul>";
		foreach($dataStructure as $key => $val)
		{
			echo "<li>$key => ";
			print_data_structure($val);
			echo "</li>";
		}
		echo "</ul>";
	}
	else
	{
		echo $dataStructure;
	}
	
	return;
	
}// end function print_data_structure

/* INVISIBLE HTML-GENERATION FUNCTIONS */

function open_page($title)
{//opens HTML page and does our page header info, down to opening body tag

	?>
	
	<!DOCTYPE html 
		PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

	<html xmlns="http://www.w3.org/1999/xhtml">
	
	<head>
	
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	
	<link rel="stylesheet" type="text/css" href="main.css" />

	<title><? echo $title; ?></title>

	</head>

	<body bgcolor = "#D5D5D5">
		
	<?
		 
	return;
}//end function open_page

function close_page()
{//closes HTML page, of course

	?>

	</body>
	
	</html>
	
	<?
	
	return;
}//end function close_page

function goto_page($url, $params = "")
{//bounces us to the appropriate page, request parameters optional

	if(!empty($params))
		$url .= "?" . $params;
	
	?>
	
	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

	<html xmlns="http://www.w3.org/1999/xhtml">
	
	<head>
	
	<meta http-equiv ="refresh" content="0; URL=<? echo $url; ?>" />
	
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
		
	<title></title>
	
	</head>
	
	<body></body>
	
	</html>
	
	<?
	
	return;
}//end function goto_page

/* VISIBLE HTML GENERATION FUNCTIONS */

function show_navbar()
{
	$actions = array
	(
		"Introduction",
		"Genes",
		"Clusters",
		"Gene Search",
		"Cluster Search",
		"Logarithms"
	);
	
	foreach($actions as $action)
	{
		if($action == $_REQUEST['action'])
		{
			?>
			<p class = "navhere"><? echo $action; ?></p>
			<?
		}
		else
		{
			$href = "index.php?action=$action";
			?>
			<p class = "naventry"><nobr>
				<a href = "<? echo $href; ?>"><? echo $action; ?></a>
			</nobr></p>
			<?
		}
	}
	
	return;
	
}//end function show_navbar

function show_introduction()
{
	?>
	
	<p>Welcome to the SiMCAL1 gene expression project!
	Please use the links to your left to navigate through the
	site.</p>

	<ul>
		
		<li>"Genes" will show statistics about the genes
		in general.</li>
		
		<li>"Gene Search" will provide an interface to search for
		and view specific genes.</li>
	
		<li>"Clusters" will show statistics about the
		overall clustering.</li>
		
		<li>"Cluster Search" will provide an interface to search for
		and view specific clusters.</li>
		
		<li>"Logarithms" will provide tables for
		conversion of logarithmic gene expression levels
		to actual changes in expression level relative to
		baseline, and vice versa.  You may wish to keep
		this page open in a separate browser window or
		tab.</li>
	
	</ul>
	
	<p>A guide to usage:</p>
	
	<ul>
		<li>Buttons found as table headers may be used to sort the 
		table by the values of their columns.  Other buttons serve
		to retrieve search results, save user annotations, or display
		additional information about the gene or cluster under
		examination.</li>
		
		<li>To open genes or clusters found in tables, click on the ID,
		or name (genes) or rank (clusters), in the table entry.</li>
		
		<li>In general, the reporting interface should perform quite
		fast.  However, certain operations may involve large amounts
		of data and may therefore take a noticeable amount of time to
		complete.  Examples include:
		
			<ul>
				<li>Viewing related information (genes, leaders, etc.)
				for clusters of high rank.  These clusters may contain
				thousands of smaller data entities.</li>
				<li>Searching with non-specific search criteria, or with
				criteria that may otherwise be expected to return large
				numbers of results.  For example, there are over 1000
				genes which were consistently upregulated under
				experimental conditions.</li>
			</ul>
		</li>
	</ul>
	
	<p>A guide to interpretation:</p>
	
	<ul>
	
		<li>All expression levels are given on a base-2 
		logarithmic scale relative to the baseline value.  For
		example, an expression level of 1 at a given time point
		means that at that point, the expression of the gene or
		cluster was double the baseline expression.  Similarly, 
		an expression level of -3 means that the expression was
		1/8 the baseline expression.</li>
		
		<li>Expression graphs represent expression levels,
		as described above, over time.  The x-axis, 
		time, is not to scale:  "0" represents 0:00 (baseline),
		"1" represents 0:30, "2" represents 2:00, and "3" 
		represents 8:30.  The y-axis, representing expression 
		levels, shows interpolated values at all points except 
		the experimental time points (0, 1, 2, and 3).  Measured
		values are shown as points, while interplolated values
		are shown as lines.</li>
		
	</ul>
	
	<?

	return;
	
}//end function show_introduction

function show_logarithms()
{	
	?>
	
	<ul>
	
	<li>The first table provides conversions of "raw" expression
	(<b>x</b>) values to their base-2 logarithm (<b>lg(x)</b>)
	equivalents, which are useful in searching for genes.
	E.g., to search for a gene that was upregulated by a factor
	of 3 or more at the 30 minute time point and downregulated 
	by a factor of 2.5 or more at the 2 hour time point, you 
	would enter "1.585" in the <nobr>"Exp. 0:30 &ge;"</nobr>
	box, and "-1.322" in the "Exp. 2:00 &le;" box, on the gene
	search form.</li>
	
	<br />
	
	<li>The second table provides conversions of reported
	expression values (<b>lg(x)</b>) to their "raw" equivalents
	(<b>x</b>), which are useful in interpreting genes and
	clusters.  E.g., if a gene has an "Exp. 0:30" value of "0.60"
	and an "Exp. 2:00" value of "-2.5", that means it was
	upregulated by a factor of 1.516 at the 30 minute time point
	and downregulated by a factor of 5.657 at the 2 hour
	time point.</li>
	
	</ul>
	
	<table width = "95%"><tr><td width = "50%" align = "center">
	
		<p class = "centered">Raw to Logarithm Conversion<br />
		<span class = "smaller">(For use in searching for genes)
		</span></p>
		<table border = "1">
			<tr>
				<th>x <small>&rarr;</small> lg(x)</th>
				<th>x <small>&rarr;</small> lg(x)</th>
				<th>x <small>&rarr;</small> lg(x)</th>
			</tr>
		
			<?
			
			for ($i = 0; $i < 18; $i++)
			{
				echo "<tr>";
				
				for($j = 0; $j < 3; $j++)
				{
					//calculation step
					$from = (1 + ($i * 0.5)) * pow(10, $j);
					$to = log($from) / log(2);
					
					//formatting step
					$from = number_format
						($from, min(3, 2 - log10($from)));
					$to = number_format
						($to, min(3,4 - log10($to)));
					
					echo "<td>$from <small>&rarr;</small> $to</td>";
				}
				
				echo "</tr>";
			}
			
			?>
		
		</table>
		
	</td><td width = "50%" align = "center">
	
		<p class = "centered">Logarithm to Raw Conversion<br />
		<span class ="smaller">(For use in interpreting expression values)
		</span></p>
		<table border = "1">
			<tr>
				<th>lg(x) <small>&rarr;</small> x</th>
				<th>lg(x) <small>&rarr;</small> x</th>
				<th>lg(x) <small>&rarr;</small> x</th>
			</tr>
		
			<?
			
			for ($i = 0; $i < 18; $i++)
			{
				echo "<tr>";
				
				for($j = 0; $j < 3; $j++)
				{
					//calculation step
					$from = (1 + ($i * 0.5)) * pow(10, $j-2);
					$to = pow(2, $from);
					
					//formatting step
					$from = number_format
						($from, min(3, 2 - log10($from)));
					$to = number_format
						($to, min(3,4 - log10($to)));
					
					echo "<td>$from <small>&rarr;</small> $to</td>";
				}
				
				echo "</tr>";
			}
			
			?>
		
		</table>
		
	</td></tr></table>
	
	<?
		
	return;
	
}//end function show_logarithms

/* GRAPHING FUNCTIONS */

function plot_from_arrays
	($name, $path, $xlabel, $ylabel, $xvals, $yvals,
	 $title = "", $interpolate = FALSE, $interptitle = "", $scale = 0,
	 $xyranges = NULL, $pointsize = 0, $xtics = "", $ytics = "",
	 $optxs = NULL, $optys = NULL, $optinterp = FALSE, $opttitle = "")
	 
{//First line of arguments is mandatory, second is optional title and
 // style information, third is optional plotting information, fourth
 // is optional extra data.  Any input with a default type of NULL will
 // be an array, as (of course) are $xvals and $yvals.  $xyranges, if
 // set, must be a four-element array of the type array(xmin, xmax, ymin,
 // ymax).  $optxs and $optys, if set, must be two-dimensional and of
 // equal length in both dimensions.
	
	//qualified names for data and graphic files
	$textf = $path . "/" . $name . ".txt";
	$plotf = $path . "/" . $name . ".png";
	
	//general plot information to write to $textf
	$prefix = "set term png\n";
	if($scale) $prefix .= "set size $scale, $scale\n";
	$prefix .= "set output \"$plotf\"\n\n";
	$prefix .= "set xlabel \"$xlabel\"\n";
	$prefix .= "set ylabel \"$ylabel\"\n";
	if($pointsize) $prefix .= "set pointsize $pointsize\n";
	if(strlen($xtics)) $prefix .= "set xtics $xtics\n";
	if(strlen($ytics)) $prefix .= "set ytics $ytics\n";
	if(is_array($xyranges))
		$prefix .= "plot [" .$xyranges[0] . ":" . $xyranges[1] . "]" .
				   "[" .$xyranges[2] . ":" . $xyranges[3] . "] \\\n";
	else
		$prefix .= "plot \\\n";

	//try to open, and write prefix to, text file
	$fhandle = fopen("$textf", "w") or die("fopen failed");
	fwrite($fhandle, $prefix) or die("fwrite failed on prefix");
	
	//format and write title and style for optional data
	if(is_array($optxs) && is_array($optys))
	{
		$optional = 
			plot_style_format_line($opttitle, $optinterp, 3) . ", \\\n";
		$osize = count($optxs);
		for($i = 1; $i < $osize; $i++)
			$optional .= 
				plot_style_format_line("", $optinterp, 3) . ", \\\n";
			//the first related is already done, so start at $i = 1 -- if
			//there is only one additional set of values, this for-loop
			//never gets to execute
		fwrite($fhandle, $optional)
			or die ("fwrite failed on related format");
	}
	else
		$osize = 0; //useful to keep track of optional data size ...
			
	//format and write title and style for main data
	//(goes last so it overstrikes everything else)
	$main = plot_style_format_line($title, FALSE, 1);
	if($interpolate)
		$main .= ", \\\n" .
				 plot_style_format_line($interptitle, TRUE, 2);
	$main .= "\n\n";
	fwrite($fhandle, $main) or die ("fwrite failed on main format");

	//format and write optional data
	if($osize)
	{
		$optional = "";
		for($i = 0; $i < $osize; $i++)
			$optional .=
				plot_data_format_text($optxs[$i], $optys[$i]) . "\n";
		fwrite($fhandle, $optional)
			or die ("fwrite failed on related data");
	}

	//format and write main data
	//(goes last so it overstrikes everything else)
	fwrite($fhandle, plot_data_format_text($xvals, $yvals))
		or die ("fwrite failed on main data");
	if($interpolate) //write it again so interpolation has data
		fwrite($fhandle, "\n" . plot_data_format_text($xvals, $yvals))
			or die ("fwrite failed on main data");
	
	//close text file, display image, clean up
	fclose($fhandle) or die("fclose failed");
	exec(exec("which gnuplot") . " " . $textf);
	?>
	<img src = "<? echo $plotf; ?>">
	<?
	exec("rm $textf"); //NOTE: we cannot get rid of the plot file because
					   //the page is still displaying, unfortunately ...
					   //it would be nice to figure out how to clean up
					   //the graphics directory a bit better someday ...
	
	return;
	
}//end function plot_from_arrays

function plot_style_format_line($name, $smooth = FALSE, $line = 0)
{
	$retn = "'-'";
	if($smooth)
		$retn .= strlen($_REQUEST['smooth']) ?
			(" smooth " . $_REQUEST['smooth']) : " smooth csplines";
	$retn .= strlen($name) ? " title \"$name\"" : " notitle";
	if($line) $retn .= " lt $line";
	
	return $retn;
	
}//end function plot_style_format_line

function plot_data_format_text($x, $y)
{
	$retn = "";
	$ct = count($x);
	for($i = 0; $i < $ct; $i++)
		$retn .= $x[$i] . " " . $y[$i] . "\n";
	$retn .= "e\n";
	
	return $retn;

}//end function plot_data_format_text

/* MISC FUNCTIONS */

function formvalue_pack($obj)
{
	return urlencode(serialize($obj));
	
}//end function formvalue_pack

function formvalue_unpack($str)
{
	return unserialize(urldecode($str));
	
}//end function formvalue_unpack

//NOTE:  There is probably an elegant way to combine the two functions
// below into a single recursive one, but not, I think, right now ...

function array_limit($arr)
{//returns the largest absolute value in a 1D array

	$arr = array_map("abs", $arr);
	return max($arr);
	
}//end function array_limit

function array_limit_2d($arr)
{//returns the largest absolute value in a 2D array

	$arr = array_map("array_limit", $arr);
	return max($arr);
	
}//end function array_limit

?>