title: Decode base45 string
body: <p>We are trying to implement the verification of the new EU corona virus test/vaccination certificates, but can't get the base45 decoding working.</p>
  <p>Specification is here: <a href="https://datatracker.ietf.org/doc/draft-faltstrom-base45/" rel="noreferrer">https://datatracker.ietf.org/doc/draft-faltstrom-base45/</a></p>
  <p>We nearly finished our class, but we sometimes get wrong values back..</p>
  <p>Target is this:</p>
  <pre><code>Encoding example 1: The string &quot;AB&quot; is the byte sequence [65 66].
  The 16 bit value is 65 * 256 + 66 = 16706. 16706 equals 11 + 45 * 11
  + 45 * 45 * 8 so the sequence in base 45 is [11 11 8].  By looking up
  these values in the Table 1 we get the encoded string &quot;BB8&quot;.
  
  Encoding example 2: The string &quot;Hello!!&quot; as ASCII is the byte
  sequence [72 101 108 108 111 33 33].  If we look at each 16 bit
  value, it is [18533 27756 28449 33].  Note the 33 for the last byte.
  When looking at the values modulo 45, we get [[38 6 9] [36 31 13] [9
  2 14] [33 0]] where the last byte is represented by two.  By looking
  up these values in the Table 1 we get the encoded string &quot;%69
  VD92EX0&quot;.
  
  Encoding example 3: The string &quot;base-45&quot; as ASCII is the byte
  sequence [98 97 115 101 45 52 53].  If we look at each 16 bit value,
  it is [25185 29541 11572 53].  Note the 53 for the last byte.  When
  looking at the values modulo 45, we get [[30 19 12] [21 26 14] [7 32
  5] [8 1]] where the last byte is represented by two.  By looking up
  these values in the Table 1 we get the encoded string &quot;UJCLQE7W581&quot;.
  </code></pre>
  <p>Here is my current code, which produces wrong values:</p>
  <pre class="lang-rb prettyprint-override"><code>class Base45
  
    ALPHABET = {
      &quot;00&quot; =&gt; &quot;0&quot;,
      &quot;01&quot; =&gt; &quot;1&quot;,
      &quot;02&quot; =&gt; &quot;2&quot;,
      &quot;03&quot; =&gt; &quot;3&quot;,
      &quot;04&quot; =&gt; &quot;4&quot;,
      &quot;05&quot; =&gt; &quot;5&quot;,
      &quot;06&quot; =&gt; &quot;6&quot;,
      &quot;07&quot; =&gt; &quot;7&quot;,
      &quot;08&quot; =&gt; &quot;8&quot;,
      &quot;09&quot; =&gt; &quot;9&quot;,
      &quot;10&quot; =&gt; &quot;A&quot;,
      &quot;11&quot; =&gt; &quot;B&quot;,
      &quot;12&quot; =&gt; &quot;C&quot;,
      &quot;13&quot; =&gt; &quot;D&quot;,
      &quot;14&quot; =&gt; &quot;E&quot;,
      &quot;15&quot; =&gt; &quot;F&quot;,
      &quot;16&quot; =&gt; &quot;G&quot;,
      &quot;17&quot; =&gt; &quot;H&quot;,
      &quot;18&quot; =&gt; &quot;I&quot;,
      &quot;19&quot; =&gt; &quot;J&quot;,
      &quot;20&quot; =&gt; &quot;K&quot;,
      &quot;21&quot; =&gt; &quot;L&quot;,
      &quot;22&quot; =&gt; &quot;M&quot;,
      &quot;23&quot; =&gt; &quot;N&quot;,
      &quot;24&quot; =&gt; &quot;O&quot;,
      &quot;25&quot; =&gt; &quot;P&quot;,
      &quot;26&quot; =&gt; &quot;Q&quot;,
      &quot;27&quot; =&gt; &quot;R&quot;,
      &quot;28&quot; =&gt; &quot;S&quot;,
      &quot;29&quot; =&gt; &quot;T&quot;,
      &quot;30&quot; =&gt; &quot;U&quot;,
      &quot;31&quot; =&gt; &quot;V&quot;,
      &quot;32&quot; =&gt; &quot;W&quot;,
      &quot;33&quot; =&gt; &quot;X&quot;,
      &quot;34&quot; =&gt; &quot;Y&quot;,
      &quot;35&quot; =&gt; &quot;Z&quot;,
      &quot;36&quot; =&gt; &quot; &quot;,
      &quot;37&quot; =&gt; &quot;$&quot;,
      &quot;38&quot; =&gt; &quot;%&quot;,
      &quot;39&quot; =&gt; &quot;*&quot;,
      &quot;40&quot; =&gt; &quot;+&quot;,
      &quot;41&quot; =&gt; &quot;-&quot;,
      &quot;42&quot; =&gt; &quot;.&quot;,
      &quot;43&quot; =&gt; &quot;/&quot;,
      &quot;44&quot; =&gt; &quot;:&quot;
    }.freeze
  
    def self.encode_base45(text)
      restsumme = text.unpack('S&gt;*')
  
      # not sure what this is doing, but without it, it works worse :D
      restsumme &lt;&lt; text.bytes[-1] if text.bytes.size &gt; 2 &amp;&amp; text.bytes[-1] &lt; 256
  
      bytearr = restsumme.map do |bytes|
        arr = []
        multiplier, rest = bytes.divmod(45**2)
        arr &lt;&lt; multiplier if multiplier &gt; 0
  
        multiplier, rest = rest.divmod(45)
        arr &lt;&lt; multiplier if multiplier &gt; 0
        arr &lt;&lt; rest if rest &gt; 0
        arr.reverse
      end
      return bytearr.flatten.map{|a| ALPHABET[a.to_s.rjust(2, &quot;0&quot;)]}.join
    end
  
    def self.decode_base45(text)
      arr = text.split(&quot;&quot;).map do |char|
        ALPHABET.invert[char]
      end
      textarr = arr.each_slice(3).to_a.map do |group|
        subarr = group.map.with_index do |val, index|
          val.to_i * (45**index)
        end
        ap subarr
        subarr.sum
      end
  
      return textarr.pack(&quot;S&gt;*&quot;) # returns wrong values
    end
  end
  </code></pre>
  <p>Results:</p>
  <pre class="lang-rb prettyprint-override"><code>Base45.encode_base45(&quot;AB&quot;)
  =&gt; &quot;BB8&quot; # works
  Base45.decode_base45(&quot;BB8&quot;)
  =&gt; &quot;AB&quot; # works
  
  Base45.encode_base45(&quot;Hello!!&quot;)
  =&gt; &quot;%69 VD92EX&quot; # works
  Base45.decode_base45(&quot;BB8&quot;)
  =&gt; &quot;Hello!\x00!&quot; # wrong \x00
  
  
  Base45.encode_base45(&quot;base-45&quot;)
  =&gt; &quot;UJCLQE7W581&quot; # works
  Base45.decode_base45(&quot;UJCLQE7W581&quot;)
  =&gt; &quot;base-4\x005&quot; # wrong \x00
  </code></pre>
  <p>Any hints appreciated :(</p>