From ac74b28fc27e3d82a905281900c645f09c94f4c3 Mon Sep 17 00:00:00 2001 From: Takumi Date: Tue, 6 Aug 2013 16:00:33 +1000 Subject: [PATCH] Add classes library --- lib/classes/.gitattributes | 22 ++ lib/classes/.gitignore | 165 ++++++++++ lib/classes/README.md | 142 ++++++++ lib/classes/eos.class.php | 624 ++++++++++++++++++++++++++++++++++++ lib/classes/stack.class.php | 113 +++++++ 5 files changed, 1066 insertions(+) create mode 100644 lib/classes/.gitattributes create mode 100644 lib/classes/.gitignore create mode 100644 lib/classes/README.md create mode 100644 lib/classes/eos.class.php create mode 100644 lib/classes/stack.class.php diff --git a/lib/classes/.gitattributes b/lib/classes/.gitattributes new file mode 100644 index 00000000..412eeda7 --- /dev/null +++ b/lib/classes/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/lib/classes/.gitignore b/lib/classes/.gitignore new file mode 100644 index 00000000..ee5c0a97 --- /dev/null +++ b/lib/classes/.gitignore @@ -0,0 +1,165 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +nbproject/ + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store +/nbproject/private/ \ No newline at end of file diff --git a/lib/classes/README.md b/lib/classes/README.md new file mode 100644 index 00000000..1b866ffb --- /dev/null +++ b/lib/classes/README.md @@ -0,0 +1,142 @@ +# Classes +--- + +## eos.class.php + +### Equation Operating System + +This class makes it incredibly easy to use and parse/solve equations in +your own applications. It includes a graph generator to turn an equation +with a variable in to a `y=x` graph, with the capability to calculate +the upper and lower `y-bounds`. __NOTE__ NONE of the functions within +these two classes are static, any example that looks like a static function +call is representational of the class being used, but should be initialized +and assigned to a variable first. It is also important to note that these +classes throw exceptions if running in to errors, please read the beginning +of the `eos.class.php` file for the defines of the exceptions thrown. Exceptions +includes a descriptive message of the error encountered and within `eqEOS` will +also typically include the full equation used. + +#### eqEOS + +This class has one important function, `eqEOS::solveIF()` which does all the legwork, +so we'll start there and end with examples. +To initialize this class, use: + + $eos = new eqEOS(); + +##### solveIF($infix, $variables) + +To use this function: + + $value = $eos->solveIF($eq, $vars); + +###### _$infix_ + +Is simply a standard equation with variable support. Variables +have two forms, one is native to PHP programmers already, prefixed with '$'. +The other way to declare a variable is with '&' and is included for +backward compatibility for with the initial version from 2005. +Example Equations: + + 2(4$x) + 2(4&x) + 5+ ((1+2)*4) +3 + 5+4(1+2)+3 + 10*sin($x) + 10*cos($x) + +The first two pairs shown are exactly the same. The parser has good implied +multiplication, for everything but allowed functions. Allowed functions require +an implicit operator on either/both sides to work properly, I hope to change +that in the next revision; but for now, note that it will not work as you would +expect. +For example: + + 5sin(1.5707963267) = 51 + 5*sin(1.5707963267) = 5 + sin(1.5707963267)5 = 15 + +The reason is because there is no implied multiplication being applied, the result +of `sin(1.5707963267) = 1` is being concatenated with the number 5, giving +incredibly odd results if you are not expecting it. + +###### _$variables_ + +The variables are fairly simple to understand. If it contains a scalar (ie +a non-array value) _every_ variable within the equation will be replaced with +that number. If it contains an array, there will be a by-variable replacement - +note that the array MUST be in the format of `'variable' => value` +Such as: + + array( + 'x' => 2, + 'y' => 3 + ) + +Given the equation: + + 5$x^$y + +If this is called by: + + eqEOS::solveIF('5$x^$y', 2) + +It will equal '20', as every variable is replaced by 2. However, if called like: + + eqEOS::solveIF('5$x^$y', array( + 'x' => 2, + 'y' => 3); + +You will get the result of '40' as it would equate to '5*2^3', as expected. + +#### eqGraph + +This is the fun class that can create graphs. It extends `eqEOS`. +To initialize use: + + $graph = new eqGraph($width, $height); + +The `$width` and `$height` are the values used for the image size, defaulting to +a `640x480` image size if initialized with `$graph = new eqGraph();` + +##### graph($eq, $xLow, $xHigh, $xStep, [$xyGrid, $yGuess, ...]) + +This method will generate the graph for the equation (`$eq`) with a min and max +`x` range that it will parse through. All Variables explained: +* `$eq` + The Standard Equation to use. _Must_ have a variable in it. (ie `$x`) +* `$xLow` + The starting point for the calculations - the left side of the graph. +* `$xHigh` + The last point calculated for the variable - the right side of the graph. +* `$xStep` + Stepping point for the variable. Suggested not to use a value less than + `.01`. This is the precision of the graph. +* `$xyGrid = false` + Show `x/y` gridlines on the graph. Defaults to false. Each grid line + is set at every integer (ie `1,2,3,...100`). If working with small ranges, + it is suggested to turn this on. +* `$yGuess = true` + Guess the Lower and Upper `y-bounds` (The bottom and top of the image + respectively.) This will set the the bounds to the lowest `y` value + encountered for the `$yLow`, and the largest `y` value for `$yHight`. +* `$yLow = false` + Lower bound for `y`, will be reset if a lower value for `y` is found. +* `$yHigh = false` + Upper bound for `y`, will be reset if a larger `y` value is found. + +TODO: +* Add `x` and `y` labels +* Smart `grid spacing` calculations so can be effective with large ranges. +* Smart (default) `$xStep` calcuations based on image size and ranges. + +To set up a graph with a `21x21` window (ie `-10 to 10`) for the equation +`sin($x)` and output as PNG, would use as: + + $graph->graph('sin($x)', -10, 10, 0.01, true, false, -10, 10); + $graph->outPNG(); + +It would look like: +![Sin(x)](http://img825.imageshack.us/img825/1380/sinx21x21.png) +--- \ No newline at end of file diff --git a/lib/classes/eos.class.php b/lib/classes/eos.class.php new file mode 100644 index 00000000..9df2d6ec --- /dev/null +++ b/lib/classes/eos.class.php @@ -0,0 +1,624 @@ + + * @copyright Copyright ©2005-2013, Jon Lawrence + * @license http://opensource.org/licenses/LGPL-2.1 LGPL 2.1 License + * @package EOS + * @version 2.0 + */ + +//The following are defines for thrown exceptions + +/** + * No matching Open/Close pair + */ +define('EQEOS_E_NO_SET', 5500); +/** + * Division by 0 + */ +define('EQEOS_E_DIV_ZERO', 5501); +/** + * No Equation + */ +define('EQEOS_E_NO_EQ', 5502); +/** + * No variable replacement available + */ +define('EQEOS_E_NO_VAR', 5503); + +if(!defined('DEBUG')) + define('DEBUG', false); + +//We use a stack class so we don't have to keep track of indices for an array +// May eventually update to use `array_pop()` `end()` and `array_push()` instead +// of this class. +require_once "stack.class.php"; + + +/** + * Equation Operating System (EOS) Parser + * + * An EOS that can safely parse equations from unknown sources returning + * the calculated value of it. Can also handle solving equations with + * variables, if the variables are defined (useful for the Graph creation + * that the second and extended class in this file provides. {@see eqGraph}) + * This class was created for PHP4 in 2005, updated to fully PHP5 in 2013. + * + * @author Jon Lawrence + * @copyright Copyright ©2005-2013, Jon Lawrence + * @license http://opensource.org/licenses/LGPL-2.1 LGPL 2.1 License + * @package Math + * @subpackage EOS + * @version 2.0 + */ +class eqEOS { + /**#@+ + *Private variables + */ + private $postFix; + private $inFix; + /**#@-*/ + /**#@+ + * Protected variables + */ + //What are opening and closing selectors + protected $SEP = array('open' => array('(', '['), 'close' => array(')', ']')); + //Top presedence following operator - not in use + protected $SGL = array('!'); + //Order of operations arrays follow + protected $ST = array('^'); + protected $ST1 = array('/', '*', '%'); + protected $ST2 = array('+', '-'); + //Allowed functions + protected $FNC = array('sin', 'cos', 'tan', 'csc', 'sec', 'cot'); + /**#@-*/ + /** + * Construct method + * + * Will initiate the class. If variable given, will assign to + * internal variable to solve with this::solveIF() without needing + * additional input. Initializing with a variable is not suggested. + * + * @see eqEOS::solveIF() + * @param String $inFix Standard format equation + */ + public function __construct($inFix = null) { + $this->inFix = (isset($inFix)) ? $inFix : null; + $this->postFix = array(); + } + + /** + * Check Infix for opening closing pair matches. + * + * This function is meant to solely check to make sure every opening + * statement has a matching closing one, and throws an exception if + * it doesn't. + * + * @param String $infix Equation to check + * @throws Exception if malformed. + * @return Bool true if passes - throws an exception if not. + */ + private function checkInfix($infix) { + if(trim($infix) == "") { + throw new Exception("No Equation given", EQEOS_E_NO_EQ); + return false; + } + //Make sure we have the same number of '(' as we do ')' + // and the same # of '[' as we do ']' + if(substr_count($infix, '(') != substr_count($infix, ')')) { + throw new Exception("Mismatched parenthesis in '{$infix}'", EQEOS_E_NO_SET); + return false; + } elseif(substr_count($infix, '[') != substr_count($infix, ']')) { + throw new Exception("Mismatched brackets in '{$infix}'", EQEOS_E_NO_SET); + return false; + } + $this->inFix = $infix; + return true; + } + + /** + * Infix to Postfix + * + * Converts an infix (standard) equation to postfix (RPN) notation. + * Sets the internal variable $this->postFix for the eqEOS::solvePF() + * function to use. + * + * @link http://en.wikipedia.org/wiki/Infix_notation Infix Notation + * @link http://en.wikipedia.org/wiki/Reverse_Polish_notation Reverse Polish Notation + * @param String $infix A standard notation equation + * @return Array Fully formed RPN Stack + */ + public function in2post($infix = null) { + // if an equation was not passed, use the one that was passed in the constructor + $infix = (isset($infix)) ? $infix : $this->inFix; + + //check to make sure 'valid' equation + $this->checkInfix($infix); + $pf = array(); + $ops = new phpStack(); + $vars = new phpStack(); + + // remove all white-space + preg_replace("/\s/", "", $infix); + + // Create postfix array index + $pfIndex = 0; + + //what was the last character? (useful for decerning between a sign for negation and subtraction) + $lChar = ''; + + //loop through all the characters and start doing stuff ^^ + for($i=0;$iSEP['open'])) { + // if the last character was a number, place an assumed '*' on the stack + if(preg_match('/[0-9.]/i', $lChar)) + $ops->push('*'); + + $ops->push($chr); + } + // if the character closes a set e.g. ')' or ']' + elseif(in_array($chr, $this->SEP['close'])) { + // find what set it was i.e. matches ')' with '(' or ']' with '[' + $key = array_search($chr, $this->SEP['close']); + // while the operator on the stack isn't the matching pair...pop it off + while($ops->peek() != $this->SEP['open'][$key]) { + $nchr = $ops->pop(); + if($nchr) + $pf[++$pfIndex] = $nchr; + else { + throw new Exception("Error while searching for '". $this->SEP['open'][$key] ."' in '{$infix}'.", EQEOS_E_NO_SET); + return false; + } + } + $ops->pop(); + } + // If a special operator that has precedence over everything else + elseif(in_array($chr, $this->ST)) { + $ops->push($chr); + $pfIndex++; + } + // Any other operator other than '+' and '-' + elseif(in_array($chr, $this->ST1)) { + while(in_array($ops->peek(), $this->ST1) || in_array($ops->peek(), $this->ST)) + $pf[++$pfIndex] = $ops->pop(); + + $ops->push($chr); + $pfIndex++; + } + // if a '+' or '-' + elseif(in_array($chr, $this->ST2)) { + // if it is a '-' and the character before it was an operator or nothingness (e.g. it negates a number) + if((in_array($lChar, array_merge($this->ST1, $this->ST2, $this->ST, $this->SEP['open'])) || $lChar=="") && $chr=="-") { + // increase the index because there is no reason that it shouldn't.. + $pfIndex++; + $pf[$pfIndex] = $chr; + } + // Otherwise it will function like a normal operator + else { + while(in_array($ops->peek(), array_merge($this->ST1, $this->ST2, $this->ST))) + $pf[++$pfIndex] = $ops->pop(); + $ops->push($chr); + $pfIndex++; + } + } + // make sure we record this character to be refered to by the next one + $lChar = $chr; + } + // if there is anything on the stack after we are done...add it to the back of the RPN array + while(($tmp = $ops->pop()) !== false) + $pf[++$pfIndex] = $tmp; + + // re-index the array at 0 + $pf = array_values($pf); + + // set the private variable for later use if needed + $this->postFix = $pf; + + // return the RPN array in case developer wants to use it fro some insane reason (bug testing ;] + return $pf; + } //end function in2post + + /** + * Solve Postfix (RPN) + * + * This function will solve a RPN array. Default action is to solve + * the RPN array stored in the class from eqEOS::in2post(), can take + * an array input to solve as well, though default action is prefered. + * + * @link http://en.wikipedia.org/wiki/Reverse_Polish_notation Postix Notation + * @param Array $pfArray RPN formatted array. Optional. + * @return Float Result of the operation. + */ + public function solvePF($pfArray = null) { + // if no RPN array is passed - use the one stored in the private var + $pf = (!is_array($pfArray)) ? $this->postFix : $pfArray; + + // create our temporary function variables + $temp = array(); + $tot = 0; + $hold = 0; + + // Loop through each number/operator + for($i=0;$iST, $this->ST1, $this->ST2))) { + $temp[$hold++] = $pf[$i]; + } + // ...Otherwise perform the operator on the last two numbers + else { + switch ($pf[$i]) { + case '+': + $temp[$hold-2] = $temp[$hold-2] + $temp[$hold-1]; + break; + case '-': + $temp[$hold-2] = $temp[$hold-2] - $temp[$hold-1]; + break; + case '*': + $temp[$hold-2] = $temp[$hold-2] * $temp[$hold-1]; + break; + case '/': + if($temp[$hold-1] == 0) { + throw new Exception("Division by 0 on: '{$temp[$hold-2]} / {$temp[$hold-1]}' in {$this->inFix}", EQEOS_E_DIV_ZERO); + return false; + } + $temp[$hold-2] = $temp[$hold-2] / $temp[$hold-1]; + break; + case '^': + $temp[$hold-2] = pow($temp[$hold-2], $temp[$hold-1]); + break; + case '%': + if($temp[$hold-1] == 0) { + throw new Exception("Division by 0 on: '{$temp[$hold-2]} % {$temp[$hold-1]}' in {$this->inFix}", EQEOS_E_DIV_ZERO); + return false; + } + $temp[$hold-2] = bcmod($temp[$hold-2], $temp[$hold-1]); + break; + } + // Decrease the hold var to one above where the last number is + $hold = $hold-1; + } + } + // return the last number in the array + return $temp[$hold-1]; + + } //end function solvePF + + + /** + * Solve Infix (Standard) Notation Equation + * + * Will take a standard equation with optional variables and solve it. Variables + * must begin with '&' will expand to allow variables to begin with '$' (TODO) + * The variable array must be in the format of 'variable' => value. If + * variable array is scalar (ie 5), all variables will be replaced with it. + * + * @param String $infix Standard Equation to solve + * @param String|Array $vArray Variable replacement + * @return Float Solved equation + */ + function solveIF($infix, $vArray = null) { + $infix = ($infix != "") ? $infix : $this->inFix; + + //Check to make sure a 'valid' expression + $this->checkInfix($infix); + + $ops = new phpStack(); + $vars = new phpStack(); + + //remove all white-space + preg_replace("/\s/", "", $infix); + if(DEBUG) + $hand=fopen("eq.txt","a"); + + //Find all the variables that were passed and replaces them + while((preg_match('/(.){0,1}[&$]([a-zA-Z]+)(.){0,1}/', $infix, $match)) != 0) { + + //remove notices by defining if undefined. + if(!isset($match[3])) { + $match[3] = ""; + } + + if(DEBUG) + fwrite($hand, "{$match[1]} || {$match[3]}\n"); + // Ensure that the variable has an operator or something of that sort in front and back - if it doesn't, add an implied '*' + if((!in_array($match[1], array_merge($this->ST, $this->ST1, $this->ST2, $this->SEP['open'])) && $match[1] != "") || is_numeric($match[1])) //$this->SEP['close'] removed + $front = "*"; + else + $front = ""; + + if((!in_array($match[3], array_merge($this->ST, $this->ST1, $this->ST2, $this->SEP['close'])) && $match[3] != "") || is_numeric($match[3])) //$this->SEP['open'] removed + $back = "*"; + else + $back = ""; + + //Make sure that the variable does have a replacement + if(!isset($vArray[$match[2]]) && (!is_array($vArray != "") && !is_numeric($vArray))) { + throw new Exception("Variable replacement does not exist for '". substr($match[0], 1, -1) ."' in {$this->inFix}", EQEOS_E_NO_VAR); + return false; + } elseif(!isset($vArray[$match[2]]) && (!is_array($vArray != "") && is_numeric($vArray))) { + $infix = str_replace($match[0], $match[1] . $front. $vArray. $back . $match[3], $infix); + } elseif(isset($vArray[$match[2]])) { + $infix = str_replace($match[0], $match[1] . $front. $vArray[$match[2]]. $back . $match[3], $infix); + } + } + + if(DEBUG) + fwrite($hand, "$infix\n"); + + // Finds all the 'functions' within the equation and calculates them + // NOTE - when using function, only 1 set of paranthesis will be found, instead use brackets for sets within functions!! + while((preg_match("/(". implode("|", $this->FNC) . ")\(([^\)\(]*(\([^\)]*\)[^\(\)]*)*[^\)\(]*)\)/", $infix, $match)) != 0) { + $func = $this->solveIF($match[2]); + switch($match[1]) { + case "cos": + $ans = cos($func); + break; + case "sin": + $ans = sin($func); + break; + case "tan": + $ans = tan($func); + break; + case "sec": + $tmp = cos($func); + if($tmp == 0) { + throw new Exception("Division by 0 on: 'sec({$func}) = 1/cos({$func})' in {$this->inFix}", EQEOS_E_DIV_ZERO); + return false; + } + $ans = 1/$tmp; + break; + case "csc": + $tmp = sin($func); + if($tmp == 0) { + throw new Exception("Division by 0 on: 'csc({$func}) = 1/sin({$func})' in {$this->inFix}", EQEOS_E_DIV_ZERO); + return false; + } + $ans = 1/$tmp; + break; + case "cot": + $tmp = tan($func); + if($tmp == 0) { + throw new Exception("Division by 0 on: 'cot({$func}) = 1/tan({$func})' in {$this->inFix}", EQEOS_E_DIV_ZERO); + return false; + } + $ans = 1/$tmp; + break; + default: + break; + } + $infix = str_replace($match[0], $ans, $infix); + } + if(DEBUG) + fclose($hand); + return $this->solvePF($this->in2post($infix)); + + + } //end function solveIF +} //end class 'eqEOS' + + +// fun class that requires the GD libraries to give visual output to the user +/* extends the eqEOS class so that it doesn't need to create it as a private var + - and it extends the functionality of that class */ +/** + * Equation Graph + * + * Fun class that requires the GD libraries to give visual output of an + * equation to the user. Extends the eqEOS class. + * + * @author Jon Lawrence + * @copyright Copyright ©2005-2013 Jon Lawrence + * @license http://opensource.org/licenses/LGPL-2.1 LGPL 2.1 License + * @package Math + * @subpackage EOS + * @version 2.0 + */ +class eqGraph extends eqEOS { + private $width; + private $height; + //GD Image reference + private $image; + + /** + * Constructor + * + * Sets up the Graph class with an image width and height defaults to + * 640x480 + * + * @param Integer $width Image width + * @param Integer $height Image height + */ + public function __construct($width=640, $height=480) { + // default width and height equal to that of a poor monitor (in early 2000s) + $this->width = $width; + $this->height = $height; + //initialize main class variables + parent::__construct(); + } //end function eqGraph + + + /** + * Create GD Graph Image + * + * Creates a GD image based on the equation given with the parameters that are set + * + * @param String $eq Equation to use. Needs variable in equation to create graph, all variables are interpreted as 'x' + * @param Integer $xLow Lower x-bound for graph + * @param Integer $xHigh Upper x-bound for graph + * @param Float $xStep Stepping points while solving, the lower, the better precision. Slow if lower than .01 + * @param Boolean $xyGrid Draw gridlines? + * @param Boolean $yGuess Guess the upper/lower yBounds? + * @param Integer $yLow Lower y-bound + * @param Integer $yHigh Upper y-bound + * @return Null + */ + public function graph($eq, $xLow, $xHigh, $xStep, $xyGrid = false, $yGuess = true, $yLow=false, $yHigh=false) { + //create our image and allocate the two colors + $img = ImageCreate($this->width, $this->height); + $white = ImageColorAllocate($img, 255, 255, 255); + $black = ImageColorAllocate($img, 0, 0, 0); + $grey = ImageColorAllocate($img, 220, 220, 220); + $xStep = abs($xStep); + //DEVELOPER, UNCOMMENT NEXT LINE IF WANTING TO PREVENT SLOW GRAPHS + //$xStep = ($xStep > .01) ? $xStep : 0.01; + if($xLow > $xHigh) + list($xLow, $xHigh) = array($xHigh, $xLow); //swap function + + $xScale = $this->width/($xHigh-$xLow); + $counter = 0; + if(DEBUG) { + $hand=fopen("eqgraph.txt","w"); + fwrite($hand, "$eq\n"); + } + for($i=$xLow;$i<=$xHigh;$i+=$xStep) { + $tester = sprintf("%10.3f",$i); + if($tester == "-0.000") $i = 0; + $y = $this->solveIF($eq, $i); + //eval('$y='. str_replace('&x', $i, $eq).";"); /* used to debug my eqEOS class results */ + if(DEBUG) { + $tmp1 = sprintf("y(%5.3f) = %10.3f\n", $i, $y); + fwrite($hand, $tmp1); + } + + // If developer asked us to find the upper and lower bounds for y... + if($yGuess==true) { + $yLow = ($yLow===false || ($y<$yLow)) ? $y : $yLow; + $yHigh = ($yHigh===false || $y>$yHigh) ? $y : $yHigh; + } + $xVars[$counter] = $y; + $counter++; + } + if(DEBUG) + fclose($hand); + // add 0.01 to each side so that if y is from 1 to 5, the lines at 1 and 5 are seen + $yLow-=0.01;$yHigh+=0.01; + + //Now that we have all the variables stored...find the yScale + $yScale = $this->height/(($yHigh)-($yLow)); + + // if developer wanted a grid on the graph, add it now + if($xyGrid==true) { + for($i=ceil($yLow);$i<=floor($yHigh);$i++) { + $i0 = abs($yHigh-$i); + ImageLine($img, 0, $i0*$yScale, $this->width, $i0*$yScale, $grey); + } + for($i=ceil($xLow);$i<=floor($xHigh);$i++) { + $i0 = abs($xLow-$i); + ImageLine($img, $i0*$xScale, 0, $i0*$xScale, $this->height, $grey); + } + } + + //Now that we have the scales, let's see if we can draw an x/y-axis + if($xLow <= 0 && $xHigh >= 0) { + //the y-axis is within our range - draw it. + $x0 = abs($xLow)*$xScale; + ImageLine($img, $x0, 0, $x0, $this->height, $black); + for($i=ceil($yLow);$i<=floor($yHigh);$i++) { + $i0 = abs($yHigh-$i); + ImageLine($img, $x0-3, $i0*$yScale, $x0+3, $i0*$yScale, $black); + } + } + if($yLow <= 0 && $yHigh >= 0) { + //the x-axis is within our range - draw it. + $y0 = abs($yHigh)*$yScale; + ImageLine($img, 0, $y0, $this->width, $y0, $black); + for($i=ceil($xLow);$i<=floor($xHigh);$i++) { + $i0 = abs($xLow-$i); + ImageLine($img, $i0*$xScale, $y0-3, $i0*$xScale, $y0+3, $black); + } + } + $counter=2; + + //now graph it all ;] + for($i=$xLow+$xStep;$i<=$xHigh;$i+=$xStep) { + $x1 = (abs($xLow - ($i - $xStep)))*$xScale; + $y1 = (($xVars[$counter-1]<$yLow) || ($xVars[$counter-1] > $yHigh)) ? -1 : (abs($yHigh - $xVars[$counter-1]))*$yScale; + $x2 = (abs($xLow - $i))*$xScale; + $y2 = (($xVars[$counter]<$yLow) || ($xVars[$counter] > $yHigh)) ? -1 : (abs($yHigh - $xVars[$counter]))*$yScale; + + // if any of the y values were found to be off of the y-bounds, don't graph those connecting lines + if($y1!=-1 && $y2!=-1) + ImageLine($img, $x1, $y1, $x2, $y2, $black); + $counter++; + } + $this->image = $img; + } //end function 'graph' + + /** + * Sends JPG to browser + * + * Sends a JPG image with proper header to output + */ + public function outJPG() { + header("Content-type: image/jpeg"); + ImageJpeg($this->image); + } + + /** + * Sends PNG to browser + * + * Sends a PNG image with proper header to output + */ + function outPNG() { + header("Content-type: image/png"); + ImagePng($this->image); + } + + /** + * Output GD Image + * + * Will give the developer the GD resource for the graph that + * can be used to store the graph to the FS or other media + * + * @return Resource GD Image Resource + */ + public function getImage() { + return $this->image; + } + + /** + * Output GD Image + * + * Alias for eqGraph::getImage() + * + * @return Resource GD Image resource + */ + public function outGD() { + return $this->getImage(); + } +} //end class 'eqGraph' +?> \ No newline at end of file diff --git a/lib/classes/stack.class.php b/lib/classes/stack.class.php new file mode 100644 index 00000000..3c52fb28 --- /dev/null +++ b/lib/classes/stack.class.php @@ -0,0 +1,113 @@ + + * @copyright Copyright ©2005-2013 Jon Lawrence + * @license http://opensource.org/licenses/LGPL-2.1 LGPL 2.1 License + * @package eos.class.php + * @version 2.0 + */ +class phpStack { + private $index; + private $locArray; + + /** + * Constructor + * + * Initializes the stack + */ + public function __construct() { + //define the private vars + $this->locArray = array(); + $this->index = -1; + } + + /** + * Peek + * + * Will view the last element of the stack without removing it + * + * @return Mixed An element of the array or false if none exist + */ + public function peek() { + if($this->index > -1) + return $this->locArray[$this->index]; + else + return false; + } + + /** + * Poke + * + * Will add an element to the end of the stack + * + * @param Mixed Element to add + */ + public function poke($data) { + $this->locArray[++$this->index] = $data; + } + + /** + * Push + * + * Alias of {@see phpStack::poke()} + * Adds element to the stack + * + * @param Mixed Element to add + */ + public function push($data) { + //allias for 'poke' + $this->poke($data); + } + + /** + * Pop + * + * Retrives an element from the end of the stack, and removes it from + * the stack at the same time. If no elements, returns boolean false + * + * @return Mixed Element at end of stack or false if none exist + */ + public function pop() { + if($this->index > -1) + { + $this->index--; + return $this->locArray[$this->index+1]; + } + else + return false; + } + + /** + * Clear + * + * Clears the stack to be reused. + */ + public function clear() { + $this->index = -1; + $this->locArray = array(); + } + + /** + * Get Stack + * + * Returns the array of stack elements, keeping all, indexed at 0 + * + * @return Mixed Array of stack elements or false if none exist. + */ + public function getStack() { + if($this->index > -1) + { + return array_values($this->locArray); + } + else + return false; + } +} + +?> \ No newline at end of file