diff --git a/config.json b/config.json index 82fe6e9..f6bdf84 100644 --- a/config.json +++ b/config.json @@ -505,6 +505,17 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "pythagorean-triplet", + "name": "Pythagorean Triplet", + "uuid": "548fa68b-98ff-4c3f-87ae-ef3a0cc605c1", + "practices": [], + "prerequisites": [], + "difficulty": 6, + "topics": [ + "math" + ] + }, { "slug": "all-your-base", "name": "All Your Base", diff --git a/exercises/practice/pythagorean-triplet/.docs/hints.md b/exercises/practice/pythagorean-triplet/.docs/hints.md new file mode 100644 index 0000000..52d65b0 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.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/pythagorean-triplet/.docs/instructions.append.md b/exercises/practice/pythagorean-triplet/.docs/instructions.append.md new file mode 100644 index 0000000..992ed3b --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.docs/instructions.append.md @@ -0,0 +1,12 @@ +# Instructions append + +## Registers + +| Register | Usage | Type | Description | +| -------- | ------------ | ------- | ---------------------------- | +| `$a0` | input | integer | N, sum of sides of triangle | +| `$a1` | input/output | address | a values | +| `$a2` | input/output | address | b values | +| `$a3` | input/output | address | c values | +| `$v0` | output | integer | number of triplets | +| `$t0-9` | temporary | any | for temporary storage | diff --git a/exercises/practice/pythagorean-triplet/.docs/instructions.md b/exercises/practice/pythagorean-triplet/.docs/instructions.md new file mode 100644 index 0000000..1c1a8ae --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for which, + +```text +a² + b² = c² +``` + +and such that, + +```text +a < b < c +``` + +For example, + +```text +3² + 4² = 5². +``` + +Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`. + +For example, with N = 1000, there is exactly one Pythagorean triplet for which `a + b + c = 1000`: `{200, 375, 425}`. diff --git a/exercises/practice/pythagorean-triplet/.meta/config.json b/exercises/practice/pythagorean-triplet/.meta/config.json new file mode 100644 index 0000000..37715ee --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "impl.mips" + ], + "test": [ + "runner.mips" + ], + "example": [ + ".meta/example.mips" + ] + }, + "blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the triplet.", + "source": "Problem 9 at Project Euler", + "source_url": "https://projecteuler.net/problem=9" +} diff --git a/exercises/practice/pythagorean-triplet/.meta/example.mips b/exercises/practice/pythagorean-triplet/.meta/example.mips new file mode 100644 index 0000000..9f6cd53 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.meta/example.mips @@ -0,0 +1,59 @@ +# | Register | Usage | Type | Description | +# | -------- | ------------ | ------- | -------------------------------------------- | +# | `$a0` | input | integer | N, sum of sides of triangle | +# | `$a1` | input/output | address | a values | +# | `$a2` | input/output | address | b values | +# | `$a3` | input/output | address | c values | +# | `$v0` | output | integer | number of triplets | +# | `$t0` | temporary | address | start of a values | +# | `$t1` | temporary | integer | a | +# | `$t2` | temporary | integer | b | +# | `$t3` | temporary | integer | c | +# | `$t5` | temporary | integer | remainder | +# | `$t6` | temporary | integer | numerator | +# | `$t7` | temporary | integer | denominator | + +# For every Pythagorean triplet with total a + b + c = N, +# a² + b² = c² +# <=> a² + b² = (N - a - b)², substituting c +# <=> 0 = N² - 2*N*a - 2*N*b + 2*a*b +# <=> (2*N - 2*a) b = (N² - 2*N*a) +# <=> b = (N² - 2*N*a) / (2*N - 2*a) +# +# The denominator is never 0, as N exceeds a side length. + +.globl triplets_with_sum + +triplets_with_sum: + move $t0, $a1 # start of a values + blt $a0, 2, return # Stop immediately if N < 2 + move $t1, $zero # Initialize a + j increment_a + +check_remainder: + mfhi $t5 # remainder + bnez $t5, increment_a + sub $t3, $a0, $t1 + sub $t3, $t3, $t2 # c = N - a - b + + sw $t1, 0($a1) + sw $t2, 0($a2) + sw $t3, 0($a3) + addi $a1, $a1, 4 # Increment pointer for a + addi $a2, $a2, 4 # Increment pointer for b + addi $a3, $a3, 4 # Increment pointer for c + +increment_a: + addi $t1, $t1, 1 + sub $t7, $a0, $t1 # N - a + sub $t6, $t7, $t1 # N - 2*a + sll $t7, $t7, 1 # denominator = 2*N - 2*a + mulu $t6, $a0, $t6 # numerator = N² - 2*N*a + div $t6, $t7 + mflo $t2 # b + bgt $t2, $t1, check_remainder # loop while b > a + +return: + sub $v0, $a1, $t0 # compute number of triplets + srl $v0, $v0, 2 + jr $ra diff --git a/exercises/practice/pythagorean-triplet/.meta/tests.toml b/exercises/practice/pythagorean-triplet/.meta/tests.toml new file mode 100644 index 0000000..719620a --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.meta/tests.toml @@ -0,0 +1,31 @@ +# 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. + +[a19de65d-35b8-4480-b1af-371d9541e706] +description = "triplets whose sum is 12" + +[48b21332-0a3d-43b2-9a52-90b2a6e5c9f5] +description = "triplets whose sum is 108" + +[dffc1266-418e-4daa-81af-54c3e95c3bb5] +description = "triplets whose sum is 1000" + +[5f86a2d4-6383-4cce-93a5-e4489e79b186] +description = "no matching triplets for 1001" + +[bf17ba80-1596-409a-bb13-343bdb3b2904] +description = "returns all matching triplets" + +[9d8fb5d5-6c6f-42df-9f95-d3165963ac57] +description = "several matching triplets" + +[f5be5734-8aa0-4bd1-99a2-02adcc4402b4] +description = "triplets for large number" diff --git a/exercises/practice/pythagorean-triplet/impl.mips b/exercises/practice/pythagorean-triplet/impl.mips new file mode 100644 index 0000000..a66cf12 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/impl.mips @@ -0,0 +1,13 @@ +# | Register | Usage | Type | Description | +# | -------- | ------------ | ------- | -------------------------------------------- | +# | `$a0` | input | integer | N, sum of sides of triangle | +# | `$a1` | input/output | address | a values | +# | `$a2` | input/output | address | b values | +# | `$a3` | input/output | address | c values | +# | `$v0` | output | integer | number of triplets | +# | `$t0-9` | temporary | any | for temporary storage | + +.globl triplets_with_sum + +triplets_with_sum: + jr $ra diff --git a/exercises/practice/pythagorean-triplet/runner.mips b/exercises/practice/pythagorean-triplet/runner.mips new file mode 100644 index 0000000..d2825c2 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/runner.mips @@ -0,0 +1,253 @@ +# +# Test triplets_with_sum with some examples +# +# a0 - input number, for callee +# a1 - pointer to output array a, for callee +# a2 - pointer to output array b, for callee +# a3 - pointer to output array c, for callee +# s0 - num of tests left to run +# s1 - address of input number +# s2 - address of expected a values +# s3 - address of expected b values +# s4 - address of expected c values +# s5 - address of expected number of triplets +# s6 - buffer location +# t0 - pointer into expected a +# t1 - pointer into expected b +# t2 - pointer into expected c +# t4 - pointer into actual a +# t5 - pointer into actual b +# t6 - pointer into actual c +# t7 - expected number of triplets +# t8 - pointer into end of test case's expected a +# v1 - saved return value, i.e. number of triplets returned + +.eqv BUFFER_SIZE 96 +.eqv BUFFER_SECTION_SIZE 32 + +.data + +# number of test cases +n: .word 7 +# input values +ins: .word + 12, + 108, + 1000, + 1001, + 90, + 840, + 30000 +# expected output values +# a values +a: .word + 3, + 27, + 200, + # none, + 9, 15, + 40, 56, 105, 120, 140, 168, 210, 240, + 1200, 1875, 5000, 6000, 7500 +# b values +b: .word + 4, + 36, + 375, + # none, + 40, 36, + 399, 390, 360, 350, 336, 315, 280, 252, + 14375, 14000, 12000, 11250, 10000 +# c values +c: .word + 5, + 45, + 425, + # none, + 41, 39, + 401, 394, 375, 370, 364, 357, 350, 348, + 14425, 14125, 13000, 12750, 12500 +numouts: .word + 1, + 1, + 1, + 0, + 2, + 8, + 5 + +failmsg: .asciiz "failed for test input: " +expectedmsg: .asciiz ". expected\n" +tobemsg: .asciiz "\nto be\n" +okmsg: .asciiz "all tests passed" +prefix: .asciiz " {" +comma: .asciiz ", " +suffix: .asciiz "}" + + +.text + +runner: + lw $s0, n + la $s1, ins + la $s2, a + la $s3, b + la $s4, c + la $s5, numouts + + li $v0, 9 # code for allocating heap memory + li $a0, BUFFER_SIZE # specify length of longest expected output + syscall + move $s6, $v0 # location of allocated memory is where callee writes result + +run_test: + jal clear_output # zero out output location + lw $a0, 0($s1) # load input value into a0 + move $a1, $s6 # output location a + addi $a2, $a1, BUFFER_SECTION_SIZE # output location b + addi $a3, $a2, BUFFER_SECTION_SIZE # output location c + jal triplets_with_sum # call subroutine under test + move $v1, $v0 # move return value in v0 to v1 because we need v0 for syscall + lw $t7, 0($s5) # expected number of triplets + bne $v1, $t7, exit_fail # check if we have the expected number of triplets + beq $t7, $zero, skip_scan + + move $t0, $s2 # address of expected a + move $t1, $s3 # address of expected b + move $t2, $s4 # address of expected c + move $t3, $s6 # address of actual a + addi $t4, $t3, BUFFER_SECTION_SIZE # address of actual b + addi $t5, $t4, BUFFER_SECTION_SIZE # address of actual c + + sll $t8, $t7, 2 # number of bytes occupied by a + add $t8, $s2, $t8 # address at end of expected a + +scan: + lw $a0, 0($t0) # load expected a + lw $a1, 0($t3) # load actual a + bne $a0, $a1, exit_fail # check if we have the expected a + addi $t0, $t0, 4 # advance to next expected a + addi $t3, $t3, 4 # advance to next actual a + lw $a0, 0($t1) # load expected b + lw $a1, 0($t4) # load actual b + bne $a0, $a1, exit_fail # check if we have the expected b + addi $t1, $t1, 4 # advance to next expected b + addi $t4, $t4, 4 # advance to next actual b + lw $a0, 0($t2) # load expected c + lw $a1, 0($t5) # load actual c + bne $a0, $a1, exit_fail # check if we have the expected c + addi $t2, $t2, 4 # advance to next expected c + addi $t5, $t5, 4 # advance to next actual c + bne $t0, $t8, scan # repeat until all a have been compared + + move $s2, $t0 # point to next expected a + move $s3, $t1 # point to next expected b + move $s4, $t2 # point to next expected c + +skip_scan: + addi $s5, $s5, 4 # point to next expected number of triplets + 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, $v1 + move $a1, $s6 + addi $a2, $a1, BUFFER_SECTION_SIZE + addi $a3, $a2, BUFFER_SECTION_SIZE + jal print_triplets + + la $a0, tobemsg + li $v0, 4 + syscall + + move $a0, $t7 + move $a1, $s2 + move $a2, $s3 + move $a3, $s4 + jal print_triplets + + li $a0, 1 # set error code to 1 + li $v0, 17 # 17 is exit with error + syscall + + +clear_output: + # zero out output by storing zeros + addi $t0, $s6, BUFFER_SIZE # pointer to end of output buffer + +clear: + subi $t0, $t0, 4 # decrement pointer + sw $zero, 0($t0) # store a 0 word + bne $t0, $s6, clear # repeat util we have reached the start of the buffer + jr $ra + + +print_triplets: + # print triplets with triplet count $a0, + # a address $a1, b address $a2, c address $a3 + move $t0, $a0 + move $t1, $a1 + move $t2, $a2 + move $t3, $a3 + beqz $t0, done + +print_next: + la $a0, prefix + li $v0, 4 + syscall + + lw $a0, 0($t1) + addi $t1, $t1, 4 # increment pointer + li $v0, 1 # 1 -> "print integer" + syscall + + la $a0, comma + li $v0, 4 + syscall + + lw $a0, 0($t2) + addi $t2, $t2, 4 # increment pointer + li $v0, 1 # 1 -> "print integer" + syscall + + la $a0, comma + li $v0, 4 + syscall + + lw $a0, 0($t3) + addi $t3, $t3, 4 # increment pointer + li $v0, 1 # 1 -> "print integer" + syscall + + la $a0, suffix + li $v0, 4 + syscall + + subi $t0, $t0, 1 + bnez $t0, print_next + +done: + jr $ra + +# # Include your implementation here if you wish to run this from the MARS GUI. +#.include "impl.mips"