Skip to content

Instantly share code, notes, and snippets.

@atoponce
Last active February 18, 2021 01:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atoponce/de92adafbde810eb9d3f63a217885ab4 to your computer and use it in GitHub Desktop.
Save atoponce/de92adafbde810eb9d3f63a217885ab4 to your computer and use it in GitHub Desktop.
<!doctype html>
<html>
<head>
<meta charset='utf-8'></meta>
<title>Lehmer Code</title>
<style>
input[type=text] {
font-family: monospace;
width: 450px;
}
</style>
</head>
<body>
<label for='chars'>Characters (A-Z):</label>
<br/>
<input type='text' id='chars' name='chars' />
<br/>
<label for='faces'>Digits (1-6):</label>
<br/>
<input type='text' id='faces' name='faces' />
<br/>
<label for='orien'>Orientations (1-4):</label>
<br/>
<input type='text' id='orien' name='orien' />
<br/>
<input type='submit' value='Submit' onclick='buildHex()'/>
<br/>
<br/>
<label for='result'>Result:</label>
<br/>
<input readonly type='text' id='result' name='result' />
<br/>
<p>Give this result to <a href='https://github.com/diracdeltas/niceware'>Niceware</a> for a fun 192-bit 12-word passphrase.</a>
<script>
function factorial(n) {
let result = BigInt(1)
while (n > 0) {
result *= BigInt(n)
n--
}
return result
}
function calculateLehmer(chars) {
const arr = chars.split('')
let lehmer = []
let counter = 0
let result = BigInt(0)
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) counter++
}
lehmer[i] = counter
counter = 0
}
counter = lehmer.length
for (let i = 0; i < lehmer.length; i++) {
counter--
result += (BigInt(lehmer[i]) * factorial(counter))
}
return result
}
function calculateDecimal(outcome, possible) {
const arr = outcome.split('')
let counter = arr.length
let result = BigInt(0)
for (let i = 0; i < arr.length; i++) {
counter--
result += BigInt((arr[i] % possible) * possible**counter)
}
return result
}
function buildHex() {
const result = document.getElementById('result')
// The three DiceKeys inputs
const chars = document.getElementById('chars').value
const faces = document.getElementById('faces').value
const orien = document.getElementById('orien').value
// The calculated results of each set
const charsDec = calculateLehmer(chars)
const facesDec = calculateDecimal(faces, 6)
const orienDec = calculateDecimal(orien, 4)
// The maxes each set can be
const charsMax = BigInt(factorial(25)) - 1n
const facesMax = BigInt(6**25) - 1n
const orienMax = BigInt(4**25) - 1n
// The calculated ID of that specific DiceKeys toss
let permutation = (orienDec * facesMax * charsMax) + (facesDec * charsMax) + charsDec
// We must keep a uniform distribution. Some tosses must be discarded.
// 2^198 is about 80% of 2^198.30557612690..., so we lose ~20% tosses.
if (permutation >= BigInt(2**198)) {
result.value = 'Result will be biased. Reroll.'
result.style.color = 'red'
}
else {
permutation = permutation & (BigInt(2**192) - 1n)
const hex = permutation.toString(16).padStart(48, '0')
result.value = hex
result.style.color = 'black'
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment