Segredos

Segredos

Exercício de aprendizagem

Introdução

Os dígitos binários acabam correspondendo diretamente aos transistores da sua CPU ou da sua RAM, e ao fato de cada um estar "ligado" ou "desligado".

A manipulação de baixo nível, chamada informalmente de "bit-twiddling", é especialmente importante em linguagens de sistema.

Linguagens de alto nível como Julia geralmente abstraem a maior parte desse detalhe. Ainda assim, uma gama completa de operações em nível de bits está disponível na linguagem base.

Nota: Para ver uma saída binária legível por humanos no REPL, quase todos os exemplos abaixo precisam estar envolvidos por uma função bitstring(). Isso atrapalha visualmente, então a maioria das ocorrências dessa função foi removida.

Operações de deslocamento de bits

Tipos inteiros, com ou sem sinal, podem ser representados como uma string de 1s e 0s.

julia> bitstring(UInt8(5))
"00000101"

Os deslocamentos de bits simplesmente movem tudo para a esquerda ou para a direita por um número especificado de posições. Com tipos UInt, alguns bits saem por uma extremidade, e a outra extremidade é preenchida com zeros:

julia> ux::UInt8 = 5
5

julia> bitstring(ux)
"00000101"

julia> ux << 2 # left by 2
"00010100"

julia> ux >> 1 # right by 1
"00000010"

Cada deslocamento para a esquerda dobra o valor e cada deslocamento para a direita o divide pela metade (sujeito a truncamento). Isso fica mais óbvio na representação decimal:

julia> 3 << 2
12

julia> 24 >> 3
3

Esse tipo de deslocamento de bits é muito mais rápido que aritmética "de verdade", o que torna a técnica muito popular na programação de baixo nível.

Com inteiros com sinal, precisamos tomar um pouco mais de cuidado.

Os deslocamentos para a esquerda são relativamente simples:

julia> sx = Int8(5)
5

julia> sx # positive integer
"00000101"

julia> sx << 2
"00010100"

julia> -sx # negative integer
"11111011"

julia> -sx << 2
"11101100"

Deslocar inteiros positivos com sinal para a esquerda é, portanto, o mesmo que com inteiros sem sinal.

Valores negativos são armazenados na forma de complemento de dois, o que significa que o bit mais à esquerda é 1. Sem problema para um deslocamento à esquerda, mas na hora de deslocar à direita, como preenchemos os bits mais à esquerda?

julia> sx >> 2 # simple for positive values!
"00000001"

julia> -sx # negative integer
"11111011"

julia> -sx >> 2 # pad with repeated sign bit
"11111110"

julia> -sx >>> 2 # pad with 0
"00111110"

O operador >> realiza deslocamento aritmético, preservando o bit de sinal.

O operador >>> realiza deslocamento lógico, preenchendo com zeros como se o número não tivesse sinal.

Lógica bit a bit

Vimos em um Conceito anterior que os operadores && (and), || (or) e ! (not) são usados com valores Boolean.

Existem operadores equivalentes, & (and bit a bit), | (or bit a bit) e ~ (um til, not bit a bit), para comparar os bits de dois inteiros.

julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"

julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"

julia> ~0b1011 # flip all bits
"11110100"

julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"

Aqui, xor() é o ou exclusivo, usado como função.

Instruções

Seu amigo acabou de te enviar uma mensagem com um segredo importante. Como ele não queria facilitar a leitura por outras pessoas, a mensagem foi criptografada por meio de uma série de manipulações de bits. Você vai precisar escrever as funções que ajudam a descriptografar a mensagem.

1. Desloque os bits de volta

O primeiro passo para descriptografar a mensagem é desfazer o deslocamento do processo de criptografia, deslocando os bits de volta para a direita. Haverá outros passos no processo de descriptografia que assumem que 0s são inseridos pelo lado esquerdo.

Implemente a função shift_back(), que recebe um valor e o número de posições a deslocar, e realiza o deslocamento.

shift_back(0b1001, 2) # => 0b0010

2. Defina alguns bits

Agora, há alguns bits que precisam ser definidos como 1.

Implemente a função set_bits(), que recebe um valor e uma máscara e retorna o resultado de definir como 1 os bits do valor. Um bit de value deve ser definido como 1 onde o bit em mask também for 1. Todos os outros bits devem permanecer inalterados.

set_bits(0b0110, 0b0101) # => 0b0111

3. Inverta bits específicos

Alguns bits são invertidos durante a criptografia. Eles precisarão ser invertidos de volta para descriptografar a mensagem.

Implemente a função flip_bits(), que recebe um valor e a máscara. A máscara indica quais bits do valor devem ser invertidos. Se o bit for 1 em mask, o bit é invertido em value. Todos os outros bits permanecem inalterados.

flip_bits(0b1100, 0b0101) # => 0b1001

4. Limpe bits específicos

Por último, também há certos bits que sempre descriptografam para 0.

Implemente a função clear_bits(), que recebe um valor e uma máscara. Os bits de value devem ser definidos como 0 onde o bit na máscara for 1. Todos os outros bits devem permanecer inalterados.

clear_bits(0b0110, 0b0101) # => 0b0010
Editar via GitHub O link abre em uma nova janela ou aba
Julia Exercism

Tudo pronto para começar Segredos?

Crie sua conta no Exercism para aprender e dominar Julia com 35 conceitos128 exercícios e mentoria humana de verdade, tudo de graça.