--- Helper module providing various generators for generating data. -- @module lqc.lqc_gen -- @alias lib local Gen = require 'lqc.generator' local random = require 'lqc.random' local reduce = require 'lqc.helpers.reduce' local lib = {} --- Picks a number randomly between min and max. -- @param min Minimum value to pick from -- @param max Maximum value to pick from -- @return a random value between min and max local function choose_pick(min, max) local function pick() return random.between(min, max) end return pick end --- Shrinks a value between min and max by dividing the sum of the closest -- number to 0 and the generated value with 2. -- This effectively reduces it to the value closest to 0 gradually in the -- chosen range. -- @param min Minimum value to pick from -- @param max Maximum value to pick from -- @return a shrunk value between min and max local function choose_shrink(min, max) local shrink_to = (math.abs(min) < math.abs(max)) and min or max local function shrink(value) local shrunk_value = (shrink_to + value) / 2 if shrunk_value < 0 then return math.ceil(shrunk_value) else return math.floor(shrunk_value) end end return shrink end --- Creates a generator, chooses an integer between min and max (inclusive range). -- @param min Minimum value to pick from -- @param max Maximum value to pick from -- @return a random value between min and max function lib.choose(min, max) return Gen.new(choose_pick(min, max), choose_shrink(min, max)) end --- Select a generator from a list of generators -- @param generators Table containing an array of generator objects. -- @return A new generator that randomly uses 1 of the generators in the list. function lib.oneof(generators) local which local function oneof_pick(numtests) which = random.between(1, #generators) return generators[which]:pick(numtests) end local function oneof_shrink(prev) return generators[which]:shrink(prev) end return Gen.new(oneof_pick, oneof_shrink) end --- Select a generator from a list of weighted generators ({{weight1, gen1}, ... }) -- @param generators A table containing an array of weighted generators. -- @return A new generator that randomly uses a generator from the list, taking the -- weights into account. function lib.frequency(generators) local which local function do_sum(generator, acc) return generator[1] + acc end local function frequency_pick(numtests) local sum = reduce(generators, 0, do_sum) local val = random.between(1, sum) which = reduce(generators, {0, 1}, function(generator, acc) local current_sum = acc[1] + generator[1] if current_sum >= val then return acc else return {current_sum, acc[2] + 1} end end)[2] return generators[which][2]:pick(numtests) end local function frequency_shrink(prev) return generators[which][2]:shrink(prev) end return Gen.new(frequency_pick, frequency_shrink) end --- Create a generator that selects an element based on the input list. -- @param array an array of constant values -- @return Generator that can pick 1 of the values in the array, shrinks -- towards beginning of the list. function lib.elements(array) local last_idx local function elements_pick() local idx = random.between(1, #array) last_idx = idx return array[idx] end local function elements_shrink(_) if last_idx > 1 then last_idx = last_idx - 1 end return array[last_idx] end return Gen.new(elements_pick, elements_shrink) end return lib