token = $this->getRandomToken(); $this->filter = new FilterCacheObject(); $this->sort = new SortCacheObject(); } public function reset() { $this->lastId = 0; $this->isModified = false; $this->intervals = array(); $this->indexes = array(); unset($this->filter); $this->filter = new FilterCacheObject();; unset($this->sort); $this->sort = new SortCacheObject(); } public function setIsModified($isModified) { $this->isModified = $isModified; } public function addInterval($startIso, $stopIso, $isNew = false, $index = -1) { $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); ++$this->lastId; $interval->setStartFromISO($startIso); $interval->setStopFromISO($stopIso); $interval->setIsNew($isNew); array_push($this->intervals, $interval); if ($index < 0) array_push($this->indexes, count($this->intervals) - 1); else array_splice($this->indexes, $index, 0, array(count($this->intervals) - 1)); if ($isNew) $this->isModified = true; return $interval; } public function removeIntervalFromId($id) { for ($i = 0; $i < count($this->intervals); ++$i) { if ($this->intervals[$i]->getId() == $id) { //Remove interval array_splice($this->intervals, $i, 1); //Remove interval index if exist in indexes list for ($j = 0; $j < count($this->indexes); ++$j) { if ($this->indexes[$j] == $i) { array_splice($this->indexes, $j, 1); break; } } //Update indexes list for ($j = 0; $j < count($this->indexes); ++$j) { if ($this->indexes[$j] >= $i) $this->indexes[$j]--; } $this->isModified = true; return true; } } return false; } public function modifyIntervalFromId($id, $start, $stop) { foreach ($this->intervals as $interval) { if ($interval->getId() == $id) { if (isset($start)) $interval->setStartFromISO($start); if (isset($stop)) $interval->setStopFromISO($stop); $interval->setIsModified(true); $this->isModified = true; return true; } } return false; } public function operationIntervals($extendTime, $shiftTime) { if (($extendTime == 0) && ($shiftTime == 0)) //Nothing to do return true; for ($i = 0; $i < count($this->indexes); ++$i) { $start = $this->intervals[$this->indexes[$i]]->getStartToStamp(); $start -= $extendTime; $start += $shiftTime; $this->intervals[$this->indexes[$i]]->setStartFromStamp($start); $stop = $this->intervals[$this->indexes[$i]]->getStopToStamp(); $stop += $extendTime; $stop += $shiftTime; $this->intervals[$this->indexes[$i]]->setStopFromStamp($stop); $this->intervals[$this->indexes[$i]]->setIsModified(true); $this->isModified = true; } return true; } public function mergeIntervals() { $this->sort->reset(); $this->sort->loadFromObject( array( (object)array("property" => "start", "direction" => "DESC") ) ); $this->updateIndexes(); $merged_intervals = array(); for ($i = 0; $i < count($this->indexes); ++$i) { if (count($merged_intervals) == 0) { array_push($merged_intervals,array( "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), "mod" => FALSE) ); continue; } if (($merged_intervals[count($merged_intervals)-1]["stop"] >= $this->intervals[$this->indexes[$i]]->getStartToStamp()) && ($merged_intervals[count($merged_intervals)-1]["stop"] < $this->intervals[$this->indexes[$i]]->getStopToStamp())) { $merged_intervals[count($merged_intervals)-1]["stop"] = $this->intervals[$this->indexes[$i]]->getStopToStamp(); $merged_intervals[count($merged_intervals)-1]["mod"] = TRUE; } else array_push($merged_intervals,array( "start" => $this->intervals[$this->indexes[$i]]->getStartToStamp(), "stop" => $this->intervals[$this->indexes[$i]]->getStopToStamp(), "mod" => FALSE) ); } $this->reset(); foreach ($merged_intervals as $merged_interval) { $interval = new IntervalCacheObject($this->lastId, count($this->intervals)); ++$this->lastId; $interval->setStartFromStamp($merged_interval["start"]); $interval->setStopFromStamp($merged_interval["stop"]); $interval->setIsNew($merged_interval["mod"]); if ($merged_interval["mod"]) $this->isModified = true; array_push($this->intervals, $interval); array_push($this->indexes, count($this->intervals) - 1); } return true; } public function getStatistics() { $minTime = NULL; $maxTime = NULL; $minDuration = NULL; $maxDuration = NULL; $indexMinDuration = -1; $indexMaxDuration = -1; $nbValid = 0; $durationTotal = 0; //Min & Max for ($i = 0; $i < count($this->indexes); ++$i) { if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) //Invalid interval continue; ++$nbValid; $durationTotal += $this->intervals[$this->indexes[$i]]->getDuration(); if (!isset($minTime) || ($minTime > $this->intervals[$this->indexes[$i]]->getStartToStamp())) $minTime = $this->intervals[$this->indexes[$i]]->getStartToStamp(); if (!isset($maxTime) || ($maxTime < $this->intervals[$this->indexes[$i]]->getStopToStamp())) $maxTime = $this->intervals[$this->indexes[$i]]->getStopToStamp(); if (!isset($minDuration) || ($minDuration > $this->intervals[$this->indexes[$i]]->getDuration())) { $minDuration = $this->intervals[$this->indexes[$i]]->getDuration(); $indexMinDuration = $i; } if (!isset($maxDuration) || ($maxDuration < $this->intervals[$this->indexes[$i]]->getDuration())) { $maxDuration = $this->intervals[$this->indexes[$i]]->getDuration(); $indexMaxDuration = $i; } } if (!isset($minTime)) $minTime = 0; if (!isset($maxTime)) $maxTime = 0; if (!isset($minDuration)) $minDuration = 0; if (!isset($maxDuration)) $maxDuration = 0; //Mean if ($nbValid > 0) $mean = $durationTotal / $nbValid; else $mean = 0; //Standard deviation $pow = 0; for ($i = 0; $i < count($this->indexes); ++$i) { if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) //Invalid interval continue; $pow += pow($this->intervals[$this->indexes[$i]]->getDuration()-$mean,2); } if ($nbValid > 0) $variance = $pow/$nbValid; else $variance = 0; $stdev = sqrt($variance); //Sort by duration to get median $this->sort->reset(); $this->sort->loadFromObject( array( (object)array("property" => "durationSec", "direction" => "DESC") ) ); $this->updateIndexes(); $durations = array(); for ($i = 0; $i < count($this->indexes); ++$i) { if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) //Invalid interval continue; array_push($durations, $this->intervals[$this->indexes[$i]]->getDuration()); } if (count($durations) > 0) { if (count($durations)%2 > 0) { $median = $durations[count($durations)/2-0.5]; } else { // else the number of intervals is an even number $median = ($durations[count($durations)/2-1] + $durations[count($durations)/2])/2; } } else $median = 0; //Merge intervals to get density $this->mergeIntervals(); $durationMergedTotal = 0; for ($i = 0; $i < count($this->indexes); ++$i) { if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) //Invalid interval continue; $durationMergedTotal += $this->intervals[$this->indexes[$i]]->getDuration(); } if (($maxTime-$minTime) > 0) $density = (($durationMergedTotal/($maxTime-$minTime))); else $density = 0; return array( "minDuration" => $minDuration, "minDurationIndex"=> $indexMinDuration, "maxDuration" => $maxDuration, "maxDurationIndex"=> $indexMaxDuration, "mean" => $mean, "stdev" => $stdev, "median" => $median, "density" => $density); } public function getStatus() { $nbFiltered = count($this->intervals) - count($this->indexes); $nbModified = 0; $nbNew = 0; $nbInvalid = 0; $nbValid = 0; for ($i = 0; $i < count($this->indexes); ++$i) { if ($this->intervals[$this->indexes[$i]]->getDuration() <= 0) ++$nbInvalid; else ++$nbValid; if ($this->intervals[$this->indexes[$i]]->isModified()) ++$nbModified; if ($this->intervals[$this->indexes[$i]]->isNew()) ++$nbNew; } return array( "nbFiltered" => $nbFiltered, "nbModified" => $nbModified, "nbNew" => $nbNew, "nbInvalid" => $nbInvalid, "nbValid" => $nbValid, "isModified" => $this->isModified ); } public function getIntervalsArray($startIndex, $limit,$skipInvalid = false) { $intervals = array(); if (!isset($startIndex)) $startIndex = 0; if (!isset($limit)) $limit = count($this->indexes); for ($i = 0; $i < $limit; ++$i) { if ($startIndex+$i >= count($this->indexes)) break; if ($skipInvalid && ($this->intervals[$this->indexes[$startIndex+$i]]->getDuration() <= 0)) continue; array_push($intervals, $this->intervals[$this->indexes[$startIndex+$i]]->toArray()); } return $intervals; } public function getLength() { return count($this->indexes); } public function getToken() { return $this->token; } public function getFilter() { return $this->filter; } public function getSort() { return $this->sort; } public function updateIndexes() { $this->indexes = array(); for ($i = 0; $i < count($this->intervals); ++$i) $this->intervals[$i]->setIndex($i); //Apply sort $sort_result = $this->sort->apply($this->intervals); //Apply filter for ($i = 0; $i < count($sort_result); ++$i) { if (!$this->filter->toFiltered($this->intervals[$sort_result[$i]])) array_push($this->indexes,$this->intervals[$sort_result[$i]]->getIndex()); } } public function writeBin($handle) { //Magic key ("TTC") fwrite($handle,pack('C3',ord('T'),ord('T'),ord('C'))); //Version fwrite($handle,pack('L',TimeTableCacheObject::$format_version)); //Token for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) fwrite($handle,pack('C',ord($this->token[$i]))); //Modified fwrite($handle,pack('L',$this->isModified)); //Filter $this->filter->writeBin($handle); //Sort $this->sort->writeBin($handle); //Intervals fwrite($handle,pack('L2',count($this->intervals), $this->lastId)); foreach($this->intervals as $interval) $interval->writeBin($handle); //Indexes fwrite($handle,pack('L',count($this->indexes))); foreach($this->indexes as $index) fwrite($handle,pack('L',$index)); } public function loadBin($handle) { //Magic key ("TTC") if (!$res = unpack('C3key',fread($handle,3))) return false; if (($res['key1'] != ord('T')) || ($res['key2'] != ord('T')) || ($res['key3'] != ord('C'))) return false; //Version if (!$res = unpack('Lversion',fread($handle,4))) return false; if (($res['version'] != TimeTableCacheObject::$format_version)) return false; //Token $token = ""; for ($i = 0; $i < TimeTableCacheObject::$token_len; ++$i) { if (!$res = unpack('Ctoken',fread($handle,1))) return false; $token .= chr($res['token']); } $this->token = $token; //Modified if (!$res = unpack('Lmodified',fread($handle,4))) return false; $this->isModified = $res['modified']; //Filter $this->filter->loadBin($handle); //Sort $this->sort->loadBin($handle); //Intervals $res = unpack('L2data',fread($handle,2*4)); $nbIntervals = $res['data1']; $this->lastId = $res['data2']; for ($i = 0; $i < $nbIntervals; ++$i) { $interval = new IntervalCacheObject(-1); $interval->loadBin($handle); array_push($this->intervals, $interval); } //Indexes $res = unpack('Ldata',fread($handle,4)); $nbIndexes = $res['data']; for ($i = 0; $i < $nbIndexes; ++$i) { $res = unpack('Lindex',fread($handle,4)); array_push($this->indexes, $res['index']); } return true; } protected function getRandomToken() { $letters = 'abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; return substr(str_shuffle($letters), 0, TimeTableCacheObject::$token_len); } public function dump() { echo " => TimeTableCacheObject : token = ".$this->token.", nb intervals = ".count($this->intervals).", last id = ".$this->lastId.", nb indexes = ".count($this->indexes).PHP_EOL; echo PHP_EOL; $this->filter->dump(); echo PHP_EOL; $this->sort->dump(); echo PHP_EOL; foreach ($this->intervals as $interval) $interval->dump(); echo PHP_EOL; echo " => Indexes list : "; foreach ($this->indexes as $index) { echo $index.", "; } echo PHP_EOL; } } ?>