commit fe7136d80136f10d0f1faa2b57f55b6e2ed1ae95 Author: Bruno Raoult Date: Sun Aug 8 21:11:22 2021 +0200 initial commit diff --git a/bash/acronym/README.md b/bash/acronym/README.md new file mode 100644 index 0000000..733d5c6 --- /dev/null +++ b/bash/acronym/README.md @@ -0,0 +1,54 @@ +# Acronym + +Convert a phrase to its acronym. + +Techies love their TLA (Three Letter Acronyms)! + +Help generate some jargon by writing a program that converts a long name +like Portable Network Graphics to its acronym (PNG). + + +Run the tests with: + +```bash +bats acronym_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats acronym_test.sh +``` + +## Source + +Julien Vanier [https://github.com/monkbroc](https://github.com/monkbroc) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/acronym/acronym.sh b/bash/acronym/acronym.sh new file mode 100755 index 0000000..40b6418 --- /dev/null +++ b/bash/acronym/acronym.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# V1: original version +# V2: final printf changeto avoid subshell + +# external tools: none + +# v1: initial version + +# set to mask to enable logs +debug=0 + +shopt -s extglob + +usage() { + echo "acronym.sh word1 [...]" >&2 + exit 1 +} + +# $1: log level, then strings to display. +log () { + (( debug & $1 )) && shift && echo "${@}" +} + + +# check for basic errors. We will accept multiple args: +# acronym.sh i love suchi = acronym "i love suchi" +(( $# < 1 )) && usage + +# valid word separators +VALID_SEPARATORS="-_*" + +# replace valid chars with space, merge args in 1 string +words="${*//[$VALID_SEPARATORS]/ }" +log 1 valid separators ${#words[@]} "$words" + +# remove remaining non alpha chars (keep blanks) +words="${words//[^[:alpha:][:blank:]]/}" +log 1 keep alpha "${words[@]}" + +# capitalize, make an array +words=(${words^^}) +log 1 "words ${#words[@]} ${words[@]}" + +# print 1st chars +printf -v result "%c" ${words[@]} +log 1 result "$result" + +echo "$result" + +exit 0 + +# Indent style for emacs +# Local Variables: +# sh-basic-offset: 4 +# sh-indentation: 4 +# indent-tabs-mode: nil +# comment-column: 60 +# End: diff --git a/bash/anagram/README.md b/bash/anagram/README.md new file mode 100644 index 0000000..75f0613 --- /dev/null +++ b/bash/anagram/README.md @@ -0,0 +1,54 @@ +# Anagram + +An anagram is a rearrangement of letters to form a new word. +Given a word and a list of candidates, select the sublist of anagrams of the given word. + +Given `"listen"` and a list of candidates like `"enlists" "google" +"inlets" "banana"` the program should return a list containing +`"inlets"`. + + +Run the tests with: + +```bash +bats anagram_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats anagram_test.sh +``` + +## Source + +Inspired by the Extreme Startup game [https://github.com/rchatley/extreme_startup](https://github.com/rchatley/extreme_startup) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/anagram/anagram.sh b/bash/anagram/anagram.sh new file mode 100755 index 0000000..b5eb949 --- /dev/null +++ b/bash/anagram/anagram.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# External tools: none. +# Subshells: none. +# +# V1: initial version + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 + #local -i i + #if (( debug & $1 )) ; then + # for ((i=1; i<$1; ++i)); do + # echo -n " " + # done + # shift + # echo "${BASH_LINENO[0]}: ${@}" >&2 + #fi +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: anagram.sh word anagrams-list" +} + +# check for basic args +(($# < 2)) && usage +word="$1" +shift +# we will accept more than 1 anagram list: +# anagram.sh "BANANA" "banana BANANA" = anagram "BANANA" "banana" "BANANA" +# we start to split words +declare -a words +while (($# > 0)); do + words=("${words[@]}" $1) + typeset -p words + shift +done +debug 1 words=${#words[@]}:${words[@]} +typeset -p words + + +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/armstrong-numbers/README.md b/bash/armstrong-numbers/README.md new file mode 100644 index 0000000..5f54330 --- /dev/null +++ b/bash/armstrong-numbers/README.md @@ -0,0 +1,58 @@ +# Armstrong Numbers + +An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits. + +For example: + +- 9 is an Armstrong number, because `9 = 9^1 = 9` +- 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 1` +- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` +- 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` + +Write some code to determine whether a number is an Armstrong number. + + +Run the tests with: + +```bash +bats armstrong_numbers_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats armstrong_numbers_test.sh +``` + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Narcissistic_number](https://en.wikipedia.org/wiki/Narcissistic_number) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/armstrong-numbers/armstrong_numbers.sh b/bash/armstrong-numbers/armstrong_numbers.sh new file mode 100755 index 0000000..fd760e9 --- /dev/null +++ b/bash/armstrong-numbers/armstrong_numbers.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# set to mask to enable logs. 0: none, 255: all +(( DEBUG=2#00000000 )) + +# $1: log level (mask), then strings to display. +debug () { + (( DEBUG & $1 )) && shift && echo "${@}" >&2 +} + +usage () { + echo "usage: ./armstrong_numbers.sh " >&2 + exit 1 +} + +# basic args checks: 1 arg, digits (at least 1) only +(( $# != 1)) || [[ ! "$1" =~ ^[[:digit:]]+$ ]] && usage + +number="$1" +digits=${#number} +armstrong=0 +result="false" + +debug 1 d=$digits n=$number + +# armstrong number calculation +for (( i=0; i&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: atbash_cypher.sh encode|decode text [...]" +} + +# we will accept the syntax: +# atbash_cypher.sh encode|decode text [...] +# in case text is separated as multiple arguments, we will consider a space is +# in between. +# example: +# atbash_cypher.sh encode "I am happy" +# will also be accepted as: +# atbash_cypher.sh encode I am happy + +# basic args check & set all chars to lowercase +(($# < 2)) || [[ "$1" != @(encode|decode) ]] && usage +action="$1" +shift +string="${*,,}" +len="${#string}" +debug 1 "string=$string" "len=$len" + +# algorithm we will use: +# to get rev element c' of c in a consecutive integer n-m list : +# c = n+i i is distance from n to c +# c' = m-i and also reverse distance from m to c' +# ------------ +# c+c' = n+m sum the 2 +# c' = n+m-c after simplification + +# for printf, a ' arg is the ascii value of +# and yes, we should use a constant, any programmer knows ascii value of 'a' +# and # of letters in alphabet, we could simply set tval to 219 +printf -v aval "%d" "'a" # 'a' ascii value +printf -v zval "%d" "'z" # 'z' ascii value +printf -v tval "%d" $((aval+zval)) # their sum + +# tval=219 # correct code for this exercise, for me + +debug 1 "aval:$aval zval:$zval tval:$tval" + +# many options here: +# - for encode, add in loop, or at the end +# - use a table with all alphabet ({a..z}), to avoid multiple printf +# - fill acceptable chars only (my choice), insert spaces at end. +# I preferred that one to avoid multiple encode/decode tests in loop. +# - likely many others +result="" # resulting string, with no spaces +for ((i=0; i&2 +} + +arg="$1" + +# trim all blank characters safely +arg="${arg//[[:space:]]/}" +debug 1 trim "$arg" + +# and check if last char is question mark +[[ ${arg: -1} == "?" ]] && question=+ + +# the tricky part in test is that non alpha characters are considered +# differently in tests, depending on the rest of the string (having +# letters or not). So we remove them in that case. +[[ "$arg" == *[[:alpha:]]* ]] && arg="${arg//[![:alpha:]]/}" +debug 1 special "$arg" $question + +# and now proceed with rules +case "$arg$question" in + "") + echo "Fine. Be that way!" + ;; + +([[:upper:]])+) + echo "Calm down, I know what I'm doing!" + ;; + *+) + echo "Sure." + ;; + +([[:upper:]])) + echo "Whoa, chill out!" + ;; + *) + echo "Whatever." + ;; +esac +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 50 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/bowling/README.md b/bash/bowling/README.md new file mode 100644 index 0000000..f29fd8d --- /dev/null +++ b/bash/bowling/README.md @@ -0,0 +1,107 @@ +# Bowling + +Score a bowling game. + +Bowling is a game where players roll a heavy ball to knock down pins +arranged in a triangle. Write code to keep track of the score +of a game of bowling. + +## Scoring Bowling + +The game consists of 10 frames. A frame is composed of one or two ball +throws with 10 pins standing at frame initialization. There are three +cases for the tabulation of a frame. + +* An open frame is where a score of less than 10 is recorded for the + frame. In this case the score for the frame is the number of pins + knocked down. + +* A spare is where all ten pins are knocked down by the second + throw. The total value of a spare is 10 plus the number of pins + knocked down in their next throw. + +* A strike is where all ten pins are knocked down by the first + throw. The total value of a strike is 10 plus the number of pins + knocked down in the next two throws. If a strike is immediately + followed by a second strike, then the value of the first strike + cannot be determined until the ball is thrown one more time. + +Here is a three frame example: + +| Frame 1 | Frame 2 | Frame 3 | +| :-------------: |:-------------:| :---------------------:| +| X (strike) | 5/ (spare) | 9 0 (open frame) | + +Frame 1 is (10 + 5 + 5) = 20 + +Frame 2 is (5 + 5 + 9) = 19 + +Frame 3 is (9 + 0) = 9 + +This means the current running total is 48. + +The tenth frame in the game is a special case. If someone throws a +strike or a spare then they get a fill ball. Fill balls exist to +calculate the total of the 10th frame. Scoring a strike or spare on +the fill ball does not give the player more fill balls. The total +value of the 10th frame is the total number of pins knocked down. + +For a tenth frame of X1/ (strike and a spare), the total value is 20. + +For a tenth frame of XXX (three strikes), the total value is 30. + +## Requirements + +Write code to keep track of the score of a game of bowling. It should +support two operations: + +* `roll(pins : int)` is called each time the player rolls a ball. The + argument is the number of pins knocked down. +* `score() : int` is called only at the very end of the game. It + returns the total score for that game. + + +Run the tests with: + +```bash +bats bowling_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats bowling_test.sh +``` + +## Source + +The Bowling Game Kata at but UncleBob [http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata](http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/bowling/bowling.sh b/bash/bowling/bowling.sh new file mode 100755 index 0000000..c0dda7b --- /dev/null +++ b/bash/bowling/bowling.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# +# V1: initial version + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: bowling.sh r1-20 [r21 [r22]]" +} +# pincount > 10. $1: frame +highroll() { + die Frame "Frame $1: Pin count exceeds pins on the lane." +} +# pincount > 10. $1: frame +negativeroll() { + die Frame "Frame $1: Negative roll is invalid." +} + + +# args test: 10 rolls mandatory +#(($# < 20)) && usage +rolls=($@) +debug 1 rolls=${rolls[@]} + +((score = 0)) # total score +((tempscore = 0)) # tmp score (for spare/strike) + +declare -a frames # frames scores +declare -a opened # spare:2, strike:1 + +((frame=-1)) # current frame + +((curroll=1)) # roll in frame: 1:1st, 2:2nd +((i=0)) +for ((roll=0; roll<$#; ++roll)); do + debug 2 loop roll="$roll" + + (( ! roll % 2 && frame++ )) + exit 0 + (( score=${rolls[$i]} )) + #(( score < 0 )) && || score > 10)) && echo 1 + #if ((!roll % 2)); then # first roll in frame + + + ((framescore=0)) # current frame score + + + for j in 0 1; do + (( roll = ${rolls[$i]} )) + ((roll < 0)) && die Frame $((curframe+1)): Negative roll is invalid. + (( framescore += roll )) + ((framescore == 10)) && ((opened[$curframe] = j+1)) + (( i++ )) + done + debug 2 curframe=$curframe score=$framescore + + #if ((roll)) + + ((framescore > 10)) && wrongroll $curframe $curroll $framescore + + frame[$curframe]=$framescore + + + ((curroll = -curroll)) +done + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 50 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/darts/README.md b/bash/darts/README.md new file mode 100644 index 0000000..766248c --- /dev/null +++ b/bash/darts/README.md @@ -0,0 +1,64 @@ +# Darts + +Write a function that returns the earned points in a single toss of a Darts game. + +[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players +throw darts to a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg). + +In our particular instance of the game, the target rewards with 4 different amounts of points, depending on where the dart lands: + +* If the dart lands outside the target, player earns no points (0 points). +* If the dart lands in the outer circle of the target, player earns 1 point. +* If the dart lands in the middle circle of the target, player earns 5 points. +* If the dart lands in the inner circle of the target, player earns 10 points. + +The outer circle has a radius of 10 units (This is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered to the same point (That is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0). + +Write a function that given a point in the target (defined by its `real` cartesian coordinates `x` and `y`), returns the correct amount earned by a dart landing in that point. + +This particular exercise, since it deals with floating point arithmetic, is natural to rely on external tools (see below). As an extra challenging challenge, find a way to implement this with plain bash. + +Run the tests with: + +```bash +bats darts_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats darts_test.sh +``` + +## Source + +Inspired by an exercise created by a professor Della Paolera in Argentina + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/darts/darts.sh b/bash/darts/darts.sh new file mode 100755 index 0000000..3c0a7cc --- /dev/null +++ b/bash/darts/darts.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# +# external tools: none (only integer operations). +# +# V1 : Initial version. +# V2 : Using reference instead of subshell, as of +# https://stackoverflow.com/questions/540298 +# moved valid numbers in parseval function. +# V3/4 : added some quotes following suggestions + +# set to mask to enable logs. 0: none, 255: all +#((DEBUG=2#00001111)) +((DEBUG=2#00000000)) + +# $1: log level (mask), then strings to display. +debug () { + (( DEBUG & $1 )) && shift && echo "${@}" >&2 +} + +usage () { + echo "usage: darts x y" >&2 + exit 1 +} + +shopt -s extglob + +# To be able to use bash only, all numbers will be miltiplied by 1,000. +# Could be higher if we want more precision. +# +# So: 0.1 will be 100, 1 will be 1,000, 10 will br 10000, etc... + +# circles. as we will use Pythagoras' theorem, so square value calc here +outer=$(( (10 * 1000 ) ** 2 )) +middle=$(( ( 5 * 1000 ) ** 2 )) +inner=$(( ( 1 * 1000 ) ** 2 )) + +debug 1 outer=$outer middle=$middle inner=$inner + +# basic args check: 2 args, and decimal numbers, which are: +# optional +- sign, optional digits, optional . and optional digits +(( ${#} != 2 )) && usage + +parseval() { + # integer and decimal parts, final value by ref + local int dec + local -n calc=$1 + + # check for valid decimal number + [[ ${2} != ?([-+])+([0-9])?(.*([0-9])) ]] && usage + + IFS=. read int dec <<< "$2" + debug 2 ${int} ${dec} + + # we accept up to 3 decimals: add 3 zeroes to dec, then keep 3 first digits + # So a decimal part of "1" will become 100, 01 will become 10, etc... + # we also take care of leadings 0 (octal notation), and remove leading "-". + dec="$dec"000 + dec="10#"${dec:0:3} + int="10#"${int#-} + + debug 2 mult ${int} ${dec} + calc=$(( (int*1000 + dec) ** 2 )) +} + +parseval x "$1" +parseval y "$2" +total=$(( x+y )) +debug 1 x=$x y=$y x+y=$total + +(( total <= inner )) && echo 10 && exit 0 +(( total <= middle )) && echo 5 && exit 0 +(( total <= outer )) && echo 1 && exit 0 +echo 0 && exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 60 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/difference-of-squares/README.md b/bash/difference-of-squares/README.md new file mode 100644 index 0000000..fe1e184 --- /dev/null +++ b/bash/difference-of-squares/README.md @@ -0,0 +1,63 @@ +# Difference Of Squares + +Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. + +The square of the sum of the first ten natural numbers is +(1 + 2 + ... + 10)² = 55² = 3025. + +The sum of the squares of the first ten natural numbers is +1² + 2² + ... + 10² = 385. + +Hence the difference between the square of the sum of the first +ten natural numbers and the sum of the squares of the first ten +natural numbers is 3025 - 385 = 2640. + +You are not expected to discover an efficient solution to this yourself from +first principles; research is allowed, indeed, encouraged. Finding the best +algorithm for the problem is a key skill in software engineering. + + +Run the tests with: + +```bash +bats difference_of_squares_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats difference_of_squares_test.sh +``` + +## Source + +Problem 6 at Project Euler [http://projecteuler.net/problem=6](http://projecteuler.net/problem=6) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/difference-of-squares/difference_of_squares.sh b/bash/difference-of-squares/difference_of_squares.sh new file mode 100755 index 0000000..1565ae6 --- /dev/null +++ b/bash/difference-of-squares/difference_of_squares.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# +# External tools: none. +# subshell: none. +# +# V1: initial version + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +#(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: difference_of_squares.sh command color" +} + +debug 1 "#=$#" "arg=$1" +(($# != 2)) || [[ $1 != @(square_of_sum|sum_of_squares|difference) ]] || \ + [[ "$2" != +([[:digit:]]) ]] && usage + +# the 2 next functions will set $1 to the calculated value for $2 +sum_of_squares () { # set $1 to sum of squares ($2) + local -n res=$1 + local val=$2 + + # the sum of 1st n integers squares is: + # S = 1² + 2² + 3² ..... + (n-1)² + n² + # = [ n * (n+1) * (2n+1) ] / 6 + # demonstration on: + # http://www.takayaiwamoto.com/Sums_and_Series/sumsqr_1.html + (( res = val * (val+1) * (2*val + 1) / 6 )) + debug 2 "sum_of_squares($val) = $res" +} +square_of_sum () { # set $1 to square of sum ($2) + local -n res=$1 + local val=$2 + + # The sum of n 1st integers is: + # S = 1 + 2 + 3 ... + (n-1) + n + # = [ n * (n+1) ] / 2 + # demonstration is trivial for this one. + (( res = (val * (val+1) / 2 ) ** 2 )) + debug 2 "square_of_sum($val) = $res" +} + +action="$1" +(( num = $2 )) + +case "$action" in + square_of_sum) + square_of_sum result "$num" + debug 3 "result after calling square_of_sum: $result" + ;; + sum_of_squares) + sum_of_squares result "$num" + debug 3 "result after calling sum_of_squares: $result" + ;; + difference) + square_of_sum SqSum "$num" + sum_of_squares SumSq "$num" + ((result = SqSum - SumSq)) + debug 3 "result after calling difference: $result" +esac + +echo $result +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/dnd-character/README.md b/bash/dnd-character/README.md new file mode 100644 index 0000000..77259bb --- /dev/null +++ b/bash/dnd-character/README.md @@ -0,0 +1,79 @@ +# D&D Character + +For a game of [Dungeons & Dragons][DND], each player starts by generating a +character they can play with. This character has, among other things, six +abilities; strength, dexterity, constitution, intelligence, wisdom and +charisma. These six abilities have scores that are determined randomly. You +do this by rolling four 6-sided dice and record the sum of the largest three +dice. You do this six times, once for each ability. + +Your character's initial hitpoints are 10 + your character's constitution +modifier. You find your character's constitution modifier by subtracting 10 +from your character's constitution, divide by 2 and round down. + +Write a random character generator that follows the rules above. + +For example, the six throws of four dice may look like: + +* 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength. +* 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity. +* 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution. +* 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence. +* 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom. +* 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma. + +Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6. + +## Notes + +Most programming languages feature (pseudo-)random generators, but few +programming languages are designed to roll dice. One such language is [Troll]. + +[DND]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons +[Troll]: http://hjemmesider.diku.dk/~torbenm/Troll/ + + +Run the tests with: + +```bash +bats dnd_character_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats dnd_character_test.sh +``` + +## Source + +Simon Shine, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945](https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/dnd-character/dnd_character.sh b/bash/dnd-character/dnd_character.sh new file mode 100755 index 0000000..405bec0 --- /dev/null +++ b/bash/dnd-character/dnd_character.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# +# v1: initial version +# v2: cleanup, replaced while loops with for loops when possible + +# external tools: none + +# set to 0 to disable log function output, 1 otherwise +debug=0 + +declare -a abilities=( strength dexterity intelligence wisdom charisma constitution ) + + +usage() { + echo "dnd_characters.sh generate|modifier " >&2 + exit 1 +} + +# log function takes 1 argument which is echoed if $debug is > 0 +# todo: Add another argument (mask ?) to selectively log portions of code +log () { + (( $debug )) && echo "$*" +} + +# simple bubble sort for numeric array (descending) +# args: 1: the array +# todo: add a parameter for asc/desc order. +sort_n() { + local -a array=( "$@" ) + local -i max=$(( ${#array[@]} - 1 )) + + for (( max= $(( ${#array[@]} - 1 )); max > 0; max-- )); do + local -i i + for (( i=0; i" + exit 1 +} +(( $# != 1 )) && usage + +echo "Hello, $1" diff --git a/bash/grains/README.md b/bash/grains/README.md new file mode 100644 index 0000000..6abc79f --- /dev/null +++ b/bash/grains/README.md @@ -0,0 +1,73 @@ +# Grains + +Calculate the number of grains of wheat on a chessboard given that the number +on each square doubles. + +There once was a wise servant who saved the life of a prince. The king +promised to pay whatever the servant could dream up. Knowing that the +king loved chess, the servant told the king he would like to have grains +of wheat. One grain on the first square of a chess board, with the number +of grains doubling on each successive square. + +There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). + +Write code that shows: +- how many grains were on a given square, and +- the total number of grains on the chessboard + +## For bonus points + +Did you get the tests passing and the code clean? If you want to, these +are some additional things you could try: + +- Optimize for speed. +- Optimize for readability. + +Then please share your thoughts in a comment on the submission. Did this +experiment make the code better? Worse? Did you learn anything from it? + + +Run the tests with: + +```bash +bats grains_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats grains_test.sh +``` + +## Source + +JavaRanch Cattle Drive, exercise 6 [http://www.javaranch.com/grains.jsp](http://www.javaranch.com/grains.jsp) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/grains/grains.sh b/bash/grains/grains.sh new file mode 100755 index 0000000..89566c1 --- /dev/null +++ b/bash/grains/grains.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# V1: initial version +# +# Note: untested on 32 bits and big-endian architectures + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +#(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "Error: invalid input" +} + +# we want 1 arg only, "total" or digits only. +(($# !=1 )) || \ + [[ "$1" != +([[:digit:]]) ]] && \ + [[ "$1" != "total" ]] && usage + +arg="${1##+(0)}" # we strip leading zeroes if any +debug 1 arg="$arg" + +case "$arg" in + total) + # formula to calculate a geometric series of common ratio r and first + # term f, that is: S=f + fr² + fr³ ... + frⁿ + # is: S = f * [ (1 - rⁿ⁺¹) / (1 - r) ] + # + # for r=2 and f=1, it becomes: + # S = 1 * (1 - 2ⁿ⁺¹) / -1 = 2ⁿ⁺¹ - 1 + # So here, as total does not accept an argument, such as "last square", + # the value is 2⁶⁴-1. + # Notes: (2**64) is 0 on 64 bits, but better not to use this property. + # We could also directly output the value, as it is a constant. + printf -v result "%llu" $(( 2**64 - 1 )) + ;; + *) # always digits here + debug 2 "digits=args" + (( arg < 1 || arg > 64 )) && usage + printf -v result "%llu" $(( 2**(arg-1) )) + ;; +esac + +echo "$result" + +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/grep/README.md b/bash/grep/README.md new file mode 100644 index 0000000..a17fc4f --- /dev/null +++ b/bash/grep/README.md @@ -0,0 +1,111 @@ +# Grep + +Search a file for lines matching a regular expression pattern. Return the line +number and contents of each matching line. + +The Unix [`grep`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) command can be used to search for lines in one or more files +that match a user-provided search query (known as the *pattern*). + +The `grep` command takes three arguments: + +1. The pattern used to match lines in a file. +2. Zero or more flags to customize the matching behavior. +3. One or more files in which to search for matching lines. + +Your task is to implement the `grep` function, which should read the contents +of the specified files, find the lines that match the specified pattern +and then output those lines as a single string. Note that the lines should +be output in the order in which they were found, with the first matching line +in the first file being output first. + +As an example, suppose there is a file named "input.txt" with the following contents: + +```text +hello +world +hello again +``` + +If we were to call `grep "hello" input.txt`, the returned string should be: + +```text +hello +hello again +``` + +### Flags + +As said earlier, the `grep` command should also support the following flags: + +- `-n` Print the line numbers of each matching line. +- `-l` Print only the names of files that contain at least one matching line. +- `-i` Match line using a case-insensitive comparison. +- `-v` Invert the program -- collect all lines that fail to match the pattern. +- `-x` Only match entire lines, instead of lines that contain a match. + +If we run `grep -n "hello" input.txt`, the `-n` flag will require the matching +lines to be prefixed with its line number: + +```text +1:hello +3:hello again +``` + +And if we run `grep -i "HELLO" input.txt`, we'll do a case-insensitive match, +and the output will be: + +```text +hello +hello again +``` + +The `grep` command should support multiple flags at once. + +For example, running `grep -l -v "hello" file1.txt file2.txt` should +print the names of files that do not contain the string "hello". + + +Run the tests with: + +```bash +bats grep_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats grep_test.sh +``` + +## Source + +Conversation with Nate Foster. [http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf](http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/grep/grep.sh b/bash/grep/grep.sh new file mode 100755 index 0000000..5083311 --- /dev/null +++ b/bash/grep/grep.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +usage () { + echo "usage: grep.sh [-nlivx] [-e] pattern file [...]" + exit 1 +} + +# no arguments +LN= # be sure variables are reset +FN= +RV= +FL= +MULTI= + +# parse args: +while getopts ":nlivxe" arg; do + case $arg in + n) LN=t ;; # will print line number + l) FN=t ;; # only file names + i) shopt -s nocasematch ;; # case insensitive + v) RV=t ;; # reverse search + x) FL=t ;; # full line search + e) break ;; # not in exercise: end args + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# need at least pattern and 1 file. +# TODO: consider stdin if no file +[[ $# < 2 ]] && usage +[[ $# > 2 ]] && MULTI=t + +# full line match +if [[ $FL = t ]]; then + pattern="^$1\$" +else + pattern=".*$1.*" +fi + +shift + +# will prevent leading/trailing whitespaces to be trimmed +IFS="" + +while [[ $# > 0 ]]; do + file="$1" + if [[ ! -r "$file" ]]; then + echo "$file: no such file, or not readable." + exit 1 + fi + + lnum=0 # line number + + match=n + while read -r line ; do # will consider '\' as normal char + (( lnum ++ )) + + if [[ $line =~ $pattern ]] ; then # line match + match=y + + # print only filename, go to next file + [[ $FN = t ]] && echo "$file" && break + + # not reverse matching only + if [[ $RV != t ]]; then + # multiple files: print filename + [[ $MULTI == t ]] && echo -n "$file:" + [[ $LN = t ]] && echo -n "$lnum:" + echo "$line" + fi + else + # reverse match + if [[ $RV == t ]]; then + # print only filename, go to next file + [[ $FN = t ]] && echo "$file" && break + + # multiple files: print filename + [[ $MULTI == t ]] && echo -n "$file:" + [[ $LN = t ]] && echo -n "$lnum:" + echo "$line" + fi + fi + done < "$1" + + shift # next file +done +exit 0 diff --git a/bash/hamming/README.md b/bash/hamming/README.md new file mode 100644 index 0000000..b738a53 --- /dev/null +++ b/bash/hamming/README.md @@ -0,0 +1,70 @@ +# Hamming + +Calculate the Hamming Distance between two DNA strands. + +Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! + +When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance". + +We read DNA using the letters C,A,G and T. Two strands might look like this: + + GAGCCTACTAACGGGAT + CATCGTAATGACGGCCT + ^ ^ ^ ^ ^ ^^ + +They have 7 differences, and therefore the Hamming Distance is 7. + +The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) + +# Implementation notes + +The Hamming distance is only defined for sequences of equal length, so +an attempt to calculate it between sequences of different lengths should +not work. The general handling of this situation (e.g., raising an +exception vs returning a special value) may differ between languages. + + +Run the tests with: + +```bash +bats hamming_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats hamming_test.sh +``` + +## Source + +The Calculating Point Mutations problem at Rosalind [http://rosalind.info/problems/hamm/](http://rosalind.info/problems/hamm/) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/hamming/hamming.sh b/bash/hamming/hamming.sh new file mode 100755 index 0000000..4e8b851 --- /dev/null +++ b/bash/hamming/hamming.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# set to 0 to disable log function output, 1 otherwise +debug=0 + +# echoes $1, then exit 1 +die () { + echo "$1" + exit 1 +} + +# log function takes 1 argument which is echoed if $debug is > 0 +# todo: Ad another argument (mask?) to selectively log portions of code +log () { + (( $debug )) && echo "$1" +} + +# check basic errors +(( $# != 2 )) && die "Usage: hamming.sh " +(( ${#1} != ${#2} )) && die "left and right strands must be of equal length" + +# compare strings +declare -i cur hamming=0 + +for (( cur=0; cur < ${#1}; cur++ )); do + log "$cur ${1:$cur:1} ${2:$cur:1}" + c1="${1:$cur:1}" + c2="${2:$cur:1}" + [[ "$c1" != "$c2" ]] && ((hamming++)) + log "hamming in loop=$hamming" +done + +echo "$hamming" +exit 0 + +# Indent style for emacs +# Local Variables: +# sh-basic-offset: 4 +# sh-indentation: 4 +# indent-tabs-mode: nil +# comment-column: 60 +# End: diff --git a/bash/hello-world/README.md b/bash/hello-world/README.md new file mode 100644 index 0000000..1c2e532 --- /dev/null +++ b/bash/hello-world/README.md @@ -0,0 +1,101 @@ +# Hello World + +The classical introductory exercise. Just say "Hello, World!". + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is +the traditional first program for beginning programming in a new language +or environment. + +The objectives are simple: + +- Write a function that returns the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +# Welcome to Bash! + +Unlike many other languages here, bash is a bit of a special snowflake. +If you are on a Mac or other unix-y platform, you almost definitely +already have bash. In fact, anything you type into the terminal is +likely going through bash. + +The downside to this is that there isn't much of a development +ecosystem around bash like there is for other languages, and there are +multiple versions of bash that can be frustratingly incompatible. Luckily +we shouldn't hit those differences for these basic examples, and if you +can get the tests to pass on your machine, we are doing great. + +## Installation + +As mentioned above, if you are on a unix-like OS (Mac OS X, Linux, Solaris, +etc), you probably already have bash. + +## Testing + +As there isn't much of a bash ecosystem, there also isn't really a de +facto leader in the bash testing area. For these examples we are using +[bats](https://github.com/sstephenson/bats). You should be able to +install it from your favorite package manager, on OS X with homebrew +this would look something like this: + +``` +$ brew install bats +==> Downloading +https://github.com/sstephenson/bats/archive/v0.4.0.tar.gz +==> Downloading from +https://codeload.github.com/sstephenson/bats/tar.gz/v0.4.0 +######################################################################## +100.0% +==> ./install.sh /opt/boxen/homebrew/Cellar/bats/0.4.0 +🍺 /opt/boxen/homebrew/Cellar/bats/0.4.0: 10 files, 60K, built in 2 +seconds +``` + + + +Run the tests with: + +```bash +bats hello_world_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats hello_world_test.sh +``` + +## Source + +This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/hello-world/hello_world.sh b/bash/hello-world/hello_world.sh new file mode 100644 index 0000000..85a0a94 --- /dev/null +++ b/bash/hello-world/hello_world.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# +# exercism: hello world + +echo "Hello, World!" diff --git a/bash/leap/README.md b/bash/leap/README.md new file mode 100644 index 0000000..34ac368 --- /dev/null +++ b/bash/leap/README.md @@ -0,0 +1,70 @@ +# Leap + +Given a year, report if it is a leap year. + +The tricky thing here is that a leap year in the Gregorian calendar occurs: + +```text +on every year that is evenly divisible by 4 + except every year that is evenly divisible by 100 + unless the year is also evenly divisible by 400 +``` + +For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap +year, but 2000 is. + +## Notes + +Though our exercise adopts some very simple rules, there is more to +learn! + +For a delightful, four minute explanation of the whole leap year +phenomenon, go watch [this youtube video][video]. + +[video]: http://www.youtube.com/watch?v=xX96xng7sAE + + +Run the tests with: + +```bash +bats leap_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats leap_test.sh +``` + +## Source + +JavaRanch Cattle Drive, exercise 3 [http://www.javaranch.com/leap.jsp](http://www.javaranch.com/leap.jsp) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/leap/leap.sh b/bash/leap/leap.sh new file mode 100755 index 0000000..ec1a5a4 --- /dev/null +++ b/bash/leap/leap.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +[[ $# != 1 || ! $1 =~ ^[0-9]+$ ]] && echo "Usage: $0 " && exit 1 + +year=$1 +leap=false +if ! (($year % 4)) && (($year % 100)) || ! (($year % 400)) +then + leap=true +fi +echo $leap + +exit 0 diff --git a/bash/luhn/README.md b/bash/luhn/README.md new file mode 100644 index 0000000..e362043 --- /dev/null +++ b/bash/luhn/README.md @@ -0,0 +1,111 @@ +# Luhn + +Given a number determine whether or not it is valid per the Luhn formula. + +The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is +a simple checksum formula used to validate a variety of identification +numbers, such as credit card numbers and Canadian Social Insurance +Numbers. + +The task is to check if a given string is valid. + +Validating a Number +------ + +Strings of length 1 or less are not valid. Spaces are allowed in the input, +but they should be stripped before checking. All other non-digit characters +are disallowed. + +## Example 1: valid credit card number + +```text +4539 1488 0343 6467 +``` + +The first step of the Luhn algorithm is to double every second digit, +starting from the right. We will be doubling + +```text +4_3_ 1_8_ 0_4_ 6_6_ +``` + +If doubling the number results in a number greater than 9 then subtract 9 +from the product. The results of our doubling: + +```text +8569 2478 0383 3437 +``` + +Then sum all of the digits: + +```text +8+5+6+9+2+4+7+8+0+3+8+3+3+4+3+7 = 80 +``` + +If the sum is evenly divisible by 10, then the number is valid. This number is valid! + +## Example 2: invalid credit card number + +```text +8273 1232 7352 0569 +``` + +Double the second digits, starting from the right + +```text +7253 2262 5312 0539 +``` + +Sum the digits + +```text +7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57 +``` + +57 is not evenly divisible by 10, so this number is not valid. + + +Run the tests with: + +```bash +bats luhn_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats luhn_test.sh +``` + +## Source + +The Luhn Algorithm on Wikipedia [http://en.wikipedia.org/wiki/Luhn_algorithm](http://en.wikipedia.org/wiki/Luhn_algorithm) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/luhn/luhn.sh b/bash/luhn/luhn.sh new file mode 100755 index 0000000..aee04ba --- /dev/null +++ b/bash/luhn/luhn.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# +# External tools: none. +# +# V1: initial version + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +#(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +shopt -s extglob + +# basic args usage. We will accept the args to be splitted (out of scope +# for exercise): "luhn.sh '0 0'" is considered equivalent to "luhn.sh 0 0" +IFS="" number="${*// /}" # merge args, remove all +debug 1 number="${number}" + +((${#number} < 2)) || # args check: only [0-9], at least 2 + [[ "$number" != +([0-9]) ]] && + echo false && exit 0 + +((len=${#number})) +((sum=0)) # final sum + +for ((i=1; i<=len; ++i)); do # we loop on each digit, from end + digit=${number:((-i)):1} + case $((i % 2)) in + 0) # even position (relative to end) + ((digit *= 2)) + ((digit >= 10)) && ((digit-=9)) + debug 3 even ${digit} + ;; + 1) # odd position (relative to end) + debug 3 odd ${digit} + ;; + + esac + + ((sum+=digit)) + debug 2 sum=${sum} +done +((sum % 10)) && echo false || echo true + +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/matching-brackets/README.md b/bash/matching-brackets/README.md new file mode 100644 index 0000000..90d55b1 --- /dev/null +++ b/bash/matching-brackets/README.md @@ -0,0 +1,51 @@ +# Matching Brackets + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, +or any combination thereof, verify that any and all pairs are matched +and nested correctly. + + +Run the tests with: + +```bash +bats matching_brackets_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats matching_brackets_test.sh +``` + +## Source + +Ginna Baker + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/matching-brackets/matching_brackets.sh b/bash/matching-brackets/matching_brackets.sh new file mode 100755 index 0000000..0af0c22 --- /dev/null +++ b/bash/matching-brackets/matching_brackets.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash -x +shopt -s extglob +str=$1 +# Brute force algorithm: +# 1 - remove all unwanted chars: keep only {}[]() +str=${str//[^\{\}\(\)\[\]]} + +res=true +# 2- remove all pairs of [], {}, (). loop until string is empty (match ok) or unchanged +# (nok) +loop=1 +while [[ ${#str} > 0 ]] +do + str2=${str//+(\[\]|\(\)|\{\})} + if [[ ${#str2} == ${#str} ]]; then + res=false + break + fi + str="$str2" +done + +echo $res +exit 0 diff --git a/bash/pangram/README.md b/bash/pangram/README.md new file mode 100644 index 0000000..21f55d8 --- /dev/null +++ b/bash/pangram/README.md @@ -0,0 +1,55 @@ +# Pangram + +Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma, +"every letter") is a sentence using every letter of the alphabet at least once. +The best known English pangram is: +> The quick brown fox jumps over the lazy dog. + +The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case +insensitive. Input will not contain non-ASCII symbols. + + +Run the tests with: + +```bash +bats pangram_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats pangram_test.sh +``` + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/pangram/pangram.sh b/bash/pangram/pangram.sh new file mode 100755 index 0000000..a242c67 --- /dev/null +++ b/bash/pangram/pangram.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# set to mask to enable logs. 0: none, 255: all +# (( debug=2#00001111 )) +(( debug=2#00000000 )) + +# $1: log level (mask), then strings to display. +debug () { + (( debug & "$1" )) && shift && echo "${@}" >&2 +} + +usage () { + echo "usage: ./pangram.sh [...]" >&2 + exit 1 +} + +# basic args checks: at least 1 arg +(( $# )) || usage + +# We will accept mutiple args here: "a" "b" is equivalent to "ab". +# get args in one string, change it to lower case. +IFS="" # not really needed +string="${*,,}" +debug 1 "string=$string" + +# empty string +if (( ! ${#string} )); then + debug 2 "empty string" + result=false +else + result=true + + # loop on all characters, from a to z. We need to check each one. + for c in {a..z} ; do + [[ ! ${string} =~ "$c" ]] && result=false && break + done +fi + +echo "$result" +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 60 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/proverb/README.md b/bash/proverb/README.md new file mode 100644 index 0000000..ac20958 --- /dev/null +++ b/bash/proverb/README.md @@ -0,0 +1,63 @@ +# Proverb + +For want of a horseshoe nail, a kingdom was lost, or so the saying goes. + +Given a list of inputs, generate the relevant proverb. For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme: + +```text +For want of a nail the shoe was lost. +For want of a shoe the horse was lost. +For want of a horse the rider was lost. +For want of a rider the message was lost. +For want of a message the battle was lost. +For want of a battle the kingdom was lost. +And all for the want of a nail. +``` + +Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content. No line of the output text should be a static, unchanging string; all should vary according to the input given. + + +Run the tests with: + +```bash +bats proverb_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats proverb_test.sh +``` + +## Source + +Wikipedia [http://en.wikipedia.org/wiki/For_Want_of_a_Nail](http://en.wikipedia.org/wiki/For_Want_of_a_Nail) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/proverb/proverb.sh b/bash/proverb/proverb.sh new file mode 100755 index 0000000..5780075 --- /dev/null +++ b/bash/proverb/proverb.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +[[ $# == 0 ]] && echo "" && exit 0 + +first="$1" +prev="$first" +shift +while [[ $# > 0 ]]; do + cur="$1" + echo "For want of a $prev the $cur was lost." + prev=$cur + shift +done +echo "And all for the want of a $first." +exit 0 diff --git a/bash/raindrops/README.md b/bash/raindrops/README.md new file mode 100644 index 0000000..8c56e60 --- /dev/null +++ b/bash/raindrops/README.md @@ -0,0 +1,64 @@ +# Raindrops + +Convert a number to a string, the contents of which depend on the number's factors. + +- If the number has 3 as a factor, output 'Pling'. +- If the number has 5 as a factor, output 'Plang'. +- If the number has 7 as a factor, output 'Plong'. +- If the number does not have 3, 5, or 7 as a factor, + just pass the number's digits straight through. + +## Examples + +- 28's factors are 1, 2, 4, **7**, 14, 28. + - In raindrop-speak, this would be a simple "Plong". +- 30's factors are 1, 2, **3**, **5**, 6, 10, 15, 30. + - In raindrop-speak, this would be a "PlingPlang". +- 34 has four factors: 1, 2, 17, and 34. + - In raindrop-speak, this would be "34". + + +Run the tests with: + +```bash +bats raindrops_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats raindrops_test.sh +``` + +## Source + +A variation on a famous interview question intended to weed out potential candidates. [http://jumpstartlab.com](http://jumpstartlab.com) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/raindrops/raindrops.sh b/bash/raindrops/raindrops.sh new file mode 100755 index 0000000..9ff588d --- /dev/null +++ b/bash/raindrops/raindrops.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +function usage() { + echo "Usage: ./raindrop.sh number" + exit 1 +} + +shopt -s extglob + +(( $# == 1 )) || usage +number="$1" +[[ "$number" == +([0-9]) ]] || usage + +output="" +(( $number % 3 )) || output+="Pling" +(( $number % 5 )) || output+="Plang" +(( $number % 7 )) || output+="Plong" + +echo ${output:-$number} +exit 0 diff --git a/bash/resistor-color-duo/README.md b/bash/resistor-color-duo/README.md new file mode 100644 index 0000000..62a92a3 --- /dev/null +++ b/bash/resistor-color-duo/README.md @@ -0,0 +1,68 @@ +# Resistor Color Duo + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. For this exercise, you need to know two things about them: + +* Each resistor has a resistance value. +* Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. +To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. Each band acts as a digit of a number. For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + +In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands. The program will take two colors as input, and output the correct number. + +The band colors are encoded as follows: + +- Black: 0 +- Brown: 1 +- Red: 2 +- Orange: 3 +- Yellow: 4 +- Green: 5 +- Blue: 6 +- Violet: 7 +- Grey: 8 +- White: 9 + + +Run the tests with: + +```bash +bats resistor_color_duo_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats resistor_color_duo_test.sh +``` + +## Source + +Maud de Vries, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/1464](https://github.com/exercism/problem-specifications/issues/1464) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/resistor-color-duo/resistor_color_duo.sh b/bash/resistor-color-duo/resistor_color_duo.sh new file mode 100755 index 0000000..fb2738f --- /dev/null +++ b/bash/resistor-color-duo/resistor_color_duo.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +usage () { + if [[ $# == 1 ]]; then + echo "$1: invalid color." + else + echo "usage: resistor_color.sh color1 color2" + fi + exit 1 +} + +declare -A colors +colors=( + [black]=0 + [brown]=1 + [red]=2 + [orange]=3 + [yellow]=4 + [green]=5 + [blue]=6 + [violet]=7 + [grey]=8 + [white]=9 +) +result="" + +# not sure here if 1 color only should be accepted (case is not in test). I assume yes: +# "resistor_color.sh Black" will return 0 +# also (not in test cases), no args will return an error. +[[ $# == 0 ]] && usage + +# we will accept capitalized colors: converting to lower case +for i in ${1,,} ${2,,} +do + [[ ${colors[$i]} == "" ]] && usage "$i" + result+=${colors[$i]} +done +echo $result +exit 0 diff --git a/bash/resistor-color-trio/README.md b/bash/resistor-color-trio/README.md new file mode 100644 index 0000000..3a78d8c --- /dev/null +++ b/bash/resistor-color-trio/README.md @@ -0,0 +1,91 @@ +# Resistor Color Trio + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. For this exercise, you need to know only three things about them: + +- Each resistor has a resistance value. +- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +- Each band acts as a digit of a number. For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands. The program will take 3 colors as input, and outputs the correct value, in ohms. + The color bands are encoded as follows: + +* Black: 0 +* Brown: 1 +* Red: 2 +* Orange: 3 +* Yellow: 4 +* Green: 5 +* Blue: 6 +* Violet: 7 +* Grey: 8 +* White: 9 + +In `resistor-color duo` you decoded the first two colors. For instance: orange-orange got the main value `33`. +The third color stands for how many zeros need to be added to the main value. The main value plus the zeros gives us a value in ohms. +For the exercise it doesn't matter what ohms really are. +For example: + +- orange-orange-black would be 33 and no zeros, which becomes 33 ohms. +- orange-orange-red would be 33 and 2 zeros, which becomes 3300 ohms. +- orange-orange-orange would be 33 and 3 zeros, which becomes 33000 ohms. + +(If Math is your thing, you may want to think of the zeros as exponents of 10. If Math is not your thing, go with the zeros. It really is the same thing, just in plain English instead of Math lingo.) + +This exercise is about translating the colors into a label: + +> "... ohms" + +So an input of `"orange", "orange", "black"` should return: + +> "33 ohms" + +When we get more than a thousand ohms, we say "kiloohms". That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams. +So an input of `"orange", "orange", "orange"` should return: + +> "33 kiloohms" + + +Run the tests with: + +```bash +bats resistor_color_trio_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats resistor_color_trio_test.sh +``` + +## Source + +Maud de Vries, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/1549](https://github.com/exercism/problem-specifications/issues/1549) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/resistor-color-trio/resistor_color_trio.sh b/bash/resistor-color-trio/resistor_color_trio.sh new file mode 100755 index 0000000..f34ab38 --- /dev/null +++ b/bash/resistor-color-trio/resistor_color_trio.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# +# V1: initial version +# V2: merged power tables, add index one, improved unit str calculation + +# set to mask to enable logs. 0: none, 255: all +((debug=2#00000000)) +#((debug=2#00011111)) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} + +usage() { + die "usage: resistor_color_trio.sh color1 [color2 [color3]]" +} + +# not sure here if 1 or 2 colors only should be accepted (not in test cases). +# I will assume yes, it looks more coherent, even if out of scope: +# "resistor_color_trio.sh Black" will return 0 +# "resistor_color_trio.sh yellow violet" will return 47 +# also: (not in test cases), no args will return an error. +# (in test case), extra colors are ignored, I don't like it, personally +(( $# == 0 )) && usage + +# value/power of different colors +declare -A colors=([black]=0 [brown]=1 [red]=2 [orange]=3 [yellow]=4 + [green]=5 [blue]=6 [violet]=7 [grey]=8 [white]=9) + +# available multiplicators (powers of 10). +declare -a powers=([9]=giga [6]=mega [3]=kilo) +idx=("${!powers[@]}") # keys copy for reverse loop + +params=( ${@,,} ) # converting to lower case + +(( result=0 )) # final number to display + +# I would have preferred to throw an error if $# > 3, instead of ignoring +# extra args... +for ((i=0; i<3 && i<${#params[@]}; ++i)); do + color="${params[$i]}" # color name + val="${colors[$color]}" # color value + + [[ -z "$val" ]] && die "$color: invalid color." + case $i in + 0) (( result=val )) + debug 2 "new color 1 $color/$val. result=$result" + ;; + # probably case 0 and 1 could be merged. Easier to read for me + # by separating them. + 1) (( result*=10 )) + (( result+=val )) + debug 2 "new color 2 $color/$val. result=$result" + ;; + 2) (( result *= 10 ** val )) + debug 2 "new color 3 $color/$val. result=$result" + ;; + esac +done + +# we have final number, scaling to corresponding strings. +scalestr="" # "giga", etc... + +if (( result )); then + for (( i = ${#idx[@]} - 1; i >= 0; i-- )); do # reverse loop by value + j=${idx[$i]} # also actual power (9, 6, 3) + (( power = 10 ** $j )) # actual value + debug 8 $i $j ${powers[$j]} $power $result + if ! (( result % power )); then # found power + scalestr=${powers[$j]} + (( result /= power )) + break + fi + done +fi + +echo "$result ${scalestr}ohms" + +exit 0 + +# emacs/vim settings below. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 50 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/resistor-color-trio/resistor_color_trio.sh.1 b/bash/resistor-color-trio/resistor_color_trio.sh.1 new file mode 100755 index 0000000..dff7de4 --- /dev/null +++ b/bash/resistor-color-trio/resistor_color_trio.sh.1 @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# set to mask to enable logs. 0: none, 255: all +# ((debug=2#00011111)) +((debug=2#00000000)) + +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} + +usage() { + die "usage: resistor_color_trio.sh color1 [color2 [color3]]" +} + +# value/power of different colors +declare -A colors=([black]=0 [brown]=1 [red]=2 [orange]=3 [yellow]=4 + [green]=5 [blue]=6 [violet]=7 [grey]=8 [white]=9) +# available multiplicators (powers of 10) +declare -a powers=(9 6 3) +# corresponding strings +declare -a powernames=(giga mega kilo) + +# not sure here if 1 or 2 colors only should be accepted (not in test cases). +# I will assume yes, it looks more coherent, even if out of scope: +# "resistor_color_trio.sh Black" will return 0 +# "resistor_color_trio.sh yellow violet" will return 47 +# also: (not in test cases), no args will return an error. +# (in test case), extra colors are ignored, I don't like it, personally +(( $# == 0 )) && usage + +scalestr="" # "giga", etc... +(( result=0 )) # number to display + +debug 64 "params=${#params[@]} ${params[@]}" + +# converting to lower case +params=( ${@,,} ) +debug 2 "#params=${#params[@]} ${params[@]}" + +# I would have preferred to throw an error if $# > 3, instead of ignoring +# extra args... +for ((i=0; i<3 && i<${#params[@]}; ++i)); do + color="${params[$i]}" # color name + val="${colors[$color]}" # color value + + debug 8 "i=$i - color=$color - val=$val" + + debug 4 val="$val" color="${color[$i]}" + [[ -z "$val" ]] && die "$color: invalid color." + case $i in + 0) (( result=val )) + debug 2 "new color 1 $color/$val. result=$result" + ;; + + # probably case 0 and 1 could be merged. Easier to read for me + # separating them. + 1) (( result*=10 )) + (( result+=val )) + debug 2 "new color 2 $color/$val. result=$result" + ;; + + 2) (( result *= 10 ** val )) + debug 2 "new color 3 $color/$val. result=$result" + + # we have final number, scaling to multiple strings. + for (( j=0; result && j<${#powers[@]}; ++j )); do + (( pow = 10 ** ${powers[j]} )) + debug 8 "val=$val result=$result j=$j pow=$pow" + if ! (( result && result % pow )); then + ((result /= pow)) + scalestr=${powernames[$j]} + break + fi + done + ;; + esac +done +debug 4 res="$result" scalestr="$scalestr" + +echo "$result ${scalestr}ohms" + +exit 0 + +# emacs/vim settings below. + +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 60 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/resistor-color-trio/resistor_color_trio.sh.sav b/bash/resistor-color-trio/resistor_color_trio.sh.sav new file mode 100755 index 0000000..78f4193 --- /dev/null +++ b/bash/resistor-color-trio/resistor_color_trio.sh.sav @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# +# V1: initial version +# V2: merged the + +# set to mask to enable logs. 0: none, 255: all +# ((debug=2#00011111)) +((debug=2#00000000)) + +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} + +usage() { + die "usage: resistor_color_trio.sh color1 [color2 [color3]]" +} + +# value/power of different colors +declare -A colors=([black]=0 [brown]=1 [red]=2 [orange]=3 [yellow]=4 + [green]=5 [blue]=6 [violet]=7 [grey]=8 [white]=9) +# available multiplicators (powers of 10) +declare -a powers=(9 6 3) + +scalestr="" # "giga", etc... +(( result=0 )) # number to display +# corresponding strings +declare -a powernames=(giga mega kilo) + +# not sure here if 1 or 2 colors only should be accepted (not in test cases). +# I will assume yes, it looks more coherent, even if out of scope: +# "resistor_color_trio.sh Black" will return 0 +# "resistor_color_trio.sh yellow violet" will return 47 +# also: (not in test cases), no args will return an error. +# (in test case), extra colors are ignored, I don't like it, personally +(( $# == 0 )) && usage + +debug 64 "params=${#params[@]} ${params[@]}" + +# converting to lower case, adding default values if args missing +params=( ${@,,} black black black ) +debug 2 "#params=${#params[@]} ${params[@]}" + +# I would have preferred to throw an error if $# > 3, instead of ignoring +# extra args... +for ((i=0; i<3 && i<${#params[@]}; ++i)); do + color="${params[$i]}" # color name + val="${colors[$color]}" # color value + + debug 4 val="$val" color="${color[$i]}" + [[ -z "$val" ]] && die "$color: invalid color." + case $i in + 0) (( result=val )) + debug 2 "new color 1 $color/$val. result=$result" + ;; + # probably case 0 and 1 could be merged. Easier to read for me + # by separating them. + 1) (( result*=10 )) + (( result+=val )) + debug 2 "new color 2 $color/$val. result=$result" + ;; + 2) (( result *= 10 ** val )) + debug 2 "new color 3 $color/$val. result=$result" + ;; + esac +done + +# we have final number, scaling to corresponding strings. +for (( j=0; result && j<${#powers[@]}; ++j )); do + (( pow = 10 ** ${powers[j]} )) + debug 8 "val=$val result=$result j=$j pow=$pow" + if ! (( result && result % pow )); then + ((result /= pow)) + scalestr=${powernames[$j]} + break + fi +done + + +debug 4 res="$result" scalestr="$scalestr" + +echo "$result ${scalestr}ohms" + +exit 0 + +# emacs/vim settings below. + +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 50 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/reverse-string/README.md b/bash/reverse-string/README.md new file mode 100644 index 0000000..5eb6b33 --- /dev/null +++ b/bash/reverse-string/README.md @@ -0,0 +1,53 @@ +# Reverse String + +Reverse a string + +For example: +input: "cool" +output: "looc" + + +Run the tests with: + +```bash +bats reverse_string_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats reverse_string_test.sh +``` + +## Source + +Introductory challenge to reverse an input string [https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb](https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/reverse-string/reverse_string.sh b/bash/reverse-string/reverse_string.sh new file mode 100755 index 0000000..b729652 --- /dev/null +++ b/bash/reverse-string/reverse_string.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +[[ $# != 1 ]] && echo "usage: $0 string" && exit 1 + + +str=$1 +len=${#str} +rev="" + +for (( i=0; i `C` +* `C` -> `G` +* `T` -> `A` +* `A` -> `U` + + +Run the tests with: + +```bash +bats rna_transcription_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats rna_transcription_test.sh +``` + +## Source + +Hyperphysics [http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html](http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/rna-transcription/rna_transcription.sh b/bash/rna-transcription/rna_transcription.sh new file mode 100755 index 0000000..b5cad89 --- /dev/null +++ b/bash/rna-transcription/rna_transcription.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# External tools: none. +# +# V1: initial version + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +#(( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: rna_transcription.sh [seq1] [...]" +} + +declare -A complement=([G]='C' + [C]='G' + [T]='A' + [A]='U') +debug 1 keys/vals=${!complement[@]}/${complement[@]} +# we will accept multiple args (out of topic for exercise), meaning that: +# rna_transcription.sh GCTA +# is equivalent to +# rna_transcription.sh G C T A +# or +# rna_transcription.sh G CTA +dna="${@}" +dna="${dna// /}" # remove spaces +(($# > 0 )) && [[ "$dna" != +([GCTA]) ]] && die "Invalid nucleotide detected." + +((ldna=${#dna})) +debug 1 "dna=$dna, length=$ldna" +rna="" # rna sequence + +for ((i=0; i&2 +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: scrabble_score.sh word" +} + +declare -A values=( + [A]=1 [E]=1 [I]=1 [O]=1 [U]=1 [L]=1 [N]=1 [R]=1 [S]=1 [T]=1 + [D]=2 [G]=2 + [B]=3 [C]=3 [M]=3 [P]=3 + [F]=4 [H]=4 [V]=4 [W]=4 [Y]=4 + [K]=5 + [J]=8 [X]=8 + [Q]=10 [Z]=10 +) + +(($# != 1)) && usage +word="${1^^}" # capitalize +[[ "$word" != +([[:upper:]]) ]] && usage # accept only alpha chars + +debug 1 arg="$word" +((score=0)) + +for (( i=0; i<${#word}; ++i )); do + c="${word:$i:1}" + ((score += ${values[$c]})) +done + +echo "$score" +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 50 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/secret-handshake/README.md b/bash/secret-handshake/README.md new file mode 100644 index 0000000..1654fc2 --- /dev/null +++ b/bash/secret-handshake/README.md @@ -0,0 +1,75 @@ +# Secret Handshake + +> There are 10 types of people in the world: Those who understand +> binary, and those who don't. + +You and your fellow cohort of those in the "know" when it comes to +binary decide to come up with a secret "handshake". + +```text +1 = wink +10 = double blink +100 = close your eyes +1000 = jump + + +10000 = Reverse the order of the operations in the secret handshake. +``` + +Given a decimal number, convert it to the appropriate sequence of events for a secret handshake. + +Here's a couple of examples: + +Given the input 3, the function would return the array +["wink", "double blink"] because 3 is 11 in binary. + +Given the input 19, the function would return the array +["double blink", "wink"] because 19 is 10011 in binary. +Notice that the addition of 16 (10000 in binary) +has caused the array to be reversed. + + +Run the tests with: + +```bash +bats secret_handshake_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats secret_handshake_test.sh +``` + +## Source + +Bert, in Mary Poppins [http://www.imdb.com/title/tt0058331/quotes/qt0437047](http://www.imdb.com/title/tt0058331/quotes/qt0437047) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/secret-handshake/secret_handshake.sh b/bash/secret-handshake/secret_handshake.sh new file mode 100755 index 0000000..51e6c3f --- /dev/null +++ b/bash/secret-handshake/secret_handshake.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +num=$1 + +strings=( + "wink" + "double blink" + "close your eyes" + "jump" +) +reverse=n +result=() + +(( $num & 2#10000 )) && reverse=y + +# get matching bits +for (( i=0 ; i<4 ; ++i )); do + (( $num & 1<<$i )) && result+=( "${strings[$i]}" ) +done +# output in normal or reverse order +for (( i=0 ; i<${#result[@]} ; ++i)) ; do + [[ $reverse == n ]] && echo -n "$sep${result[i]}" || echo -n "$sep${result[~i]}" + sep="," +done +echo +exit 0 diff --git a/bash/sieve/README.md b/bash/sieve/README.md new file mode 100644 index 0000000..4e08a18 --- /dev/null +++ b/bash/sieve/README.md @@ -0,0 +1,76 @@ +# Sieve + +Use the Sieve of Eratosthenes to find all the primes from 2 up to a given +number. + +The Sieve of Eratosthenes is a simple, ancient algorithm for finding all +prime numbers up to any given limit. It does so by iteratively marking as +composite (i.e. not prime) the multiples of each prime, starting with the +multiples of 2. It does not use any division or remainder operation. + +Create your range, starting at two and continuing up to and including the given limit. (i.e. [2, limit]) + +The algorithm consists of repeating the following over and over: + +- take the next available unmarked number in your list (it is prime) +- mark all the multiples of that number (they are not prime) + +Repeat until you have processed each number in your range. + +When the algorithm terminates, all the numbers in the list that have not +been marked are prime. + +The wikipedia article has a useful graphic that explains the algorithm: +https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + +Notice that this is a very specific algorithm, and the tests don't check +that you've implemented the algorithm, only that you've come up with the +correct list of primes. A good first test is to check that you do not use +division or remainder operations (div, /, mod or % depending on the +language). + + +Run the tests with: + +```bash +bats sieve_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats sieve_test.sh +``` + +## Source + +Sieve of Eratosthenes at Wikipedia [http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/sieve/sieve.sh b/bash/sieve/sieve.sh new file mode 100755 index 0000000..f310f23 --- /dev/null +++ b/bash/sieve/sieve.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +usage() { + echo "Usage: ./seave.sh " + exit 1 +} +end=$1 + +[[ ! "$end" =~ ^[[:digit:]]*$ ]] && usage + +declare -a numbers + +sep="" +for (( i=2; i<=end; ++i )); do + if [[ ${numbers[$i]} == "" ]]; then + echo -n "$sep$i" + for (( j=i*2; j<=end; j+=i )); do + numbers[$j]=t + done + sep=" " + fi +done +echo +exit 0 diff --git a/bash/template.sh b/bash/template.sh new file mode 100755 index 0000000..42e7d68 --- /dev/null +++ b/bash/template.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# +# External tools: none. +# +# V1: initial version + +shopt -s extglob + +# set to mask to enable logs. 0: none, 255: all +(( debug=2#00000000 )) +# (( debug=2#00001111 )) +# $1: log level (mask), then strings to display. +debug () { + (( debug & $1 )) && shift && echo Line ${BASH_LINENO[0]}: "${@}" >&2 + #local -i i + #if (( debug & $1 )) ; then + # for ((i=1; i<$1; ++i)); do + # echo -n " " + # done + # shift + # echo "${BASH_LINENO[0]}: ${@}" >&2 + #fi + +} + +die () { + echo "${@}" >&2 + exit 1 +} +usage() { + die "usage: resistor_color_trio.sh color1 [color2 [color3]]" +} + +exit 0 + +# emacs/vim settings. +# Local Variables: +# sh-basic-offset: 4 +# indent-tabs-mode: nil +# comment-column: 40 +# fill-column: 80 +# End: +# vim: set tabstop=4 expandtab: diff --git a/bash/tournament/README.md b/bash/tournament/README.md new file mode 100644 index 0000000..ba9938c --- /dev/null +++ b/bash/tournament/README.md @@ -0,0 +1,108 @@ +# Tournament + +Tally the results of a small football competition. + +Based on an input file containing which team played against which and what the +outcome was, create a file with a table like this: + +```text +Team | MP | W | D | L | P +Devastating Donkeys | 3 | 2 | 1 | 0 | 7 +Allegoric Alaskans | 3 | 2 | 0 | 1 | 6 +Blithering Badgers | 3 | 1 | 0 | 2 | 3 +Courageous Californians | 3 | 0 | 1 | 2 | 1 +``` + +What do those abbreviations mean? + +- MP: Matches Played +- W: Matches Won +- D: Matches Drawn (Tied) +- L: Matches Lost +- P: Points + +A win earns a team 3 points. A draw earns 1. A loss earns 0. + +The outcome should be ordered by points, descending. In case of a tie, teams are ordered alphabetically. + +### + +Input + +Your tallying program will receive input that looks like: + +```text +Allegoric Alaskans;Blithering Badgers;win +Devastating Donkeys;Courageous Californians;draw +Devastating Donkeys;Allegoric Alaskans;win +Courageous Californians;Blithering Badgers;loss +Blithering Badgers;Devastating Donkeys;loss +Allegoric Alaskans;Courageous Californians;win +``` + +The result of the match refers to the first team listed. So this line + +```text +Allegoric Alaskans;Blithering Badgers;win +``` + +Means that the Allegoric Alaskans beat the Blithering Badgers. + +This line: + +```text +Courageous Californians;Blithering Badgers;loss +``` + +Means that the Blithering Badgers beat the Courageous Californians. + +And this line: + +```text +Devastating Donkeys;Courageous Californians;draw +``` + +Means that the Devastating Donkeys and Courageous Californians tied. + + +Run the tests with: + +```bash +bats tournament_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats tournament_test.sh +``` + + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/tournament/tournament.sh b/bash/tournament/tournament.sh new file mode 100755 index 0000000..d83c9e4 --- /dev/null +++ b/bash/tournament/tournament.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# v1: - initial version +# v2-3: - backport bubble sort (while -> for) & debug system from next exercises +# - always read from stdin +# - some syntax improvement +# v4: - cosmetic/syntax changes +# - change from empty file handling (empty string becomes valid filename) +# v5: - cosmetic changes following "shellcheck" usage. +# v6: - removed debug calls. +# v7-8: - cosmetic changes + +die() { + echo "${@}" >&2 + exit 1 +} + +# the results, associative arrays with team name as key. Some tables look +# redundant, but easier to understand. +declare -A mp # matches played +declare -A win # matches won +declare -A draw # matches tied +declare -A loss # matches lost +declare -A points # points +declare -a order # final order. + +nteams=0 + +# create a team entry in arrays if non existent. $1: team +create_team() { + if ! [[ -v "mp[$1]" ]]; then + (( nteams++ )) + (( mp["$1"]=0 )) + (( win["$1"]=0 )) + (( loss["$1"]=0 )) + (( draw["$1"]=0 )) + (( points["$1"]=0 )) + order[$nteams]="$1" + fi +} + +# update results for a team: $1, $2: teams, $3: result for $1: win/draw/loss +update_results() { + create_team "$1" + create_team "$2" + + ((mp["$1"]++)) + ((mp["$2"]++)) + case "$3" in + win) + ((win["$1"]++)) + ((loss["$2"]++)) + ((points["$1"]+=3)) + ;; + loss) + ((win["$2"]++)) + ((loss["$1"]++)) + ((points["$2"]+=3)) + ;; + draw) + ((draw["$1"]++)) + ((draw["$2"]++)) + ((points["$1"]++)) + ((points["$2"]++)) + ;; + *) # should not happen + die "fatal: invalid result $3, exiting." + esac +} + +# inspired from https://stackoverflow.com/questions/7442417/how-to-sort-an-array-in-bash +# (bubble sort) +sort_teams() +{ + local max i + + for ((max=nteams; max>0; max--)); do + for ((i=1; i "$team2" ]] ); then + order[$i]="$team2" + order[$i + 1]="$team1" + fi + done + done +} + +output() { + printf "%-30s |%3s |%3s |%3s |%3s |%3s\n" "${@:1:6}" +} + +output_team() { + output "$1" "${mp[$1]}" "${win[$1]}" "${draw[$1]}" "${loss[$1]}" "${points[$1]}" +} + +# output teams, according to "order" table +output_teams() { + local i + # header + output "Team" "MP" "W" "D" "L" "P" + for (( i=1; i<= nteams; ++i )); do + output_team "${order[$i]}" + done +} + +load_teams() { + # set separator. Note we ignore escaping ';': a\;a;b;win wont work + local team1 team2 result + local IFS=$';\n' + + while read -r team1 team2 result ; do + # should empty result considered as error or ignored? Ignored here. + [[ -z "$team1" || -z "$team2" || -z "$result" ]] && continue + update_results "$team1" "$team2" "$result" + done +} + +# we will accept both argument (file name) or stdin input +if (( $# == 0 )); then + load_teams +elif [[ -f "$1" ]]; then + load_teams < "$1" +else + die Invalid "[$1]" file. Exiting. +fi + +sort_teams +output_teams + +exit 0 diff --git a/bash/two-fer/README.md b/bash/two-fer/README.md new file mode 100644 index 0000000..fe863b1 --- /dev/null +++ b/bash/two-fer/README.md @@ -0,0 +1,72 @@ +# Two Fer + +`Two-fer` or `2-fer` is short for two for one. One for you and one for me. + +Given a name, return a string with the message: + +```text +One for X, one for me. +``` + +Where X is the given name. + +However, if the name is missing, return the string: + +```text +One for you, one for me. +``` + +Here are some examples: + +|Name |String to return +|:-------|:------------------ +|Alice |One for Alice, one for me. +|Bob |One for Bob, one for me. +| |One for you, one for me. +|Zaphod |One for Zaphod, one for me. + + +Run the tests with: + +```bash +bats two_fer_test.sh +``` + +After the first test(s) pass, continue by commenting out or removing the +`[[ $BATS_RUN_SKIPPED == true ]] || skip` +annotations prepending other tests. + +To run all tests, including the ones with `skip` annotations, run: + +```bash +BATS_RUN_SKIPPED=true bats two_fer_test.sh +``` + +## Source + +[https://github.com/exercism/problem-specifications/issues/757](https://github.com/exercism/problem-specifications/issues/757) + + +## External utilities +`Bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding +of the language, try to re-implement the solution in pure `Bash`, +without using any external tools. Note that there are some types of +problems that bash cannot solve, such as performing floating point +arithmetic and manipulating dates: for those, you must call out to an +external tool. + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others +have completed the exercise. diff --git a/bash/two-fer/two_fer.sh b/bash/two-fer/two_fer.sh new file mode 100755 index 0000000..c108916 --- /dev/null +++ b/bash/two-fer/two_fer.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +#str=$1 + +# the next two lines would trim leading & trailing blank chars. I believe the exercise +# should be amended to avoid "One for , one for me." +#str="${str#"${str%%[![:space:]]*}"}" # remove leading blanks +#str="${str%"${str##*[![:space:]]}"}" # remove trailing blanks + +#[[ ${#str} = 0 ]] && str="you" + +echo "One for ${1:-you}, one for me." diff --git a/c/acronym/GNUmakefile b/c/acronym/GNUmakefile new file mode 100644 index 0000000..bbca969 --- /dev/null +++ b/c/acronym/GNUmakefile @@ -0,0 +1,41 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL -g +all: clean test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG -g +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/acronym/README.md b/c/acronym/README.md new file mode 100644 index 0000000..ba39844 --- /dev/null +++ b/c/acronym/README.md @@ -0,0 +1,46 @@ +# Acronym + +Convert a phrase to its acronym. + +Techies love their TLA (Three Letter Acronyms)! + +Help generate some jargon by writing a program that converts a long name +like Portable Network Graphics to its acronym (PNG). + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Julien Vanier [https://github.com/monkbroc](https://github.com/monkbroc) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/acronym/makefile b/c/acronym/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/acronym/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/acronym/src/acronym.c b/c/acronym/src/acronym.c new file mode 100644 index 0000000..3edbc5f --- /dev/null +++ b/c/acronym/src/acronym.c @@ -0,0 +1,69 @@ +#include +#include +#include + +#include "acronym.h" + +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +#define SKIPWORD(p) { while (*(p) && (isalpha(*(p)) || *p=='\'')) (p)++;} +#define NEXTWORD(p) { while (*(p) && !isalpha(*(p))) (p)++;} + +char *abbreviate(const char *phrase) +{ + /* Yet another approach (to avoid scanning phrase twice): + * We (re)allocate a ALLOCSIZE buffer when current one is not large + * enough to accept next character + 1 ('\0') + * + * Other solutions would be to scan phrase twice (for example an initial + * strlen() to find out a maximum length), or the (bad idea) using a fixed + * size buffer. + * + * The usual choices. + */ + char *buf=NULL; + int c=0, size=0; + + if (!phrase) + return NULL; + + while (*phrase) { + NEXTWORD(phrase); + if (*phrase) { + /* buffer too small */ + if (c>=size-1) { + size+=ALLOCSIZE; + if (!(buf=realloc(buf, size))) + return NULL; + } + *(buf+c++)=toupper(*phrase++); + SKIPWORD(phrase); + } + } + + /* at least one character */ + if (c) + *(buf+c)=0; + return buf; +} + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + char *p; + + for (; arg +#include +#include "armstrong_numbers.h" + +static inline int power(int n, int p) { + int res=n; + + /* useless here + * if (p==0) + * return 1; + */ + while (--p) + res*=n; + return res; +} + +bool is_armstrong_number(int candidate) +{ + int p=1, r=0, tmp=candidate; + + while (tmp/=10) + p++; + + for (tmp=candidate; tmp; tmp /=10) + r+=power(tmp%10, p); + return r==candidate; +} + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1, n; + + for (; arg + +bool is_armstrong_number(int candidate); + +#endif diff --git a/c/darts/GNUmakefile b/c/darts/GNUmakefile new file mode 100644 index 0000000..9f4171a --- /dev/null +++ b/c/darts/GNUmakefile @@ -0,0 +1,34 @@ +# the original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means we need to edit 'makefile' for different builds (DEBUG, etc...), +# which is not practical at all. +# +# To use this makefile: +# "make": build with all predefined tests +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (make test, etc...) + +.PHONY: default all unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/darts/README.md b/c/darts/README.md new file mode 100644 index 0000000..be1f381 --- /dev/null +++ b/c/darts/README.md @@ -0,0 +1,55 @@ +# Darts + +Write a function that returns the earned points in a single toss of a Darts game. + +[Darts](https://en.wikipedia.org/wiki/Darts) is a game where players +throw darts to a [target](https://en.wikipedia.org/wiki/Darts#/media/File:Darts_in_a_dartboard.jpg). + +In our particular instance of the game, the target rewards with 4 different amounts of points, depending on where the dart lands: + +* If the dart lands outside the target, player earns no points (0 points). +* If the dart lands in the outer circle of the target, player earns 1 point. +* If the dart lands in the middle circle of the target, player earns 5 points. +* If the dart lands in the inner circle of the target, player earns 10 points. + +The outer circle has a radius of 10 units (This is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1. Of course, they are all centered to the same point (That is, the circles are [concentric](http://mathworld.wolfram.com/ConcentricCircles.html)) defined by the coordinates (0, 0). + +Write a function that given a point in the target (defined by its `real` cartesian coordinates `x` and `y`), returns the correct amount earned by a dart landing in that point. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Inspired by an exercise created by a professor Della Paolera in Argentina + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/darts/makefile b/c/darts/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/darts/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/darts/src/darts.c b/c/darts/src/darts.c new file mode 100644 index 0000000..ed305d7 --- /dev/null +++ b/c/darts/src/darts.c @@ -0,0 +1,49 @@ +#include "darts.h" + +score_t scores[] ={ + { 1.0F, 10 }, + { 25.0F, 5 }, + { 100.0F, 1 }, + { -1.0F, 0 } +}; + +/* Below function is basically incorrect for general case. + * However, it should mostly work here, as we compare relatively small numbers. + * see below for better alternatives : + * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + */ +static inline bool float_almost_equal(float x, float y) +{ + register float f=x-y; + return f-MIN_FLOAT_EQUAL? true: false; +} + +unsigned score(coordinate_t c) +{ + float x=c.x, y=c.y, radius=x*x+y*y; + int i; + + for (i=0; scores[i].score; ++i) { + if (float_almost_equal(radius, scores[i].radius) || radius < scores[i].radius) + break; + } + return scores[i].score; +} + +#ifdef UNIT_TEST +#include +#include + +int main(int ac, char **av) +{ + int arg=1; + float x, y; + + for (; arg +#include + +typedef struct { + float x, y; +} coordinate_t; + +typedef struct { + float radius; + int score; +} score_t; + +// to allow float comparisons we consider 2 floats are equal if +// their difference is below this value. +// Use: avoid the '<' & '>' which may be wrong. +#define MIN_FLOAT_EQUAL FLT_EPSILON + +extern unsigned score(coordinate_t); + +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/difference-of-squares/GNUmakefile b/c/difference-of-squares/GNUmakefile new file mode 100644 index 0000000..0150cfa --- /dev/null +++ b/c/difference-of-squares/GNUmakefile @@ -0,0 +1,41 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/difference-of-squares/README.md b/c/difference-of-squares/README.md new file mode 100644 index 0000000..cfb7602 --- /dev/null +++ b/c/difference-of-squares/README.md @@ -0,0 +1,55 @@ +# Difference Of Squares + +Find the difference between the square of the sum and the sum of the squares of the first N natural numbers. + +The square of the sum of the first ten natural numbers is +(1 + 2 + ... + 10)² = 55² = 3025. + +The sum of the squares of the first ten natural numbers is +1² + 2² + ... + 10² = 385. + +Hence the difference between the square of the sum of the first +ten natural numbers and the sum of the squares of the first ten +natural numbers is 3025 - 385 = 2640. + +You are not expected to discover an efficient solution to this yourself from +first principles; research is allowed, indeed, encouraged. Finding the best +algorithm for the problem is a key skill in software engineering. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Problem 6 at Project Euler [http://projecteuler.net/problem=6](http://projecteuler.net/problem=6) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/difference-of-squares/makefile b/c/difference-of-squares/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/difference-of-squares/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/difference-of-squares/src/difference_of_squares.c b/c/difference-of-squares/src/difference_of_squares.c new file mode 100644 index 0000000..e522aca --- /dev/null +++ b/c/difference-of-squares/src/difference_of_squares.c @@ -0,0 +1,52 @@ +#include "difference_of_squares.h" + +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +unsigned int sum_of_squares(unsigned int number) +{ + /* the sum of 1st n integers squares is: + * S = 1² + 2² + 3² ..... + (n-1)² + n² + * = [ n * (n+1) * (2n+1) ] / 6 + * some visual explanations on: + * http://www.takayaiwamoto.com/Sums_and_Series/sumsqr_1.html + */ + return number * (number+1) * (2*number + 1) / 6; +} + +unsigned int square_of_sum(unsigned int number) +{ + register int res; + /* The sum of n 1st integers is: + * S = 1 + 2 + 3 ... + (n-1) + n + * = [ n * (n+1) ] / 2 + * demonstration is trivial for this one. + */ + res=number * (number+1) / 2; + return res*res; +} + +unsigned int difference_of_squares(unsigned int number) +{ + return square_of_sum(number) - sum_of_squares(number); +} + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + int i; + + for (; arg +#include +#endif + +uint64_t square(uint8_t index) +{ + return index>0 && index <65 ? (uint64_t)1<<(index-1): 0; +} +uint64_t total(void) +{ + /* Geometric series sum formula for 64 terms, ratio 2 and first term 1: + * S = 2⁰ + 2¹ + 2² + ... + 2⁶³ + * = (1-2⁶⁴) / (1-2) + * = 2⁶⁴ - 1 + * = 0 - 1 for uint64_t + * We should write (2<<64)-1, but gcc won't compile, due to: + * -Werror=shift-count-overflow + * Then we could avoid it by using: + * #pragma GCC diagnostic ignored "-Wshift-count-overflow" + * but i think it is out of scope for this exercise (we should also take + * care of other compilers). + */ + return (uint64_t) -1; +} + + + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + uint64_t i; + + for (; arg + +extern uint64_t square(uint8_t index); +extern uint64_t total(void); + +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/hamming/GNUmakefile b/c/hamming/GNUmakefile new file mode 100644 index 0000000..0150cfa --- /dev/null +++ b/c/hamming/GNUmakefile @@ -0,0 +1,41 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/hamming/README.md b/c/hamming/README.md new file mode 100644 index 0000000..4660668 --- /dev/null +++ b/c/hamming/README.md @@ -0,0 +1,62 @@ +# Hamming + +Calculate the Hamming Distance between two DNA strands. + +Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! + +When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance". + +We read DNA using the letters C,A,G and T. Two strands might look like this: + + GAGCCTACTAACGGGAT + CATCGTAATGACGGCCT + ^ ^ ^ ^ ^ ^^ + +They have 7 differences, and therefore the Hamming Distance is 7. + +The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) + +# Implementation notes + +The Hamming distance is only defined for sequences of equal length, so +an attempt to calculate it between sequences of different lengths should +not work. The general handling of this situation (e.g., raising an +exception vs returning a special value) may differ between languages. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +The Calculating Point Mutations problem at Rosalind [http://rosalind.info/problems/hamm/](http://rosalind.info/problems/hamm/) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/hamming/makefile b/c/hamming/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/hamming/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/hamming/src/hamming.c b/c/hamming/src/hamming.c new file mode 100644 index 0000000..144625a --- /dev/null +++ b/c/hamming/src/hamming.c @@ -0,0 +1,41 @@ +#include "hamming.h" + +/* Note: For explanation on section below, see 'GNUfilename' included in + * link below : + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +/* test does not include invalid input, but it should, as the subject is + * about DNA sequence, not ASCII chars sequence :-) + * exercism test needs only: + * #define V(p) (*p) + */ +#define V(p) (*(p)=='A' || *(p)=='C' || *(p)=='G' || *(p)=='T') + +int compute(const char *lhs, const char *rhs) +{ + int res=0; + const char *l=lhs, *r=rhs; + + if (!l || !r) + return -1; + for (; V(l) && V(r); ++l, ++r) { + if (*l != *r) + res++; + } + return *r || *l? -1: res; +} + + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + if (ac==3) { + printf("compute(%s, %s)=%d\n", *(av+1), *(av+2), compute(*(av+1), *(av+2))); + } +} +#endif diff --git a/c/hamming/src/hamming.h b/c/hamming/src/hamming.h new file mode 100644 index 0000000..8e31f0b --- /dev/null +++ b/c/hamming/src/hamming.h @@ -0,0 +1,15 @@ +#ifndef HAMMING_H +#define HAMMING_H + +int compute(const char *lhs, const char *rhs); + +/* Note: For explanation on section below, see 'GNUfilename' included in + * link below : + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/hello-world/README.md b/c/hello-world/README.md new file mode 100644 index 0000000..3444c95 --- /dev/null +++ b/c/hello-world/README.md @@ -0,0 +1,53 @@ +# Hello World + +The classical introductory exercise. Just say "Hello, World!". + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is +the traditional first program for beginning programming in a new language +or environment. + +The objectives are simple: + +- Write a function that returns the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/hello-world/makefile b/c/hello-world/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/hello-world/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/hello-world/src/hello_world.c b/c/hello-world/src/hello_world.c new file mode 100644 index 0000000..af18e79 --- /dev/null +++ b/c/hello-world/src/hello_world.c @@ -0,0 +1,7 @@ +#include +#include "hello_world.h" + +const char *hello(void) +{ + return "Hello, World!"; +} diff --git a/c/hello-world/src/hello_world.h b/c/hello-world/src/hello_world.h new file mode 100644 index 0000000..ae1cd07 --- /dev/null +++ b/c/hello-world/src/hello_world.h @@ -0,0 +1,14 @@ +// This is called an include guard, which ensures that the header is only +// included once. You could alternatively use '#pragma once'. See +// https://en.wikipedia.org/wiki/Include_guard. +#ifndef HELLO_WORLD_H +#define HELLO_WORLD_H + +// Declare the 'hello()' function, which takes no arguments and returns a +// 'const char *', i.e. a pointer to a character (in this case the first +// character in a string). The function itself is defined in the hello_world.c +// source file. Ths function is called by the test case(s) in the test source +// file test/test_hello_world.c. +const char *hello(void); + +#endif diff --git a/c/isogram/GNUmakefile b/c/isogram/GNUmakefile new file mode 100644 index 0000000..7119656 --- /dev/null +++ b/c/isogram/GNUmakefile @@ -0,0 +1,39 @@ +# the original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means we need to edit 'makefile' for different builds (DEBUG, etc...), +# which is not practical at all. Also, it does not allow to run all tests without +# editing the test source code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (make test, etc...) + +.PHONY: default all mem unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/isogram/README.md b/c/isogram/README.md new file mode 100644 index 0000000..2469248 --- /dev/null +++ b/c/isogram/README.md @@ -0,0 +1,52 @@ +# Isogram + +Determine if a word or phrase is an isogram. + +An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times. + +Examples of isograms: + +- lumberjacks +- background +- downstream +- six-year-old + +The word *isograms*, however, is not an isogram, because the s repeats. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Isogram](https://en.wikipedia.org/wiki/Isogram) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/isogram/makefile b/c/isogram/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/isogram/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/isogram/src/isogram.c b/c/isogram/src/isogram.c new file mode 100644 index 0000000..df2dd6f --- /dev/null +++ b/c/isogram/src/isogram.c @@ -0,0 +1,34 @@ +#include "isogram.h" +#include + +#define POS(c) (tolower(c)-'a') + +/* This does not work outside English world */ +bool is_isogram(const char phrase[]) +{ + //int map['z'-'a'+1]={0}; + int map[1000]={0}; + const char *p=phrase; + + if (!p) + return false; + for (; *p; ++p) { + if (*p==' ' || *p=='-') + continue; + if (!isalpha((unsigned char)*p) || (++map[POS((unsigned char)*p)])>1) + return false; + } + return true; +} + +#ifdef UNIT_TEST +#include +int main(int ac, char **av) +{ + int arg=1; + + for (; arg + +bool is_isogram(const char phrase[]); + +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/resistor-color-duo/GNUmakefile b/c/resistor-color-duo/GNUmakefile new file mode 100644 index 0000000..0150cfa --- /dev/null +++ b/c/resistor-color-duo/GNUmakefile @@ -0,0 +1,41 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/resistor-color-duo/README.md b/c/resistor-color-duo/README.md new file mode 100644 index 0000000..afa9303 --- /dev/null +++ b/c/resistor-color-duo/README.md @@ -0,0 +1,72 @@ +# Resistor Color Duo + +If you want to build something using a Raspberry Pi, you'll probably use _resistors_. +For this exercise, you need to know two things about them: + +* Each resistor has a resistance value. +* Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read. + +To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values. +Each band has a position and a numeric value. + +The first 2 bands of a resistor have a simple encoding scheme: each color maps to a single number. +For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15. + +In this exercise you are going to create a helpful program so that you don't have to remember the values of the bands. +The program will take color names as input and output a two digit number, even if the input is more than two colors! + +The band colors are encoded as follows: + +- Black: 0 +- Brown: 1 +- Red: 2 +- Orange: 3 +- Yellow: 4 +- Green: 5 +- Blue: 6 +- Violet: 7 +- Grey: 8 +- White: 9 + +From the example above: +brown-green should return 15 +brown-green-violet should return 15 too, ignoring the third color. + + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Maud de Vries, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/1464](https://github.com/exercism/problem-specifications/issues/1464) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/resistor-color-duo/makefile b/c/resistor-color-duo/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/resistor-color-duo/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/resistor-color-duo/src/resistor_color_duo.c b/c/resistor-color-duo/src/resistor_color_duo.c new file mode 100644 index 0000000..7bed2be --- /dev/null +++ b/c/resistor-color-duo/src/resistor_color_duo.c @@ -0,0 +1,26 @@ +#include "resistor_color_duo.h" +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +resistor_band_t color_code(resistor_band_t *colors) +{ + resistor_band_t c1=*colors, c2=*(colors+1); + + return c1>=BLACK && c1<=WHITE && c2>=BLACK && c2<=WHITE? c1*10+c2: ERROR; +} + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + resistor_band_t i[2]; + + for (; arg "... ohms" + +So an input of `"orange", "orange", "black"` should return: + +> "33 ohms" + +When we get more than a thousand ohms, we say "kiloohms". That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams. +So an input of `"orange", "orange", "orange"` should return: + +> "33 kiloohms" + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Maud de Vries, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/1549](https://github.com/exercism/problem-specifications/issues/1549) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/resistor-color-trio/makefile b/c/resistor-color-trio/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/resistor-color-trio/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/resistor-color-trio/src/resistor_color_trio.c b/c/resistor-color-trio/src/resistor_color_trio.c new file mode 100644 index 0000000..4ed314c --- /dev/null +++ b/c/resistor-color-trio/src/resistor_color_trio.c @@ -0,0 +1,62 @@ +#include "resistor_color_trio.h" + +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +static const unsigned p10[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, + 1e5, 1e6, 1e7, 1e8, 1e9, +}; +static const unsigned unit[] = { + p10[9], p10[6], p10[3], p10[0] +}; + +#define S(a) ((int)(sizeof(a)/sizeof(*a))) +#define RB_OK(b) ((b)>=BLACK && (b)<=WHITE) + +static resistor_value_t ui2val(unsigned value) +{ + resistor_value_t res={0, OHMS}; + int i; + + for (i=0; i=BLACK && color<=WHITE? color: ERROR; +} + +resistor_band_t *colors() +{ + return mapping; +} + +#ifdef UNIT_TEST +#include +#include + +int main(int ac, char **av) +{ + int arg=1, n; + + for (; arg +#include +#endif + +/* Newton's method: + * https://en.wikipedia.org/wiki/Newton%27s_method#Square_root + * return the largest integer equal or less than i square root. + */ +unsigned square_root(unsigned i) +{ + unsigned sq, sq2; + + if (i<=1) /* 0 and 1 */ + return i; + + sq = i/2; /* we take i/2 as initial seed */ + sq2 = ((i/sq) + sq) / 2; /* first iteration */ + + while (sq2 != sq) { + sq = sq2; + sq2 = ((i/sq) + sq) /2; /* next iteration */ + } + + return sq; +} + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + unsigned i; + + for (; arg + +extern unsigned square_root(unsigned); + +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/templates/GNUmakefile b/c/templates/GNUmakefile new file mode 100644 index 0000000..ec085d0 --- /dev/null +++ b/c/templates/GNUmakefile @@ -0,0 +1,51 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make debugall": build with all predefined tests and debug code +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std debugtest + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +debugall: CFLAGS+=-DDEBUG +debugall: all + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o test.out diff --git a/c/templates/ex.c b/c/templates/ex.c new file mode 100644 index 0000000..477f76e --- /dev/null +++ b/c/templates/ex.c @@ -0,0 +1,23 @@ +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + + + +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + resistor_band_t i[2]; + + for (; arg +#include + +#include "hash.h" + +#define HASH_SIZE 50 + +//static hash_t *hash_table[HASH_SIZE]; +static h_entry_t *pool_free, *alloc_entries; +static int n_entries; + +void h_init(hash_t *hash) +{ + memset(hash->entries, 0, sizeof(h_entry_t)*hash->size); +} + +hash_t *h_create(int size) +{ + hash_t *hash; + + if ( !(hash=calloc(sizeof(hash_t) + size*(sizeof (h_entry_t *)), 1)) ) + return NULL; + hash->size=size; + return hash; +} + +void h_destroy(hash_t *h) +{ + h_entry_t *tmp; + + for (int i=0; isize; ++i) { + while (h->entries[i]) { + tmp=h->entries[i]->next; + h_entry_free(h->entries[i]); + h->entries[i]=tmp; + } + } + free(h); +} + +/* Daniel J. Bernstein's djb2 hash function */ +unsigned long hash_djb2(const unsigned char *str, const int len) +{ + unsigned long hash = 5381; + + for (int i=0; inext=pool_free; + pool_free=e; +} + +h_entry_t *h_entry_find(hash_t *h, const unsigned char *s, const int l) +{ + unsigned long hash=hash_djb2(s, l); + h_entry_t *entry; + int found=0; + +# ifdef DEBUG + printf("h_entry_find([%.*s]): hash=%#lx (%lu) - ", l, s, hash, hash%h->size); +# endif + hash%=h->size; + for (entry=h->entries[hash]; entry; entry=entry->next) { + if (l == entry->key_len && !memcmp(entry->data, s, l)) { + found=1; + break; + } + } +# ifdef DEBUG + printf("ret=%p\n", found? (void *)entry: (void *)-1); +# endif + return found? entry: NULL; +} + +h_entry_t *h_entry_add(hash_t *h, const unsigned char *s, const int l, int *insert) +{ + unsigned long hash; + h_entry_t *entry; + + *insert=0; + + if (!pool_free) { + register int i=n_entries; + + n_entries+=ENTRY_ALLOC_SIZE; +# ifdef DEBUG + printf("get_hash: allocating %d new entries - total entries=%d\n", + ENTRY_ALLOC_SIZE, n_entries); +# endif + alloc_entries=reallocarray(alloc_entries, n_entries, sizeof(h_entry_t)); + + for (; inext=pool_free; + pool_free=alloc_entries+i; + } + } + if ((entry=h_entry_find(h, s, l))) + return entry; + + *insert=1; + + hash=hash_djb2(s, l)%h->size; + + /* get a free entry from pool */ + entry=pool_free; + pool_free=pool_free->next; + + /* set entry in hash */ + entry->next=h->entries[hash]; + h->entries[hash]=entry; + + entry->data=(unsigned char *)s; + entry->key_len=l; + + //assert(entry!=freenodes); +# ifdef DEBUG + printf("h_entry_add: %p\n", (void *)entry); +# endif + return entry; +} diff --git a/c/word-count/src/hash.h b/c/word-count/src/hash.h new file mode 100644 index 0000000..98ac2b0 --- /dev/null +++ b/c/word-count/src/hash.h @@ -0,0 +1,52 @@ +#ifndef _HASH_H +#define _HASH_H + +#include + +typedef struct h_entry { + void *data; + int key_len; + struct h_entry *prev, *next; +} h_entry_t; + +typedef struct { + int size; + h_entry_t *entries[]; +} hash_t; + +/* a few predefined hash sizes */ +typedef struct { + int size; + h_entry_t *entries[16]; +} hash_16_t; + +typedef struct { + int size; + h_entry_t *entries[128]; +} hash_128_t; + +typedef struct { + int size; + h_entry_t *entries[1024]; +} hash_1024_t; + +#define ENTRY_ALLOC_SIZE 20 + +/* hash map functions */ +hash_t *h_create(int size); +void h_init(hash_t *); +void h_destroy(hash_t *); + +/* static free_nodes */ +void set_pool_free_static(h_entry_t *p); + +/* hash entries functions */ +h_entry_t *h_entry_add(hash_t *, const unsigned char *, const int, int *); +h_entry_t *h_entry_find(hash_t *, const unsigned char *, const int); +void h_entry_free(h_entry_t *); + + +/* hash function */ +unsigned long hash_djb2(const unsigned char *str, const int len); + +#endif diff --git a/c/word-count/src/word_count.c b/c/word-count/src/word_count.c new file mode 100644 index 0000000..b2a0f06 --- /dev/null +++ b/c/word-count/src/word_count.c @@ -0,0 +1,162 @@ +#include +#include +#include + +#include "word_count.h" +#include "hash.h" + +/* See GNUmakefile in following link for explanation + * https://exercism.io/my/solutions/103b2f7d92db42309c1988030f5202c7 + */ +#if defined UNIT_TEST || defined DEBUG +#include +#endif + +/* Some cases are not well explained. So I will consider words mixing + * alphabetic characters and digits as invalid, as well as multiple "'" + * inside an alphabetic word: + * The following will return INVALID_WORD: + * P2P + * 0xFF + * A'2 + * The following will return 2 numbers/words: + * 1'2 + * A''B + */ + +/* get next word in string + */ +static word_t next_word(const char **p) +{ + word_t res={NULL, 0}; + const char *q, *p1=*p; + static char tmp[1024]; + int pos=0; + +# ifdef DEBUG + printf("next_word(%s)\n", *p); +# endif + for (; *p1 && !isalpha(*p1) && !isdigit(*p1); ++p1) + ; + + if (*p1) { + q=p1; + /* alphabetic word */ + if (isalpha(*p1)) { + for (; *q &&(isalpha(*q) || *q=='\''); q++) { + if (*q=='\'' && *(q-1)=='\'') { /* two consecutive apostrophes */ + res.len=INVALID_WORD; + goto end; + } + tmp[pos++]=tolower(*q); + } + if (*(q-1) == '\'') + q--; + else if (isdigit(*q)) { /* digit in word */ + res.len=INVALID_WORD; + goto end; + } + } else { + for (; *q &&(isdigit(*q)); q++) + tmp[pos++]=tolower(*q); + if (isalpha(*q)) { /* alphabetic char in number */ + res.len=INVALID_WORD; + goto end; + } + + } + res.word=tmp; + res.len=q-p1; + *p=q; + } +end: +# ifdef DEBUG + printf("next_word: [%s], %d\n", res.word? res.word: "NULL", res.len); +# endif + return res; +} + +static int insert_word(word_count_word_t *words, word_t w, int pos) +{ +#ifdef DEBUG + printf("insert words(len=%d word=[%.*s])\n", w.len, w.len, w.word); +#endif + + memcpy(words[pos].text, w.word, w.len); + words[pos].text[w.len]=0; + words[pos].count=0; + return 1; +} + +int count_words(const char *sentence, word_count_word_t *words) +{ + word_t w; + int current=0, new, index; + hash_t *hash; + h_entry_t *e; +# ifdef DEBUG + const char *s=sentence; +# endif + + hash=h_create(16); +# ifdef DEBUG + printf("count_words([%s], %p)\n", sentence, (void *)words); +# endif + for (; *sentence;) { + w=next_word(&sentence); + if (!w.word) + break; + if (w.len > MAX_WORD_LENGTH) + return EXCESSIVE_LENGTH_WORD; + if (!(e=h_entry_find(hash, (const unsigned char *)w.word, w.len))) { + if (current==MAX_WORDS) + return EXCESSIVE_NUMBER_OF_WORDS; + insert_word(words, w, current); + e=h_entry_add(hash, (void *)&words[current], w.len, &new); + current++; + } + index=(word_count_word_t *)e->data-&words[0]; + words[index].count++; + //sentence=w.word+w.len; + //sentence+=w.len; +# ifdef DEBUG + printf("count_words: index=%d\n", index); + printf("offset=%d\n", (int)(sentence-s)); +# endif + } + h_destroy(hash); + return current; +} + +#ifdef UNIT_TEST +static word_count_word_t wtable[MAX_WORDS]; + +static void reset_wtable() +{ + memset(wtable, 0, sizeof(wtable)); +} +static void print_wtable(int n) +{ + for (int i=0; i