const CONSONANTS = "bdfghjklmnprstvz" | split chars const VOWELS = "aiou" | split chars def consonant-value [letter: string] { $CONSONANTS | enumerate | where item == $letter | first | get index } def vowel-value [letter: string] { $VOWELS | enumerate | where item == $letter | first | get index } # Encode binary as lowercase proquint blocks. # Each five-letter CVCVC block holds two bytes; a final three-letter CVC block # holds one byte. Its last consonant is restricted to b, d, f, or g. export def main [value?: binary] { let bytes = if $value == null { $in } else { $value } if ($bytes | describe) != "binary" { error make {msg: "proquint requires binary input"} } $bytes | chunks 2 | each {|chunk| let high = $chunk.0 let low = $chunk.1? if $low == null { [ ($CONSONANTS | get ($high // 16)) ($VOWELS | get (($high // 4) mod 4)) ($CONSONANTS | get ($high mod 4)) ] | str join } else { let word = $high * 256 + $low [ ($CONSONANTS | get ($word // 4096)) ($VOWELS | get (($word // 1024) mod 4)) ($CONSONANTS | get (($word // 64) mod 16)) ($VOWELS | get (($word // 16) mod 4)) ($CONSONANTS | get ($word mod 16)) ] | str join } } | str join "-" } # Decode proquints to binary, without inferring a Nushell value type. export def decode [value?: string] { let text = if $value == null { $in } else { $value } if ($text | describe) != "string" { error make {msg: "proquint decode requires text input"} } if $text == "" { return 0x[] } let blocks = $text | split row "-" let last = ($blocks | length) - 1 $blocks | enumerate | each {|entry| let block = $entry.item let letters = $block | split chars if $block =~ '^[bdfghjklmnprstvz][aiou][bdfghjklmnprstvz][aiou][bdfghjklmnprstvz]$' { let word = ( (consonant-value ($letters | get 0)) * 4096 + (vowel-value ($letters | get 1)) * 1024 + (consonant-value ($letters | get 2)) * 64 + (vowel-value ($letters | get 3)) * 16 + (consonant-value ($letters | get 4)) ) [ (($word // 256) | into binary --compact --endian big) (($word mod 256) | into binary --compact --endian big) ] | bytes collect } else if $entry.index == $last and $block =~ '^[bdfghjklmnprstvz][aiou][bdfg]$' { let byte = ( (consonant-value ($letters | get 0)) * 16 + (vowel-value ($letters | get 1)) * 4 + (consonant-value ($letters | get 2)) ) $byte | into binary --compact --endian big } else { error make {msg: $"invalid proquint block: ($block)"} } } | bytes collect }