diff --git a/config.json b/config.json index e910031b30..7e211a7840 100644 --- a/config.json +++ b/config.json @@ -2380,6 +2380,24 @@ ], "difficulty": 9 }, + { + "slug": "satellite", + "name": "Satellite", + "uuid": "a07e01e6-7cea-4db9-a514-7c95788e90a9", + "prerequisites": [ + "pattern-matching", + "tuples", + "cond", + "recursion", + "atoms", + "lists", + "enum" + ], + "practices": [ + "recursion" + ], + "difficulty": 6 + }, { "slug": "zebra-puzzle", "name": "Zebra Puzzle", diff --git a/exercises/practice/satellite/.docs/instructions.md b/exercises/practice/satellite/.docs/instructions.md new file mode 100644 index 0000000000..3a8077960f --- /dev/null +++ b/exercises/practice/satellite/.docs/instructions.md @@ -0,0 +1,29 @@ +# Description + +Imagine you need to transmit a binary tree to a satellite approaching Alpha +Centauri and you have limited bandwidth. Since the tree has no repeating +items it can be uniquely represented by its [pre-order and in-order traversals][wiki]. + +Write the software for the satellite to rebuild the tree from the traversals. + +A pre-order traversal reads the value of the current node before (hence "pre") +reading the left subtree in pre-order. Afterwards the right subtree is read +in pre-order. + +An in-order traversal reads the left subtree in-order then the current node and +finally the right subtree in-order. So in order from left to right. + +For example the pre-order traversal of this tree is [a, i, x, f, r]. +The in-order traversal of this tree is [i, a, f, x, r] + +``` + a + / \ +i x + / \ + f r +``` + +Note: the first item in the pre-order traversal is always the root. + +[wiki]: https://en.wikipedia.org/wiki/Tree_traversal diff --git a/exercises/practice/satellite/.formatter.exs b/exercises/practice/satellite/.formatter.exs new file mode 100644 index 0000000000..d2cda26edd --- /dev/null +++ b/exercises/practice/satellite/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/exercises/practice/satellite/.meta/config.json b/exercises/practice/satellite/.meta/config.json new file mode 100644 index 0000000000..3cd9373242 --- /dev/null +++ b/exercises/practice/satellite/.meta/config.json @@ -0,0 +1,20 @@ +{ + "blurb": "Rebuild binary trees from pre-order and in-order traversals.", + "authors": [ + "jiegillet" + ], + "contributors": [ + "angelikatyborska" + ], + "files": { + "solution": [ + "lib/satellite.ex" + ], + "test": [ + "test/satellite_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + } +} diff --git a/exercises/practice/satellite/.meta/example.ex b/exercises/practice/satellite/.meta/example.ex new file mode 100644 index 0000000000..778a30e40c --- /dev/null +++ b/exercises/practice/satellite/.meta/example.ex @@ -0,0 +1,41 @@ +defmodule Satellite do + @typedoc """ + A tree, which can be empty, or made from a left branch, a node and a right branch + """ + @type tree :: {} | {tree, any, tree} + + @doc """ + Build a tree from the elements given in a pre-order and in-order style + """ + @spec build_tree(preorder :: [any], inorder :: [any]) :: {:ok, tree} | {:error, String.t()} + + def build_tree(preorder, inorder) do + p_length = length(preorder) + i_length = length(inorder) + p_set = MapSet.new(preorder) + i_set = MapSet.new(inorder) + + cond do + p_length != i_length -> {:error, "traversals must have the same length"} + p_set != i_set -> {:error, "traversals must have the same elements"} + p_length != MapSet.size(p_set) -> {:error, "traversals must contain unique items"} + true -> {:ok, build_tree(preorder, inorder, :safe)} + end + end + + def build_tree([], [], :safe), do: {} + + def build_tree([root | preorder], inorder, :safe) do + {in_left, in_right, pre_left, pre_right} = split(root, inorder, preorder) + + {build_tree(pre_left, in_left, :safe), root, build_tree(pre_right, in_right, :safe)} + end + + defp split(root, [root | inorder], preorder), do: {[], inorder, [], preorder} + + defp split(root, [i | inorder], [p | preorder]) do + {in_left, in_right, pre_left, pre_right} = split(root, inorder, preorder) + + {[i | in_left], in_right, [p | pre_left], pre_right} + end +end diff --git a/exercises/practice/satellite/.meta/tests.toml b/exercises/practice/satellite/.meta/tests.toml new file mode 100644 index 0000000000..2d5ff6015d --- /dev/null +++ b/exercises/practice/satellite/.meta/tests.toml @@ -0,0 +1,22 @@ +# This is an auto-generated file. Regular comments will be removed when this +# file is regenerated. Regenerating will not touch any manually added keys, +# so comments can be added in a "comment" key. + +[8df3fa26-811a-4165-9286-ff9ac0850d19] +description = "Empty tree" + +[f945ccfc-05e3-47d7-825b-0270559d43ad] +description = "Tree with one item" + +[a0121d5f-37b0-48dd-9c64-cba4c4464135] +description = "Tree with many items" + +[6074041f-4891-4d81-a128-401050c2a3b0] +description = "Reject traversals of different length" + +[27916ce4-45f3-4d8b-8528-496fedc157ca] +description = "Reject inconsistent traversals of same length" + +[d86a3d72-76a9-43b5-9d3a-e64cb1216035] +description = "Reject traversals with repeated items" + diff --git a/exercises/practice/satellite/lib/satellite.ex b/exercises/practice/satellite/lib/satellite.ex new file mode 100644 index 0000000000..7768a85e9a --- /dev/null +++ b/exercises/practice/satellite/lib/satellite.ex @@ -0,0 +1,14 @@ +defmodule Satellite do + @typedoc """ + A tree, which can be empty, or made from a left branch, a node and a right branch + """ + @type tree :: {} | {tree, any, tree} + + @doc """ + Build a tree from the elements given in a pre-order and in-order style + """ + @spec build_tree(preorder :: [any], inorder :: [any]) :: {:ok, tree} | {:error, String.t()} + + def build_tree(preorder, inorder) do + end +end diff --git a/exercises/practice/satellite/mix.exs b/exercises/practice/satellite/mix.exs new file mode 100644 index 0000000000..370bdd443c --- /dev/null +++ b/exercises/practice/satellite/mix.exs @@ -0,0 +1,28 @@ +defmodule Satellite.MixProject do + use Mix.Project + + def project do + [ + app: :satellite, + version: "0.1.0", + # elixir: "~> 1.8", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/exercises/practice/satellite/test/satellite_test.exs b/exercises/practice/satellite/test/satellite_test.exs new file mode 100644 index 0000000000..f4b393a84a --- /dev/null +++ b/exercises/practice/satellite/test/satellite_test.exs @@ -0,0 +1,51 @@ +defmodule SatelliteTest do + use ExUnit.Case + + # @tag :pending + test "Empty tree" do + preorder = [] + inorder = [] + tree = {} + assert Satellite.build_tree(preorder, inorder) == {:ok, tree} + end + + @tag :pending + test "Tree with one item" do + preorder = [:a] + inorder = [:a] + tree = {{}, :a, {}} + assert Satellite.build_tree(preorder, inorder) == {:ok, tree} + end + + @tag :pending + test "Tree with many items" do + preorder = ~w(a i x f r)a + inorder = ~w(i a f x r)a + tree = {{{}, :i, {}}, :a, {{{}, :f, {}}, :x, {{}, :r, {}}}} + assert Satellite.build_tree(preorder, inorder) == {:ok, tree} + end + + @tag :pending + test "Reject traversals of different length" do + preorder = [:a, :b] + inorder = [:b, :a, :r] + error = {:error, "traversals must have the same length"} + assert Satellite.build_tree(preorder, inorder) == error + end + + @tag :pending + test "Reject inconsistent traversals of same length" do + preorder = [:x, :y, :z] + inorder = [:a, :b, :c] + error = {:error, "traversals must have the same elements"} + assert Satellite.build_tree(preorder, inorder) == error + end + + @tag :pending + test "Reject traversals with repeated items" do + preorder = [:a, :b, :a] + inorder = [:b, :a, :a] + error = {:error, "traversals must contain unique items"} + assert Satellite.build_tree(preorder, inorder) == error + end +end diff --git a/exercises/practice/satellite/test/test_helper.exs b/exercises/practice/satellite/test/test_helper.exs new file mode 100644 index 0000000000..35fc5bff82 --- /dev/null +++ b/exercises/practice/satellite/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true)