Code Coverage for /src/SciPhp/NdArray/Formatter.php

 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
50 / 50
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Formatter
100.00% covered (success)
100.00%
50 / 50
100.00% covered (success)
100.00%
5 / 5
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
6
 toString
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 traverse
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 formatNumber
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
9
 indent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1 <?php
2
3 declare(strict_types=1);
4
5 namespace SciPhp\NdArray;
6
7 use SciPhp\NdArray;
8 use SciPhp\NumPhp as np;
9
10 /**
11  * NdArray formatter
12  */
13 final class Formatter
14 {
15     /**
16      * @var int
17      */
18     private $maxColSize = 0;
19
20     /**
21      * @var int
22      */
23     private $maxRows = 10;
24
25     /**
26      * @var \SciPhp\NdArray
27      */
28     private $array;
29
30     /**
31      * @var int
32      */
33     private $indent = 0;
34
35     /**
36      * @var string
37      */
38     private $string = '';
39
40     /**
41      * @var bool
42      *
43      * Is there any negative number ?
44      */
45     private $negative = false;
46
47     /**
48      * Set array data and options
49      */
50     public function __construct(NdArray $array)
51     {
52         // truncate
53         if (count($array->data) > $this->maxRows) {
54             $data = $array->data;
55             array_splice(
56                 $data,
57                 intval(ceil($this->maxRows / 2)),
58                 count($data) - $this->maxRows,
59                 [ array_pad([], count($data[0]), '...') ]
60             );
61             $array = np::ar($data);
62         }
63
64         $this->array = $array;
65
66         // Estimate max column size
67         $this->array->walk_recursive(
68             function ($item): void {
69                 $negative = \is_numeric($item) && $item < 0;
70                 $length = strlen("{$item}");
71
72                 if ($negative) {
73                     $length++;
74                     if (! $this->negative) {
75                         $this->negative = true;
76                     }
77                 }
78
79                 if ($length > $this->maxColSize) {
80                     $this->maxColSize = $length;
81                 }
82             }
83         );
84     }
85
86     /**
87      * Stringify an array
88      */
89     public function toString(): string
90     {
91         $this->traverse($this->array->data);
92
93         return "[{$this->string}]\n";
94     }
95
96     /**
97      * Traverse an array and render as string
98      */
99     private function traverse(array $array): void
100     {
101         $count = count($array);
102
103         $this->indent++;
104
105         array_walk(
106             $array,
107             function ($item, $key) use ($count): void {
108                 if (\is_array($item)) {
109                     if ($key > 0) {
110                         $this->string .= PHP_EOL . $this->indent();
111                     }
112
113                     $this->string .= '[';
114                     $this->traverse($item);
115                     $this->string .= ']';
116                 } else {
117                     $this->string .= $this->formatNumber(
118                         $item,
119                         $key === $count - 1
120                     );
121                 }
122             }
123         );
124
125         $this->indent--;
126     }
127
128     /**
129      * Format a number with column sizing
130      *
131      * @param mixed $number
132      */
133     private function formatNumber($number, bool $last): string
134     {
135         switch (true) {
136             case $number === null:
137                 $number = 'null';
138                 break;
139             case $number === true:
140                 $number = 'true';
141                 break;
142             case $number === false:
143                 $number = 'false';
144                 break;
145             default: # Workaround for code coverage
146         }
147
148         $representation = $number;
149
150         // Format positive numbers
151         if (is_numeric($number)) {
152             // Number is not negative, but there are some
153             if ($this->negative && $number >= 0) {
154                 $representation = ' ' . $representation;
155             }
156         }
157
158         return $last
159             ? sprintf("%-{$this->maxColSize}s", $representation)
160             : sprintf("%-{$this->maxColSize}s  ", $representation);
161     }
162
163     /**
164      * Render an indent
165      */
166     private function indent(): string
167     {
168         return str_repeat(' ', $this->indent);
169     }
170 }