1<?php
2
3/**
4 * This file is part of PHP K-Means
5 *
6 * Copyright (c) 2014 Benjamin Delespierre
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is furnished
13 * to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27namespace KMeans;
28
29class Point implements \ArrayAccess
30{
31    protected $space;
32    protected $dimention;
33    protected $coordinates;
34
35    public function __construct(Space $space, array $coordinates)
36    {
37        $this->space       = $space;
38        $this->dimention   = $space->getDimention();
39        $this->coordinates = $coordinates;
40    }
41
42    public function toArray(): array
43    {
44        return [
45            'coordinates' => $this->coordinates,
46            'data' => isset($this->space[$this]) ? $this->space[$this] : null,
47        ];
48    }
49
50    public function getDistanceWith(self $point, bool $precise = true): float
51    {
52        if ($point->space !== $this->space) {
53            throw new \LogicException("can only calculate distances from points in the same space");
54        }
55
56        $distance = 0;
57        for ($n = 0; $n < $this->dimention; $n++) {
58            $difference = $this->coordinates[$n] - $point->coordinates[$n];
59            $distance  += $difference * $difference;
60        }
61
62        return $precise ? sqrt($distance) : $distance;
63    }
64
65    public function getClosest(iterable $points): ?Point
66    {
67        $minDistance = PHP_INT_MAX;
68        $minPoint = null;
69        foreach ($points as $point) {
70            $distance = $this->getDistanceWith($point, false);
71
72            if ($distance < $minDistance) {
73                $minDistance = $distance;
74                $minPoint    = $point;
75            }
76        }
77
78        return $minPoint;
79    }
80
81    public function belongsTo(Space $space): bool
82    {
83        return $this->space === $space;
84    }
85
86    public function getSpace(): Space
87    {
88        return $this->space;
89    }
90
91    public function getCoordinates(): array
92    {
93        return $this->coordinates;
94    }
95
96    public function offsetExists($offset): bool
97    {
98        return isset($this->coordinates[$offset]);
99    }
100
101    public function offsetGet($offset)
102    {
103        return $this->coordinates[$offset];
104    }
105
106    public function offsetSet($offset, $value): void
107    {
108        $this->coordinates[$offset] = $value;
109    }
110
111    public function offsetUnset($offset): void
112    {
113        unset($this->coordinates[$offset]);
114    }
115}
116