From 8425c7514449dde8faa12425a72ec6f412c09a08 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Thu, 18 Jan 2024 18:01:53 +1100 Subject: [PATCH] Add roman-numerals exercise --- config.json | 9 + .../practice/roman-numerals/.docs/hints.md | 6 + .../.docs/instructions.append.md | 9 + .../roman-numerals/.docs/instructions.md | 41 +++++ .../practice/roman-numerals/.meta/config.json | 19 +++ .../roman-numerals/.meta/example.mips | 96 +++++++++++ .../practice/roman-numerals/.meta/tests.toml | 88 ++++++++++ exercises/practice/roman-numerals/impl.mips | 10 ++ exercises/practice/roman-numerals/runner.mips | 160 ++++++++++++++++++ 9 files changed, 438 insertions(+) create mode 100644 exercises/practice/roman-numerals/.docs/hints.md create mode 100644 exercises/practice/roman-numerals/.docs/instructions.append.md create mode 100644 exercises/practice/roman-numerals/.docs/instructions.md create mode 100644 exercises/practice/roman-numerals/.meta/config.json create mode 100644 exercises/practice/roman-numerals/.meta/example.mips create mode 100644 exercises/practice/roman-numerals/.meta/tests.toml create mode 100644 exercises/practice/roman-numerals/impl.mips create mode 100644 exercises/practice/roman-numerals/runner.mips diff --git a/config.json b/config.json index 71b37fb..e2c861a 100644 --- a/config.json +++ b/config.json @@ -152,6 +152,15 @@ "difficulty": 4, "topics": [] }, + { + "slug": "roman-numerals", + "name": "Roman Numerals", + "uuid": "fa283d1e-2005-40d3-9a30-252d06d98523", + "practices": [], + "prerequisites": [], + "difficulty": 4, + "topics": [] + }, { "slug": "scrabble-score", "name": "Scrabble Score", diff --git a/exercises/practice/roman-numerals/.docs/hints.md b/exercises/practice/roman-numerals/.docs/hints.md new file mode 100644 index 0000000..52d65b0 --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/hints.md @@ -0,0 +1,6 @@ +# Hints + +## General + +- The `$t0-9` registers can be used to temporarily store values +- The instructions specify which registers are used as input and output diff --git a/exercises/practice/roman-numerals/.docs/instructions.append.md b/exercises/practice/roman-numerals/.docs/instructions.append.md new file mode 100644 index 0000000..acc515a --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/instructions.append.md @@ -0,0 +1,9 @@ +# Instructions append + +## Registers + +| Register | Usage | Type | Description | +| -------- | ------------ | ------- | ----------------------------- | +| `$a0` | input | integer | given number | +| `$a1` | input/output | address | null-terminated result string | +| `$t0-9` | temporary | any | for temporary storage | diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md new file mode 100644 index 0000000..247ea08 --- /dev/null +++ b/exercises/practice/roman-numerals/.docs/instructions.md @@ -0,0 +1,41 @@ +# Instructions + +Write a function to convert from normal numbers to Roman Numerals. + +The Romans were a clever bunch. +They conquered most of Europe and ruled it for hundreds of years. +They invented concrete and straight roads and even bikinis. +One thing they never discovered though was the number zero. +This made writing and dating extensive histories of their exploits slightly more challenging, but the system of numbers they came up with is still in use today. +For example the BBC uses Roman numerals to date their programs. + +The Romans wrote numbers using letters - I, V, X, L, C, D, M. +(notice these letters have lots of straight lines and are hence easy to hack into stone tablets). + +```text + 1 => I +10 => X + 7 => VII +``` + +The maximum number supported by this notation is 3,999. +(The Romans themselves didn't tend to go any higher) + +Wikipedia says: Modern Roman numerals ... are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero. + +To see this in practice, consider the example of 1990. + +In Roman numerals 1990 is MCMXC: + +1000=M +900=CM +90=XC + +2008 is written as MMVIII: + +2000=MM +8=VIII + +Learn more about [Roman numerals on Wikipedia][roman-numerals]. + +[roman-numerals]: https://wiki.imperivm-romanvm.com/wiki/Roman_Numerals diff --git a/exercises/practice/roman-numerals/.meta/config.json b/exercises/practice/roman-numerals/.meta/config.json new file mode 100644 index 0000000..49a215c --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "impl.mips" + ], + "test": [ + "runner.mips" + ], + "example": [ + ".meta/example.mips" + ] + }, + "blurb": "Write a function to convert from normal numbers to Roman Numerals.", + "source": "The Roman Numeral Kata", + "source_url": "https://codingdojo.org/kata/RomanNumerals/" +} diff --git a/exercises/practice/roman-numerals/.meta/example.mips b/exercises/practice/roman-numerals/.meta/example.mips new file mode 100644 index 0000000..c5b3a6c --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/example.mips @@ -0,0 +1,96 @@ +# Converts number to Roman Numerals. +# +# $a0 - input value of integer in question +# $a1 - input, pointer to expected location of transcribed string +# $t0 - address of fives +# $t1 - address of ones +# $t2 - return address +# $t3 - low digits of input +# $t4 - current digit value (0..9) +# $t5 - current digit place (0 for units, 1 for tens, 2 for hundreds, 3 for thousands) +# $t6 - divisor +# $t7 - character for roman numeral, in {I, V, X, L, C, D, M} + +.globl roman + +.data + +ones: .asciiz "IXCM" +fives: .asciiz "VLD" + +.text + +roman: + move $t2, $ra # make a copy of return address + la $t1, ones # address of ones + la $t0, fives # address of fives + + li $t6, 1000 + div $a0, $t6 + mfhi $t3 # input % 1000 + mflo $t4 # thousands digit + li $t5, 3 # thousands place + jal convert_digit + + li $t6, 100 + div $t3, $t6 + mfhi $t3 # input % 100 + mflo $t4 # hundreds digit + li $t5, 2 # hundreds place + jal convert_digit + + li $t6, 10 + div $t3, $t6 + mfhi $t3 # input % 10 + mflo $t4 # tens digit + li $t5, 1 # tens place + jal convert_digit + + move $t4, $t3 # units digit + li $t5, 0 # units place + jal convert_digit + + sb $zero, 0($a1) # write '\0' terminator + + jr $t2 # return to caller + +convert_digit: + # subroutine that converts current digit + # (with value $t4, place $t5) to roman numerals. + li $t6, 5 + rem $t6, $t4, $t6 + bne $t6, 4, simple # jump forward if the digit is not 4 (mod 5) + + add $t7, $t1, $t5 # address of character for digit 1 + lb $t7, 0($t7) # read character for digit 1 + sb $t7, 0($a1) # write character for digit 1 + addi $a1, $a1, 1 # increment output address + + addi $t4, $t4, 1 # increment digit by 1 + bne $t4, 10, simple # jump forward if the digit has not become 10 + + li $t4, 1 # replace 10 with digit 1 + addi $t5, $t5, 1 # with increased place value + +simple: + blt $t4, 5, while_ones # jump forward if the digit is below 5 + + add $t7, $t0, $t5 # address of character for digit 5 + lb $t7, 0($t7) # read character for digit 5 + sb $t7, 0($a1) # write character for digit 5 + addi $a1, $a1, 1 # increment output address + + subi $t4, $t4, 5 # decrease digit by 5 + j while_ones # check if we need to write digit 1 + +do_ones: + add $t7, $t1, $t5 # address of character for digit 1 + lb $t7, 0($t7) # read character for digit 1 + sb $t7, 0($a1) # write character for digit 1 + addi $a1, $a1, 1 # increment output address + + subi $t4, $t4, 1 # decrease digit by 1 + +while_ones: + bne $t4, $zero, do_ones # loop until digit is 0 + jr $ra # return to caller (roman) diff --git a/exercises/practice/roman-numerals/.meta/tests.toml b/exercises/practice/roman-numerals/.meta/tests.toml new file mode 100644 index 0000000..57c6c4b --- /dev/null +++ b/exercises/practice/roman-numerals/.meta/tests.toml @@ -0,0 +1,88 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[19828a3a-fbf7-4661-8ddd-cbaeee0e2178] +description = "1 is I" + +[f088f064-2d35-4476-9a41-f576da3f7b03] +description = "2 is II" + +[b374a79c-3bea-43e6-8db8-1286f79c7106] +description = "3 is III" + +[05a0a1d4-a140-4db1-82e8-fcc21fdb49bb] +description = "4 is IV" + +[57c0f9ad-5024-46ab-975d-de18c430b290] +description = "5 is V" + +[20a2b47f-e57f-4797-a541-0b3825d7f249] +description = "6 is VI" + +[ff3fb08c-4917-4aab-9f4e-d663491d083d] +description = "9 is IX" + +[6d1d82d5-bf3e-48af-9139-87d7165ed509] +description = "16 is XVI" + +[2bda64ca-7d28-4c56-b08d-16ce65716cf6] +description = "27 is XXVII" + +[a1f812ef-84da-4e02-b4f0-89c907d0962c] +description = "48 is XLVIII" + +[607ead62-23d6-4c11-a396-ef821e2e5f75] +description = "49 is XLIX" + +[d5b283d4-455d-4e68-aacf-add6c4b51915] +description = "59 is LIX" + +[4465ffd5-34dc-44f3-ada5-56f5007b6dad] +description = "66 is LXVI" + +[46b46e5b-24da-4180-bfe2-2ef30b39d0d0] +description = "93 is XCIII" + +[30494be1-9afb-4f84-9d71-db9df18b55e3] +description = "141 is CXLI" + +[267f0207-3c55-459a-b81d-67cec7a46ed9] +description = "163 is CLXIII" + +[902ad132-0b4d-40e3-8597-ba5ed611dd8d] +description = "166 is CLXVI" + +[cdb06885-4485-4d71-8bfb-c9d0f496b404] +description = "402 is CDII" + +[6b71841d-13b2-46b4-ba97-dec28133ea80] +description = "575 is DLXXV" + +[dacb84b9-ea1c-4a61-acbb-ce6b36674906] +description = "666 is DCLXVI" + +[432de891-7fd6-4748-a7f6-156082eeca2f] +description = "911 is CMXI" + +[e6de6d24-f668-41c0-88d7-889c0254d173] +description = "1024 is MXXIV" + +[efbe1d6a-9f98-4eb5-82bc-72753e3ac328] +description = "1666 is MDCLXVI" + +[bb550038-d4eb-4be2-a9ce-f21961ac3bc6] +description = "3000 is MMM" + +[3bc4b41c-c2e6-49d9-9142-420691504336] +description = "3001 is MMMI" + +[4e18e96b-5fbb-43df-a91b-9cb511fe0856] +description = "3999 is MMMCMXCIX" diff --git a/exercises/practice/roman-numerals/impl.mips b/exercises/practice/roman-numerals/impl.mips new file mode 100644 index 0000000..31ad808 --- /dev/null +++ b/exercises/practice/roman-numerals/impl.mips @@ -0,0 +1,10 @@ +# | Register | Usage | Type | Description | +# | -------- | ------------ | ------- | ----------------------------- | +# | `$a0` | input | integer | given number | +# | `$a1` | input/output | address | null-terminated result string | +# | `$t0-9` | temporary | any | for temporary storage | + +.globl roman + +roman: + jr $ra diff --git a/exercises/practice/roman-numerals/runner.mips b/exercises/practice/roman-numerals/runner.mips new file mode 100644 index 0000000..3fbe948 --- /dev/null +++ b/exercises/practice/roman-numerals/runner.mips @@ -0,0 +1,160 @@ +# +# Test roman with some examples +# +# a0 - input word, for callee +# a1 - pointer to output string, for callee +# s0 - num of tests left to run +# s1 - address of input string +# s2 - address of expected output string +# s3 - char byte of input +# s4 - char byte of output +# s5 - copy of output location + +.data + +# number of test cases +n: .word 26 +# input values (word sized ints) +ins: .word + 1, + 2, + 3, + 4, + 5, + 6, + 9, + 16, + 27, + 48, + 49, + 59, + 66, + 93, + 141, + 163, + 166, + 402, + 575, + 666, + 911, + 1024, + 1666, + 3000, + 3001, + 3999 +# expected output values (null terminated) +outs: .asciiz + "I", + "II", + "III", + "IV", + "V", + "VI", + "IX", + "XVI", + "XXVII", + "XLVIII", + "XLIX", + "LIX", + "LXVI", + "XCIII", + "CXLI", + "CLXIII", + "CLXVI", + "CDII", + "DLXXV", + "DCLXVI", + "CMXI", + "MXXIV", + "MDCLXVI", + "MMM", + "MMMI", + "MMMCMXCIX" + +failmsg: .asciiz "failed for test input: " +expectedmsg: .asciiz ". expected " +tobemsg: .asciiz " to be " +okmsg: .asciiz "all tests passed" + + +.text + +runner: + lw $s0, n + la $s1, ins + la $s2, outs + + li $v0, 9 # code for allocating heap memory + li $a0, 16 # specify 16 bytes - length of longest expected output + syscall + move $a1, $v0 # location of allocated memory is where callee writes result + move $s5, $v0 # also keep a copy for ourselves + +run_test: + jal clear_output # zero out output location + + lw $a0, 0($s1) # load input value into a0 - MIPS calling convention + move $a1, $s5 # load @ output location - MIPS calling convention + jal roman # call subroutine under test + + move $t0, $s5 # set a temporary pointer to allocated memory + move $s7, $s2 # copy pointer to current expected output string +scan: + lb $s3, 0($s2) # load one byte of the expectation + lb $s4, 0($t0) # load one byte of the actual + bne $s3, $s4, exit_fail # if the two differ, the test has failed + addi $s2, $s2, 1 # point to next expectation byte + addi $t0, $t0, 1 # point to next actual byte + bne $s3, $zero, scan # if one char (and therefore the other) was not null, loop + +done_scan: + addi $s1, $s1, 4 # point to next input word + sub $s0, $s0, 1 # decrement num of tests left to run + bgt $s0, $zero, run_test # if more than zero tests to run, jump to run_test + +exit_ok: + la $a0, okmsg # put address of okmsg into a0 + li $v0, 4 # 4 is print string + syscall + + li $v0, 10 # 10 is exit with zero status (clean exit) + syscall + +exit_fail: + la $a0, failmsg # put address of failmsg into a0 + li $v0, 4 # 4 is print string + syscall + + lw $a0, 0($s1) # print input that failed on + li $v0, 1 # 1 -> "print integer" + syscall + + la $a0, expectedmsg + li $v0, 4 + syscall + + move $a0, $s5 # print actual that failed on + li $v0, 4 + syscall + + la $a0, tobemsg + li $v0, 4 + syscall + + move $a0, $s7 # print expected value that failed on + li $v0, 4 + syscall + + li $a0, 1 # set error code to 1 + li $v0, 17 # 17 is exit with error + syscall + +clear_output: + sw $zero, 0($s5) # zero out output by storing 4 words (16 bytes) of zeros + sw $zero, 4($s5) + sw $zero, 8($s5) + sw $zero, 12($s5) + jr $ra + +# # Include your implementation here if you wish to run this from the MARS GUI. +#.include "impl.mips"