RomanNumber::startingAt()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * League.Period (https://period.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Period\Chart;
15
16
use function in_array;
17
use function strtolower;
18
use function strtoupper;
19
20
final class RomanNumber implements LabelGenerator
21
{
22
    public const UPPER = 1;
23
    public const LOWER = 2;
24
    private const CHARACTER_MAP = [
25
        'M'  => 1000, 'CM' => 900,  'D' => 500,
26
        'CD' => 400,   'C' => 100, 'XC' => 90,
27
        'L'  => 50,   'XL' => 40,   'X' => 10,
28
        'IX' => 9,     'V' => 5,   'IV' => 4,
29
        'I'  => 1,
30
    ];
31
32
    /**
33
     * @var DecimalNumber
34
     */
35
    private $decimalNumber;
36
37
    /**
38
     * @var int
39
     */
40
    private $case;
41
42
    /**
43
     * New instance.
44
     */
45 30
    public function __construct(DecimalNumber $decimalNumber, int $case = self::UPPER)
46
    {
47 30
        $this->decimalNumber = $decimalNumber;
48 30
        $this->case = $this->filterLetterCase($case);
49 30
    }
50
51
    /**
52
     * filter letter case state.
53
     */
54 30
    private function filterLetterCase(int $case): int
55
    {
56 30
        if (!in_array($case, [self::UPPER, self::LOWER], true)) {
57 3
            return self::UPPER;
58
        }
59
60 27
        return $case;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66 15
    public function generate(int $nbLabels): \Iterator
67
    {
68 15
        foreach ($this->decimalNumber->generate($nbLabels) as $key => $label) {
69 12
            yield $key => $this->convert($label);
70
        }
71 15
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 6
    public function format(string $label): string
77
    {
78 6
        if (self::UPPER === $this->case) {
79 6
            return strtoupper($label);
80
        }
81
82 3
        return strtolower($label);
83
    }
84
85
    /**
86
     * Convert a integer number into its roman representation.
87
     *
88
     * @see https://stackoverflow.com/a/15023547
89
     */
90 12
    private function convert(string $number): string
91
    {
92 12
        $retVal = '';
93 12
        while ($number > 0) {
94 12
            foreach (self::CHARACTER_MAP as $roman => $int) {
95 12
                if ($number >= $int) {
96 12
                    $number -= $int;
97 12
                    $retVal .= $roman;
98 12
                    break;
99
                }
100
            }
101
        }
102
103 12
        if (self::LOWER === $this->case) {
104 6
            return strtolower($retVal);
105
        }
106
107 6
        return $retVal;
108
    }
109
110
    /**
111
     * Returns the starting Letter.
112
     */
113 3
    public function startingAt(): int
114
    {
115 3
        return $this->decimalNumber->startingAt();
116
    }
117
118
    /**
119
     * Tells whether the roman letter is upper cased.
120
     */
121 3
    public function isUpper(): bool
122
    {
123 3
        return self::UPPER === $this->case;
124
    }
125
126
    /**
127
     * Tells whether the roman letter is lower cased.
128
     */
129 3
    public function isLower(): bool
130
    {
131 3
        return self::LOWER === $this->case;
132
    }
133
134
    /**
135
     * Return an instance with the starting Letter.
136
     *
137
     * This method MUST retain the state of the current instance, and return
138
     * an instance that contains the starting Letter.
139
     */
140 3
    public function startsWith(int $int): self
141
    {
142 3
        $labelGenerator = $this->decimalNumber->startsWith($int);
143 3
        if ($labelGenerator === $this->decimalNumber) {
144 3
            return $this;
145
        }
146
147 3
        $clone = clone $this;
148 3
        $clone->decimalNumber = $labelGenerator;
149
150 3
        return $clone;
151
    }
152
153
    /**
154
     * Return an instance with the new letter case setting.
155
     *
156
     * This method MUST retain the state of the current instance, and return
157
     * an instance that contains the letter case setting.
158
     */
159 6
    public function withLetterCase(int $case): self
160
    {
161 6
        $case = $this->filterLetterCase($case);
162 6
        if ($case === $this->case) {
163 3
            return $this;
164
        }
165
166 6
        $clone = clone $this;
167 6
        $clone->case = $case;
168
169 6
        return $clone;
170
    }
171
}
172