state = 0 === $seed ? 0x9E3779B9 : $seed; // Warm up: seeds from similar strings start close together. for ( $i = 0; $i < 8; $i++ ) { $this->next(); } } /** * A generator for one item. * * @since 1.0.0 * @param string $batch Batch ID. * @param string $type Item type (products, orders …). * @param int $index Item index within the run. * @return self */ public static function for( string $batch, string $type, int $index ): self { return new self( crc32( $batch . '|' . $type . '|' . $index ) ); } /** * Next raw 32-bit value. * * @since 1.0.0 * @return int 1 … 2^32-1. */ public function next(): int { $x = $this->state; $x ^= ( $x << 13 ) & 0xFFFFFFFF; $x ^= $x >> 17; $x ^= ( $x << 5 ) & 0xFFFFFFFF; $this->state = $x & 0xFFFFFFFF; return $this->state; } /** * A float in [0, 1). * * @since 1.0.0 * @return float */ public function float(): float { return ( $this->next() - 1 ) / 4294967295; } /** * An integer between two bounds, both included (swapped when given the wrong way round). * * @since 1.0.0 * @param int $min Lowest. * @param int $max Highest. * @return int */ public function int( int $min, int $max ): int { if ( $max < $min ) { list( $min, $max ) = array( $max, $min ); } $span = $max - $min + 1; return $min + (int) floor( $this->float() * $span ); } /** * True with a given chance. * * @since 1.0.0 * @param float $percent 0–100. * @return bool */ public function chance( float $percent ): bool { return $this->float() * 100 < $percent; } /** * One value of a list (null for an empty list). * * @since 1.0.0 * @param array $items Items. * @return mixed */ public function pick( array $items ) { if ( ! $items ) { return null; } $items = array_values( $items ); return $items[ $this->int( 0, count( $items ) - 1 ) ]; } /** * A key chosen by weight. Keys with zero or negative weight are never chosen; '' when none can be. * * @since 1.0.0 * @param array $weights Key => weight. * @return string */ public function weighted( array $weights ): string { $total = 0.0; foreach ( $weights as $weight ) { $total += max( 0, (float) $weight ); } if ( $total <= 0 ) { return ''; } $roll = $this->float() * $total; $last = ''; foreach ( $weights as $key => $weight ) { $weight = max( 0, (float) $weight ); if ( $weight <= 0 ) { continue; } $last = (string) $key; if ( $roll < $weight ) { return $last; } $roll -= $weight; } return $last; } /** * A shuffled copy of a list (Fisher–Yates with this generator). * * @since 1.0.0 * @param array $items Items. * @return array */ public function shuffle( array $items ): array { $items = array_values( $items ); for ( $i = count( $items ) - 1; $i > 0; $i-- ) { $j = $this->int( 0, $i ); $tmp = $items[ $i ]; $items[ $i ] = $items[ $j ]; $items[ $j ] = $tmp; } return $items; } }