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, $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}