Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
89.58% |
86 / 96 |
|
77.27% |
17 / 22 |
CRAP | |
0.00% |
0 / 1 |
Matrix | |
89.58% |
86 / 96 |
|
77.27% |
17 / 22 |
45.09 | |
0.00% |
0 / 1 |
__construct | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
6.03 | |||
getLines | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTop | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTop | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getRenderedColumnSpecs | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setColumnSpecs | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
hasColumnInfo | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAlignInfo | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getMainarg | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
containsFunc | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
inCurlies | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
render | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
renderMML | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
renderMatrix | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
renderLine | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
extractIdentifiers | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
flatDeep | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
reduceCallback | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getIterator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
renderColumnSpecs | |
86.36% |
19 / 22 |
|
0.00% |
0 / 1 |
6.09 | |||
getBoarder | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
renderRowSpec | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | declare( strict_types = 1 ); |
4 | |
5 | namespace MediaWiki\Extension\Math\WikiTexVC\Nodes; |
6 | |
7 | use Generator; |
8 | use InvalidArgumentException; |
9 | |
10 | class Matrix extends TexArray { |
11 | |
12 | /** @var string */ |
13 | private $top; |
14 | private array $lines = []; |
15 | |
16 | private ?TexArray $columnSpecs = null; |
17 | |
18 | private ?string $renderedColumSpecs = null; |
19 | private ?array $boarder = null; |
20 | |
21 | private ?string $alignInfo = null; |
22 | |
23 | public function __construct( string $top, TexArray $mainarg, $rowSpec = null ) { |
24 | foreach ( $mainarg->args as $row ) { |
25 | if ( !$row instanceof TexArray ) { |
26 | throw new InvalidArgumentException( 'Nested arguments have to be type of TexArray' ); |
27 | } |
28 | $this->lines[] = $row->containsFunc( '\hline' ); |
29 | } |
30 | if ( $mainarg instanceof Matrix ) { |
31 | $this->args = $mainarg->args; |
32 | $this->curly = $mainarg->curly; |
33 | } else { |
34 | parent::__construct( ...$mainarg->args ); |
35 | } |
36 | $this->top = $top; |
37 | if ( $rowSpec && count( $this->args ) ) { |
38 | // @phan-suppress-next-line PhanUndeclaredMethod |
39 | $this->first()->setRowSpecs( $rowSpec ); |
40 | } |
41 | } |
42 | |
43 | public function getLines(): array { |
44 | return $this->lines; |
45 | } |
46 | |
47 | /** |
48 | * @return string |
49 | */ |
50 | public function getTop(): string { |
51 | return $this->top; |
52 | } |
53 | |
54 | public function setTop( string $top ): Matrix { |
55 | $this->top = $top; |
56 | return $this; |
57 | } |
58 | |
59 | public function getRenderedColumnSpecs(): string { |
60 | if ( $this->renderedColumSpecs == null ) { |
61 | $this->renderColumnSpecs(); |
62 | } |
63 | return $this->renderedColumSpecs; |
64 | } |
65 | |
66 | public function setColumnSpecs( TexArray $specs ): Matrix { |
67 | $this->columnSpecs = $specs; |
68 | $this->renderedColumSpecs = null; |
69 | $this->alignInfo = null; |
70 | $this->boarder = null; |
71 | return $this; |
72 | } |
73 | |
74 | public function hasColumnInfo(): bool { |
75 | return $this->getRenderedColumnSpecs() !== ''; |
76 | } |
77 | |
78 | public function getAlignInfo(): string { |
79 | if ( $this->alignInfo == null ) { |
80 | $this->renderColumnSpecs(); |
81 | } |
82 | return $this->alignInfo; |
83 | } |
84 | |
85 | /** |
86 | * @return TexArray |
87 | */ |
88 | public function getMainarg(): TexArray { |
89 | return $this; |
90 | } |
91 | |
92 | public function containsFunc( $target, $args = null ) { |
93 | if ( $args == null ) { |
94 | $args = [ |
95 | '\\begin{' . $this->top . '}', |
96 | '\\end{' . $this->top . '}', |
97 | ...$this->args, |
98 | ]; |
99 | } |
100 | return parent::containsFunc( $target, $args ); |
101 | } |
102 | |
103 | public function inCurlies() { |
104 | return $this->render(); |
105 | } |
106 | |
107 | public function render() { |
108 | $colSpecs = $this->columnSpecs !== null ? $this->columnSpecs->render() : ''; |
109 | return '{\\begin{' . $this->top . '}' . $colSpecs . $this->renderMatrix( $this ) . '\\end{' . |
110 | $this->top . '}}'; |
111 | } |
112 | |
113 | public function renderMML( $arguments = [], $state = [] ): string { |
114 | return $this->parseToMML( $this->getTop(), $arguments, null ); |
115 | } |
116 | |
117 | private function renderMatrix( Matrix $matrix ): string { |
118 | $renderedLines = ''; |
119 | for ( $i = 0; $i < count( $matrix->args ); $i++ ) { |
120 | $renderedLines .= self::renderLine( $matrix->args[$i] ); |
121 | if ( $i < count( $matrix->args ) - 1 ) { |
122 | // @phan-suppress-next-line PhanTypeMismatchArgumentSuperType |
123 | $renderedLines .= $matrix->renderRowSpec( $matrix->args[$i] ); |
124 | } |
125 | } |
126 | return $renderedLines; |
127 | } |
128 | |
129 | private static function renderLine( $l ) { |
130 | $mapped = array_map( static function ( $x ){ |
131 | return $x->render(); |
132 | }, $l->args ); |
133 | return implode( '&', $mapped ); |
134 | } |
135 | |
136 | public function extractIdentifiers( $args = null ) { |
137 | if ( $args == null ) { |
138 | $args = $this->args; |
139 | } |
140 | |
141 | $mapped = array_map( function ( $a ){ |
142 | return array_map( function ( $p ){ |
143 | return parent::extractIdentifiers( $p->args ); |
144 | }, $a->args ); |
145 | }, $args ); |
146 | |
147 | return self::flatDeep( $mapped ); |
148 | } |
149 | |
150 | private static function flatDeep( $a ) { |
151 | if ( !is_array( $a ) ) { |
152 | return $a; |
153 | } |
154 | |
155 | $reduced = array_reduce( $a, [ self::class, 'reduceCallback' ], [] ); |
156 | return $reduced; |
157 | } |
158 | |
159 | private static function reduceCallback( $acc, $val ) { |
160 | // Casting to array if output is string, this is required for array_merge function. |
161 | $fld = self::flatDeep( $val ); |
162 | if ( !is_array( $fld ) ) { |
163 | $fld = [ $fld ]; |
164 | } |
165 | return array_merge( $acc, $fld ); |
166 | } |
167 | |
168 | /** |
169 | * @suppress PhanTypeMismatchReturn |
170 | * @return Generator<TexArray> |
171 | */ |
172 | public function getIterator(): Generator { |
173 | return parent::getIterator(); |
174 | } |
175 | |
176 | /** |
177 | * @return void |
178 | */ |
179 | public function renderColumnSpecs(): void { |
180 | $colSpecs = $this->columnSpecs ?? new TexArray(); |
181 | $this->renderedColumSpecs = trim( $colSpecs->render(), "{} \n\r\t\v\x00" ); |
182 | $align = ''; |
183 | $colNo = 0; |
184 | $this->boarder = []; |
185 | foreach ( str_split( $this->renderedColumSpecs ) as $chr ) { |
186 | switch ( $chr ) { |
187 | case '|': |
188 | $this->boarder[$colNo] = true; |
189 | break; |
190 | case 'r': |
191 | $align .= 'right '; |
192 | $colNo++; |
193 | break; |
194 | case 'l': |
195 | $align .= 'left '; |
196 | $colNo++; |
197 | break; |
198 | case 'c': |
199 | $colNo++; |
200 | $align .= 'center '; |
201 | break; |
202 | } |
203 | } |
204 | $this->alignInfo = $align; |
205 | } |
206 | |
207 | public function getBoarder(): array { |
208 | if ( $this->boarder == null ) { |
209 | $this->renderColumnSpecs(); |
210 | } |
211 | return $this->boarder; |
212 | } |
213 | |
214 | public function renderRowSpec( TexArray $row ): string { |
215 | $rowSpecs = ''; |
216 | if ( $row->getRowSpecs() !== null ) { |
217 | $rowSpecs = $row->getRowSpecs()->render(); |
218 | } |
219 | return '\\\\' . $rowSpecs; |
220 | } |
221 | |
222 | } |