1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\Math\Sampler;
38
39use Hoa\Consistency;
40use Hoa\Math;
41use Hoa\Zformat;
42
43/**
44 * Class \Hoa\Math\Sampler.
45 *
46 * Generic sampler.
47 *
48 * @copyright  Copyright © 2007-2017 Hoa community
49 * @license    New BSD License
50 */
51abstract class Sampler implements Zformat\Parameterizable
52{
53    /**
54     * Parameters.
55     *
56     * @var \Hoa\Zformat\Parameter
57     */
58    protected $_parameters = null;
59
60
61
62    /**
63     * Construct an abstract sampler.
64     *
65     * @param   array  $parameters    Parameters.
66     */
67    public function __construct(array $parameters = [])
68    {
69        $this->_parameters = new Zformat\Parameter(
70            __CLASS__,
71            [],
72            [
73                'integer.min' => -16,
74                'integer.max' => 15,
75                'float.min'   => -128.0,
76                'float.max'   => 127.0
77            ]
78        );
79        $this->_parameters->setParameters($parameters);
80
81        if (null === $this->_parameters->getParameter('integer.min')) {
82            $this->_parameters->setParameter('integer.min', PHP_INT_MIN);
83        }
84
85        if (null === $this->_parameters->getParameter('integer.max')) {
86            $this->_parameters->setParameter('integer.max', PHP_INT_MAX);
87        }
88
89        if (null === $this->_parameters->getParameter('float.min')) {
90            $this->_parameters->setParameter('float.min', PHP_INT_MIN);
91        }
92
93        if (null === $this->_parameters->getParameter('float.max')) {
94            $this->_parameters->setParameter('float.max', PHP_INT_MAX);
95        }
96
97        $this->construct();
98
99        return;
100    }
101
102    /**
103     * Construct.
104     *
105     * @return  void
106     */
107    public function construct()
108    {
109        return;
110    }
111
112    /**
113     * Get parameters.
114     *
115     * @return  \Hoa\Zformat\Parameter
116     */
117    public function getParameters()
118    {
119        return $this->_parameters;
120    }
121
122    /**
123     * Generate a discrete uniform distribution.
124     *
125     * @param   int    $lower       Lower bound value.
126     * @param   int    $upper       Upper bound value.
127     * @param   array  &$exclude    Excluded values.
128     * @return  int
129     */
130    public function getInteger(
131        $lower          = null,
132        $upper          = null,
133        array &$exclude = null
134    ) {
135        if (null === $lower) {
136            $lower = $this->_parameters->getParameter('integer.min');
137        }
138
139        if (null === $upper) {
140            $upper = $this->_parameters->getParameter('integer.max');
141        }
142
143        if (null === $exclude) {
144            if ($lower > $upper) {
145                throw new Math\Exception(
146                    'Unexpected values, integer %d should be lower than %d',
147                    0,
148                    [$lower, $upper]
149                );
150            }
151
152            return $this->_getInteger($lower, $upper);
153        }
154
155
156        sort($exclude);
157
158        $upper -= count($exclude);
159
160        if ($lower > $upper) {
161            throw new Math\Exception(
162                'Unexpected values, integer %d should be lower than %d',
163                1,
164                [$lower, $upper]
165            );
166        }
167
168        $sampled = $this->_getInteger($lower, $upper);
169
170        foreach ($exclude as $e) {
171            if ($sampled >= $e) {
172                ++$sampled;
173            } else {
174                break;
175            }
176        }
177
178        return $sampled;
179    }
180
181    /**
182     * Generate a discrete uniform distribution.
183     *
184     * @param   int  $lower    Lower bound value.
185     * @param   int  $upper    Upper bound value.
186     * @return  int
187     */
188    abstract protected function _getInteger($lower, $upper);
189
190    /**
191     * Generate a continuous uniform distribution.
192     *
193     * @param   float   $lower    Lower bound value.
194     * @param   float   $upper    Upper bound value.
195     * @return  float
196     */
197    public function getFloat($lower = null, $upper = null)
198    {
199        if (null === $lower) {
200            $lower = $this->_parameters->getParameter('float.min');
201        }
202            /*
203            $lower = true === S_32\BITS
204                         ? -3.4028235e38 + 1
205                         : -1.7976931348623157e308 + 1;
206            */
207
208        if (null === $upper) {
209            $upper = $this->_parameters->getParameter('float.max');
210        }
211            /*
212            $upper = true === S_32\BITS
213                         ? 3.4028235e38 - 1
214                         : 1.7976931348623157e308 - 1;
215            */
216
217        if ($lower > $upper) {
218            throw new Math\Exception(
219                'Unexpected values, float %f should be lower than %f',
220                2, [$lower, $upper]);
221        }
222
223        return $this->_getFloat($lower, $upper);
224    }
225
226    /**
227     * Generate a continuous uniform distribution.
228     *
229     * @param   float      $lower    Lower bound value.
230     * @param   float      $upper    Upper bound value.
231     * @return  float
232     */
233    abstract protected function _getFloat($lower, $upper);
234
235    /**
236     * Get an exclude set.
237     *
238     * @return  array
239     */
240    public function getExcludeSet()
241    {
242        return [];
243    }
244}
245
246/**
247 * Flex entity.
248 */
249Consistency::flexEntity('Hoa\Math\Sampler\Sampler');
250