diff --git a/elixir/all-your-base/README.md b/elixir/all-your-base/README.md new file mode 100644 index 0000000..681c187 --- /dev/null +++ b/elixir/all-your-base/README.md @@ -0,0 +1,69 @@ +# All Your Base + +Convert a number, represented as a sequence of digits in one base, to any other base. + +Implement general base conversion. Given a number in base **a**, +represented as a sequence of digits, convert it to base **b**. + +## Note + +- Try to implement the conversion yourself. + Do not use something else to perform the conversion for you. + +## About [Positional Notation](https://en.wikipedia.org/wiki/Positional_notation) + +In positional notation, a number in base **b** can be understood as a linear +combination of powers of **b**. + +The number 42, *in base 10*, means: + +(4 * 10^1) + (2 * 10^0) + +The number 101010, *in base 2*, means: + +(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0) + +The number 1120, *in base 3*, means: + +(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0) + +I think you got the idea! + +*Yes. Those three numbers above are exactly the same. Congratulations!* + +## Running tests + +Execute the tests with: + +```bash +$ elixir all_your_base_test.exs +``` + +### Pending tests + +In the test suites, all but the first test have been skipped. + +Once you get a test passing, you can unskip the next one by +commenting out the relevant `@tag :pending` with a `#` symbol. + +For example: + +```elixir +# @tag :pending +test "shouting" do + assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" +end +``` + +Or, you can enable all the tests by commenting out the +`ExUnit.configure` line in the test suite. + +```elixir +# ExUnit.configure exclude: :pending, trace: true +``` + +For more detailed information about the Elixir track, please +see the [help page](http://exercism.io/languages/elixir). + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/elixir/all-your-base/all-your-base-test.exs b/elixir/all-your-base/all-your-base-test.exs new file mode 100644 index 0000000..c2afb12 --- /dev/null +++ b/elixir/all-your-base/all-your-base-test.exs @@ -0,0 +1,114 @@ +if !System.get_env("EXERCISM_TEST_EXAMPLES") do + Code.load_file("all-your-base.exs", __DIR__) +end + +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true) + +defmodule AllYourBaseTest do + use ExUnit.Case + + test "convert single bit one to decimal" do + assert AllYourBase.convert([1], 2, 10) == [1] + end + + @tag :pending + test "convert binary to single decimal" do + assert AllYourBase.convert([1, 0, 1], 2, 10) == [5] + end + + @tag :pending + test "convert single decimal to binary" do + assert AllYourBase.convert([5], 10, 2) == [1, 0, 1] + end + + @tag :pending + test "convert binary to multiple decimal" do + assert AllYourBase.convert([1, 0, 1, 0, 1, 0], 2, 10) == [4, 2] + end + + @tag :pending + test "convert decimal to binary" do + assert AllYourBase.convert([4, 2], 10, 2) == [1, 0, 1, 0, 1, 0] + end + + @tag :pending + test "convert trinary to hexadecimal" do + assert AllYourBase.convert([1, 1, 2, 0], 3, 16) == [2, 10] + end + + @tag :pending + test "convert hexadecimal to trinary" do + assert AllYourBase.convert([2, 10], 16, 3) == [1, 1, 2, 0] + end + + @tag :pending + test "convert 15-bit integer" do + assert AllYourBase.convert([3, 46, 60], 97, 73) == [6, 10, 45] + end + + @tag :pending + test "convert empty list" do + assert AllYourBase.convert([], 2, 10) == nil + end + + @tag :pending + test "convert single zero" do + assert AllYourBase.convert([0], 10, 2) == [0] + end + + @tag :pending + test "convert multiple zeros" do + assert AllYourBase.convert([0, 0, 0], 10, 2) == [0] + end + + @tag :pending + test "convert leading zeros" do + assert AllYourBase.convert([0, 6, 0], 7, 10) == [4, 2] + end + + @tag :pending + test "convert negative digit" do + assert AllYourBase.convert([1, -1, 1, 0, 1, 0], 2, 10) == nil + end + + @tag :pending + test "convert invalid positive digit" do + assert AllYourBase.convert([1, 2, 1, 0, 1, 0], 2, 10) == nil + end + + @tag :pending + test "convert first base is one" do + assert AllYourBase.convert([0], 1, 10) == nil + end + + @tag :pending + test "convert second base is one" do + assert AllYourBase.convert([1, 0, 1, 0, 1, 0], 2, 1) == nil + end + + @tag :pending + test "convert first base is zero" do + assert AllYourBase.convert([], 0, 10) == nil + end + + @tag :pending + test "convert second base is zero" do + assert AllYourBase.convert([7], 10, 0) == nil + end + + @tag :pending + test "convert first base is negative" do + assert AllYourBase.convert([1], -2, 10) == nil + end + + @tag :pending + test "convert second base is negative" do + assert AllYourBase.convert([1], 2, -7) == nil + end + + @tag :pending + test "convert both bases are negative" do + assert AllYourBase.convert([1], -2, -7) == nil + end +end diff --git a/elixir/all-your-base/all-your-base.exs b/elixir/all-your-base/all-your-base.exs new file mode 100644 index 0000000..02d6011 --- /dev/null +++ b/elixir/all-your-base/all-your-base.exs @@ -0,0 +1,17 @@ +defmodule AllYourBase do + @doc """ + Given a number in base a, represented as a sequence of digits, converts it to base b, + or returns nil if either of the bases are less than 2 + """ + + @spec convert(list, integer, integer) :: list + def convert(digits, base_a, base_b) do + cond do + base_a < 2 || base_b < 2 -> + nil + + true -> + "" + end + end +end diff --git a/elixir/nth-prime/README.md b/elixir/nth-prime/README.md new file mode 100644 index 0000000..72dc0c2 --- /dev/null +++ b/elixir/nth-prime/README.md @@ -0,0 +1,50 @@ +# Nth Prime + +Given a number n, determine what the nth prime is. + +By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that +the 6th prime is 13. + +If your language provides methods in the standard library to deal with prime +numbers, pretend they don't exist and implement them yourself. + +## Running tests + +Execute the tests with: + +```bash +$ elixir nth_prime_test.exs +``` + +### Pending tests + +In the test suites, all but the first test have been skipped. + +Once you get a test passing, you can unskip the next one by +commenting out the relevant `@tag :pending` with a `#` symbol. + +For example: + +```elixir +# @tag :pending +test "shouting" do + assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" +end +``` + +Or, you can enable all the tests by commenting out the +`ExUnit.configure` line in the test suite. + +```elixir +# ExUnit.configure exclude: :pending, trace: true +``` + +For more detailed information about the Elixir track, please +see the [help page](http://exercism.io/languages/elixir). + +## Source + +A variation on Problem 7 at Project Euler [http://projecteuler.net/problem=7](http://projecteuler.net/problem=7) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/elixir/nth-prime/nth_prime.exs b/elixir/nth-prime/nth_prime.exs new file mode 100644 index 0000000..999d40d --- /dev/null +++ b/elixir/nth-prime/nth_prime.exs @@ -0,0 +1,26 @@ +defmodule Prime do + @doc """ + Generates the nth prime. + """ + @spec nth(non_neg_integer) :: non_neg_integer + def nth(count) when count > 0 do + Stream.iterate(1, &(&1 + 1)) + |> Stream.filter(&prime?/1) + |> Enum.at(count - 1) + end + + defp prime?(n) do + case n do + _n when n <= 1 -> + false + + 2 -> + true + + _ -> + 2..round(:math.sqrt(n)) + |> Enum.filter(&(rem(n, &1) == 0)) + |> Enum.empty?() + end + end +end diff --git a/elixir/nth-prime/nth_prime_test.exs b/elixir/nth-prime/nth_prime_test.exs new file mode 100644 index 0000000..0012ae8 --- /dev/null +++ b/elixir/nth-prime/nth_prime_test.exs @@ -0,0 +1,35 @@ +if !System.get_env("EXERCISM_TEST_EXAMPLES") do + Code.load_file("nth_prime.exs", __DIR__) +end + +ExUnit.start() +ExUnit.configure(trace: true) + +defmodule NthPrimeTest do + use ExUnit.Case + + # @tag :pending + test "first prime" do + assert Prime.nth(1) == 2 + end + + @tag :pending + test "second prime" do + assert Prime.nth(2) == 3 + end + + @tag :pending + test "sixth prime" do + assert Prime.nth(6) == 13 + end + + @tag :pending + test "100th prime" do + assert Prime.nth(100) == 541 + end + + @tag :pending + test "weird case" do + catch_error(Prime.nth(0)) + end +end diff --git a/elixir/phone-number/phone_number.exs b/elixir/phone-number/phone_number.exs index 1ec6425..ec2d967 100644 --- a/elixir/phone-number/phone_number.exs +++ b/elixir/phone-number/phone_number.exs @@ -24,6 +24,17 @@ defmodule Phone do """ @spec number(String.t()) :: String.t() def number(raw) do + n = + Regex.scan(~r/\d/, raw) + |> Enum.join() + |> String.replace_prefix("1", "") + + if String.length(n) == 10 && not Regex.match?(~r/[01]/, String.at(n, 3)) && + not Regex.match?(~r/[01]/, String.at(n, 0)) && not Regex.match?(~r/[a-z]/i, raw) do + n + else + "0000000000" + end end @doc """ @@ -48,6 +59,7 @@ defmodule Phone do """ @spec area_code(String.t()) :: String.t() def area_code(raw) do + String.slice(number(raw), 0..2) end @doc """ @@ -72,5 +84,7 @@ defmodule Phone do """ @spec pretty(String.t()) :: String.t() def pretty(raw) do + n = number(raw) + "(#{String.slice(n, 0..2)}) #{String.slice(n, 3..5)}-#{String.slice(n, 6..9)}" end end diff --git a/elixir/phone-number/phone_number_test.exs b/elixir/phone-number/phone_number_test.exs index 8c0b3d0..bf9ee2b 100644 --- a/elixir/phone-number/phone_number_test.exs +++ b/elixir/phone-number/phone_number_test.exs @@ -3,7 +3,7 @@ if !System.get_env("EXERCISM_TEST_EXAMPLES") do end ExUnit.start() -ExUnit.configure(exclude: :pending, trace: true) +ExUnit.configure(trace: true) defmodule PhoneTest do use ExUnit.Case diff --git a/elixir/roman-numerals/README.md b/elixir/roman-numerals/README.md new file mode 100644 index 0000000..7f9f7e1 --- /dev/null +++ b/elixir/roman-numerals/README.md @@ -0,0 +1,84 @@ +# Roman Numerals + +Write a function to convert from normal numbers to Roman Numerals. + +The Romans were a clever bunch. They conquered most of Europe and ruled +it for hundreds of years. They invented concrete and straight roads and +even bikinis. One thing they never discovered though was the number +zero. This made writing and dating extensive histories of their exploits +slightly more challenging, but the system of numbers they came up with +is still in use today. For example the BBC uses Roman numerals to date +their programmes. + +The Romans wrote numbers using letters - I, V, X, L, C, D, M. (notice +these letters have lots of straight lines and are hence easy to hack +into stone tablets). + +```text + 1 => I +10 => X + 7 => VII +``` + +There is no need to be able to convert numbers larger than about 3000. +(The Romans themselves didn't tend to go any higher) + +Wikipedia says: Modern Roman numerals ... are written by expressing each +digit separately starting with the left most digit and skipping any +digit with a value of zero. + +To see this in practice, consider the example of 1990. + +In Roman numerals 1990 is MCMXC: + +1000=M +900=CM +90=XC + +2008 is written as MMVIII: + +2000=MM +8=VIII + +See also: http://www.novaroma.org/via_romana/numbers.html + +## Running tests + +Execute the tests with: + +```bash +$ elixir roman_numerals_test.exs +``` + +### Pending tests + +In the test suites, all but the first test have been skipped. + +Once you get a test passing, you can unskip the next one by +commenting out the relevant `@tag :pending` with a `#` symbol. + +For example: + +```elixir +# @tag :pending +test "shouting" do + assert Bob.hey("WATCH OUT!") == "Whoa, chill out!" +end +``` + +Or, you can enable all the tests by commenting out the +`ExUnit.configure` line in the test suite. + +```elixir +# ExUnit.configure exclude: :pending, trace: true +``` + +For more detailed information about the Elixir track, please +see the [help page](http://exercism.io/languages/elixir). + +## Source + +The Roman Numeral Kata [http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals](http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/elixir/roman-numerals/roman.exs b/elixir/roman-numerals/roman.exs new file mode 100644 index 0000000..5d94162 --- /dev/null +++ b/elixir/roman-numerals/roman.exs @@ -0,0 +1,35 @@ +defmodule Roman do + @romans [ + {"M", 1000}, + {"CM", 900}, + {"D", 500}, + {"CD", 400}, + {"C", 100}, + {"XC", 90}, + {"L", 50}, + {"XL", 40}, + {"X", 10}, + {"IX", 9}, + {"V", 5}, + {"IV", 4}, + {"I", 1} + ] + + @doc """ + Convert the number to a roman number. + """ + @spec numerals(pos_integer) :: String.t() + def numerals(number) do + numerals(@romans, number, "") + end + + defp numerals(_romans, 0, res), do: res + + defp numerals([{_letter, number} | t], c, res) when c < number do + numerals(t, c, res) + end + + defp numerals([{letter, number} | _t] = list, c, res) do + numerals(list, c - number, res <> letter) + end +end diff --git a/elixir/roman-numerals/roman_numerals_test.exs b/elixir/roman-numerals/roman_numerals_test.exs new file mode 100644 index 0000000..761c51f --- /dev/null +++ b/elixir/roman-numerals/roman_numerals_test.exs @@ -0,0 +1,100 @@ +if !System.get_env("EXERCISM_TEST_EXAMPLES") do + Code.load_file("roman.exs", __DIR__) +end + +ExUnit.start() +ExUnit.configure(trace: true) + +defmodule RomanTest do + use ExUnit.Case + + # @tag :pending + test "1" do + assert Roman.numerals(1) == "I" + end + + @tag :pending + test "2" do + assert Roman.numerals(2) == "II" + end + + @tag :pending + test "3" do + assert Roman.numerals(3) == "III" + end + + @tag :pending + test "4" do + assert Roman.numerals(4) == "IV" + end + + @tag :pending + test "5" do + assert Roman.numerals(5) == "V" + end + + @tag :pending + test "6" do + assert Roman.numerals(6) == "VI" + end + + @tag :pending + test "9" do + assert Roman.numerals(9) == "IX" + end + + @tag :pending + test "27" do + assert Roman.numerals(27) == "XXVII" + end + + @tag :pending + test "48" do + assert Roman.numerals(48) == "XLVIII" + end + + @tag :pending + test "59" do + assert Roman.numerals(59) == "LIX" + end + + @tag :pending + test "93" do + assert Roman.numerals(93) == "XCIII" + end + + @tag :pending + test "141" do + assert Roman.numerals(141) == "CXLI" + end + + @tag :pending + test "163" do + assert Roman.numerals(163) == "CLXIII" + end + + @tag :pending + test "402" do + assert Roman.numerals(402) == "CDII" + end + + @tag :pending + test "575" do + assert Roman.numerals(575) == "DLXXV" + end + + @tag :pending + test "911" do + assert Roman.numerals(911) == "CMXI" + end + + @tag :pending + test "1024" do + assert Roman.numerals(1024) == "MXXIV" + end + + @tag :pending + test "3000" do + assert Roman.numerals(3000) == "MMM" + end +end diff --git a/ruby/nth-prime/nth_prime.rb b/ruby/nth-prime/nth_prime.rb index 4915b10..ff864a8 100644 --- a/ruby/nth-prime/nth_prime.rb +++ b/ruby/nth-prime/nth_prime.rb @@ -3,16 +3,13 @@ class Prime def self.nth(n) raise ArgumentError.new('N must be positive') if n < 1 - primes ||= [2, 3] - curr = primes.last - while n > primes.length - curr += 2 - unless primes.any? { |p| curr % p == 0 } - # very naive and slow :( - primes.push(curr) - end - end - primes[n - 1] + sieve(50 * n)[n - 1] + end + + def self.sieve(n) + (2..Math.sqrt(n)).each_with_object([nil, nil, *2..n]) do |p, res| + (p*p).step(n, p) { |m| res[m] = nil } + end.compact end end diff --git a/ruby/sieve/sieve.rb b/ruby/sieve/sieve.rb index 1c70034..9e8227c 100644 --- a/ruby/sieve/sieve.rb +++ b/ruby/sieve/sieve.rb @@ -1,20 +1,18 @@ class Sieve - @num - def initialize(base) @num = base end def primes - primes = (0..@num).to_a - primes[0] = primes[1] = nil - primes.select{|p| p && p*p < @num}.each do |p| - (p*p).step(@num, p) {|m| primes[m] = nil} - end - primes.compact + (2..Math.sqrt(@num)).each_with_object([nil, nil, *2..@num]) do |p, res| + (p*p).step(@num, p) { |m| res[m] = nil } + end.compact end end module BookKeeping VERSION = 1 end + + +