Skip to content

Commit 91ebffa

Browse files
Merge pull request #118 from danilovict2/rand
Rand
2 parents fa4c0e9 + 04c98e7 commit 91ebffa

File tree

4 files changed

+100
-31
lines changed

4 files changed

+100
-31
lines changed

EXAMPLES.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ func main() {
660660
## 6. Random
661661

662662

663-
### Generate a Random Number
663+
### Generate a Pseudo-Random Number
664664

665665
```go
666666
package main
@@ -671,17 +671,41 @@ import (
671671
)
672672

673673
func main() {
674-
num, err := rand.Number()
674+
i := rand.Int()
675+
i64 := rand.Int64()
676+
fmt.Println("Random Number:", i)
677+
fmt.Println("Random Number(63-bit):", i64)
678+
}
679+
```
680+
#### Output:
681+
```
682+
Random Number: 1983964840637203872
683+
Random Number(63-bit): 8714503361527813617
684+
```
685+
686+
### Generate a Cryptographically Secure Random Number
687+
688+
```go
689+
package main
690+
691+
import (
692+
"fmt"
693+
"github.com/kashifkhan0771/utils/rand"
694+
)
695+
696+
func main() {
697+
n, err := rand.SecureNumber()
675698
if err != nil {
676699
fmt.Println("Error:", err)
677700
return
678701
}
679-
fmt.Println("Random Number:", num)
702+
703+
fmt.Println("Cryptographically Secure Random Number:", n)
680704
}
681705
```
682706
#### Output:
683707
```
684-
Random Number: 8507643814357583841
708+
Cryptographically Secure Random Number: 5251369289452281710
685709
```
686710

687711
### Generate a Random Number in a Range

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ require github.com/kashifkhan0771/utils v0.3.0
7171
- **NullableString**: Returns the value of a string pointer or an empty string if nil.
7272

7373
### 6. Random (rand)
74-
- **Number**: Generates a random number.
74+
- **Int**: Generates a pseudo-random integer.
75+
- **Int64**: Generates a pseudo-random 63-bit integer.
76+
- **SecureNumber**: Generates a cryptographically secure random number.
7577
- **NumberInRange**: Generates a random number within a specified range.
7678
- **String**: Generates a random alphanumeric string.
7779
- **StringWithLength**: Generates a random string of a custom length.

rand/rand.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math"
77
"math/big"
8+
randv2 "math/rand/v2"
89
"strings"
910
)
1011

@@ -16,7 +17,19 @@ const (
1617
DefaultLength = 10
1718
)
1819

19-
func Number() (int64, error) {
20+
// Int returns a pseudo-random integer
21+
func Int() int {
22+
return randv2.Int()
23+
}
24+
25+
// Int64 returns a pseudo-random 63-bit integer
26+
func Int64() int64 {
27+
return randv2.Int64()
28+
}
29+
30+
// SecureNumber returns a cryptographically secure random number.
31+
// Note: This function is significantly slower than Int() and Int64() due to the use of crypto/rand.
32+
func SecureNumber() (int64, error) {
2033
n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
2134
if err != nil {
2235
return 0, fmt.Errorf("failed to generate random number: %w", err)
@@ -25,7 +38,7 @@ func Number() (int64, error) {
2538
return n.Int64(), nil
2639
}
2740

28-
// NumberInRange generates a random number between min and max
41+
// NumberInRange generates a pseudo-random number between min and max
2942
func NumberInRange(min, max int64) (int64, error) {
3043
if min > max {
3144
return 0, fmt.Errorf("min (%d) cannot be greater than max (%d)", min, max)
@@ -41,13 +54,10 @@ func NumberInRange(min, max int64) (int64, error) {
4154
limit := math.MaxInt64 - (math.MaxInt64 % rangeSize)
4255

4356
for {
44-
n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
45-
if err != nil {
46-
return 0, fmt.Errorf("failed to generate random number in range: %w", err)
47-
}
57+
n := randv2.Int64N(math.MaxInt64)
4858

49-
if n.Int64() < limit {
50-
return min + (n.Int64() % rangeSize), nil
59+
if n < limit {
60+
return min + (n % rangeSize), nil
5161
}
5262
// If we're above the limit, try again to ensure uniform distribution
5363
}
@@ -107,14 +117,10 @@ func StringWithCharset(length int, charset string) (string, error) {
107117
}
108118

109119
result := make([]byte, length)
110-
charsetLength := big.NewInt(int64(len(trimmedCharset)))
111120

112-
for i := 0; i < length; i++ {
113-
n, err := rand.Int(rand.Reader, charsetLength)
114-
if err != nil {
115-
return "", fmt.Errorf("failed to generate random string: %w", err)
116-
}
117-
result[i] = trimmedCharset[n.Int64()]
121+
for i := range length {
122+
n := randv2.Int64N(int64(len(trimmedCharset)))
123+
result[i] = trimmedCharset[n]
118124
}
119125

120126
return string(result), nil

rand/rand_test.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,54 @@ package rand
22

33
import (
44
"math/rand"
5-
randv2 "math/rand/v2"
65
"strings"
76
"testing"
87
)
98

10-
func TestNumber(t *testing.T) {
9+
func TestInt(t *testing.T) {
10+
const iterations = 1000
11+
seen := make(map[int]bool)
12+
13+
for range iterations {
14+
n := Int()
15+
// Track unique numbers
16+
seen[n] = true
17+
}
18+
19+
// With true randomness, we expect a high percentage of unique numbers
20+
// Given the massive range of int64, getting even 2 duplicates would be extremely unlikely
21+
uniqueRatio := float64(len(seen)) / float64(iterations)
22+
if uniqueRatio < 0.99 {
23+
t.Errorf("Expected mostly unique numbers, but got uniqueness ratio of %v", uniqueRatio)
24+
}
25+
}
26+
27+
func TestInt64(t *testing.T) {
28+
const iterations = 1000
29+
seen := make(map[int64]bool)
30+
31+
for range iterations {
32+
n := Int64()
33+
// Track unique numbers
34+
seen[n] = true
35+
}
36+
37+
// With true randomness, we expect a high percentage of unique numbers
38+
// Given the massive range of int64, getting even 2 duplicates would be extremely unlikely
39+
uniqueRatio := float64(len(seen)) / float64(iterations)
40+
if uniqueRatio < 0.99 {
41+
t.Errorf("Expected mostly unique numbers, but got uniqueness ratio of %v", uniqueRatio)
42+
}
43+
}
44+
45+
func TestSecureNumber(t *testing.T) {
1146
const iterations = 1000
1247
seen := make(map[int64]bool)
1348

14-
for i := 0; i < iterations; i++ {
15-
n, err := Number()
49+
for range iterations {
50+
n, err := SecureNumber()
1651
if err != nil {
17-
t.Errorf("Number() error = %v", err)
52+
t.Errorf("SecureNumber() error = %v", err)
1853

1954
return
2055
}
@@ -313,7 +348,7 @@ func contains[T comparable](slice []T, item T) bool {
313348
func BenchmarkNumberCrypto(b *testing.B) {
314349
b.ReportAllocs()
315350
for i := 0; i < b.N; i++ {
316-
_, _ = Number()
351+
_, _ = SecureNumber()
317352
}
318353
}
319354

@@ -329,15 +364,17 @@ func BenchmarkNumberMath(b *testing.B) {
329364
}
330365
}
331366

332-
// Using math/rand/v2
333-
func numberMathRandV2() (int64, error) {
334-
return randv2.Int64(), nil
367+
func BenchmarkIntMathV2(b *testing.B) {
368+
b.ReportAllocs()
369+
for i := 0; i < b.N; i++ {
370+
_ = Int()
371+
}
335372
}
336373

337-
func BenchmarkNumberMathV2(b *testing.B) {
374+
func BenchmarkInt64MathV2(b *testing.B) {
338375
b.ReportAllocs()
339376
for i := 0; i < b.N; i++ {
340-
_, _ = numberMathRandV2()
377+
_ = Int64()
341378
}
342379
}
343380

0 commit comments

Comments
 (0)