diff --git a/config.json b/config.json index 7731b9f..0cbe874 100644 --- a/config.json +++ b/config.json @@ -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", diff --git a/exercises/practice/octal/.docs/instructions.md b/exercises/practice/octal/.docs/instructions.md new file mode 100644 index 0000000..0074560 --- /dev/null +++ b/exercises/practice/octal/.docs/instructions.md @@ -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 +``` diff --git a/exercises/practice/octal/.meta/Example.roc b/exercises/practice/octal/.meta/Example.roc new file mode 100644 index 0000000..d27695a --- /dev/null +++ b/exercises/practice/octal/.meta/Example.roc @@ -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 diff --git a/exercises/practice/octal/.meta/config.json b/exercises/practice/octal/.meta/config.json new file mode 100644 index 0000000..49e579e --- /dev/null +++ b/exercises/practice/octal/.meta/config.json @@ -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" +} diff --git a/exercises/practice/octal/Octal.roc b/exercises/practice/octal/Octal.roc new file mode 100644 index 0000000..d7bb45f --- /dev/null +++ b/exercises/practice/octal/Octal.roc @@ -0,0 +1,5 @@ +module [parse] + +parse : Str -> Result U64 _ +parse = \string -> + crash "Please implement the 'parse' function" diff --git a/exercises/practice/octal/octal-test.roc b/exercises/practice/octal/octal-test.roc new file mode 100644 index 0000000..628dc88 --- /dev/null +++ b/exercises/practice/octal/octal-test.roc @@ -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