Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.58% covered (warning)
89.58%
86 / 96
77.27% covered (warning)
77.27%
17 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
Matrix
89.58% covered (warning)
89.58%
86 / 96
77.27% covered (warning)
77.27%
17 / 22
45.09
0.00% covered (danger)
0.00%
0 / 1
 __construct
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
6.03
 getLines
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTop
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTop
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getRenderedColumnSpecs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setColumnSpecs
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 hasColumnInfo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAlignInfo
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getMainarg
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 containsFunc
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 inCurlies
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 renderMML
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderMatrix
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 renderLine
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 extractIdentifiers
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 flatDeep
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 reduceCallback
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderColumnSpecs
86.36% covered (warning)
86.36%
19 / 22
0.00% covered (danger)
0.00%
0 / 1
6.09
 getBoarder
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 renderRowSpec
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare( strict_types = 1 );
4
5namespace MediaWiki\Extension\Math\WikiTexVC\Nodes;
6
7use Generator;
8use InvalidArgumentException;
9
10class 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, ?LengthSpec $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    /** @inheritDoc */
93    public function containsFunc( $target, $args = null ) {
94        if ( $args == null ) {
95            $args = [
96                '\\begin{' . $this->top . '}',
97                '\\end{' . $this->top . '}',
98                ...$this->args,
99            ];
100        }
101        return parent::containsFunc( $target, $args );
102    }
103
104    /** @inheritDoc */
105    public function inCurlies() {
106        return $this->render();
107    }
108
109    /** @inheritDoc */
110    public function render() {
111        $colSpecs = $this->columnSpecs !== null ? $this->columnSpecs->render() : '';
112        return '{\\begin{' . $this->top . '}' . $colSpecs . $this->renderMatrix( $this ) . '\\end{' .
113            $this->top . '}}';
114    }
115
116    /** @inheritDoc */
117    public function renderMML( $arguments = [], &$state = [] ): string {
118        return $this->parseToMML( $this->getTop(), $arguments, null );
119    }
120
121    private function renderMatrix( Matrix $matrix ): string {
122        $renderedLines = '';
123        for ( $i = 0; $i < count( $matrix->args ); $i++ ) {
124            $renderedLines .= self::renderLine( $matrix->args[$i] );
125            if ( $i < count( $matrix->args ) - 1 ) {
126                // @phan-suppress-next-line PhanTypeMismatchArgumentSuperType
127                $renderedLines .= $matrix->renderRowSpec( $matrix->args[$i] );
128            }
129        }
130        return $renderedLines;
131    }
132
133    private static function renderLine( TexNode $l ): string {
134        $mapped = array_map( static function ( $x ){
135            return $x->render();
136        }, $l->args );
137        return implode( '&', $mapped );
138    }
139
140    /** @inheritDoc */
141    public function extractIdentifiers( $args = null ) {
142        if ( $args == null ) {
143            $args = $this->args;
144        }
145
146        $mapped = array_map( function ( $a ){
147            return array_map( function ( $p ){
148                return parent::extractIdentifiers( $p->args );
149            }, $a->args );
150        }, $args );
151
152        return self::flatDeep( $mapped );
153    }
154
155    /**
156     * @param array|string $a
157     * @return array|string
158     */
159    private static function flatDeep( $a ) {
160        if ( !is_array( $a ) ) {
161            return $a;
162        }
163
164        $reduced = array_reduce( $a, [ self::class, 'reduceCallback' ], [] );
165        return $reduced;
166    }
167
168    /**
169     * @param array $acc
170     * @param array|string $val
171     * @return array
172     */
173    private static function reduceCallback( $acc, $val ) {
174        // Casting to array if output is string, this is required for array_merge function.
175        $fld = self::flatDeep( $val );
176        if ( !is_array( $fld ) ) {
177            $fld = [ $fld ];
178        }
179        return array_merge( $acc, $fld );
180    }
181
182    /**
183     * @suppress PhanTypeMismatchReturn
184     * @return Generator<TexArray>
185     */
186    public function getIterator(): Generator {
187        return parent::getIterator();
188    }
189
190    /**
191     * @return void
192     */
193    public function renderColumnSpecs(): void {
194        $colSpecs = $this->columnSpecs ?? new TexArray();
195        $this->renderedColumSpecs = trim( $colSpecs->render(), "{} \n\r\t\v\x00" );
196        $align = '';
197        $colNo = 0;
198        $this->boarder = [];
199        foreach ( str_split( $this->renderedColumSpecs ) as $chr ) {
200            switch ( $chr ) {
201                case '|':
202                    $this->boarder[$colNo] = true;
203                    break;
204                case 'r':
205                    $align .= 'right ';
206                    $colNo++;
207                    break;
208                case 'l':
209                    $align .= 'left ';
210                    $colNo++;
211                    break;
212                case 'c':
213                    $colNo++;
214                    $align .= 'center ';
215                    break;
216            }
217        }
218        $this->alignInfo = $align;
219    }
220
221    public function getBoarder(): array {
222        if ( $this->boarder == null ) {
223            $this->renderColumnSpecs();
224        }
225        return $this->boarder;
226    }
227
228    public function renderRowSpec( TexArray $row ): string {
229        $rowSpecs = '';
230        if ( $row->getRowSpecs() !== null ) {
231            $rowSpecs = $row->getRowSpecs()->render();
232        }
233        return '\\\\' . $rowSpecs;
234    }
235
236}