audit-labs/audit-tools
A collection of scripts, queries, and other goodies you can use in an audit.
clone: git clone https://gitbay.org/audit-labs/audit-tools.git
v1.0.0: sampling/sample.html · raw
1<!doctype html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>Sampling Tool</title>
7 <style>
8 form {
9 display: table;
10 }
11 form div {
12 display: table-row;
13 }
14 label, input {
15 display: table-cell;
16 margin-bottom: 10px;
17 }
18 label {
19 padding-right: 10px;
20 }
21 table {
22 width: 100%;
23 border-collapse: collapse;
24 margin-top: 10px;
25 }
26 table, th, td {
27 border: 1px solid black;
28 }
29 th, td {
30 padding: 5px;
31 text-align: center;
32 }
33 </style>
34</head>
35<body>
36<h1>Sampling Tool</h1>
37<p>This sampling tool provides a quick and easy way to generate
38psuedo-random samples within a defined range. Simply enter the size of your
39population, enter your desired number of samples, and generate!</p>
40<p>To reproduce and validate a previously-generated sample, please ensure
41you have entered the seed value correctly when submitting the form. All
42inputs must match the previously-generated sample's inputs to generate the
43same output.</p>
44<form id="sampleForm" onsubmit="return handleFormSubmit(event)">
45 <div>
46 <label for="populationSize">Population Size*:</label>
47 <input type="number" id="populationSize" name="populationSize" min="1" autofocus required>
48 </div>
49 <div>
50 <label for="sampleSize">Sample Size*:</label>
51 <input type="number" id="sampleSize" name="sampleSize" min="1" required>
52 </div>
53 <div>
54 <label for="replacementSize">Replacement Sample Size:</label>
55 <input type="number" id="replacementSize" name="replacementSize" min="0">
56 </div>
57 <div>
58 <label for="customSeed">Custom Seed (optional):</label>
59 <input type="number" id="customSeed" name="customSeed" min="0">
60 </div>
61 <div>
62 <input type="submit" value="Calculate">
63 </div>
64 <p><i>* Indicates a required field</i></p>
65</form>
66<hr>
67<div id="results"></div>
68<script>
69function seededRandom(seed) {
70 let s = seed % 2147483647;
71 return function() {
72 s = (s * 16807) % 2147483647;
73 return (s - 1) / 2147483646;
74 };
75}
76
77function handleFormSubmit(event) {
78 event.preventDefault(); // Prevent the default form submission behavior
79 const customSeedInput = document.getElementById('customSeed').value;
80 // Use the custom seed if provided; otherwise draw a strong random seed and
81 // write it back so the (reproducible) sample can always be tied to a seed.
82 const seed = customSeedInput
83 ? Number.parseInt(customSeedInput)
84 : crypto.getRandomValues(new Uint32Array(1))[0] % 1000000;
85 if (!customSeedInput) {
86 document.getElementById('customSeed').value = seed;
87 }
88 generateSamples(seed); // Call the function with the seed
89}
90
91function generateSamples(seed) {
92 const populationSize = Number.parseInt(document.getElementById('populationSize').value);
93 const sampleSize = Number.parseInt(document.getElementById('sampleSize').value);
94 const replacementSize = Number.parseInt(document.getElementById('replacementSize').value || 0);
95 const resultsDiv = document.getElementById('results');
96
97 // Clear previous results
98 resultsDiv.innerHTML = '';
99
100 // Validate inputs
101 if (Number.isNaN(populationSize) || Number.isNaN(sampleSize) || populationSize <= 0 || sampleSize <= 0) {
102 alert("Please enter valid numbers for required fields.");
103 return;
104 }
105 if (sampleSize + replacementSize > populationSize) {
106 alert("Not enough unique samples available. Reduce the sample size or replacement size.");
107 return;
108 }
109
110 // Create seeded random function
111 const random = seededRandom(seed);
112
113 // Generate an array of population numbers
114 const population = Array.from({ length: populationSize }, (_, i) => i + 1);
115
116 // Shuffle the population array using the seeded random function
117 const shuffledPopulation = population
118 .map(value => ({ value, sort: random() }))
119 .sort((a, b) => a.sort - b.sort)
120 .map(({ value }) => value);
121
122 // Select original samples
123 const originalSamples = shuffledPopulation.slice(0, sampleSize);
124
125 // Select replacement samples
126 const replacementSamples = shuffledPopulation.slice(sampleSize, sampleSize + replacementSize);
127
128 // Display the seed
129 const seedInfo = document.createElement('p');
130 seedInfo.textContent = `Seed: ${seed}`;
131 resultsDiv.appendChild(seedInfo);
132
133 // Generate tables
134 const originalTable = createTable(originalSamples, "Original Samples");
135 const replacementTable = createTable(replacementSamples, "Replacement Samples");
136
137 // Append tables to the results div
138 resultsDiv.appendChild(originalTable);
139 if (replacementSamples.length > 0) {
140 resultsDiv.appendChild(replacementTable);
141 }
142}
143
144function createTable(dataArray, tableTitle) {
145 const table = document.createElement('table');
146 const caption = document.createElement('caption');
147 caption.textContent = tableTitle;
148 table.appendChild(caption);
149
150 const headerRow = document.createElement('tr');
151 const indexHeader = document.createElement('th');
152 indexHeader.textContent = 'Index';
153 const sampleHeader = document.createElement('th');
154 sampleHeader.textContent = 'Sample';
155 headerRow.appendChild(indexHeader);
156 headerRow.appendChild(sampleHeader);
157 table.appendChild(headerRow);
158
159 dataArray.forEach((sample, index) => {
160 const row = document.createElement('tr');
161 const indexCell = document.createElement('td');
162 const sampleCell = document.createElement('td');
163 indexCell.textContent = index + 1;
164 sampleCell.textContent = sample;
165 row.appendChild(indexCell);
166 row.appendChild(sampleCell);
167 table.appendChild(row);
168 });
169
170 return table;
171}
172</script>
173</body>
174</html>