Skip to content

Commit

Permalink
Implement exercise 'collatz-conjecture'
Browse files Browse the repository at this point in the history
  • Loading branch information
ingydotnet committed Sep 17, 2024
1 parent 9be4ff9 commit 342bcb1
Show file tree
Hide file tree
Showing 15 changed files with 418 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "collatz-conjecture",
"name": "Collatz Conjecture",
"uuid": "dd18fd4d-21de-40f8-9d92-2c5a1d1e7133",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "difference-of-squares",
"name": "Difference of Squares",
Expand Down
7 changes: 7 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ exercises:
prerequisites: []
difficulty: 2

- slug: collatz-conjecture
name: Collatz Conjecture
uuid: dd18fd4d-21de-40f8-9d92-2c5a1d1e7133
practices: []
prerequisites: []
difficulty: 2

- slug: difference-of-squares
name: Difference of Squares
uuid: de0c68d4-f0c3-4bb7-9ab2-378f9034bc82
Expand Down
29 changes: 29 additions & 0 deletions exercises/practice/collatz-conjecture/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Instructions

The Collatz Conjecture or 3x+1 problem can be summarized as follows:

Take any positive integer n.
If n is even, divide n by 2 to get n / 2.
If n is odd, multiply n by 3 and add 1 to get 3n + 1.
Repeat the process indefinitely.
The conjecture states that no matter which number you start with, you will always reach 1 eventually.

Given a number n, return the number of steps required to reach 1.

## Examples

Starting with n = 12, the steps would be as follows:

0. 12
1. 6
2. 3
3. 10
4. 5
5. 16
6. 8
7. 4
8. 2
9. 1

Resulting in 9 steps.
So for input n = 12, the return value would be 9.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
YS_VERSION := 0.1.75
28 changes: 28 additions & 0 deletions exercises/practice/collatz-conjecture/.meta/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
SHELL := bash

BASE := $(shell pwd)

export YS_VERSION := 0.1.76

YS_LOCAL_PREFIX := ../../../../.local/v$(YS_VERSION)

YS_LOCAL_BIN := $(YS_LOCAL_PREFIX)/bin

YS_BIN := $(YS_LOCAL_BIN)/ys-$(YS_VERSION)

TEST_FILE ?= $(wildcard *-test.ys)


export PATH := $(YS_LOCAL_BIN):$(PATH)

export YSPATH := $(BASE)


default:

test: $(YS_BIN)
prove -v $(TEST_FILE)

$(YS_BIN):
curl -s https://yamlscript.org/install | \
BIN=1 VERSION=$(YS_VERSION) PREFIX=$(YS_LOCAL_PREFIX) bash >/dev/null
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env ys-0

require ys::taptest: :all

use: collatz-conjecture

test::
- name: Zero steps for one
code: steps(1)
want: 0
uuid: 540a3d51-e7a6-47a5-92a3-4ad1838f0bfd

- name: Divide if even
code: steps(16)
want: 4
uuid: 3d76a0a6-ea84-444a-821a-f7857c2c1859

- name: Even and odd steps
code: steps(12)
want: 9
uuid: 754dea81-123c-429e-b8bc-db20b05a87b9

- name: Large number of even and odd steps
code: steps(1000000)
want: 152
uuid: ecfd0210-6f85-44f6-8280-f65534892ff6

- name: Zero is an error
code: steps(0)
what: error
want: Only positive integers are allowed
uuid: 2187673d-77d6-4543-975e-66df6c50e2da

- name: Negative value is an error
code: steps(-15)
what: error
want: Only positive integers are allowed
uuid: ec11f479-56bc-47fd-a434-bcd7a31a7a2e

done: 6
11 changes: 11 additions & 0 deletions exercises/practice/collatz-conjecture/.meta/collatz-conjecture.ys
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
!yamlscript/v0

defn steps(number):
when-not pos?(number):
die('Only positive integers are allowed')

loop num number, steps 0:
cond:
num == 1: steps
even?(num): recur((num / 2), steps.++)
else: recur((num * 3).++, steps.++)
23 changes: 23 additions & 0 deletions exercises/practice/collatz-conjecture/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"authors": [
"ingydotnet"
],
"files": {
"solution": [
"collatz-conjecture.ys"
],
"test": [
"collatz-conjecture-test.ys",
"GNUmakefile",
"Makefile",
".yamlscript/exercise.mk",
".yamlscript/exercism-ys-installer"
],
"example": [
".meta/collatz-conjecture.ys"
]
},
"blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.",
"source": "An unsolved problem in mathematics named after mathematician Lothar Collatz",
"source_url": "https://en.wikipedia.org/wiki/3x_%2B_1_problem"
}
38 changes: 38 additions & 0 deletions exercises/practice/collatz-conjecture/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 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.

[540a3d51-e7a6-47a5-92a3-4ad1838f0bfd]
description = "zero steps for one"

[3d76a0a6-ea84-444a-821a-f7857c2c1859]
description = "divide if even"

[754dea81-123c-429e-b8bc-db20b05a87b9]
description = "even and odd steps"

[ecfd0210-6f85-44f6-8280-f65534892ff6]
description = "large number of even and odd steps"

[7d4750e6-def9-4b86-aec7-9f7eb44f95a3]
description = "zero is an error"
include = false

[2187673d-77d6-4543-975e-66df6c50e2da]
description = "zero is an error"
reimplements = "7d4750e6-def9-4b86-aec7-9f7eb44f95a3"

[c6c795bf-a288-45e9-86a1-841359ad426d]
description = "negative value is an error"
include = false

[ec11f479-56bc-47fd-a434-bcd7a31a7a2e]
description = "negative value is an error"
reimplements = "c6c795bf-a288-45e9-86a1-841359ad426d"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
YS_VERSION := 0.1.75
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/env/bin/env bash

set -euo pipefail

intro-prompt() (
cat <<...
--------------------------------------------------------------------------------
This YAMLScript Exercism exercise requires the YAMLScript version $version
interpreter command file to be installed here:
$prefix/bin/ys
You can install it by pressing Enter now, or by running this command:
$make install-ys
This should only take a few seconds and you only need to do this once.
Other exercises will use the same file.
See https://yamlscript.org/doc/install/ for more YAMLScript installation info.
--------------------------------------------------------------------------------
Would you like to install the 'ys' file now?
...

printf "Press Enter to install. Ctl-C to Quit."; read -r
)

main() {
setup "$@"

install-from-local

$auto && intro-prompt

installed || install-from-release || true
installed || install-from-build || true
installed ||
die "Installing '$installed' failed. Giving up." \
"Consider filing an issue at: $gh_issue_url"

echo
echo 'Success!'
echo "$installed is now installed."
echo
}

installed() {
[[ -f $installed ]]
}

install-from-local() {
local path
path=$(command -v "$ysfq") || true
if [[ -f $path ]]; then
mkdir -p "$bin"
cp "$path" "$bin"/
ln -fs "$ysfq" "$bin/ys-0"
ln -fs "$ysfq" "$bin/ys"
(installed && $auto) && exit
true
fi
}

install-from-release() (
set -x
curl -s https://yamlscript.org/install |
BIN=1 VERSION="$version" PREFIX="$prefix" bash
)

install-from-build() (
cat <<...
The binary release installation failed.
We can attempt to build and install $ysfq now.
This can take from 1 to 5 minutes to complete.
...

printf "Press Enter to install. Ctl-C to Quit."; read -r

[[ -d /tmp && -w /tmp ]] ||
die "Can't write to /tmp" \
'Giving up.'

set -x

rm -fr "$yamlscript_clone"

git clone --branch="$version" "$yamlscript_repo" "$yamlscript_clone"

"$make" -C "$yamlscript_clone/ys" install PREFIX="$prefix"
)

setup() {
version=$1
prefix=$2
make=$3
auto=false
[[ ${4-} ]] && auto=true

[[ $version =~ ^0\.1\.[0-9]+$ ]] ||
die "Invalid YS_VERSION '$version'"

bin=$prefix/bin
ysfq=ys-$version
installed=$bin/$ysfq

if installed; then
echo "'$installed' is already installed."
exit
fi

yamlscript_repo=https://github.com/yaml/yamlscript
yamlscript_clone=/tmp/yamlscript-exercism
gh_issue_url=https://github.com/exercism/yamlscript/issues
}

die() {
printf '%s\n' "$@" >&2
exit 1
}

main "$@"
49 changes: 49 additions & 0 deletions exercises/practice/collatz-conjecture/GNUmakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
SHELL := bash

BASE := $(shell pwd)

export YS_VERSION := 0.1.76

YS_LOCAL_PREFIX := ../../../.local/v$(YS_VERSION)
ifeq (,$(shell [[ -d "$(YS_LOCAL_PREFIX)" ]] && echo ok))
YS_LOCAL_PREFIX := $(shell cd .. && pwd -P)/.local/v$(YS_VERSION)
endif

YS_LOCAL_BIN := $(YS_LOCAL_PREFIX)/bin
YS_BIN := $(YS_LOCAL_BIN)/ys-$(YS_VERSION)

YS_INSTALLER := .yamlscript/exercism-ys-installer
YS_INSTALLER_CMD := \
bash $(YS_INSTALLER) $(YS_VERSION) $(YS_LOCAL_PREFIX) $(MAKE)

TEST_FILE ?= $(wildcard *-test.ys)

export PATH := $(YS_LOCAL_BIN):$(PATH)
export YSPATH := $(BASE)


#-------------------------------------------------------------------------------
default:
@echo " No default make rule. Try 'make test'."

test: $(YS_BIN)
prove -v $(TEST_FILE)

install-ys:
@$(YS_INSTALLER_CMD)

uninstall-ys:
rm -fr $(YS_LOCAL_PREFIX)


#-------------------------------------------------------------------------------
ifdef EXERCISM_YAMLSCRIPT_GHA
$(YS_BIN):

else ifeq (/mnt/,$(dir $(BASE)))
$(YS_BIN):

else
$(YS_BIN):
@$(YS_INSTALLER_CMD) auto
endif
8 changes: 8 additions & 0 deletions exercises/practice/collatz-conjecture/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This Makefile is a decoy to attempt to detect when a non-GNU make is being
# used and alert the user.

test:
@echo "You appear to be using a non-GNU version of the 'make' program."
@echo "The YAMLScript Exercism track requires you to use GNU make."
@echo "Please try 'make $@' again using GNU make."
@exit 1
Loading

0 comments on commit 342bcb1

Please sign in to comment.