Skip to content

Commit e98017a

Browse files
committed
random: Add Random\IntervalBoundary
1 parent 31805d9 commit e98017a

File tree

5 files changed

+128
-12
lines changed

5 files changed

+128
-12
lines changed

ext/random/php_random.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,11 @@ extern PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
270270
extern PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
271271
extern PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
272272
extern PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
273+
273274
extern PHPAPI zend_class_entry *random_ce_Random_Randomizer;
274275

276+
extern PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
277+
275278
static inline php_random_engine *php_random_engine_from_obj(zend_object *object) {
276279
return (php_random_engine *)((char *)(object) - XtOffsetOf(php_random_engine, std));
277280
}

ext/random/random.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "php.h"
2828

29+
#include "Zend/zend_enum.h"
2930
#include "Zend/zend_exceptions.h"
3031

3132
#include "php_random.h"
@@ -76,6 +77,8 @@ PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
7677

7778
PHPAPI zend_class_entry *random_ce_Random_Randomizer;
7879

80+
PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
81+
7982
PHPAPI zend_class_entry *random_ce_Random_RandomError;
8083
PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
8184
PHPAPI zend_class_entry *random_ce_Random_RandomException;
@@ -896,6 +899,9 @@ PHP_MINIT_FUNCTION(random)
896899
random_randomizer_object_handlers.free_obj = randomizer_free_obj;
897900
random_randomizer_object_handlers.clone_obj = NULL;
898901

902+
/* Random\IntervalBoundary */
903+
random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
904+
899905
register_random_symbols(module_number);
900906

901907
return SUCCESS;

ext/random/random.stub.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function nextInt(): int {}
135135

136136
public function nextFloat(): float {}
137137

138-
public function getFloat(float $min, float $max): float {}
138+
public function getFloat(float $min, float $max, IntervalBoundary $boundary = IntervalBoundary::ClosedOpen): float {}
139139

140140
public function getInt(int $min, int $max): int {}
141141

@@ -154,6 +154,13 @@ public function __serialize(): array {}
154154
public function __unserialize(array $data): void {}
155155
}
156156

157+
enum IntervalBoundary {
158+
case ClosedOpen;
159+
case ClosedClosed;
160+
case OpenClosed;
161+
case OpenOpen;
162+
}
163+
157164
/**
158165
* @strict-properties
159166
*/

ext/random/random_arginfo.h

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/random/randomizer.c

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ext/standard/php_array.h"
2525
#include "ext/standard/php_string.h"
2626

27+
#include "Zend/zend_enum.h"
2728
#include "Zend/zend_exceptions.h"
2829

2930
static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) {
@@ -160,6 +161,58 @@ static uint64_t getFloat_ceilint(double a, double b, double g)
160161
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
161162
}
162163

164+
static double getFloat_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max)
165+
{
166+
double g = getFloat_gamma(min, max);
167+
uint64_t hi = getFloat_ceilint(min, max, g);
168+
uint64_t k = algo->range(status, 1, hi);
169+
170+
if (fabs(min) <= fabs(max)) {
171+
return k == hi ? min : max - k * g;
172+
} else {
173+
return min + (k - 1) * g;
174+
}
175+
}
176+
177+
static double getFloat_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
178+
{
179+
double g = getFloat_gamma(min, max);
180+
uint64_t hi = getFloat_ceilint(min, max, g);
181+
uint64_t k = algo->range(status, 0, hi);
182+
183+
if (fabs(min) <= fabs(max)) {
184+
return k == hi ? min : max - k * g;
185+
} else {
186+
return k == hi ? max : min + k * g;
187+
}
188+
}
189+
190+
static double getFloat_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
191+
{
192+
double g = getFloat_gamma(min, max);
193+
uint64_t hi = getFloat_ceilint(min, max, g);
194+
uint64_t k = algo->range(status, 0, hi - 1);
195+
196+
if (fabs(min) <= fabs(max)) {
197+
return max - k * g;
198+
} else {
199+
return k == (hi - 1) ? max : min + (k + 1) * g;
200+
}
201+
}
202+
203+
static double getFloat_open_open(const php_random_algo *algo, php_random_status *status, double min, double max)
204+
{
205+
double g = getFloat_gamma(min, max);
206+
uint64_t hi = getFloat_ceilint(min, max, g);
207+
uint64_t k = algo->range(status, 1, hi - 1);
208+
209+
if (fabs(min) <= fabs(max)) {
210+
return max - k * g;
211+
} else {
212+
return min + k * g;
213+
}
214+
}
215+
163216
/* {{{ Generates a random float within [min, max).
164217
*
165218
* The algorithm used is the γ-section algorithm as published in:
@@ -172,30 +225,56 @@ PHP_METHOD(Random_Randomizer, getFloat)
172225
{
173226
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
174227
double min, max;
228+
zend_object *bounds = NULL;
229+
zend_string *bounds_name = zend_string_init("ClosedOpen", strlen("ClosedOpen"), 1);
175230

176-
ZEND_PARSE_PARAMETERS_START(2, 2)
231+
ZEND_PARSE_PARAMETERS_START(2, 3)
177232
Z_PARAM_DOUBLE(min)
178233
Z_PARAM_DOUBLE(max)
234+
Z_PARAM_OPTIONAL
235+
Z_PARAM_OBJ_OF_CLASS(bounds, random_ce_Random_IntervalBoundary);
179236
ZEND_PARSE_PARAMETERS_END();
180237

181238
#ifndef __STDC_IEC_559__
182239
zend_throw_exception(random_ce_Random_RandomException, "The getFloat() method requires the underlying 'double' representation to be IEEE-754.", 0);
183240
RETURN_THROWS();
184241
#endif
185242

186-
if (UNEXPECTED(max < min)) {
187-
zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
188-
RETURN_THROWS();
243+
if (bounds) {
244+
zval *case_name = zend_enum_fetch_case_name(bounds);
245+
bounds_name = Z_STR_P(case_name);
189246
}
247+
248+
if (zend_string_equals_literal(bounds_name, "ClosedOpen")) {
249+
if (UNEXPECTED(max <= min)) {
250+
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
251+
RETURN_THROWS();
252+
}
190253

191-
double g = getFloat_gamma(min, max);
192-
uint64_t hi = getFloat_ceilint(min, max, g);
193-
uint64_t k = randomizer->algo->range(randomizer->status, 1, hi);
254+
RETURN_DOUBLE(getFloat_closed_open(randomizer->algo, randomizer->status, min, max));
255+
} else if (zend_string_equals_literal(bounds_name, "ClosedClosed")) {
256+
if (UNEXPECTED(max < min)) {
257+
zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
258+
RETURN_THROWS();
259+
}
194260

195-
if (fabs(min) <= fabs(max)) {
196-
RETURN_DOUBLE(k == hi ? min : max - k * g);
261+
RETURN_DOUBLE(getFloat_closed_closed(randomizer->algo, randomizer->status, min, max));
262+
} else if (zend_string_equals_literal(bounds_name, "OpenClosed")) {
263+
if (UNEXPECTED(max <= min)) {
264+
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
265+
RETURN_THROWS();
266+
}
267+
268+
RETURN_DOUBLE(getFloat_open_closed(randomizer->algo, randomizer->status, min, max));
269+
} else if (zend_string_equals_literal(bounds_name, "OpenOpen")) {
270+
if (UNEXPECTED(max <= min)) {
271+
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
272+
RETURN_THROWS();
273+
}
274+
275+
RETURN_DOUBLE(getFloat_open_open(randomizer->algo, randomizer->status, min, max));
197276
} else {
198-
RETURN_DOUBLE(min + (k - 1) * g);
277+
ZEND_UNREACHABLE();
199278
}
200279
}
201280
/* }}} */

0 commit comments

Comments
 (0)