d0c-s4vage/gramfuzz

Feature Request: Weighted Or (already have implementation to use)

docfate111 opened this issue · 4 comments

Is your feature request related to a problem? Please describe

A request for Or() but with weights
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Or() equally gives each option the same probability.

Describe the solution you'd like

Each statement in the Or is in a tuple of which the first is the option the or chooses and second is the probability that the option is taken.
A clear and concise description of what you want to happen.

Describe alternatives you've considered

class WeightedOr(Field):
    """A ``Field`` subclass that chooses one of the provided values at
    random as the result of a call to the ``build()`` method. Takes an
    odds array rather than just direct values."""
    def __init__(self, *values, **kwargs):
        '''
        values is a list of tuples
        '''
        self.shortest_vals = None
        vals=[x[0] for x in values]
        self.values= list(map(maybe_binstr, vals))
        self.weights = [x[1] for x in values]
        #print(self.weights)
        if abs(1.0 - sum(self.weights)) > 0.0001:
            raise("Weights in WeightedOr don't sum to 1.0: {}".format(self.weights))
        if "options" in kwargs and len(values) == 0:
            self.values = list(map(maybe_binstr, kwargs["options"]))
        self.rolling = kwargs.setdefault("rolling", False)
    def build(self, pre=None, shortest=False):
        """
        :param list pre: The prerequisites list
        :param bool shortest: Whether or not the shortest reference-chain (most minimal) 
        version of the field should be generated.
        """
        #self.values=list(map(val, self.values))
        #self.shortest_vals=list(map(val, self.values))
        if pre is None:
            pre = []
        # self.shortest_vals will be set by the GramFuzzer and will
        # contain a list of value options that have a minimal reference chain
        if shortest and self.shortest_vals is not None:
            return utils.val(random.choices(self.shortest_vals, self.weights)[0], pre, shortest=shortest)
        else:
            return utils.val(random.choices(self.values, self.weights)[0], pre, shortest=shortest)

A clear and concise description of any alternative solutions or features you've considered.
implementation of solution

Additional context

Add any other context or screenshots about the feature request here.

The code got messed up formatting
The implementation I mean is here:
https://github.com/mgree/smoosh-fuzz/blob/master/weightedOr.py

Thanks for the feature request! I like this idea a lot!

Added as part of v1.4.0:

from collections import defaultdict
from gramfuzz.fields import WeightedOr, WOr

counts = defaultdict(int)
wor = WOr(
    ("50%", 0.5),
    ("30%", 0.3),
    ("20%", 0.2),
)

iters = 100000
for x in range(iters):
    val = wor.build()
    counts[val] += 1

for k, v in counts.items():
    print("{}: {:.03f}%".format(k, (v / iters) * 100))

outputs

b'50%': 50.034%
b'30%': 30.163%
b'20%': 19.803%

Thanks for implementing! I believe there is a bug my code that ran before is not getting ""Weights in WeightedOr don't sum to 1.0" error when it worked before and I checked the values. Also I am unable to run it in Python 3 only in Python 2.7 does it work after running both setup.py in the repo and pip/pip3.