docs: def sha2_crypt(key, salt, hashfunc, rounds=_ROUNDS_DEFAULT):
      """
      This algorithm is insane. History can be found at
      https://en.wikipedia.org/wiki/Crypt_%28C%29
      """
      key = key.encode('utf-8')
      h = hashfunc()
      alt_h = hashfunc()
      digest_size = h.digest_size
      key_len = len(key)
  
      # First, feed key, salt and then key again to the alt hash
      alt_h.update(key)
      alt_h.update(salt.encode('utf-8'))
      alt_h.update(key)
      alt_result = alt_h.digest()
  
      # Feed key and salt to the primary hash
      h.update(key)
      h.update(salt.encode('utf-8'))
  
      # Feed as many (loopping) bytes from alt digest as the length of the key
      for i in range(key_len//digest_size):
          h.update(alt_result)
      h.update(alt_result[:(key_len % digest_size)])
  
      # Take the binary representation of the length of the key and for every
      # 1 add the alternate digest, for every 0 the key
      bits = key_len
      while bits > 0:
          if bits & 1 == 0:
              h.update(key)
          else:
              h.update(alt_result)
          bits >>= 1
  
      # Store the results from the primary hash
      alt_result = h.digest()
  
      h = hashfunc()
  
      # Add password for each character in the password
      for i in range(key_len):
          h.update(key)
  
      temp_result = h.digest()
  
      # Compute a P array of the bytes in temp repeated for the length of the key
      p_bytes = temp_result * (key_len // digest_size)
      p_bytes += temp_result[:(key_len % digest_size)]
  
      alt_h = hashfunc()
  
      # Add the salt 16 + arbitrary amount decided by first byte in alt digest
      for i in range(16 + byte2int(alt_result[0])):
          alt_h.update(salt.encode('utf-8'))
  
      temp_result = alt_h.digest()
  
      # Compute a S array of the bytes in temp_result repeated for the length
      # of the salt
      s_bytes = temp_result * (len(salt) // digest_size)
      s_bytes += temp_result[:(len(salt) % digest_size)]
  
      # Do the actual iterations
      for i in range(rounds):
          h = hashfunc()
  
          # Alternate adding either the P array or the alt digest
          if i & 1 != 0:
              h.update(p_bytes)
          else:
              h.update(alt_result)
  
          # If the round is divisible by 3, add the S array
          if i % 3 != 0:
              h.update(s_bytes)
  
          # If the round is divisible by 7, add the P array
          if i % 7 != 0:
              h.update(p_bytes)
  
          # Alternate adding either the P array or the alt digest, opposite
          # of first step
          if i & 1 != 0:
              h.update(alt_result)
          else:
              h.update(p_bytes)
  
          alt_result = h.digest()
  
      # Compute the base64-ish representation of the hash
      ret = []
      if digest_size == 64:
          # SHA-512
          ret.append(b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4))
          ret.append(b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4))
          ret.append(b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4))
          ret.append(b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4))
          ret.append(b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4))
          ret.append(b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4))
          ret.append(b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4))
          ret.append(b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4))
          ret.append(b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4))
          ret.append(b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4))
          ret.append(b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4))
          ret.append(b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4))
          ret.append(b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4))
          ret.append(b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4))
          ret.append(b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4))
          ret.append(b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4))
          ret.append(b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4))
          ret.append(b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4))
          ret.append(b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4))
          ret.append(b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4))
          ret.append(b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4))
          ret.append(b64_from_24bit(int2byte(0), int2byte(0), alt_result[63], 2))
      else:
          # SHA-256
          ret.append(b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4))
          ret.append(b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4))
          ret.append(b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4))
          ret.append(b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4))
          ret.append(b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4))
          ret.append(b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4))
          ret.append(b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4))
          ret.append(b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4))
          ret.append(b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4))
          ret.append(b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4))
          ret.append(b64_from_24bit(int2byte(0), alt_result[31], alt_result[30], 3))
  
      algo = 6 if digest_size == 64 else 5
      if rounds == _ROUNDS_DEFAULT:
          return '${0}${1}${2}'.format(algo, salt, ''.join(ret))
      else:
          return '${0}$rounds={1}${2}${3}'.format(algo, rounds, salt, ''.join(ret))