From 8b6272106872012a582f0eef08ca29620cfcf425 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Fri, 15 May 2020 22:46:19 +0200 Subject: [PATCH] Initial commit, console stuff works --- .gitignore | 3 + composer.json | 17 ++++++ examples/basic.php | 25 ++++++++ phpunit.xml | 22 +++++++ src/Buffer.php | 71 ++++++++++++++++++++++ src/Output/TerminalHistogram.php | 100 +++++++++++++++++++++++++++++++ tests/BufferTest.php | 42 +++++++++++++ 7 files changed, 280 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 examples/basic.php create mode 100644 phpunit.xml create mode 100644 src/Buffer.php create mode 100644 src/Output/TerminalHistogram.php create mode 100644 tests/BufferTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9c6994 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/composer.lock +/vendor +/.* diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5de988e --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name": "noccylabs/histogram", + "description": "Useful histograms everywhere", + "type": "library", + "license": "GPL-3.0-OR-LATER", + "authors": [ + { + "name": "Christopher Vagnetoft", + "email": "cvagnetoft@gmail.com" + } + ], + "autoload": { + "psr-4": { + "NoccyLabs\\Histogram\\": "src/" + } + } +} \ No newline at end of file diff --git a/examples/basic.php b/examples/basic.php new file mode 100644 index 0000000..4bb6fb9 --- /dev/null +++ b/examples/basic.php @@ -0,0 +1,25 @@ +getBuffer(); + +for ($n = 0; $n<40; $n++) { + $s = sin($n/5) + 1; + $buffer->pushSamples($s); +} + +//$buffer->pushSamples(0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.5, 0.7, 0.9, 1.0, 1.1, 1.5, 1.0, 0.9, 0.7, null, 0.4, null, 0.3); + + + +echo $histogram . "\n\n"; + +echo join("\n", $histogram->getMultiHistogram(3)) . "\n\n"; + +$histogram->setMax(2.0); +echo join("\n", $histogram->getMultiHistogram(3)) . "\n\n"; + +$histogram->setMax(5.0); +echo join("\n", $histogram->getMultiHistogram(3)) . "\n\n"; diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..206ad44 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,22 @@ + + + + + tests + + + + + + src + + + diff --git a/src/Buffer.php b/src/Buffer.php new file mode 100644 index 0000000..3887f06 --- /dev/null +++ b/src/Buffer.php @@ -0,0 +1,71 @@ +maxSamples = max(1, $maxSamples); + } + + public function count(): int + { + return count($this->samples); + } + + public function pushSamples(...$samples) + { + array_push($this->samples, ...$samples); + $this->trim(); + } + + public function getSampleMax(): float + { + return (float)max($this->samples); + } + + public function getSampleMin(): float + { + return (float)min($this->samples); + } + + public function getSampleAverage(): ?float + { + $clean = array_filter($this->samples, function ($s) { return $s !== null; }); + if (count($clean) === 0) + return null; + + $total = array_sum($clean); + $average = $total / count($clean); + return (float)$average; + } + + public function getMappedSamples(float $inMin, float $inMax, float $outMin=0.0, float $outMax=1.0, ?int $numSamples=null) + { + $map = function ($v) use ($inMin, $inMax, $outMin, $outMax) { + if ($v === null) return null; + $v = max($inMin, min($inMax, $v)); + return (($v - $inMin) / ($inMax - $inMin)) * ($outMax - $outMin) + $outMin; + }; + return array_map($map, $numSamples?array_slice($this->samples,-$numSamples,$numSamples):$this->samples); + } + + public function getSamples(?int $numSamples=null) + { + return $numSamples?array_slice($this->samples,-$numSamples,$numSamples):$this->samples; + } + + private function trim() + { + while (count($this->samples) > $this->maxSamples) + array_shift($this->samples); + } +} \ No newline at end of file diff --git a/src/Output/TerminalHistogram.php b/src/Output/TerminalHistogram.php new file mode 100644 index 0000000..ea1aa4a --- /dev/null +++ b/src/Output/TerminalHistogram.php @@ -0,0 +1,100 @@ +width = max(1, min($width, self::$MaxWidth)); + $this->buffer = $buffer ?? new Buffer(50); + } + + public function getBuffer(): Buffer + { + return $this->buffer; + } + + public function setMax(?float $max) + { + $this->max = $max; + } + + public function getHistogram() + { + $max = ($this->max === null)?1.0:$this->max; + $samples = $this->buffer->getMappedSamples(0.0, $max, 0, 8, $this->width); + $output = null; + if (count($samples) < $this->width) { + $output .= str_repeat(" ", $this->width - count($samples)); + } + foreach ($samples as $sample) { + if ($sample === null) { + $output .= "\u{2a09}"; + } else { + $output .= $this->bars[floor($sample)]; + } + } + return $output; + } + + public function getMultiHistogram(int $lines): array + { + $max = ($this->max === null)?1.0:$this->max; + $samples = $this->buffer->getMappedSamples(0.0, $max, 0, 8*$lines, $this->width); + $result = []; + $topmin = 8*($lines-1); + for ($line = 0; $line < $lines; $line++) { + $rowmin = $topmin - ($line * 8); + $output = null; + if (count($samples) < $this->width) { + $output .= str_repeat(" ", $this->width - count($samples)); + } + foreach ($samples as $sample) { + if ($sample === null) { + $output .= "\u{2a09}"; + } else { + $remain = $sample - $rowmin; + if ($remain <= 0) { + $output.= " "; + } else { + $sample = min(8, $remain); + $output .= $this->bars[floor($sample)]; + } + } + } + $result[] = $output; + } + return $result; + } + + public function __toString() + { + return $this->getHistogram(); + } + +} \ No newline at end of file diff --git a/tests/BufferTest.php b/tests/BufferTest.php new file mode 100644 index 0000000..a137ae0 --- /dev/null +++ b/tests/BufferTest.php @@ -0,0 +1,42 @@ +pushSamples(1, 2, 3); + + $this->assertEquals(2, count($buffer)); + $this->assertEquals([2, 3], $buffer->getSamples()); + } + + public function testGettingSampleMaxMinValues() + { + $buffer = new Buffer(5); + $buffer->pushSamples(10, 20, 30, 40, 50); + + $this->assertEquals(10, $buffer->getSampleMin()); + $this->assertEquals(30, $buffer->getSampleAverage()); + $this->assertEquals(50, $buffer->getSampleMax()); + } + + public function testMappingTheBuffer() + { + $buffer = new Buffer(5); + $buffer->pushSamples(10, 20, 30, 40, 50); + + $mapped = $buffer->getMappedSamples(0, 1000, 0, 100); + $this->assertEquals([1, 2, 3, 4, 5], $mapped); + + $mapped = $buffer->getMappedSamples(0, 100, 500, 600); + $this->assertEquals([510, 520, 530, 540, 550], $mapped); + + $mapped = $buffer->getMappedSamples(0, 100); + $this->assertEquals([0.1, 0.2, 0.3, 0.4, 0.5], $mapped); + } +} \ No newline at end of file