• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cisagov / lcgit / 4000121067

pending completion
4000121067

Pull #12

github

GitHub
Merge 8f357741a into 7601e0006
Pull Request #12: Bump actions/setup-go from 2 to 3

24 of 24 branches covered (100.0%)

Branch coverage included in aggregate %.

64 of 64 relevant lines covered (100.0%)

5.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/lcgit/lcgit.py
1
#!/usr/bin/env python3
2
"""
5✔
3
Implementation of a Linear Congruential Generator.
4

5
This LCG can be used to quickly generate random, non-repeating, maximal length sequences
6
from existing sequences and IP networks.
7

8
see: https://stackoverflow.com/questions/44818884/all-numbers-in-a-given-range-but-random-order
9
see: https://en.wikipedia.org/wiki/Linear_congruential_generator
10
"""
11

12
# Standard Python Libraries
13
from collections.abc import Sequence
5✔
14
from ipaddress import _BaseNetwork
5✔
15
from math import sin
5✔
16
from random import Random, randint
5✔
17

18

19
def _lcg_params(u, v):
5✔
20
    """Generate parameters for an LCG.
21

22
    The parameters will produces a maximal length sequence of numbers
23
    in the range (u..v)
24
    """
25
    diff = v - u
5✔
26
    if diff < 4:
5✔
27
        raise ValueError("range must be at least 4.")
5✔
28
    # Modulus
29
    m = 2 ** diff.bit_length()
5✔
30
    # Random odd integer, (a-1) divisible by 4
31
    a = (randint(1, (m >> 2) - 1) * 4) + 1  # nosec
5✔
32
    # Any odd integer will do
33
    c = randint(3, m) | 1  # nosec
5✔
34
    return (m, a, c)
5✔
35

36

37
class lcg(object):
5✔
38
    """A Linear Congruential Generator object.
39

40
    This LCG class contains methods which are used to generate random, non-repeating,
41
    maximal length sequences from sequences or IP networks.
42
    """
43

44
    def __init__(self, sequence, state=None, emit=False):
5✔
45
        """Instanciate a new LCG.
46

47
        Args:
48
            sequence: A Python Sequence, IPv4, or IPv6 network.
49
                range(10)
50
                [1, 2, 3, 4, 5]
51
                foobarbaz
52
                IPv4Network("192.0.2.0/24")
53
                IPv6Network('::8bad:f00d:0/112')
54
            state: A tuple describing state, saved from a previous LCG iterator.
55
                An LCG iterator can be configured to emit state along with its
56
                randomized sequence.  This state can be saved and used to resume an LCG
57
                at a later time.
58
                State is of the form: (multiplier, increment, seed, index)
59
            emit: A boolean that enables the state emission of iterators.  When state
60
                emission is on, LCG iterators will yield random sequence values as well
61
                as the current state of the generator.
62

63
        Raises:
64
            ValueError: If the sequence is not of the correct type.
65

66
        """
67
        self.emit = emit
5✔
68
        if isinstance(sequence, Sequence):
5✔
69
            self.seqlength = len(sequence)
5✔
70
            self.start = 0
5✔
71
            self.end = self.seqlength - 1
5✔
72
        elif isinstance(sequence, _BaseNetwork):
5✔
73
            self.start = int(sequence[0])
5✔
74
            self.end = int(sequence[-1])
5✔
75
            self.seqlength = self.end - self.start + 1
5✔
76
        else:
77
            raise ValueError(
5✔
78
                "sequence must be an instance of sequence or ipaddress._BaseNetwork"
79
            )
80
        self.seq = sequence
5✔
81
        if self.seqlength > 4:
5✔
82
            (m, a, c) = _lcg_params(self.start, self.end)
5✔
83
        else:
84
            (m, a, c) = (1, 1, 1)
5✔
85
        self.modulus = m
5✔
86
        if state is None:
5✔
87
            # create a new state
88
            (self.multiplier, self.increment) = (a, c)
5✔
89
            self.seed = 1
5✔
90
            self.index = 0
5✔
91
        else:
92
            # load passed in state
93
            (self.multiplier, self.increment, self.seed, self.index) = state
5✔
94

95
    def __iter__(self):
5✔
96
        """Generate Iterator over the randomized sequence.
97

98
        Yields:
99
            if the LCG was created with emit=True: (random_value, state_tuple)
100
            if the LCG was created with emit=False only the value is yielded.
101

102
        """
103
        seed = self.seed
5✔
104
        index = self.index
5✔
105

106
        # The LCG algorithm requires a sequence of more than 4 elements to operate.
107
        # If the sequence is not sufficiently large, we will fall back to the shuffle method.
108
        if self.seqlength > 4:
5✔
109
            # Sequence is large enough to use LCG algorithm
110
            while index < self.seqlength:
5✔
111
                while True:
3✔
112
                    seed = (seed * self.multiplier + self.increment) % self.modulus
5✔
113
                    if seed < self.seqlength:
5✔
114
                        break
5✔
115
                index += 1
5✔
116
                if self.emit:
5✔
117
                    yield self.seq[seed], (self.multiplier, self.increment, seed, index)
5✔
118
                else:
119
                    yield self.seq[seed]
5✔
120
        else:
121
            # Sequence is too small to use LCG algorithm, use shuffle instead
122
            shuffled_seq = list(self.seq)
5✔
123
            seeded_random = Random()
5✔
124
            seeded_random.seed(
5✔
125
                abs(sin(self.multiplier + self.increment + seed)), version=1
126
            )
127
            seeded_random.shuffle(shuffled_seq)
5✔
128
            for i in shuffled_seq[index:]:
5✔
129
                index += 1
5✔
130
                if self.emit:
5✔
131
                    yield i, (self.multiplier, self.increment, seed, index)
5✔
132
                else:
133
                    yield i
5✔
134

135
    def __repr__(self):
5✔
136
        """Return repr(self)."""
137
        return f"{self.__class__.__name__}({self.start}, {self.end})"
5✔
138

139
    def __len__(self):
5✔
140
        """Return len(self)."""
141
        return self.seqlength
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc