Skip to content

Commit

Permalink
Add octal exercise (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
ageron authored Oct 2, 2024
1 parent 6d443d2 commit c13d006
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "octal",
"name": "Octal",
"uuid": "98f71971-66cd-48b0-8f54-dab6b1505086",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "proverb",
"name": "Proverb",
Expand Down
45 changes: 45 additions & 0 deletions exercises/practice/octal/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Instructions

Convert an octal number, represented as a string (e.g. '1735263'), to its decimal equivalent using first principles (i.e. no, you may not use built-in or external libraries to accomplish the conversion).

Implement octal to decimal conversion.
Given an octal input string, your program should produce a decimal output.

## Note

- Implement the conversion yourself.
Do not use something else to perform the conversion for you.
- Treat invalid input as octal 0.

## About Octal (Base-8)

Decimal is a base-10 system.

A number 233 in base 10 notation can be understood
as a linear combination of powers of 10:

- The rightmost digit gets multiplied by 10^0 = 1
- The next number gets multiplied by 10^1 = 10
- ...
- The nth number gets multiplied by 10^_(n-1)_.
- All these values are summed.

So:

```text
233 # decimal
= 2*10^2 + 3*10^1 + 3*10^0
= 2*100 + 3*10 + 3*1
```

Octal is similar, but uses powers of 8 rather than powers of 10.

So:

```text
233 # octal
= 2*8^2 + 3*8^1 + 3*8^0
= 2*64 + 3*8 + 3*1
= 128 + 24 + 3
= 155
```
22 changes: 22 additions & 0 deletions exercises/practice/octal/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module [parse]

parseOctalDigit = \char ->
if char >= '0' && char <= '7' then
Ok (char - '0' |> Num.toU64)
else
Err InvalidNumStr

parse : Str -> Result U64 _
parse = \string ->
if string == "" then
Err InvalidNumStr
else

string
|> Str.toUtf8
|> List.walkTry 0 \number, char ->
octalDigit = parseOctalDigit? char
if number > 0x1fffffffffffffff then
Err InvalidNumStr
else
number |> Num.shiftLeftBy 3 |> Num.add octalDigit |> Ok
19 changes: 19 additions & 0 deletions exercises/practice/octal/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"ageron"
],
"files": {
"solution": [
"Octal.roc"
],
"test": [
"octal-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"blurb": "Convert a octal number, represented as a string (e.g. '1735263'), to its decimal equivalent using first principles (i.e. no, you may not use built-in or external libraries to accomplish the conversion).",
"source": "All of Computer Science",
"source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
}
5 changes: 5 additions & 0 deletions exercises/practice/octal/Octal.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module [parse]

parse : Str -> Result U64 _
parse = \string ->
crash "Please implement the 'parse' function"
219 changes: 219 additions & 0 deletions exercises/practice/octal/octal-test.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

main =
Task.ok {}

import Octal exposing [parse]

# Parse "0"
expect
result = parse "0"
result == Ok 0

# Parse "1"
expect
result = parse "1"
result == Ok 1

# Parse "2"
expect
result = parse "2"
result == Ok 2

# Parse "3"
expect
result = parse "3"
result == Ok 3

# Parse "4"
expect
result = parse "4"
result == Ok 4

# Parse "5"
expect
result = parse "5"
result == Ok 5

# Parse "6"
expect
result = parse "6"
result == Ok 6

# Parse "7"
expect
result = parse "7"
result == Ok 7

# Parse "10"
expect
result = parse "10"
result == Ok 8

# Parse "11"
expect
result = parse "11"
result == Ok 9

# Parse "12"
expect
result = parse "12"
result == Ok 10

# Parse "13"
expect
result = parse "13"
result == Ok 11

# Parse "14"
expect
result = parse "14"
result == Ok 12

# Parse "15"
expect
result = parse "15"
result == Ok 13

# Parse "16"
expect
result = parse "16"
result == Ok 14

# Parse "17"
expect
result = parse "17"
result == Ok 15

# Parse "20"
expect
result = parse "20"
result == Ok 16

# Parse "21"
expect
result = parse "21"
result == Ok 17

# Parse "22"
expect
result = parse "22"
result == Ok 18

# Parse "77"
expect
result = parse "77"
result == Ok 63

# Parse "100"
expect
result = parse "100"
result == Ok 64

# Parse "1234567654321"
expect
result = parse "1234567654321"
result == Ok 89755965649

# Parse "1777777777777777777777", the largest U64 value
expect
result = parse "1777777777777777777777"
result == Ok 18446744073709551615

# Ignore leading zeros in "00000"
expect
result = parse "00000"
result == Ok 0

# Ignore leading zeros in "00001"
expect
result = parse "00001"
result == Ok 1

# Ignore leading zeros in "00007"
expect
result = parse "00007"
result == Ok 7

# Ignore leading zeros in "000010"
expect
result = parse "000010"
result == Ok 8

# Ignore leading zeros in "000077"
expect
result = parse "000077"
result == Ok 63

# Ignore leading zeros in "000070000"
expect
result = parse "000070000"
result == Ok 28672

# Ignore leading zeros even before the largest U64 value
expect
result = parse "00001777777777777777777777"
result == Ok 18446744073709551615

# Empty strings are invalid
expect
result = parse ""
result |> Result.isErr

# A string with only spaces is invalid
expect
result = parse " "
result |> Result.isErr

# Leading spaces are invalid
expect
result = parse " 1234567"
result |> Result.isErr

# Trailing spaces are invalid
expect
result = parse "1234567 "
result |> Result.isErr

# Spaces anywhere are invalid
expect
result = parse "123 4567"
result |> Result.isErr

# Invalid character in "1*234"
expect
result = parse "1*234"
result |> Result.isErr

# Invalid character in "12abc"
expect
result = parse "12abc"
result |> Result.isErr

# Invalid character in "12/34"
expect
result = parse "12/34"
result |> Result.isErr

# Invalid character in "1234\n"
expect
result = parse "1234\n"
result |> Result.isErr

# Invalid character in "-1234"
expect
result = parse "-1234"
result |> Result.isErr

# Invalid character in "+1234"
expect
result = parse "+1234"
result |> Result.isErr

# For numbers that don't fit in an U64, `parse` should return an error
# instead of crashing
expect
result = parse "2000000000000000000000"
result |> Result.isErr

0 comments on commit c13d006

Please sign in to comment.