Completed
Push — master ( caad92...bf4c7a )
by Peter
05:39
created

IntervalType::eq()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php 
2
/**
3
 * GpsLab component.
4
 *
5
 * @author    Peter Gribanov <[email protected]>
6
 * @copyright Copyright (c) 2016, Peter Gribanov
7
 * @license   http://opensource.org/licenses/MIT
8
 */
9
10
namespace GpsLab\Component\Interval;
11
12
use GpsLab\Component\Interval\Exception\IncorrectIntervalTypeException;
13
use GpsLab\Component\Interval\Exception\InvalidIntervalFormatException;
14
15
/**
16
 * @link https://en.wikipedia.org/wiki/Interval_(mathematics)
17
 */
18
final class IntervalType
19
{
20
    /**
21
     * @var int
22
     */
23
    const TYPE_START_EXCLUDED = 1;
24
25
    /**
26
     * @var int
27
     */
28
    const TYPE_END_EXCLUDED = 2;
29
30
    /**
31
     * [a, b]
32
     *
33
     * @var int
34
     */
35
    const TYPE_CLOSED = 0;
36
37
    /**
38
     * [a, b)
39
     *
40
     * @var int
41
     */
42
    const TYPE_HALF_CLOSED = self::TYPE_CLOSED | self::TYPE_END_EXCLUDED;
43
44
    /**
45
     * (a, b]
46
     *
47
     * @var int
48
     */
49
    const TYPE_HALF_OPEN = self::TYPE_CLOSED | self::TYPE_START_EXCLUDED;
50
51
    /**
52
     * (a, b)
53
     *
54
     * @var int
55
     */
56
    const TYPE_OPEN = self::TYPE_CLOSED | self::TYPE_START_EXCLUDED | self::TYPE_END_EXCLUDED;
57
58
    /**
59
     * @var string
60
     */
61
    const REGEXP = '/^
62
        (?<start>\(|\[) # start type char
63
        \s*
64
        [^\s]+          # start point
65
        \s*,\s*         # separator
66
        [^\s]+          # end point
67
        \s*
68
        (?<end>\)|\])   # end type char
69
    $/x';
70
71
    /**
72
     * @var IntervalType[]
73
     */
74
    private static $instances = [];
75
76
    /**
77
     * @var string
78
     */
79
    private $type = '';
80
81
    /**
82
     * @var array
83
     */
84
    private $formats = [
85
        self::TYPE_CLOSED => '[%s, %s]',
86
        self::TYPE_OPEN => '(%s, %s)',
87
        self::TYPE_HALF_CLOSED => '[%s, %s)',
88
        self::TYPE_HALF_OPEN => '(%s, %s]',
89
    ];
90
91
    /**
92
     * @param string $type
93
     */
94
    private function __construct($type)
95
    {
96
        $this->type = $type;
97
    }
98
    /**
99
     * @return int[]
100
     */
101
    public static function getAvailableValues()
102
    {
103
        return [
104
            self::TYPE_CLOSED,
105
            self::TYPE_OPEN,
106
            self::TYPE_HALF_CLOSED,
107
            self::TYPE_HALF_OPEN,
108
        ];
109
    }
110
111
    /**
112
     * @param int $value
113
     *
114
     * @return bool
115
     */
116
    public static function isValueSupported($value)
117
    {
118
        return in_array($value, self::getAvailableValues());
119
    }
120
121
    /**
122
     * @return int
123
     */
124
    public function type()
125
    {
126
        return $this->type;
127
    }
128
129
    /**
130
     * @param string $value
131
     *
132
     * @return self
133
     */
134
    private static function safe($value)
135
    {
136
        // limitation of count object instances
137
        if (!isset(self::$instances[$value])) {
138
            self::$instances[$value] = new self($value);
139
        }
140
141
        return self::$instances[$value];
142
    }
143
144
    /**
145
     * @param int $value
146
     *
147
     * @return self
148
     */
149
    public static function create($value)
150
    {
151
        if (!self::isValueSupported($value)) {
152
            throw IncorrectIntervalTypeException::create($value);
153
        }
154
155
        return self::safe($value);
156
    }
157
158
    /**
159
     * @return self
160
     */
161
    public static function closed()
162
    {
163
        return self::safe(self::TYPE_CLOSED);
164
    }
165
166
    /**
167
     * @return self
168
     */
169
    public static function halfClosed()
170
    {
171
        return self::safe(self::TYPE_HALF_CLOSED);
172
    }
173
174
    /**
175
     * @return self
176
     */
177
    public static function halfOpen()
178
    {
179
        return self::safe(self::TYPE_HALF_OPEN);
180
    }
181
182
    /**
183
     * @return self
184
     */
185
    public static function open()
186
    {
187
        return self::safe(self::TYPE_OPEN);
188
    }
189
190
    /**
191
     * Create interval type from string.
192
     *
193
     * All types format:
194
     *   [a, b]
195
     *   (a, b]
196
     *   [a, b)
197
     *   (a, b)
198
     *
199
     * Spaces are ignored in format.
200
     *
201
     * @param string $string
202
     *
203
     * @return self
204
     */
205
    public static function fromString($string)
206
    {
207
        if (!preg_match(self::REGEXP, $string, $match)) {
208
            throw InvalidIntervalFormatException::create('[a, b]', $string);
209
        }
210
211
        $type = IntervalType::TYPE_CLOSED;
212
213
        if ($match['start'] == '(') {
214
            $type |= IntervalType::TYPE_START_EXCLUDED;
215
        }
216
217
        if ($match['end'] == ')') {
218
            $type |= IntervalType::TYPE_END_EXCLUDED;
219
        }
220
221
        return self::create($type);
222
    }
223
224
    /**
225
     * @param IntervalType $type
226
     *
227
     * @return bool
228
     */
229
    public function eq(IntervalType $type)
230
    {
231
        return $this->type() == $type->type();
232
    }
233
234
    /**
235
     * @return bool
236
     */
237
    public function startExcluded()
238
    {
239
        return ($this->type() & self::TYPE_START_EXCLUDED) == self::TYPE_START_EXCLUDED;
240
    }
241
242
    /**
243
     * @return bool
244
     */
245
    public function endExcluded()
246
    {
247
        return ($this->type() & self::TYPE_END_EXCLUDED) == self::TYPE_END_EXCLUDED;
248
    }
249
250
    /**
251
     * @param IntervalInterface $interval
252
     *
253
     * @return string
254
     */
255
    public function getReadable(IntervalInterface $interval)
256
    {
257
        return $this->format((string)$interval->startPoint(), (string)$interval->endPoint());
258
    }
259
260
    /**
261
     * @return string
262
     */
263
    public function __toString()
264
    {
265
        return $this->format('a', 'b');
266
    }
267
268
    /**
269
     * @param string $start
270
     * @param string $end
271
     *
272
     * @return string
273
     */
274
    private function format($start, $end)
275
    {
276
        return sprintf($this->formats[$this->type()], $start, $end);
277
    }
278
}
279