Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bitwise concept #722

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
90ed84e
Initial stub.
stewartmurrie Nov 2, 2024
906a767
Adds stub for bitwise concept exericse (secrets)
stewartmurrie Nov 2, 2024
c925970
Adds first draft of concept
stewartmurrie Nov 2, 2024
52770b9
Added notes on masking
stewartmurrie Nov 3, 2024
82e0424
Adds introduction.md (a copy of about.md)
stewartmurrie Nov 3, 2024
77679ee
Updates config and renames concept folder
stewartmurrie Nov 3, 2024
cbbe48f
Updates configs (lints OK now)
stewartmurrie Nov 3, 2024
2f18e88
Updates allergies metadata to refer to bitwise-operations
stewartmurrie Nov 3, 2024
2d5dea3
Adds secrets exercise docs
stewartmurrie Nov 3, 2024
9a17e1c
Adds stub, example implementation, and tests
stewartmurrie Nov 3, 2024
6e4d3f3
Removes the WIP status from the exercise
stewartmurrie Nov 5, 2024
f8fca1a
Fixes typo in the exercise instructions
stewartmurrie Nov 5, 2024
b073b12
Adds Secret handshake to bitwise exercises
stewartmurrie Nov 7, 2024
54545de
Updates heading
stewartmurrie Nov 7, 2024
b0cf475
Updates for consistency & formatting
stewartmurrie Nov 7, 2024
7d00134
Update for consistency with other exercises
stewartmurrie Nov 7, 2024
aa79d27
Attempts to use templates
stewartmurrie Nov 17, 2024
ce39d30
Updates to the templated
stewartmurrie Nov 17, 2024
fc09e07
Simplified the examples. Added spacing.
stewartmurrie Nov 17, 2024
e76389a
Added general hint linking to package docs
stewartmurrie Nov 17, 2024
6ceb47f
Adds helpful links to the hints
stewartmurrie Nov 17, 2024
40ad002
Fixes typos in the hints
stewartmurrie Nov 17, 2024
6a60cab
Adds 5th concept step to tie it all together
stewartmurrie Nov 17, 2024
662f383
Formatting
stewartmurrie Nov 17, 2024
d7cfa4b
Fixes doc bugs
stewartmurrie Nov 24, 2024
0ca8335
Add contribs
stewartmurrie Nov 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions concepts/bitwise-operations/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"authors": [
"stewartmurrie"
],
"contributors": [ ],
"blurb": "Learn how to use integer bitwise operations in Elm"
}
1 change: 1 addition & 0 deletions concepts/bitwise-operations/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(Will be a copy of `introduction.md` once that is approved)
115 changes: 115 additions & 0 deletions concepts/bitwise-operations/introduction.md
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Introduction

Bitwise operations allow us to manipulate individual digits within binary numbers.
These operations are fundamental in computing, providing an efficient way to perform low-level tasks, such as controlling specific bits in memory, optimizing mathematical calculations, encryption, communications, and encoding/decoding data.

## Understanding 32-bit Integers in Elm
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved

In Elm, integers are stored as 32-bit signed integers using [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) representation, meaning they use 32 binary digits (_bits_) to store each integer value.
This bit limit affects the range of integers that Elm can represent directly:

Positive Range: 0 to 2<sup>31</sup> - 1 (or 0 to 2,147,483,647)
Negative Range: -2<sup>31</sup> to -1 (or -2,147,483,648 to -1).

For example, the integer `5` is represented in binary as `00000000000000000000000000000101`, although usually we ignore the leading zeros and just say `101`.

## Bitwise operations

Elm provides several bitwise operators in its [Bitwise module](https://package.elm-lang.org/packages/elm/core/latest/Bitwise)

## Basic operations

Modifying individual bits of a number is called _masking_.
A _mask_ is a number where specific bits have been set in a particular way to manipulate another number using bitwise operators such as `and`, `or`, and `xor`.

### and

`and` combines two numbers by keeping only the bits that are `1` in both.
This is useful for checking to see if an individual bit is set.
For example, to check if the 4th bit of a number is set to `1`, `and` it with a mask of `01000` (`8` in decimal) and see if the result is non-zero:

````elm
Bitwise.and 13 8 --> 8
-- 13 = 01101
-- 8 = 01000
-- and = 01000 = 8


### or

`or` combines two numbers by setting each bit to `1` if it is `1` in either or both numbers.
This is useful for setting a specific bit to `1`.
For example, to set the 2nd bit in `10101`, `or` it with the mask `00010`:

```elm
Bitwise.or 21 2 --> 23
-- 21 = 10101
-- 2 = 00010
-- or = 10111 = 23
````

### Exclusive-or (xor)

`xor` combines two numbers by setting each bit to `1` if it is `1` in one number but `0` in the other.
This is useful for flipping a bit to its opposite value:

````elm
Bitwise.xor 21 4 --> 17
-- 21 = 10101
-- 4 = 00100
-- xor = 10001 = 17

### Complement

`complement` inverts each bit of a number (`0` becomes `1`, `1` becomes `0`).

Note that this will result in positive numbers becoming negative, and negative numbers becoming positive.
This is because negative numbers in binary are represented with `1` in the left-most position.

```elm
Bitwise.complement 21 --> -22
-- 21 = 00000000000000000000000000010101
-- complement = 11111111111111111111111111101010 = -22
````

## Bit shifting

The following operators move bits left or right by a specified number of positions, effectively multiplying or dividing by powers of 2.

`shiftLeftBy` moves bits to the left, filling in with `0` from the right-hand side.
For example, to shift `21` left by 3 places:

```elm
Bitwise.shiftLeftBy 3 21 --> 168
-- 21 = 10101
-- shiftLeftBy 3 = 10101000 = 168
```

This is the same as saying `21 * 2^3 = 21 * 2 * 2 * 2 = 168`

`shiftRightBy`: Moves bits to the right:

```elm
Bitwise.shiftRightBy 2 21 --> 5
-- 21 = 10101
-- shiftRightBy 2 = 00101 = 5
```

Shifting to the right by 2 places is the same as integer division by 4.

Note that this function duplicates whatever value is in the leftmost bit.
So, negative numbers will stay negative:

```elm
Bitwise.shiftRightBy 3 -21 --> -6
-- -21 = 111...10101
-- shiftRightZfBy 3 = 111...11101 = -6
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
```

If you want to shift right and fill in with zeros, use `shiftRightZfBy`:

```elm
Bitwise.shiftRightZfBy 3 -21 --> 1073741818
-- -21 = 111...10101
-- shiftRightBy 3 = 00111...11101 = 1073741818
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
```
10 changes: 10 additions & 0 deletions concepts/bitwise-operations/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://package.elm-lang.org/packages/elm/core/latest/Bitwise",
"description": "Bitwise module"
},
{
"url": "https://guide.elm-lang.org/appendix/types_as_bits",
"description": "Documentation on how Elm represents types as bits"
}
]
31 changes: 27 additions & 4 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,18 @@
"booleans"
],
"status": "beta"
},
{
"slug": "secrets",
"name": "Secrets",
"uuid": "89a899b1-8cb2-4099-98c9-468723728a28",
"concepts": [
"bitwise-operations"
],
"prerequisites": [
"basics-1",
"basics-2"
]
}
],
"practice": [
Expand Down Expand Up @@ -503,11 +515,14 @@
"slug": "allergies",
"name": "Allergies",
"uuid": "29b5a28a-417a-4cee-ba6f-9dd942ceffaa",
"practices": [],
"prerequisites": [],
"practices": [
"bitwise-operations"
],
"prerequisites": [
"bitwise-operations"
],
"difficulty": 4,
"topics": [
"bitwise_operations",
"enumerations",
"filtering"
]
Expand Down Expand Up @@ -1255,8 +1270,11 @@
"slug": "secret-handshake",
"name": "Secret Handshake",
"uuid": "6c7a96ec-0bfb-4284-bd9f-2aa6989c3bb5",
"practices": [],
"practices": [
"bitwise-operations"
],
"prerequisites": [
"bitwise-operations",
"custom-types",
"pattern-matching",
"maybe",
Expand Down Expand Up @@ -1505,6 +1523,11 @@
"uuid": "357ffb6e-5a73-49b6-8b69-98ecd3c74c33",
"slug": "random",
"name": "Random"
},
{
"uuid": "8737961b-c96f-4313-8737-6b88034c66d8",
"slug": "bitwise-operations",
"name": "Bitwise Operations"
}
],
"key_features": [
Expand Down
38 changes: 38 additions & 0 deletions exercises/concept/secrets/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Hints
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved

- The documentation for the `Bitwise` package can be found [here][bitwise-docs].

## 1. Shift back the bits

- There are two functions for shifting bits to the right, but [only one][bitwise-shiftRightZfBy] will always insert a 0.

## 2. Set some bits

- [One of the bitwise functions][bitwise-or] will always set a bit to 1 where the bits in both values are 1.

## 3. Flip specific bits

- There is [a bitwise function][bitwise-xor] that will flip a bit where the mask is 1.

## 4. Clear specific bits

- [One of the bitwise functions][bitwise-and] clears bits where the bit in the mask is 0.
- But, you may need to combine it with [another function][bitwise-complement] to clear bits where the mask is 1.

## 5. Decrypt a message

- Apply the other functions you wrote to the input in the following order, taking the output of one and using it as the input to the next one:

1. `setBits`
2. `flipBits`
3. `shiftBack`
4. `clearBits`

For step 4, you'll need to convert the binary number with the 1st and 5th bits set (10001) to decimal.

[bitwise-docs]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise
[bitwise-shiftRightZfBy]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise#shiftRightZfBy
[bitwise-or]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise#or
[bitwise-xor]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise#xor
[bitwise-complement]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise#complement
[bitwise-and]: https://package.elm-lang.org/packages/elm/core/latest/Bitwise#and
68 changes: 68 additions & 0 deletions exercises/concept/secrets/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Instructions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you think of adding a fifth step that uses the previous ones?
Something like "decrypt the message": set the bits of your friend's birth year (1988) to 1, then flip the bits of your birth year (1996), shift back the bits by your friend's favorite power of two (2) and clear the last bit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea! I've added something along those lines; let me know what you think

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's perfect :)
Nice call replacing "your birth year" with the year you met.


Your friend has just sent you a message with an important secret.
Not wanting to make it easy for others to read it, the message was encrypted by performing a series of bit manipulations.
You will need to write the functions to help decrypt the message.

## 1. Shift back the bits

The first step in decrypting the message is to undo the shifting from the encryption process by shifting the bits back to the right.
There will be further steps in the decryption process that assume `0`s are inserted from the left hand side.

Implement the `shiftBack` function that takes a number of places to shift by and a value and peforms the shift.
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved

```elm
shiftBack 2 42 --> 10
```

## 2. Set some bits

Next, there are some bits that need to be set to `1`.

Implement the `setBits` function that takes a mask and value and returns the result of setting the bits in value to `1`.
A bit from value should be set to 1 where the bit in the mask is also `1`.
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
All other bits should be kept unchanged.

```elm
setBits 66 212 --> 64
```

## 3. Flip specific bits

Some bits are flipped during encryption.
They will need to be flipped back to decrypt the message.

Implement the `flipBits` function that takes a mask and a value.
The mask indicates which bits in the value to flip.
If the bit is `1` in mask, the bit is flipped in the value.
All other bits are kept unchanged.

```elm
flipBits 23 157 --> 138
```

## 4. Clear specific bits

There are also certain bits that always decrypt to 0.

Implement the `clearBits` functions that takes a mask and a value.
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
The bits in the `value` should be set to 0 where the bit in the mask is 1.
All other bits should be kept unchanged.

```elm
clearBits 2 15 --> 13
```

## 5. Decrypt a message

Now that you have all the functions you need, you can decode your friend's message.
Implement the `decrypt` function that performs the following operations:

1. Set the bits from the year your friend was born (1996)
2. Flip the result with the year that you first met (2009)
3. Shift the bits back by the number of classes you take together (5)
4. Clear the first and fifth bit.

```elm
decrypt 380182 -->
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
```
1 change: 1 addition & 0 deletions exercises/concept/secrets/.docs/introduction.md.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%{concept: bitwise-operations}
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 29 additions & 0 deletions exercises/concept/secrets/.meta/Exemplar.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module Secrets exposing (clearBits, decrypt, flipBits, setBits, shiftBack)

import Bitwise


shiftBack amount value =
Bitwise.shiftRightZfBy amount value


setBits mask value =
Bitwise.or mask value


flipBits mask value =
Bitwise.xor mask value


clearBits mask value =
mask
|> Bitwise.complement
|> Bitwise.and value


decrypt secret =
secret
|> setBits 1996
|> flipBits 2009
|> shiftBack 5
|> clearBits 17
20 changes: 20 additions & 0 deletions exercises/concept/secrets/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"stewartmurrie"
],
"files": {
"solution": [
"src/Secrets.elm"
],
"test": [
"tests/Tests.elm"
],
"exemplar": [
".meta/Exemplar.elm"
]
},
"forked_from": [
"java/secrets"
],
"blurb": "Learn about bit manipulation by writing the software for an encryption device."
}
29 changes: 29 additions & 0 deletions exercises/concept/secrets/elm.json
stewartmurrie marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/core": "1.0.5",
"elm/json": "1.1.3",
"elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm/regex": "1.0.0",
"elm/time": "1.0.0"
},
"indirect": {}
},
"test-dependencies": {
"direct": {
"elm-explorations/test": "2.1.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/html": "1.0.0",
"elm/virtual-dom": "1.0.3"
}
}
}
Loading
Loading