a number. return Sampling.bufferToHex(hash).slice(0, 12); }, /** * Sample by splitting the input into two buckets, one with a size (rate) and * another with a size (1.0 - rate), and then check if the input's hash falls * into the first bucket. * * @param {object} input Input to hash to determine the sample. * @param {number} rate Number between 0.0 and 1.0 to sample at. A value of * 0.25 returns true 25% of the time. * @returns {Promise} * Resovles to true if the input is in the sample. */ async stableSample(input, rate) { const inputHash = await Sampling.truncatedHash(input); const samplePoint = Sampling.fractionToKey(rate); return inputHash < samplePoint; }, /** * Sample by splitting the input space into a series of buckets, and checking * if the given input is in a range of buckets. * * The range to check is defined by a start point and length, and can wrap * around the input space. For example, if there are 100 buckets, and we ask to * check 50 buckets starting from bucket 70, then buckets 70-99 and 0-19 will * be checked. * * @param {object} input Input to hash to determine the matching bucket. * @param {integer} start Index of the bucket to start checking. * @param {integer} count Number of buckets to check. * @param {integer} total Total number of buckets to group inputs into. * @returns {Promise} * Resolves to true if the given input is within the range of buckets we're * checking. */ async bucketSample(input, start, count, total) { const inputHash = await Sampling.truncatedHash(input); const wrappedStart = start % total; const end = wrappedStart + count; // If the range we're testing wraps, we have to check two ranges: from start // to max, and from min to end. if (end > total) { return ( Sampling.isHashInBucket(inputHash, 0, end % total, total) || Sampling.isHashInBucket(inputHash, wrappedStart, total, total) ); } return Sampling.isHashInBucket(inputHash, wrappedStart, end, total); }, /** * Sample over a list of ratios such that, over the input space, each ratio * has a number of matches in correct proportion to the other ratios. * * For example, given the ratios: * * [1, 2, 3, 4] * * 10% of all inputs will return 0, 20% of all inputs will return 1, 30% will * return 2, and 40% will return 3. You can determine the percent of inputs * that will return an index by dividing the ratio by the sum of all ratios * passed in. In the case above, 4 / (1 + 2 + 3 + 4) == 0.4, or 40% of the * inputs. * * @param {object} input * @param {Array} ratios * @returns {Promise} * Resolves to the index of the ratio that matched the input. * @rejects {Error} * If the list of ratios doesn't have at least one element */ async ratioSample(input, ratios) { if (ratios.length < 1) { throw new Error( `ratios must be at least 1 element long (got length: ${ratios.length})` ); } const inputHash = await Sampling.truncatedHash(input); const ratioTotal = ratios.reduce((acc, ratio) => acc + ratio); let samplePoint = 0; for (let k = 0; k < ratios.length - 1; k++) { samplePoint += ratios[k]; if (inputHash <= Sampling.fractionToKey(samplePoint / ratioTotal)) { return k; } } // No need to check the last bucket if the others didn't match. return ratios.length - 1; }, }; PK